PAI Knowledge OS — Personal AI Infrastructure with federated memory and project management
AI Security Review
scanned 3d ago · by lpm-firewall-ai
The source confirms a user-enabled agent-control capability that changes Claude Code subagent behavior, warranting a warning. The setup discloses the effect and the manifest has no npm install lifecycle hook, so the evidence does not support a malware block.
The user enables workers in setup or runs the worker installation command.
Impact
Claude Code subagent behavior changes across sessions while the hook remains registered.
Mechanism
The CLI adds a global Claude Code hook. The hook denies subagent calls by default and allows them when its bypass environment variable is set.
AI rationale
The source confirms a user-enabled agent-control capability that changes Claude Code subagent behavior, warranting a warning. The setup discloses the effect and the manifest has no npm install lifecycle hook, so the evidence does not support a malware block.
AI called this Suspicious at 84.0% confidence as Dangerous Capability with medium false-positive risk.
Evidence for warning
The manifest exposes CLI entrypoints and has only a `prepublishOnly` lifecycle script, so the worker hook is not installed by an npm install hook.
The setup wizard explains that worker mode denies and reroutes Agent calls, asks whether to turn it on, and skips installation if the user declines.
The explicit worker install command registers the hook; its gate denies subagent calls by default, with an environment variable bypass.
The reviewed runtime locations perform routine text, model, worker, project, and search operations; they do not establish credential transfer or an unrequested account change.
The daemon account-action location saves a memory-search result locally.
Evidence against
The worker hook changes Claude Code behavior across sessions once enabled, so residual agent-control risk remains despite the disclosed, user-triggered setup.
The package has no `preinstall`, `install`, or `postinstall` hook.
1501* iTerm's split command parameter execs without a shell (proven live: panes
L1502: * survive `sleep 30` and a single-quoted `/bin/sh -c`, die instantly on
L1503: * `export …; …` or a redirect), so the command must be one exec-able line.
import { r as isLoopbackProvider } from "./run-env-DBJ8ml-Z.mjs";
L10: import { $ as waitingLine, D as bashExitCode, E as WAIT_EXIT_STILL_RUNNING, G as statusMarker, I as renderReport, J as detachedLine, K as statusQueue, O as isWaitPollCommand, P as ...
L11: import { E as probeLoopback, O as resolveLocalProvider, S as LOCAL_MECHANISM_LINE, k as resolveRunnerProvider } from "./budget-CuL0Q5aB.mjs";
...
L23: import { basename, dirname, join, relative, resolve } from "node:path";
L24: import { execFileSync, spawn } from "node:child_process";
L25: import { Document, parse } from "yaml";
...
L78: function agentsDir() {
L79: return join(homedir(), ".claude", "agents");
L80: }
...
L499: /**
L500: * `HH:MM:SS` at an offset east of UTC in minutes (Date.getTimezoneOffset()
L501: * negated), fro
•Manifest-trigger-reachable source links an external AI-agent control path to a behavior-bearing write operation.
dir(), "pai-sk[redacted]");
if (!existssync(templatepath)) {
console.log(c.warn("skill template not found: " + templatepath));
console.log(c.dim(" skipping pai skill installation."));
return false;
}
const skilldir = join(homedir(), ".claude", "skills", "pai");
const skillfile = join(skilldir, "skill.md");
if (existssync(skillfile)) {
const content = readfilesync(skillfile, "utf-8");
const isgenerated = content.includes("generated by pai setup");
if (isgenerated) console.log(c.dim(" found existing pai-generated skill.md."));
else {
console.log(c.yellow(" found existing skill.md (not pai-generated)."));
console.log(c.dim(" a backup will be created bef
High
Trigger Reachable External Ai Agent Control Surface Mutation
Manifest-trigger-reachable source writes behavior-bearing configuration into a user or project AI-agent control surface.
3Cross-file remote execution chain: dist/cli/index.mjs spawns dist/daemon-mcp/index.mjs; helper contains network access plus dynamic code execution.
L3: import { a as slugify$1, i as parseSessionFilename } from "../migrate-B1t833CE.mjs";
L4: import { $ as workersYamlPath, $t as readRunLock, A as nativeAnthropicProvider, An as effectiveCap, At as requestPlanStop, B as readWorkersSection, Bt as PlanTracker, Cn as buildSl...
L5: import { a as schedulerLogPath, c as migratePaiFile, d as resolvePaiFile, i as paiSocketPath, l as paiHomeDir, n as daemonLogPath, o as PaiFileMigrationError, s as migratePaiDir, u...
...
L48: import chalk from "chalk";
L49: import { execFile, execFileSync, execSync, spawn, spawnSync } from "node:child_process";
L50: import { parseDocument } from "yaml";
L51: import {
High
Cross File Remote Execution Context
Source spawns a local helper that also contains network and dynamic execution context; review data flow before blocking.
3Trigger-reachable persistence chain: manifest.bin -> dist/cli/index.mjs
L3: import { a as slugify$1, i as parseSessionFilename } from "../migrate-B1t833CE.mjs";
L4: import { $ as workersYamlPath, $t as readRunLock, A as nativeAnthropicProvider, An as effectiveCap, At as requestPlanStop, B as readWorkersSection, Bt as PlanTracker, Cn as buildSl...
L5: import { a as schedulerLogPath, c as migratePaiFile, d as resolvePaiFile, i as paiSocketPath, l as paiHomeDir, n as daemonLogPath, o as PaiFileMigrationError, s as migratePaiDir, u...
...
L48: import chalk from "chalk";
L49: import { execFile, execFileSync, execSync, spawn, spawnSync } from "node:child_process";
L50: import { parseDocument } from "yaml";
L51: import { createHash, randomBytes, randomUUID } from "node:crypto";
L52: import http f
High
Trigger Reachable Persistence
A manifest entrypoint or package-local install chain reaches persistence behavior.
1501* iTerm's split command parameter execs without a shell (proven live: panes
L1502: * survive `sleep 30` and a single-quoted `/bin/sh -c`, die instantly on
L1503: * `export …; …` or a redirect), so the command must be one exec-able line.
9import { r as isLoopbackProvider } from "./run-env-DBJ8ml-Z.mjs";
L10: import { $ as waitingLine, D as bashExitCode, E as WAIT_EXIT_STILL_RUNNING, G as statusMarker, I as renderReport, J as detachedLine, K as statusQueue, O as isWaitPollCommand, P as ...
L11: import { E as probeLoopback, O as resolveLocalProvider, S as LOCAL_MECHANISM_LINE, k as resolveRunnerProvider } from "./budget-CuL0Q5aB.mjs";
...
L23: import { basename, dirname, join, relative, resolve } from "node:path";
L24: import { execFileSync, spawn } from "node:child_process";
L25: import { Document, parse } from "yaml";
...
L78: function agentsDir() {
L79: return join(homedir(), ".claude", "agents");
L80: }
...
L499: /**
L500: * `HH:MM:SS` at an offset east of UTC in minutes (Date.getTimezoneOffset()
L501: * negated), fro
•Manifest-trigger-reachable source links an external AI-agent control path to a behavior-bearing write operation.
dir(), "pai-sk[redacted]");
if (!existssync(templatepath)) {
console.log(c.warn("skill template not found: " + templatepath));
console.log(c.dim(" skipping pai skill installation."));
return false;
}
const skilldir = join(homedir(), ".claude", "skills", "pai");
const skillfile = join(skilldir, "skill.md");
if (existssync(skillfile)) {
const content = readfilesync(skillfile, "utf-8");
const isgenerated = content.includes("generated by pai setup");
if (isgenerated) console.log(c.dim(" found existing pai-generated skill.md."));
else {
console.log(c.yellow(" found existing skill.md (not pai-generated)."));
console.log(c.dim(" a backup will be created bef
High
Trigger Reachable External Ai Agent Control Surface Mutation
Manifest-trigger-reachable source writes behavior-bearing configuration into a user or project AI-agent control surface.
3Cross-file remote execution chain: dist/cli/index.mjs spawns dist/daemon-mcp/index.mjs; helper contains network access plus dynamic code execution.
L3: import { a as slugify$1, i as parseSessionFilename } from "../migrate-B1t833CE.mjs";
L4: import { $ as workersYamlPath, $t as readRunLock, A as nativeAnthropicProvider, An as effectiveCap, At as requestPlanStop, B as readWorkersSection, Bt as PlanTracker, Cn as buildSl...
L5: import { a as schedulerLogPath, c as migratePaiFile, d as resolvePaiFile, i as paiSocketPath, l as paiHomeDir, n as daemonLogPath, o as PaiFileMigrationError, s as migratePaiDir, u...
...
L48: import chalk from "chalk";
L49: import { execFile, execFileSync, execSync, spawn, spawnSync } from "node:child_process";
L50: import { parseDocument } from "yaml";
L51: import {
High
Cross File Remote Execution Context
Source spawns a local helper that also contains network and dynamic execution context; review data flow before blocking.
3Trigger-reachable persistence chain: manifest.bin -> dist/cli/index.mjs
L3: import { a as slugify$1, i as parseSessionFilename } from "../migrate-B1t833CE.mjs";
L4: import { $ as workersYamlPath, $t as readRunLock, A as nativeAnthropicProvider, An as effectiveCap, At as requestPlanStop, B as readWorkersSection, Bt as PlanTracker, Cn as buildSl...
L5: import { a as schedulerLogPath, c as migratePaiFile, d as resolvePaiFile, i as paiSocketPath, l as paiHomeDir, n as daemonLogPath, o as PaiFileMigrationError, s as migratePaiDir, u...
...
L48: import chalk from "chalk";
L49: import { execFile, execFileSync, execSync, spawn, spawnSync } from "node:child_process";
L50: import { parseDocument } from "yaml";
L51: import { createHash, randomBytes, randomUUID } from "node:crypto";
L52: import http f
High
Trigger Reachable Persistence
A manifest entrypoint or package-local install chain reaches persistence behavior.