open-multi-agent-kit 0.90.6 → 0.90.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -1
- package/README.md +11 -10
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts +3 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +26 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/command-safety.d.ts.map +1 -1
- package/dist/core/command-safety.js +7 -0
- package/dist/core/command-safety.js.map +1 -1
- package/dist/core/context-budget-headroom.d.ts.map +1 -1
- package/dist/core/context-budget-headroom.js +5 -1
- package/dist/core/context-budget-headroom.js.map +1 -1
- package/dist/core/context-budget-v2-cache-provider.d.ts +4 -2
- package/dist/core/context-budget-v2-cache-provider.d.ts.map +1 -1
- package/dist/core/context-budget-v2-cache-provider.js +9 -5
- package/dist/core/context-budget-v2-cache-provider.js.map +1 -1
- package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
- package/dist/core/context-budget-v2-selection.js +9 -0
- package/dist/core/context-budget-v2-selection.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +4 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/loop-gates.d.ts.map +1 -1
- package/dist/core/loop-gates.js +25 -1
- package/dist/core/loop-gates.js.map +1 -1
- package/dist/core/pi-package-intake.d.ts +1 -0
- package/dist/core/pi-package-intake.d.ts.map +1 -1
- package/dist/core/pi-package-intake.js +7 -0
- package/dist/core/pi-package-intake.js.map +1 -1
- package/dist/core/reasoning-router-v4-normalize.d.ts +11 -0
- package/dist/core/reasoning-router-v4-normalize.d.ts.map +1 -0
- package/dist/core/reasoning-router-v4-normalize.js +224 -0
- package/dist/core/reasoning-router-v4-normalize.js.map +1 -0
- package/dist/core/reasoning-router-v4-weights.d.ts +13 -5
- package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4-weights.js +8 -4
- package/dist/core/reasoning-router-v4-weights.js.map +1 -1
- package/dist/core/reasoning-router-v4.d.ts +3 -3
- package/dist/core/reasoning-router-v4.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4.js +394 -58
- package/dist/core/reasoning-router-v4.js.map +1 -1
- package/dist/core/router-feedback-collector.d.ts +1 -1
- package/dist/core/router-feedback-collector.d.ts.map +1 -1
- package/dist/core/router-feedback-collector.js +9 -1
- package/dist/core/router-feedback-collector.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +15 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/guardrails/evidence-system.d.ts +8 -1
- package/dist/guardrails/evidence-system.d.ts.map +1 -1
- package/dist/guardrails/evidence-system.js +154 -16
- package/dist/guardrails/evidence-system.js.map +1 -1
- package/dist/modes/interactive/components/control-panel-layout.d.ts.map +1 -1
- package/dist/modes/interactive/components/control-panel-layout.js +1 -1
- package/dist/modes/interactive/components/control-panel-layout.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +1 -0
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +2 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/types/evidence.d.ts +4 -0
- package/dist/types/evidence.d.ts.map +1 -1
- package/dist/types/evidence.js.map +1 -1
- package/docs/adaptorch-preview.md +84 -0
- package/docs/compaction.md +6 -0
- package/docs/loadout-domains/frontend-ui.md +5 -1
- package/docs/providers.md +6 -0
- package/docs/settings.md +17 -2
- package/examples/extensions/correctness-wall/README.md +6 -6
- package/examples/extensions/correctness-wall/index.ts +37 -11
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/preset.ts +2 -2
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-safety.d.ts","sourceRoot":"","sources":["../../src/core/command-safety.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AA+nBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAEpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9D","sourcesContent":["// allow: SIZE_OK - legacy safety rules table; this change only preserves existing typed behavior.\nexport type CommandRisk = \"block\" | \"confirm\" | \"allow\";\n\nexport interface CommandVerdict {\n\trisk: CommandRisk;\n\trule: string;\n\treason: string;\n}\n\ninterface CommandPrefixResult {\n\ttokens: string[];\n\tprivilege: string | undefined;\n}\n\nconst RISK_RANK: Readonly<Record<CommandRisk, number>> = {\n\tallow: 0,\n\tconfirm: 1,\n\tblock: 2,\n};\n\nconst PRIVILEGE_COMMANDS = new Set([\"sudo\", \"su\", \"doas\", \"pkexec\"]);\nconst SUDO_OPTIONS_WITH_VALUE = new Set([\n\t\"-C\",\n\t\"--close-from\",\n\t\"-g\",\n\t\"--group\",\n\t\"-h\",\n\t\"--host\",\n\t\"-p\",\n\t\"--prompt\",\n\t\"-T\",\n\t\"--command-timeout\",\n\t\"-u\",\n\t\"--user\",\n]);\nconst GIT_GLOBAL_OPTIONS_WITH_VALUE = new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--namespace\"]);\n\n/** Maximum recursion depth when unwrapping shell wrappers (bash -c, eval, xargs, find -exec). */\nconst MAX_WRAP_DEPTH = 6;\n\n/** Executables that run an inline command string passed via a `-c`-style flag. */\nconst SHELL_WRAPPERS = new Set([\"bash\", \"sh\", \"zsh\", \"dash\", \"ksh\", \"ash\"]);\n\n/**\n * Script interpreters that execute an inline program string (via a -c/-e/-r\n * style flag). Running inline code can execute arbitrary shell commands, so a\n * matched destructive/exec keyword inside the program escalates the call to\n * confirm-tier. A plain script-file path stays allow-tier.\n */\nconst INTERPRETER_INLINE_FLAGS: Readonly<Record<string, readonly string[]>> = {\n\tpython: [\"-c\"],\n\tpython2: [\"-c\"],\n\tpython3: [\"-c\"],\n\tnode: [\"-e\", \"--eval\", \"-p\", \"--print\"],\n\tperl: [\"-e\", \"-E\"],\n\truby: [\"-e\", \"-E\"],\n\tphp: [\"-r\", \"--run\"],\n\tlua: [\"-e\"],\n\texpect: [\"-c\"],\n};\n\n/**\n * Patterns that indicate an inline program invokes a shell command or\n * performs a destructive filesystem operation. Matching any of these inside\n * a `-c`/`-e`/`-r` program string escalates the call to confirm-tier.\n */\nconst INLINE_DESTRUCTIVE_PATTERNS: readonly RegExp[] = [\n\t/\\bos\\.system\\s*\\(/,\n\t/\\bos\\.remove\\s*\\(/,\n\t/\\bos\\.unlink\\s*\\(/,\n\t/\\bshutil\\.rmtree\\s*\\(/,\n\t/\\bsubprocess\\.(?:call|run|check_call|check_output|Popen)\\s*\\(/,\n\t/\\bexec(?:Sync|File)?\\s*\\(/,\n\t/\\bspawn(?:Sync)?\\s*\\(/,\n\t/\\bchild_process\\b/,\n\t/\\brequire\\s*\\(\\s*['\"]child_process['\"]\\s*\\)/,\n\t/\\b(?:system|popen)\\s*\\(/,\n\t/`[^`]*\\brm\\s+-/m,\n\t/\\$\\(\\s*[^)]*\\brm\\s+-/,\n];\n\n/** xargs options that consume a following value token before the wrapped command begins. */\nconst XARGS_OPTIONS_WITH_VALUE = new Set([\n\t\"-I\",\n\t\"-i\",\n\t\"-n\",\n\t\"-L\",\n\t\"-P\",\n\t\"-d\",\n\t\"-E\",\n\t\"-s\",\n\t\"-a\",\n\t\"--max-args\",\n\t\"--max-procs\",\n\t\"--delimiter\",\n\t\"--arg-file\",\n\t\"--max-lines\",\n\t\"--replace\",\n]);\n\n/**\n * Credential / secret file patterns. A bash argv token matching one of these is\n * treated as a `confirm`-tier access: headless callers (LLM tool calls, RPC bash)\n * deny by default, while an interactive user can still approve reading their own\n * files. These complement the §0.1 freedom safety floor, which hard-denies the\n * same paths on the entry points it guards.\n */\nconst SECRET_FILE_STRICT_PATTERNS: readonly RegExp[] = [\n\t/(^|\\/)\\.env(\\.|$)/i,\n\t/(^|\\/)\\.npmrc$/i,\n\t/(^|\\/)\\.netrc$/i,\n\t/(^|\\/)\\.pgpass$/i,\n\t/(^|\\/)\\.aws\\/credentials$/i,\n\t/(^|\\/)credentials$/i,\n\t/(^|\\/)auth\\.json$/i,\n\t/(^|\\/)id_(rsa|dsa|ecdsa|ed25519)$/i,\n\t/\\.(pem|key|p12|pfx|keystore|jks)$/i,\n\t/(^|\\/)secrets?(\\.[^/]+)?$/i,\n];\n\n/** Benign sibling files that look secret-ish but never hold credentials. */\nconst SECRET_FILE_ALLOW_PATTERNS: readonly RegExp[] = [/(^|\\/)\\.env\\.(example|sample|template|dist|defaults?)$/i];\nconst HOME_VARIABLE_RM_TARGETS = new Set([\"$home\", \"$\" + \"{home}\", \"$home/\", \"$\" + \"{home}/\"]);\n\nfunction verdict(risk: CommandRisk, rule: string, reason: string): CommandVerdict {\n\treturn { risk, rule, reason };\n}\n\nfunction allowVerdict(): CommandVerdict {\n\treturn verdict(\"allow\", \"command.allow\", \"No destructive or protected command pattern matched.\");\n}\n\nfunction isEnvAssignment(token: string | undefined): boolean {\n\treturn token !== undefined && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(token);\n}\n\nfunction tokenizeShellSegment(segment: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (const character of segment.trim()) {\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tif (character === quote) {\n\t\t\t\tquote = undefined;\n\t\t\t} else {\n\t\t\t\tcurrent += character;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/.test(character)) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (escaped) current += \"\\\\\";\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n\nfunction splitShellCommands(command: string): string[] {\n\tconst segments: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (let index = 0; index < command.length; index += 1) {\n\t\tconst character = command[index];\n\t\tconst next = command[index + 1];\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tcurrent += character;\n\t\t\tif (character === quote) quote = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \";\" || character === \"|\" || character === \"\\n\" || character === \"\\r\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"&\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tif (next === \"&\") index += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (current.trim()) segments.push(current.trim());\n\treturn segments;\n}\n\nfunction skipEnvironmentPrefix(tokens: readonly string[], startIndex: number): number {\n\tlet index = startIndex;\n\twhile (isEnvAssignment(tokens[index])) index += 1;\n\tif (tokens[index]?.toLowerCase() !== \"env\") return index;\n\n\tindex += 1;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (isEnvAssignment(token)) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (token?.startsWith(\"-\")) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn index;\n}\n\nfunction optionConsumesValue(option: string, optionsWithValue: ReadonlySet<string>): boolean {\n\tif (option.includes(\"=\")) return false;\n\treturn optionsWithValue.has(option);\n}\n\nfunction skipPrivilegeOptions(tokens: readonly string[], startIndex: number, privilege: string): number {\n\tif (privilege !== \"sudo\" && privilege !== \"doas\") return startIndex;\n\n\tlet index = startIndex;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (!token?.startsWith(\"-\")) break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (optionConsumesValue(token, SUDO_OPTIONS_WITH_VALUE)) index += 1;\n\t}\n\treturn index;\n}\n\nfunction stripCommandPrefixes(tokens: readonly string[]): CommandPrefixResult {\n\tlet index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) {\n\t\treturn { tokens: tokens.slice(index), privilege: undefined };\n\t}\n\n\tindex += 1;\n\tindex = skipPrivilegeOptions(tokens, index, privilege ?? \"\");\n\tindex = skipEnvironmentPrefix(tokens, index);\n\treturn { tokens: tokens.slice(index), privilege };\n}\n\nfunction hasForkBomb(command: string): boolean {\n\treturn command.replace(/\\s/g, \"\").includes(\":(){:|:&};:\");\n}\n\nfunction normalizePathTarget(target: string): string {\n\treturn target.replace(/\\/+$/g, (match) => (target === match ? \"/\" : \"\")).replace(/\\/+/g, \"/\");\n}\n\nfunction classifyRmTarget(target: string): CommandVerdict | null {\n\tconst normalized = normalizePathTarget(target);\n\tif (normalized === \"/\" || normalized === \"/*\" || normalized === \"/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_root\", \"Recursive forced rm targets filesystem root.\");\n\t}\n\tif (normalized === \"~\" || normalized === \"~/\" || normalized === \"~/*\" || normalized === \"~/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_home\", \"Recursive forced rm targets the home directory.\");\n\t}\n\t// $HOME / ${HOME} expands to the user home directory at runtime.\n\tconst lower = normalized.toLowerCase();\n\tif (HOME_VARIABLE_RM_TARGETS.has(lower)) {\n\t\treturn verdict(\n\t\t\t\"confirm\",\n\t\t\t\"fs.rm_rf_home_var\",\n\t\t\t\"Recursive forced rm targets $HOME, which expands to the user home directory.\",\n\t\t);\n\t}\n\t// ~username targets another user home directory.\n\tif (/^~[A-Za-z0-9._-]/.test(normalized)) {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_user_home\", \"Recursive forced rm targets another user home directory.\");\n\t}\n\t// \".\" or \"./\" — current working directory (project root deletion risk).\n\tif (normalized === \".\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_cwd\", \"Recursive forced rm targets the current working directory.\");\n\t}\n\t// \"..\" — parent directory.\n\tif (normalized === \"..\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_parent\", \"Recursive forced rm targets the parent directory.\");\n\t}\n\treturn null;\n}\n\nfunction classifyRm(tokens: readonly string[]): CommandVerdict | null {\n\tlet hasRecursive = false;\n\tlet hasForce = false;\n\tlet endOfOptions = false;\n\tconst targets: string[] = [];\n\n\tfor (const token of tokens.slice(1)) {\n\t\tif (!endOfOptions && token === \"--\") {\n\t\t\tendOfOptions = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!endOfOptions && token.startsWith(\"-\") && token !== \"-\") {\n\t\t\tif (token === \"--recursive\") hasRecursive = true;\n\t\t\tif (token === \"--force\") hasForce = true;\n\t\t\tif (!token.startsWith(\"--\")) {\n\t\t\t\tconst flagCharacters = token.slice(1).split(\"\");\n\t\t\t\tif (flagCharacters.some((flag) => flag === \"r\" || flag === \"R\")) hasRecursive = true;\n\t\t\t\tif (flagCharacters.includes(\"f\")) hasForce = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\ttargets.push(token);\n\t}\n\n\tif (!hasRecursive || !hasForce) return null;\n\tfor (const target of targets) {\n\t\tconst targetVerdict = classifyRmTarget(target);\n\t\tif (targetVerdict) return targetVerdict;\n\t}\n\treturn null;\n}\n\nfunction isBlockDeviceOutput(token: string): boolean {\n\treturn /^of=\\/dev\\/(?:sd[a-z][a-z0-9]*|nvme[0-9a-z]+|disk(?:[0-9].*|\\/.*|$))/i.test(token);\n}\n\nfunction classifyDestructiveFilesystemCommand(command: string): CommandVerdict | null {\n\tif (hasForkBomb(command)) {\n\t\treturn verdict(\"block\", \"process.fork_bomb\", \"Shell fork bomb pattern would exhaust process resources.\");\n\t}\n\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\tif (executable === \"rm\") return classifyRm(tokens);\n\tif (executable.startsWith(\"mkfs\")) {\n\t\treturn verdict(\"block\", \"fs.mkfs\", \"Filesystem formatting commands are blocked.\");\n\t}\n\tif (executable === \"dd\" && tokens.slice(1).some(isBlockDeviceOutput)) {\n\t\treturn verdict(\"block\", \"fs.dd_block_device\", \"dd writes directly to a block-device output path.\");\n\t}\n\treturn null;\n}\n\nfunction findGitCommand(tokens: readonly string[]): { command: string; args: string[]; originalArgs: string[] } | null {\n\tif (tokens[0]?.toLowerCase() !== \"git\") return null;\n\tlet index = 1;\n\twhile (index < tokens.length && tokens[index]?.startsWith(\"-\")) {\n\t\tconst option = tokens[index];\n\t\tconst normalizedOption = option.toLowerCase();\n\t\tindex += 1;\n\t\tif (option === \"--\") break;\n\t\tif (\n\t\t\toptionConsumesValue(option, GIT_GLOBAL_OPTIONS_WITH_VALUE) ||\n\t\t\toptionConsumesValue(normalizedOption, GIT_GLOBAL_OPTIONS_WITH_VALUE)\n\t\t) {\n\t\t\tindex += 1;\n\t\t}\n\t}\n\tconst command = tokens[index]?.toLowerCase();\n\tif (!command) return null;\n\tconst originalArgs = tokens.slice(index + 1);\n\treturn {\n\t\tcommand,\n\t\targs: originalArgs.map((argument) => argument.toLowerCase()),\n\t\toriginalArgs,\n\t};\n}\n\nfunction hasGitCleanForceDirectory(args: readonly string[]): boolean {\n\tlet hasForce = false;\n\tlet hasDirectory = false;\n\tfor (const arg of args) {\n\t\tif (arg === \"--force\") hasForce = true;\n\t\tif (arg === \"-f\") hasForce = true;\n\t\tif (arg === \"-d\") hasDirectory = true;\n\t\tif (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tconst flags = arg.slice(1).split(\"\");\n\t\t\tif (flags.includes(\"f\")) hasForce = true;\n\t\t\tif (flags.includes(\"d\")) hasDirectory = true;\n\t\t}\n\t}\n\treturn hasForce && hasDirectory;\n}\n\nfunction classifyProtectedGitCommand(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst gitCommand = findGitCommand(tokens);\n\tif (!gitCommand) return null;\n\n\tif (gitCommand.command === \"reset\" && gitCommand.args.includes(\"--hard\")) {\n\t\treturn verdict(\"confirm\", \"git.reset_hard\", \"git reset --hard can discard worktree changes.\");\n\t}\n\tif (gitCommand.command === \"restore\") {\n\t\tconst targetAll = gitCommand.originalArgs.includes(\".\") || gitCommand.originalArgs.includes(\"*\");\n\t\tconst rewritesWorktree = gitCommand.args.includes(\"--worktree\") || gitCommand.args.includes(\"--source\");\n\t\tif (targetAll || rewritesWorktree) {\n\t\t\treturn verdict(\"confirm\", \"git.restore\", \"git restore can discard or overwrite local worktree changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"checkout\" && gitCommand.originalArgs.includes(\".\")) {\n\t\treturn verdict(\"confirm\", \"git.checkout_dot\", \"git checkout . can overwrite local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"clean\" && hasGitCleanForceDirectory(gitCommand.args)) {\n\t\treturn verdict(\"confirm\", \"git.clean_force\", \"git clean -fd can delete untracked files and directories.\");\n\t}\n\tif (gitCommand.command === \"stash\" && gitCommand.originalArgs.length === 0) {\n\t\treturn verdict(\"confirm\", \"git.stash_bare\", \"Bare git stash can hide local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"add\") {\n\t\tif (gitCommand.originalArgs.includes(\".\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_dot\", \"git add . can stage unrelated local changes.\");\n\t\t}\n\t\tif (gitCommand.originalArgs.includes(\"-A\") || gitCommand.args.includes(\"--all\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_all\", \"git add -A can stage unrelated local changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"commit\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.no_verify\", \"git commit --no-verify bypasses repository verification hooks.\");\n\t}\n\tif (gitCommand.command === \"push\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.push_no_verify\", \"git push --no-verify bypasses remote verification hooks.\");\n\t}\n\tif (\n\t\tgitCommand.command === \"push\" &&\n\t\t(gitCommand.originalArgs.includes(\"-f\") ||\n\t\t\tgitCommand.args.includes(\"--force\") ||\n\t\t\tgitCommand.args.includes(\"--force-with-lease\"))\n\t) {\n\t\treturn verdict(\"confirm\", \"git.force_push\", \"Force-pushing can rewrite remote history.\");\n\t}\n\treturn null;\n}\n\nfunction classifyPrivilegeCommand(command: string): CommandVerdict | null {\n\tconst tokens = tokenizeShellSegment(command);\n\tconst index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) return null;\n\treturn verdict(\"confirm\", `priv.${privilege}`, `${privilege} requires explicit per-command confirmation.`);\n}\n\n/**\n * Locate the inline command-string argument for a `shell -c \"<cmd>\"` invocation.\n * Returns the token index of the command string, or -1 when the shell runs a\n * script file (no inline command to classify).\n */\nfunction findShellCommandStringIndex(tokens: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (/^-[a-z]*c$/i.test(token)) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Locate the inline program-string argument for an interpreter invocation\n * (`python -c`, `node -e`, `perl -e`, ...). Returns the token index of the\n * program string, or -1 when the interpreter runs a script file (no inline\n * program to classify).\n */\nfunction findInterpreterInlineIndex(tokens: readonly string[], flags: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (token === \"--\") return -1;\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (flags.includes(token) || flags.includes(token.toLowerCase())) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/** Drop leading xargs options (and their values) to expose the wrapped command. */\nfunction stripXargsOptions(args: readonly string[]): string[] {\n\tlet index = 0;\n\twhile (index < args.length) {\n\t\tconst token = args[index];\n\t\tif (!token.startsWith(\"-\") || token === \"-\") break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (!token.includes(\"=\") && XARGS_OPTIONS_WITH_VALUE.has(token)) index += 1;\n\t}\n\treturn args.slice(index);\n}\n\n/** Extract the command tokens of a `find ... -exec <cmd> {} ;|+` clause, if present. */\nfunction extractFindExec(tokens: readonly string[]): string[] | null {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index].toLowerCase();\n\t\tif (token === \"-exec\" || token === \"-execdir\" || token === \"-ok\" || token === \"-okdir\") {\n\t\t\tconst collected: string[] = [];\n\t\t\tfor (let inner = index + 1; inner < tokens.length; inner += 1) {\n\t\t\t\tconst piece = tokens[inner];\n\t\t\t\tif (piece === \";\" || piece === \"+\") break;\n\t\t\t\tif (piece === \"{}\") continue;\n\t\t\t\tcollected.push(piece);\n\t\t\t}\n\t\t\treturn collected.length > 0 ? collected : null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Classify the effective command hidden inside a wrapper executable so a\n * destructive or protected command cannot be smuggled past the classifier via\n * `bash -c`, `eval`, `xargs`, or `find -exec`. Returns null when the segment is\n * not a recognized wrapper.\n */\nfunction classifyWrappedCommand(command: string, depth: number): CommandVerdict | null {\n\tif (depth >= MAX_WRAP_DEPTH) return null;\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\n\tif (SHELL_WRAPPERS.has(executable)) {\n\t\tconst innerIndex = findShellCommandStringIndex(tokens);\n\t\tconst inner = innerIndex === -1 ? undefined : tokens[innerIndex];\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"eval\") {\n\t\tconst inner = tokens.slice(1).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"xargs\") {\n\t\tconst inner = stripXargsOptions(tokens.slice(1)).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"find\") {\n\t\tconst lowered = tokens.map((token) => token.toLowerCase());\n\t\tconst hasDelete = lowered.includes(\"-delete\");\n\t\tconst execTokens = extractFindExec(tokens);\n\t\tconst inner = execTokens?.join(\" \").trim();\n\t\tconst innerVerdict = inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t\tif (hasDelete) {\n\t\t\tconst deleteVerdict = verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t\"fs.find_delete\",\n\t\t\t\t\"find -delete removes matched files and directories.\",\n\t\t\t);\n\t\t\tif (!innerVerdict || RISK_RANK[deleteVerdict.risk] > RISK_RANK[innerVerdict.risk]) {\n\t\t\t\treturn deleteVerdict;\n\t\t\t}\n\t\t}\n\t\treturn innerVerdict ?? null;\n\t}\n\tconst inlineFlags = INTERPRETER_INLINE_FLAGS[executable];\n\tif (inlineFlags) {\n\t\tconst innerIndex = findInterpreterInlineIndex(tokens, inlineFlags);\n\t\tif (innerIndex === -1) return null; // script file path — allow\n\t\tconst program = tokens[innerIndex];\n\t\tif (INLINE_DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(program))) {\n\t\t\treturn verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t`interp.${executable}_inline_destructive`,\n\t\t\t\t`${executable} inline program can execute destructive shell commands.`,\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn null;\n}\n\n/** Flag commands that read, copy, or transmit a credential / secret file path. */\nfunction classifySecretAccess(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tif (tokens.length === 0) return null;\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token || token.startsWith(\"-\")) continue;\n\t\tif (/\\s/.test(token)) continue;\n\t\tif (SECRET_FILE_ALLOW_PATTERNS.some((pattern) => pattern.test(token))) continue;\n\t\tif (SECRET_FILE_STRICT_PATTERNS.some((pattern) => pattern.test(token))) {\n\t\t\treturn verdict(\"confirm\", \"secret.read_path\", \"Command references a credential or secret file path.\");\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction classifySingleCommand(command: string, depth: number): CommandVerdict {\n\tconst trimmed = command.trim();\n\tif (!trimmed) return allowVerdict();\n\n\tconst destructiveFilesystem = classifyDestructiveFilesystemCommand(trimmed);\n\tif (destructiveFilesystem) return destructiveFilesystem;\n\n\tconst wrapped = classifyWrappedCommand(trimmed, depth);\n\tif (wrapped && wrapped.risk === \"block\") return wrapped;\n\n\tconst protectedGit = classifyProtectedGitCommand(trimmed);\n\tif (protectedGit) return protectedGit;\n\n\tconst privilege = classifyPrivilegeCommand(trimmed);\n\tif (privilege) return privilege;\n\n\tconst secret = classifySecretAccess(trimmed);\n\tif (secret) return secret;\n\n\tif (wrapped && RISK_RANK[wrapped.risk] > RISK_RANK.allow) return wrapped;\n\n\treturn allowVerdict();\n}\n\nfunction classifyShellCommandInternal(command: string, depth: number): CommandVerdict {\n\tlet selected = classifySingleCommand(command, depth);\n\tfor (const segment of splitShellCommands(command)) {\n\t\tconst candidate = classifySingleCommand(segment, depth);\n\t\tif (RISK_RANK[candidate.risk] > RISK_RANK[selected.risk]) selected = candidate;\n\t\tif (selected.risk === \"block\") break;\n\t}\n\treturn selected;\n}\n\nexport function classifyShellCommand(command: string): CommandVerdict {\n\treturn classifyShellCommandInternal(command, 0);\n}\n\nexport function isDestructiveFilesystem(command: string): boolean {\n\treturn classifyDestructiveFilesystemCommand(command) !== null;\n}\n\nexport function isProtectedGitOperation(command: string): boolean {\n\treturn classifyProtectedGitCommand(command) !== null;\n}\n\nexport function isPrivilegeEscalation(command: string): boolean {\n\treturn classifyPrivilegeCommand(command) !== null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"command-safety.d.ts","sourceRoot":"","sources":["../../src/core/command-safety.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AAsoBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAEpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9D","sourcesContent":["// allow: SIZE_OK - legacy safety rules table; this change only preserves existing typed behavior.\nexport type CommandRisk = \"block\" | \"confirm\" | \"allow\";\n\nexport interface CommandVerdict {\n\trisk: CommandRisk;\n\trule: string;\n\treason: string;\n}\n\ninterface CommandPrefixResult {\n\ttokens: string[];\n\tprivilege: string | undefined;\n}\n\nconst RISK_RANK: Readonly<Record<CommandRisk, number>> = {\n\tallow: 0,\n\tconfirm: 1,\n\tblock: 2,\n};\n\nconst PRIVILEGE_COMMANDS = new Set([\"sudo\", \"su\", \"doas\", \"pkexec\"]);\nconst SUDO_OPTIONS_WITH_VALUE = new Set([\n\t\"-C\",\n\t\"--close-from\",\n\t\"-g\",\n\t\"--group\",\n\t\"-h\",\n\t\"--host\",\n\t\"-p\",\n\t\"--prompt\",\n\t\"-T\",\n\t\"--command-timeout\",\n\t\"-u\",\n\t\"--user\",\n]);\nconst GIT_GLOBAL_OPTIONS_WITH_VALUE = new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--namespace\"]);\n\n/** Maximum recursion depth when unwrapping shell wrappers (bash -c, eval, xargs, find -exec). */\nconst MAX_WRAP_DEPTH = 6;\n\n/** Executables that run an inline command string passed via a `-c`-style flag. */\nconst SHELL_WRAPPERS = new Set([\"bash\", \"sh\", \"zsh\", \"dash\", \"ksh\", \"ash\"]);\n\n/**\n * Script interpreters that execute an inline program string (via a -c/-e/-r\n * style flag). Running inline code can execute arbitrary shell commands, so a\n * matched destructive/exec keyword inside the program escalates the call to\n * confirm-tier. A plain script-file path stays allow-tier.\n */\nconst INTERPRETER_INLINE_FLAGS: Readonly<Record<string, readonly string[]>> = {\n\tpython: [\"-c\"],\n\tpython2: [\"-c\"],\n\tpython3: [\"-c\"],\n\tnode: [\"-e\", \"--eval\", \"-p\", \"--print\"],\n\tperl: [\"-e\", \"-E\"],\n\truby: [\"-e\", \"-E\"],\n\tphp: [\"-r\", \"--run\"],\n\tlua: [\"-e\"],\n\texpect: [\"-c\"],\n};\n\n/**\n * Patterns that indicate an inline program invokes a shell command or\n * performs a destructive filesystem operation. Matching any of these inside\n * a `-c`/`-e`/`-r` program string escalates the call to confirm-tier.\n */\nconst INLINE_DESTRUCTIVE_PATTERNS: readonly RegExp[] = [\n\t/\\bos\\.system\\s*\\(/,\n\t/\\bos\\.remove\\s*\\(/,\n\t/\\bos\\.unlink\\s*\\(/,\n\t/\\bshutil\\.rmtree\\s*\\(/,\n\t/\\bsubprocess\\.(?:call|run|check_call|check_output|Popen)\\s*\\(/,\n\t/\\bexec(?:Sync|File)?\\s*\\(/,\n\t/\\bspawn(?:Sync)?\\s*\\(/,\n\t/\\bchild_process\\b/,\n\t/\\brequire\\s*\\(\\s*['\"]child_process['\"]\\s*\\)/,\n\t/\\b(?:system|popen)\\s*\\(/,\n\t/`[^`]*\\brm\\s+-/m,\n\t/\\$\\(\\s*[^)]*\\brm\\s+-/,\n];\n\n/** xargs options that consume a following value token before the wrapped command begins. */\nconst XARGS_OPTIONS_WITH_VALUE = new Set([\n\t\"-I\",\n\t\"-i\",\n\t\"-n\",\n\t\"-L\",\n\t\"-P\",\n\t\"-d\",\n\t\"-E\",\n\t\"-s\",\n\t\"-a\",\n\t\"--max-args\",\n\t\"--max-procs\",\n\t\"--delimiter\",\n\t\"--arg-file\",\n\t\"--max-lines\",\n\t\"--replace\",\n]);\n\n/**\n * Credential / secret file patterns. A bash argv token matching one of these is\n * treated as a `confirm`-tier access: headless callers (LLM tool calls, RPC bash)\n * deny by default, while an interactive user can still approve reading their own\n * files. These complement the §0.1 freedom safety floor, which hard-denies the\n * same paths on the entry points it guards.\n */\nconst SECRET_FILE_STRICT_PATTERNS: readonly RegExp[] = [\n\t/(^|\\/)\\.env(\\.|$)/i,\n\t/(^|\\/)\\.npmrc$/i,\n\t/(^|\\/)\\.netrc$/i,\n\t/(^|\\/)\\.pgpass$/i,\n\t/(^|\\/)\\.aws\\/credentials$/i,\n\t/(^|\\/)credentials$/i,\n\t/(^|\\/)auth\\.json$/i,\n\t/(^|\\/)id_(rsa|dsa|ecdsa|ed25519)$/i,\n\t/\\.(pem|key|p12|pfx|keystore|jks)$/i,\n\t/(^|\\/)secrets?(\\.[^/]+)?$/i,\n];\n\n/** Benign sibling files that look secret-ish but never hold credentials. */\nconst SECRET_FILE_ALLOW_PATTERNS: readonly RegExp[] = [/(^|\\/)\\.env\\.(example|sample|template|dist|defaults?)$/i];\nconst SEARCH_COMMANDS = new Set([\"grep\", \"egrep\", \"fgrep\", \"rg\"]);\nconst HOME_VARIABLE_RM_TARGETS = new Set([\"$home\", \"$\" + \"{home}\", \"$home/\", \"$\" + \"{home}/\"]);\n\nfunction verdict(risk: CommandRisk, rule: string, reason: string): CommandVerdict {\n\treturn { risk, rule, reason };\n}\n\nfunction allowVerdict(): CommandVerdict {\n\treturn verdict(\"allow\", \"command.allow\", \"No destructive or protected command pattern matched.\");\n}\n\nfunction isEnvAssignment(token: string | undefined): boolean {\n\treturn token !== undefined && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(token);\n}\n\nfunction tokenizeShellSegment(segment: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (const character of segment.trim()) {\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tif (character === quote) {\n\t\t\t\tquote = undefined;\n\t\t\t} else {\n\t\t\t\tcurrent += character;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/.test(character)) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (escaped) current += \"\\\\\";\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n\nfunction splitShellCommands(command: string): string[] {\n\tconst segments: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (let index = 0; index < command.length; index += 1) {\n\t\tconst character = command[index];\n\t\tconst next = command[index + 1];\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tcurrent += character;\n\t\t\tif (character === quote) quote = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \";\" || character === \"|\" || character === \"\\n\" || character === \"\\r\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"&\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tif (next === \"&\") index += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (current.trim()) segments.push(current.trim());\n\treturn segments;\n}\n\nfunction skipEnvironmentPrefix(tokens: readonly string[], startIndex: number): number {\n\tlet index = startIndex;\n\twhile (isEnvAssignment(tokens[index])) index += 1;\n\tif (tokens[index]?.toLowerCase() !== \"env\") return index;\n\n\tindex += 1;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (isEnvAssignment(token)) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (token?.startsWith(\"-\")) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn index;\n}\n\nfunction optionConsumesValue(option: string, optionsWithValue: ReadonlySet<string>): boolean {\n\tif (option.includes(\"=\")) return false;\n\treturn optionsWithValue.has(option);\n}\n\nfunction skipPrivilegeOptions(tokens: readonly string[], startIndex: number, privilege: string): number {\n\tif (privilege !== \"sudo\" && privilege !== \"doas\") return startIndex;\n\n\tlet index = startIndex;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (!token?.startsWith(\"-\")) break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (optionConsumesValue(token, SUDO_OPTIONS_WITH_VALUE)) index += 1;\n\t}\n\treturn index;\n}\n\nfunction stripCommandPrefixes(tokens: readonly string[]): CommandPrefixResult {\n\tlet index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) {\n\t\treturn { tokens: tokens.slice(index), privilege: undefined };\n\t}\n\n\tindex += 1;\n\tindex = skipPrivilegeOptions(tokens, index, privilege ?? \"\");\n\tindex = skipEnvironmentPrefix(tokens, index);\n\treturn { tokens: tokens.slice(index), privilege };\n}\n\nfunction hasForkBomb(command: string): boolean {\n\treturn command.replace(/\\s/g, \"\").includes(\":(){:|:&};:\");\n}\n\nfunction normalizePathTarget(target: string): string {\n\treturn target.replace(/\\/+$/g, (match) => (target === match ? \"/\" : \"\")).replace(/\\/+/g, \"/\");\n}\n\nfunction classifyRmTarget(target: string): CommandVerdict | null {\n\tconst normalized = normalizePathTarget(target);\n\tif (normalized === \"/\" || normalized === \"/*\" || normalized === \"/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_root\", \"Recursive forced rm targets filesystem root.\");\n\t}\n\tif (normalized === \"~\" || normalized === \"~/\" || normalized === \"~/*\" || normalized === \"~/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_home\", \"Recursive forced rm targets the home directory.\");\n\t}\n\t// $HOME / ${HOME} expands to the user home directory at runtime.\n\tconst lower = normalized.toLowerCase();\n\tif (HOME_VARIABLE_RM_TARGETS.has(lower)) {\n\t\treturn verdict(\n\t\t\t\"confirm\",\n\t\t\t\"fs.rm_rf_home_var\",\n\t\t\t\"Recursive forced rm targets $HOME, which expands to the user home directory.\",\n\t\t);\n\t}\n\t// ~username targets another user home directory.\n\tif (/^~[A-Za-z0-9._-]/.test(normalized)) {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_user_home\", \"Recursive forced rm targets another user home directory.\");\n\t}\n\t// \".\" or \"./\" — current working directory (project root deletion risk).\n\tif (normalized === \".\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_cwd\", \"Recursive forced rm targets the current working directory.\");\n\t}\n\t// \"..\" — parent directory.\n\tif (normalized === \"..\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_parent\", \"Recursive forced rm targets the parent directory.\");\n\t}\n\treturn null;\n}\n\nfunction classifyRm(tokens: readonly string[]): CommandVerdict | null {\n\tlet hasRecursive = false;\n\tlet hasForce = false;\n\tlet endOfOptions = false;\n\tconst targets: string[] = [];\n\n\tfor (const token of tokens.slice(1)) {\n\t\tif (!endOfOptions && token === \"--\") {\n\t\t\tendOfOptions = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!endOfOptions && token.startsWith(\"-\") && token !== \"-\") {\n\t\t\tif (token === \"--recursive\") hasRecursive = true;\n\t\t\tif (token === \"--force\") hasForce = true;\n\t\t\tif (!token.startsWith(\"--\")) {\n\t\t\t\tconst flagCharacters = token.slice(1).split(\"\");\n\t\t\t\tif (flagCharacters.some((flag) => flag === \"r\" || flag === \"R\")) hasRecursive = true;\n\t\t\t\tif (flagCharacters.includes(\"f\")) hasForce = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\ttargets.push(token);\n\t}\n\n\tif (!hasRecursive || !hasForce) return null;\n\tfor (const target of targets) {\n\t\tconst targetVerdict = classifyRmTarget(target);\n\t\tif (targetVerdict) return targetVerdict;\n\t}\n\treturn null;\n}\n\nfunction isBlockDeviceOutput(token: string): boolean {\n\treturn /^of=\\/dev\\/(?:sd[a-z][a-z0-9]*|nvme[0-9a-z]+|disk(?:[0-9].*|\\/.*|$))/i.test(token);\n}\n\nfunction classifyDestructiveFilesystemCommand(command: string): CommandVerdict | null {\n\tif (hasForkBomb(command)) {\n\t\treturn verdict(\"block\", \"process.fork_bomb\", \"Shell fork bomb pattern would exhaust process resources.\");\n\t}\n\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\tif (executable === \"rm\") return classifyRm(tokens);\n\tif (executable.startsWith(\"mkfs\")) {\n\t\treturn verdict(\"block\", \"fs.mkfs\", \"Filesystem formatting commands are blocked.\");\n\t}\n\tif (executable === \"dd\" && tokens.slice(1).some(isBlockDeviceOutput)) {\n\t\treturn verdict(\"block\", \"fs.dd_block_device\", \"dd writes directly to a block-device output path.\");\n\t}\n\treturn null;\n}\n\nfunction findGitCommand(tokens: readonly string[]): { command: string; args: string[]; originalArgs: string[] } | null {\n\tif (tokens[0]?.toLowerCase() !== \"git\") return null;\n\tlet index = 1;\n\twhile (index < tokens.length && tokens[index]?.startsWith(\"-\")) {\n\t\tconst option = tokens[index];\n\t\tconst normalizedOption = option.toLowerCase();\n\t\tindex += 1;\n\t\tif (option === \"--\") break;\n\t\tif (\n\t\t\toptionConsumesValue(option, GIT_GLOBAL_OPTIONS_WITH_VALUE) ||\n\t\t\toptionConsumesValue(normalizedOption, GIT_GLOBAL_OPTIONS_WITH_VALUE)\n\t\t) {\n\t\t\tindex += 1;\n\t\t}\n\t}\n\tconst command = tokens[index]?.toLowerCase();\n\tif (!command) return null;\n\tconst originalArgs = tokens.slice(index + 1);\n\treturn {\n\t\tcommand,\n\t\targs: originalArgs.map((argument) => argument.toLowerCase()),\n\t\toriginalArgs,\n\t};\n}\n\nfunction hasGitCleanForceDirectory(args: readonly string[]): boolean {\n\tlet hasForce = false;\n\tlet hasDirectory = false;\n\tfor (const arg of args) {\n\t\tif (arg === \"--force\") hasForce = true;\n\t\tif (arg === \"-f\") hasForce = true;\n\t\tif (arg === \"-d\") hasDirectory = true;\n\t\tif (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tconst flags = arg.slice(1).split(\"\");\n\t\t\tif (flags.includes(\"f\")) hasForce = true;\n\t\t\tif (flags.includes(\"d\")) hasDirectory = true;\n\t\t}\n\t}\n\treturn hasForce && hasDirectory;\n}\n\nfunction classifyProtectedGitCommand(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst gitCommand = findGitCommand(tokens);\n\tif (!gitCommand) return null;\n\n\tif (gitCommand.command === \"reset\" && gitCommand.args.includes(\"--hard\")) {\n\t\treturn verdict(\"confirm\", \"git.reset_hard\", \"git reset --hard can discard worktree changes.\");\n\t}\n\tif (gitCommand.command === \"restore\") {\n\t\tconst targetAll = gitCommand.originalArgs.includes(\".\") || gitCommand.originalArgs.includes(\"*\");\n\t\tconst rewritesWorktree = gitCommand.args.includes(\"--worktree\") || gitCommand.args.includes(\"--source\");\n\t\tif (targetAll || rewritesWorktree) {\n\t\t\treturn verdict(\"confirm\", \"git.restore\", \"git restore can discard or overwrite local worktree changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"checkout\" && gitCommand.originalArgs.includes(\".\")) {\n\t\treturn verdict(\"confirm\", \"git.checkout_dot\", \"git checkout . can overwrite local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"clean\" && hasGitCleanForceDirectory(gitCommand.args)) {\n\t\treturn verdict(\"confirm\", \"git.clean_force\", \"git clean -fd can delete untracked files and directories.\");\n\t}\n\tif (gitCommand.command === \"stash\" && gitCommand.originalArgs.length === 0) {\n\t\treturn verdict(\"confirm\", \"git.stash_bare\", \"Bare git stash can hide local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"add\") {\n\t\tif (gitCommand.originalArgs.includes(\".\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_dot\", \"git add . can stage unrelated local changes.\");\n\t\t}\n\t\tif (gitCommand.originalArgs.includes(\"-A\") || gitCommand.args.includes(\"--all\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_all\", \"git add -A can stage unrelated local changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"commit\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.no_verify\", \"git commit --no-verify bypasses repository verification hooks.\");\n\t}\n\tif (gitCommand.command === \"push\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.push_no_verify\", \"git push --no-verify bypasses remote verification hooks.\");\n\t}\n\tif (\n\t\tgitCommand.command === \"push\" &&\n\t\t(gitCommand.originalArgs.includes(\"-f\") ||\n\t\t\tgitCommand.args.includes(\"--force\") ||\n\t\t\tgitCommand.args.includes(\"--force-with-lease\"))\n\t) {\n\t\treturn verdict(\"confirm\", \"git.force_push\", \"Force-pushing can rewrite remote history.\");\n\t}\n\treturn null;\n}\n\nfunction classifyPrivilegeCommand(command: string): CommandVerdict | null {\n\tconst tokens = tokenizeShellSegment(command);\n\tconst index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) return null;\n\treturn verdict(\"confirm\", `priv.${privilege}`, `${privilege} requires explicit per-command confirmation.`);\n}\n\n/**\n * Locate the inline command-string argument for a `shell -c \"<cmd>\"` invocation.\n * Returns the token index of the command string, or -1 when the shell runs a\n * script file (no inline command to classify).\n */\nfunction findShellCommandStringIndex(tokens: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (/^-[a-z]*c$/i.test(token)) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Locate the inline program-string argument for an interpreter invocation\n * (`python -c`, `node -e`, `perl -e`, ...). Returns the token index of the\n * program string, or -1 when the interpreter runs a script file (no inline\n * program to classify).\n */\nfunction findInterpreterInlineIndex(tokens: readonly string[], flags: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (token === \"--\") return -1;\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (flags.includes(token) || flags.includes(token.toLowerCase())) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/** Drop leading xargs options (and their values) to expose the wrapped command. */\nfunction stripXargsOptions(args: readonly string[]): string[] {\n\tlet index = 0;\n\twhile (index < args.length) {\n\t\tconst token = args[index];\n\t\tif (!token.startsWith(\"-\") || token === \"-\") break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (!token.includes(\"=\") && XARGS_OPTIONS_WITH_VALUE.has(token)) index += 1;\n\t}\n\treturn args.slice(index);\n}\n\n/** Extract the command tokens of a `find ... -exec <cmd> {} ;|+` clause, if present. */\nfunction extractFindExec(tokens: readonly string[]): string[] | null {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index].toLowerCase();\n\t\tif (token === \"-exec\" || token === \"-execdir\" || token === \"-ok\" || token === \"-okdir\") {\n\t\t\tconst collected: string[] = [];\n\t\t\tfor (let inner = index + 1; inner < tokens.length; inner += 1) {\n\t\t\t\tconst piece = tokens[inner];\n\t\t\t\tif (piece === \";\" || piece === \"+\") break;\n\t\t\t\tif (piece === \"{}\") continue;\n\t\t\t\tcollected.push(piece);\n\t\t\t}\n\t\t\treturn collected.length > 0 ? collected : null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Classify the effective command hidden inside a wrapper executable so a\n * destructive or protected command cannot be smuggled past the classifier via\n * `bash -c`, `eval`, `xargs`, or `find -exec`. Returns null when the segment is\n * not a recognized wrapper.\n */\nfunction classifyWrappedCommand(command: string, depth: number): CommandVerdict | null {\n\tif (depth >= MAX_WRAP_DEPTH) return null;\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\n\tif (SHELL_WRAPPERS.has(executable)) {\n\t\tconst innerIndex = findShellCommandStringIndex(tokens);\n\t\tconst inner = innerIndex === -1 ? undefined : tokens[innerIndex];\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"eval\") {\n\t\tconst inner = tokens.slice(1).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"xargs\") {\n\t\tconst inner = stripXargsOptions(tokens.slice(1)).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"find\") {\n\t\tconst lowered = tokens.map((token) => token.toLowerCase());\n\t\tconst hasDelete = lowered.includes(\"-delete\");\n\t\tconst execTokens = extractFindExec(tokens);\n\t\tconst inner = execTokens?.join(\" \").trim();\n\t\tconst innerVerdict = inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t\tif (hasDelete) {\n\t\t\tconst deleteVerdict = verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t\"fs.find_delete\",\n\t\t\t\t\"find -delete removes matched files and directories.\",\n\t\t\t);\n\t\t\tif (!innerVerdict || RISK_RANK[deleteVerdict.risk] > RISK_RANK[innerVerdict.risk]) {\n\t\t\t\treturn deleteVerdict;\n\t\t\t}\n\t\t}\n\t\treturn innerVerdict ?? null;\n\t}\n\tconst inlineFlags = INTERPRETER_INLINE_FLAGS[executable];\n\tif (inlineFlags) {\n\t\tconst innerIndex = findInterpreterInlineIndex(tokens, inlineFlags);\n\t\tif (innerIndex === -1) return null; // script file path — allow\n\t\tconst program = tokens[innerIndex];\n\t\tif (INLINE_DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(program))) {\n\t\t\treturn verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t`interp.${executable}_inline_destructive`,\n\t\t\t\t`${executable} inline program can execute destructive shell commands.`,\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn null;\n}\n\nfunction isSearchPatternArgument(tokens: readonly string[], index: number): boolean {\n\tconst executable = tokens[0]?.toLowerCase();\n\treturn SEARCH_COMMANDS.has(executable ?? \"\") && tokens[index - 1] === \"--\";\n}\n\n/** Flag commands that read, copy, or transmit a credential / secret file path. */\nfunction classifySecretAccess(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tif (tokens.length === 0) return null;\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token || token.startsWith(\"-\")) continue;\n\t\tif (isSearchPatternArgument(tokens, index)) continue;\n\t\tif (/\\s/.test(token)) continue;\n\t\tif (SECRET_FILE_ALLOW_PATTERNS.some((pattern) => pattern.test(token))) continue;\n\t\tif (SECRET_FILE_STRICT_PATTERNS.some((pattern) => pattern.test(token))) {\n\t\t\treturn verdict(\"confirm\", \"secret.read_path\", \"Command references a credential or secret file path.\");\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction classifySingleCommand(command: string, depth: number): CommandVerdict {\n\tconst trimmed = command.trim();\n\tif (!trimmed) return allowVerdict();\n\n\tconst destructiveFilesystem = classifyDestructiveFilesystemCommand(trimmed);\n\tif (destructiveFilesystem) return destructiveFilesystem;\n\n\tconst wrapped = classifyWrappedCommand(trimmed, depth);\n\tif (wrapped && wrapped.risk === \"block\") return wrapped;\n\n\tconst protectedGit = classifyProtectedGitCommand(trimmed);\n\tif (protectedGit) return protectedGit;\n\n\tconst privilege = classifyPrivilegeCommand(trimmed);\n\tif (privilege) return privilege;\n\n\tconst secret = classifySecretAccess(trimmed);\n\tif (secret) return secret;\n\n\tif (wrapped && RISK_RANK[wrapped.risk] > RISK_RANK.allow) return wrapped;\n\n\treturn allowVerdict();\n}\n\nfunction classifyShellCommandInternal(command: string, depth: number): CommandVerdict {\n\tlet selected = classifySingleCommand(command, depth);\n\tfor (const segment of splitShellCommands(command)) {\n\t\tconst candidate = classifySingleCommand(segment, depth);\n\t\tif (RISK_RANK[candidate.risk] > RISK_RANK[selected.risk]) selected = candidate;\n\t\tif (selected.risk === \"block\") break;\n\t}\n\treturn selected;\n}\n\nexport function classifyShellCommand(command: string): CommandVerdict {\n\treturn classifyShellCommandInternal(command, 0);\n}\n\nexport function isDestructiveFilesystem(command: string): boolean {\n\treturn classifyDestructiveFilesystemCommand(command) !== null;\n}\n\nexport function isProtectedGitOperation(command: string): boolean {\n\treturn classifyProtectedGitCommand(command) !== null;\n}\n\nexport function isPrivilegeEscalation(command: string): boolean {\n\treturn classifyPrivilegeCommand(command) !== null;\n}\n"]}
|
|
@@ -98,6 +98,7 @@ const SECRET_FILE_STRICT_PATTERNS = [
|
|
|
98
98
|
];
|
|
99
99
|
/** Benign sibling files that look secret-ish but never hold credentials. */
|
|
100
100
|
const SECRET_FILE_ALLOW_PATTERNS = [/(^|\/)\.env\.(example|sample|template|dist|defaults?)$/i];
|
|
101
|
+
const SEARCH_COMMANDS = new Set(["grep", "egrep", "fgrep", "rg"]);
|
|
101
102
|
const HOME_VARIABLE_RM_TARGETS = new Set(["$home", "$" + "{home}", "$home/", "$" + "{home}/"]);
|
|
102
103
|
function verdict(risk, rule, reason) {
|
|
103
104
|
return { risk, rule, reason };
|
|
@@ -563,6 +564,10 @@ function classifyWrappedCommand(command, depth) {
|
|
|
563
564
|
}
|
|
564
565
|
return null;
|
|
565
566
|
}
|
|
567
|
+
function isSearchPatternArgument(tokens, index) {
|
|
568
|
+
const executable = tokens[0]?.toLowerCase();
|
|
569
|
+
return SEARCH_COMMANDS.has(executable ?? "") && tokens[index - 1] === "--";
|
|
570
|
+
}
|
|
566
571
|
/** Flag commands that read, copy, or transmit a credential / secret file path. */
|
|
567
572
|
function classifySecretAccess(command) {
|
|
568
573
|
const { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));
|
|
@@ -572,6 +577,8 @@ function classifySecretAccess(command) {
|
|
|
572
577
|
const token = tokens[index];
|
|
573
578
|
if (!token || token.startsWith("-"))
|
|
574
579
|
continue;
|
|
580
|
+
if (isSearchPatternArgument(tokens, index))
|
|
581
|
+
continue;
|
|
575
582
|
if (/\s/.test(token))
|
|
576
583
|
continue;
|
|
577
584
|
if (SECRET_FILE_ALLOW_PATTERNS.some((pattern) => pattern.test(token)))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-safety.js","sourceRoot":"","sources":["../../src/core/command-safety.ts"],"names":[],"mappings":"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","sourcesContent":["// allow: SIZE_OK - legacy safety rules table; this change only preserves existing typed behavior.\nexport type CommandRisk = \"block\" | \"confirm\" | \"allow\";\n\nexport interface CommandVerdict {\n\trisk: CommandRisk;\n\trule: string;\n\treason: string;\n}\n\ninterface CommandPrefixResult {\n\ttokens: string[];\n\tprivilege: string | undefined;\n}\n\nconst RISK_RANK: Readonly<Record<CommandRisk, number>> = {\n\tallow: 0,\n\tconfirm: 1,\n\tblock: 2,\n};\n\nconst PRIVILEGE_COMMANDS = new Set([\"sudo\", \"su\", \"doas\", \"pkexec\"]);\nconst SUDO_OPTIONS_WITH_VALUE = new Set([\n\t\"-C\",\n\t\"--close-from\",\n\t\"-g\",\n\t\"--group\",\n\t\"-h\",\n\t\"--host\",\n\t\"-p\",\n\t\"--prompt\",\n\t\"-T\",\n\t\"--command-timeout\",\n\t\"-u\",\n\t\"--user\",\n]);\nconst GIT_GLOBAL_OPTIONS_WITH_VALUE = new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--namespace\"]);\n\n/** Maximum recursion depth when unwrapping shell wrappers (bash -c, eval, xargs, find -exec). */\nconst MAX_WRAP_DEPTH = 6;\n\n/** Executables that run an inline command string passed via a `-c`-style flag. */\nconst SHELL_WRAPPERS = new Set([\"bash\", \"sh\", \"zsh\", \"dash\", \"ksh\", \"ash\"]);\n\n/**\n * Script interpreters that execute an inline program string (via a -c/-e/-r\n * style flag). Running inline code can execute arbitrary shell commands, so a\n * matched destructive/exec keyword inside the program escalates the call to\n * confirm-tier. A plain script-file path stays allow-tier.\n */\nconst INTERPRETER_INLINE_FLAGS: Readonly<Record<string, readonly string[]>> = {\n\tpython: [\"-c\"],\n\tpython2: [\"-c\"],\n\tpython3: [\"-c\"],\n\tnode: [\"-e\", \"--eval\", \"-p\", \"--print\"],\n\tperl: [\"-e\", \"-E\"],\n\truby: [\"-e\", \"-E\"],\n\tphp: [\"-r\", \"--run\"],\n\tlua: [\"-e\"],\n\texpect: [\"-c\"],\n};\n\n/**\n * Patterns that indicate an inline program invokes a shell command or\n * performs a destructive filesystem operation. Matching any of these inside\n * a `-c`/`-e`/`-r` program string escalates the call to confirm-tier.\n */\nconst INLINE_DESTRUCTIVE_PATTERNS: readonly RegExp[] = [\n\t/\\bos\\.system\\s*\\(/,\n\t/\\bos\\.remove\\s*\\(/,\n\t/\\bos\\.unlink\\s*\\(/,\n\t/\\bshutil\\.rmtree\\s*\\(/,\n\t/\\bsubprocess\\.(?:call|run|check_call|check_output|Popen)\\s*\\(/,\n\t/\\bexec(?:Sync|File)?\\s*\\(/,\n\t/\\bspawn(?:Sync)?\\s*\\(/,\n\t/\\bchild_process\\b/,\n\t/\\brequire\\s*\\(\\s*['\"]child_process['\"]\\s*\\)/,\n\t/\\b(?:system|popen)\\s*\\(/,\n\t/`[^`]*\\brm\\s+-/m,\n\t/\\$\\(\\s*[^)]*\\brm\\s+-/,\n];\n\n/** xargs options that consume a following value token before the wrapped command begins. */\nconst XARGS_OPTIONS_WITH_VALUE = new Set([\n\t\"-I\",\n\t\"-i\",\n\t\"-n\",\n\t\"-L\",\n\t\"-P\",\n\t\"-d\",\n\t\"-E\",\n\t\"-s\",\n\t\"-a\",\n\t\"--max-args\",\n\t\"--max-procs\",\n\t\"--delimiter\",\n\t\"--arg-file\",\n\t\"--max-lines\",\n\t\"--replace\",\n]);\n\n/**\n * Credential / secret file patterns. A bash argv token matching one of these is\n * treated as a `confirm`-tier access: headless callers (LLM tool calls, RPC bash)\n * deny by default, while an interactive user can still approve reading their own\n * files. These complement the §0.1 freedom safety floor, which hard-denies the\n * same paths on the entry points it guards.\n */\nconst SECRET_FILE_STRICT_PATTERNS: readonly RegExp[] = [\n\t/(^|\\/)\\.env(\\.|$)/i,\n\t/(^|\\/)\\.npmrc$/i,\n\t/(^|\\/)\\.netrc$/i,\n\t/(^|\\/)\\.pgpass$/i,\n\t/(^|\\/)\\.aws\\/credentials$/i,\n\t/(^|\\/)credentials$/i,\n\t/(^|\\/)auth\\.json$/i,\n\t/(^|\\/)id_(rsa|dsa|ecdsa|ed25519)$/i,\n\t/\\.(pem|key|p12|pfx|keystore|jks)$/i,\n\t/(^|\\/)secrets?(\\.[^/]+)?$/i,\n];\n\n/** Benign sibling files that look secret-ish but never hold credentials. */\nconst SECRET_FILE_ALLOW_PATTERNS: readonly RegExp[] = [/(^|\\/)\\.env\\.(example|sample|template|dist|defaults?)$/i];\nconst HOME_VARIABLE_RM_TARGETS = new Set([\"$home\", \"$\" + \"{home}\", \"$home/\", \"$\" + \"{home}/\"]);\n\nfunction verdict(risk: CommandRisk, rule: string, reason: string): CommandVerdict {\n\treturn { risk, rule, reason };\n}\n\nfunction allowVerdict(): CommandVerdict {\n\treturn verdict(\"allow\", \"command.allow\", \"No destructive or protected command pattern matched.\");\n}\n\nfunction isEnvAssignment(token: string | undefined): boolean {\n\treturn token !== undefined && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(token);\n}\n\nfunction tokenizeShellSegment(segment: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (const character of segment.trim()) {\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tif (character === quote) {\n\t\t\t\tquote = undefined;\n\t\t\t} else {\n\t\t\t\tcurrent += character;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/.test(character)) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (escaped) current += \"\\\\\";\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n\nfunction splitShellCommands(command: string): string[] {\n\tconst segments: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (let index = 0; index < command.length; index += 1) {\n\t\tconst character = command[index];\n\t\tconst next = command[index + 1];\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tcurrent += character;\n\t\t\tif (character === quote) quote = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \";\" || character === \"|\" || character === \"\\n\" || character === \"\\r\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"&\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tif (next === \"&\") index += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (current.trim()) segments.push(current.trim());\n\treturn segments;\n}\n\nfunction skipEnvironmentPrefix(tokens: readonly string[], startIndex: number): number {\n\tlet index = startIndex;\n\twhile (isEnvAssignment(tokens[index])) index += 1;\n\tif (tokens[index]?.toLowerCase() !== \"env\") return index;\n\n\tindex += 1;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (isEnvAssignment(token)) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (token?.startsWith(\"-\")) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn index;\n}\n\nfunction optionConsumesValue(option: string, optionsWithValue: ReadonlySet<string>): boolean {\n\tif (option.includes(\"=\")) return false;\n\treturn optionsWithValue.has(option);\n}\n\nfunction skipPrivilegeOptions(tokens: readonly string[], startIndex: number, privilege: string): number {\n\tif (privilege !== \"sudo\" && privilege !== \"doas\") return startIndex;\n\n\tlet index = startIndex;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (!token?.startsWith(\"-\")) break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (optionConsumesValue(token, SUDO_OPTIONS_WITH_VALUE)) index += 1;\n\t}\n\treturn index;\n}\n\nfunction stripCommandPrefixes(tokens: readonly string[]): CommandPrefixResult {\n\tlet index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) {\n\t\treturn { tokens: tokens.slice(index), privilege: undefined };\n\t}\n\n\tindex += 1;\n\tindex = skipPrivilegeOptions(tokens, index, privilege ?? \"\");\n\tindex = skipEnvironmentPrefix(tokens, index);\n\treturn { tokens: tokens.slice(index), privilege };\n}\n\nfunction hasForkBomb(command: string): boolean {\n\treturn command.replace(/\\s/g, \"\").includes(\":(){:|:&};:\");\n}\n\nfunction normalizePathTarget(target: string): string {\n\treturn target.replace(/\\/+$/g, (match) => (target === match ? \"/\" : \"\")).replace(/\\/+/g, \"/\");\n}\n\nfunction classifyRmTarget(target: string): CommandVerdict | null {\n\tconst normalized = normalizePathTarget(target);\n\tif (normalized === \"/\" || normalized === \"/*\" || normalized === \"/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_root\", \"Recursive forced rm targets filesystem root.\");\n\t}\n\tif (normalized === \"~\" || normalized === \"~/\" || normalized === \"~/*\" || normalized === \"~/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_home\", \"Recursive forced rm targets the home directory.\");\n\t}\n\t// $HOME / ${HOME} expands to the user home directory at runtime.\n\tconst lower = normalized.toLowerCase();\n\tif (HOME_VARIABLE_RM_TARGETS.has(lower)) {\n\t\treturn verdict(\n\t\t\t\"confirm\",\n\t\t\t\"fs.rm_rf_home_var\",\n\t\t\t\"Recursive forced rm targets $HOME, which expands to the user home directory.\",\n\t\t);\n\t}\n\t// ~username targets another user home directory.\n\tif (/^~[A-Za-z0-9._-]/.test(normalized)) {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_user_home\", \"Recursive forced rm targets another user home directory.\");\n\t}\n\t// \".\" or \"./\" — current working directory (project root deletion risk).\n\tif (normalized === \".\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_cwd\", \"Recursive forced rm targets the current working directory.\");\n\t}\n\t// \"..\" — parent directory.\n\tif (normalized === \"..\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_parent\", \"Recursive forced rm targets the parent directory.\");\n\t}\n\treturn null;\n}\n\nfunction classifyRm(tokens: readonly string[]): CommandVerdict | null {\n\tlet hasRecursive = false;\n\tlet hasForce = false;\n\tlet endOfOptions = false;\n\tconst targets: string[] = [];\n\n\tfor (const token of tokens.slice(1)) {\n\t\tif (!endOfOptions && token === \"--\") {\n\t\t\tendOfOptions = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!endOfOptions && token.startsWith(\"-\") && token !== \"-\") {\n\t\t\tif (token === \"--recursive\") hasRecursive = true;\n\t\t\tif (token === \"--force\") hasForce = true;\n\t\t\tif (!token.startsWith(\"--\")) {\n\t\t\t\tconst flagCharacters = token.slice(1).split(\"\");\n\t\t\t\tif (flagCharacters.some((flag) => flag === \"r\" || flag === \"R\")) hasRecursive = true;\n\t\t\t\tif (flagCharacters.includes(\"f\")) hasForce = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\ttargets.push(token);\n\t}\n\n\tif (!hasRecursive || !hasForce) return null;\n\tfor (const target of targets) {\n\t\tconst targetVerdict = classifyRmTarget(target);\n\t\tif (targetVerdict) return targetVerdict;\n\t}\n\treturn null;\n}\n\nfunction isBlockDeviceOutput(token: string): boolean {\n\treturn /^of=\\/dev\\/(?:sd[a-z][a-z0-9]*|nvme[0-9a-z]+|disk(?:[0-9].*|\\/.*|$))/i.test(token);\n}\n\nfunction classifyDestructiveFilesystemCommand(command: string): CommandVerdict | null {\n\tif (hasForkBomb(command)) {\n\t\treturn verdict(\"block\", \"process.fork_bomb\", \"Shell fork bomb pattern would exhaust process resources.\");\n\t}\n\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\tif (executable === \"rm\") return classifyRm(tokens);\n\tif (executable.startsWith(\"mkfs\")) {\n\t\treturn verdict(\"block\", \"fs.mkfs\", \"Filesystem formatting commands are blocked.\");\n\t}\n\tif (executable === \"dd\" && tokens.slice(1).some(isBlockDeviceOutput)) {\n\t\treturn verdict(\"block\", \"fs.dd_block_device\", \"dd writes directly to a block-device output path.\");\n\t}\n\treturn null;\n}\n\nfunction findGitCommand(tokens: readonly string[]): { command: string; args: string[]; originalArgs: string[] } | null {\n\tif (tokens[0]?.toLowerCase() !== \"git\") return null;\n\tlet index = 1;\n\twhile (index < tokens.length && tokens[index]?.startsWith(\"-\")) {\n\t\tconst option = tokens[index];\n\t\tconst normalizedOption = option.toLowerCase();\n\t\tindex += 1;\n\t\tif (option === \"--\") break;\n\t\tif (\n\t\t\toptionConsumesValue(option, GIT_GLOBAL_OPTIONS_WITH_VALUE) ||\n\t\t\toptionConsumesValue(normalizedOption, GIT_GLOBAL_OPTIONS_WITH_VALUE)\n\t\t) {\n\t\t\tindex += 1;\n\t\t}\n\t}\n\tconst command = tokens[index]?.toLowerCase();\n\tif (!command) return null;\n\tconst originalArgs = tokens.slice(index + 1);\n\treturn {\n\t\tcommand,\n\t\targs: originalArgs.map((argument) => argument.toLowerCase()),\n\t\toriginalArgs,\n\t};\n}\n\nfunction hasGitCleanForceDirectory(args: readonly string[]): boolean {\n\tlet hasForce = false;\n\tlet hasDirectory = false;\n\tfor (const arg of args) {\n\t\tif (arg === \"--force\") hasForce = true;\n\t\tif (arg === \"-f\") hasForce = true;\n\t\tif (arg === \"-d\") hasDirectory = true;\n\t\tif (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tconst flags = arg.slice(1).split(\"\");\n\t\t\tif (flags.includes(\"f\")) hasForce = true;\n\t\t\tif (flags.includes(\"d\")) hasDirectory = true;\n\t\t}\n\t}\n\treturn hasForce && hasDirectory;\n}\n\nfunction classifyProtectedGitCommand(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst gitCommand = findGitCommand(tokens);\n\tif (!gitCommand) return null;\n\n\tif (gitCommand.command === \"reset\" && gitCommand.args.includes(\"--hard\")) {\n\t\treturn verdict(\"confirm\", \"git.reset_hard\", \"git reset --hard can discard worktree changes.\");\n\t}\n\tif (gitCommand.command === \"restore\") {\n\t\tconst targetAll = gitCommand.originalArgs.includes(\".\") || gitCommand.originalArgs.includes(\"*\");\n\t\tconst rewritesWorktree = gitCommand.args.includes(\"--worktree\") || gitCommand.args.includes(\"--source\");\n\t\tif (targetAll || rewritesWorktree) {\n\t\t\treturn verdict(\"confirm\", \"git.restore\", \"git restore can discard or overwrite local worktree changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"checkout\" && gitCommand.originalArgs.includes(\".\")) {\n\t\treturn verdict(\"confirm\", \"git.checkout_dot\", \"git checkout . can overwrite local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"clean\" && hasGitCleanForceDirectory(gitCommand.args)) {\n\t\treturn verdict(\"confirm\", \"git.clean_force\", \"git clean -fd can delete untracked files and directories.\");\n\t}\n\tif (gitCommand.command === \"stash\" && gitCommand.originalArgs.length === 0) {\n\t\treturn verdict(\"confirm\", \"git.stash_bare\", \"Bare git stash can hide local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"add\") {\n\t\tif (gitCommand.originalArgs.includes(\".\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_dot\", \"git add . can stage unrelated local changes.\");\n\t\t}\n\t\tif (gitCommand.originalArgs.includes(\"-A\") || gitCommand.args.includes(\"--all\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_all\", \"git add -A can stage unrelated local changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"commit\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.no_verify\", \"git commit --no-verify bypasses repository verification hooks.\");\n\t}\n\tif (gitCommand.command === \"push\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.push_no_verify\", \"git push --no-verify bypasses remote verification hooks.\");\n\t}\n\tif (\n\t\tgitCommand.command === \"push\" &&\n\t\t(gitCommand.originalArgs.includes(\"-f\") ||\n\t\t\tgitCommand.args.includes(\"--force\") ||\n\t\t\tgitCommand.args.includes(\"--force-with-lease\"))\n\t) {\n\t\treturn verdict(\"confirm\", \"git.force_push\", \"Force-pushing can rewrite remote history.\");\n\t}\n\treturn null;\n}\n\nfunction classifyPrivilegeCommand(command: string): CommandVerdict | null {\n\tconst tokens = tokenizeShellSegment(command);\n\tconst index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) return null;\n\treturn verdict(\"confirm\", `priv.${privilege}`, `${privilege} requires explicit per-command confirmation.`);\n}\n\n/**\n * Locate the inline command-string argument for a `shell -c \"<cmd>\"` invocation.\n * Returns the token index of the command string, or -1 when the shell runs a\n * script file (no inline command to classify).\n */\nfunction findShellCommandStringIndex(tokens: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (/^-[a-z]*c$/i.test(token)) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Locate the inline program-string argument for an interpreter invocation\n * (`python -c`, `node -e`, `perl -e`, ...). Returns the token index of the\n * program string, or -1 when the interpreter runs a script file (no inline\n * program to classify).\n */\nfunction findInterpreterInlineIndex(tokens: readonly string[], flags: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (token === \"--\") return -1;\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (flags.includes(token) || flags.includes(token.toLowerCase())) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/** Drop leading xargs options (and their values) to expose the wrapped command. */\nfunction stripXargsOptions(args: readonly string[]): string[] {\n\tlet index = 0;\n\twhile (index < args.length) {\n\t\tconst token = args[index];\n\t\tif (!token.startsWith(\"-\") || token === \"-\") break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (!token.includes(\"=\") && XARGS_OPTIONS_WITH_VALUE.has(token)) index += 1;\n\t}\n\treturn args.slice(index);\n}\n\n/** Extract the command tokens of a `find ... -exec <cmd> {} ;|+` clause, if present. */\nfunction extractFindExec(tokens: readonly string[]): string[] | null {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index].toLowerCase();\n\t\tif (token === \"-exec\" || token === \"-execdir\" || token === \"-ok\" || token === \"-okdir\") {\n\t\t\tconst collected: string[] = [];\n\t\t\tfor (let inner = index + 1; inner < tokens.length; inner += 1) {\n\t\t\t\tconst piece = tokens[inner];\n\t\t\t\tif (piece === \";\" || piece === \"+\") break;\n\t\t\t\tif (piece === \"{}\") continue;\n\t\t\t\tcollected.push(piece);\n\t\t\t}\n\t\t\treturn collected.length > 0 ? collected : null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Classify the effective command hidden inside a wrapper executable so a\n * destructive or protected command cannot be smuggled past the classifier via\n * `bash -c`, `eval`, `xargs`, or `find -exec`. Returns null when the segment is\n * not a recognized wrapper.\n */\nfunction classifyWrappedCommand(command: string, depth: number): CommandVerdict | null {\n\tif (depth >= MAX_WRAP_DEPTH) return null;\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\n\tif (SHELL_WRAPPERS.has(executable)) {\n\t\tconst innerIndex = findShellCommandStringIndex(tokens);\n\t\tconst inner = innerIndex === -1 ? undefined : tokens[innerIndex];\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"eval\") {\n\t\tconst inner = tokens.slice(1).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"xargs\") {\n\t\tconst inner = stripXargsOptions(tokens.slice(1)).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"find\") {\n\t\tconst lowered = tokens.map((token) => token.toLowerCase());\n\t\tconst hasDelete = lowered.includes(\"-delete\");\n\t\tconst execTokens = extractFindExec(tokens);\n\t\tconst inner = execTokens?.join(\" \").trim();\n\t\tconst innerVerdict = inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t\tif (hasDelete) {\n\t\t\tconst deleteVerdict = verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t\"fs.find_delete\",\n\t\t\t\t\"find -delete removes matched files and directories.\",\n\t\t\t);\n\t\t\tif (!innerVerdict || RISK_RANK[deleteVerdict.risk] > RISK_RANK[innerVerdict.risk]) {\n\t\t\t\treturn deleteVerdict;\n\t\t\t}\n\t\t}\n\t\treturn innerVerdict ?? null;\n\t}\n\tconst inlineFlags = INTERPRETER_INLINE_FLAGS[executable];\n\tif (inlineFlags) {\n\t\tconst innerIndex = findInterpreterInlineIndex(tokens, inlineFlags);\n\t\tif (innerIndex === -1) return null; // script file path — allow\n\t\tconst program = tokens[innerIndex];\n\t\tif (INLINE_DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(program))) {\n\t\t\treturn verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t`interp.${executable}_inline_destructive`,\n\t\t\t\t`${executable} inline program can execute destructive shell commands.`,\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn null;\n}\n\n/** Flag commands that read, copy, or transmit a credential / secret file path. */\nfunction classifySecretAccess(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tif (tokens.length === 0) return null;\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token || token.startsWith(\"-\")) continue;\n\t\tif (/\\s/.test(token)) continue;\n\t\tif (SECRET_FILE_ALLOW_PATTERNS.some((pattern) => pattern.test(token))) continue;\n\t\tif (SECRET_FILE_STRICT_PATTERNS.some((pattern) => pattern.test(token))) {\n\t\t\treturn verdict(\"confirm\", \"secret.read_path\", \"Command references a credential or secret file path.\");\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction classifySingleCommand(command: string, depth: number): CommandVerdict {\n\tconst trimmed = command.trim();\n\tif (!trimmed) return allowVerdict();\n\n\tconst destructiveFilesystem = classifyDestructiveFilesystemCommand(trimmed);\n\tif (destructiveFilesystem) return destructiveFilesystem;\n\n\tconst wrapped = classifyWrappedCommand(trimmed, depth);\n\tif (wrapped && wrapped.risk === \"block\") return wrapped;\n\n\tconst protectedGit = classifyProtectedGitCommand(trimmed);\n\tif (protectedGit) return protectedGit;\n\n\tconst privilege = classifyPrivilegeCommand(trimmed);\n\tif (privilege) return privilege;\n\n\tconst secret = classifySecretAccess(trimmed);\n\tif (secret) return secret;\n\n\tif (wrapped && RISK_RANK[wrapped.risk] > RISK_RANK.allow) return wrapped;\n\n\treturn allowVerdict();\n}\n\nfunction classifyShellCommandInternal(command: string, depth: number): CommandVerdict {\n\tlet selected = classifySingleCommand(command, depth);\n\tfor (const segment of splitShellCommands(command)) {\n\t\tconst candidate = classifySingleCommand(segment, depth);\n\t\tif (RISK_RANK[candidate.risk] > RISK_RANK[selected.risk]) selected = candidate;\n\t\tif (selected.risk === \"block\") break;\n\t}\n\treturn selected;\n}\n\nexport function classifyShellCommand(command: string): CommandVerdict {\n\treturn classifyShellCommandInternal(command, 0);\n}\n\nexport function isDestructiveFilesystem(command: string): boolean {\n\treturn classifyDestructiveFilesystemCommand(command) !== null;\n}\n\nexport function isProtectedGitOperation(command: string): boolean {\n\treturn classifyProtectedGitCommand(command) !== null;\n}\n\nexport function isPrivilegeEscalation(command: string): boolean {\n\treturn classifyPrivilegeCommand(command) !== null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"command-safety.js","sourceRoot":"","sources":["../../src/core/command-safety.ts"],"names":[],"mappings":"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","sourcesContent":["// allow: SIZE_OK - legacy safety rules table; this change only preserves existing typed behavior.\nexport type CommandRisk = \"block\" | \"confirm\" | \"allow\";\n\nexport interface CommandVerdict {\n\trisk: CommandRisk;\n\trule: string;\n\treason: string;\n}\n\ninterface CommandPrefixResult {\n\ttokens: string[];\n\tprivilege: string | undefined;\n}\n\nconst RISK_RANK: Readonly<Record<CommandRisk, number>> = {\n\tallow: 0,\n\tconfirm: 1,\n\tblock: 2,\n};\n\nconst PRIVILEGE_COMMANDS = new Set([\"sudo\", \"su\", \"doas\", \"pkexec\"]);\nconst SUDO_OPTIONS_WITH_VALUE = new Set([\n\t\"-C\",\n\t\"--close-from\",\n\t\"-g\",\n\t\"--group\",\n\t\"-h\",\n\t\"--host\",\n\t\"-p\",\n\t\"--prompt\",\n\t\"-T\",\n\t\"--command-timeout\",\n\t\"-u\",\n\t\"--user\",\n]);\nconst GIT_GLOBAL_OPTIONS_WITH_VALUE = new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--namespace\"]);\n\n/** Maximum recursion depth when unwrapping shell wrappers (bash -c, eval, xargs, find -exec). */\nconst MAX_WRAP_DEPTH = 6;\n\n/** Executables that run an inline command string passed via a `-c`-style flag. */\nconst SHELL_WRAPPERS = new Set([\"bash\", \"sh\", \"zsh\", \"dash\", \"ksh\", \"ash\"]);\n\n/**\n * Script interpreters that execute an inline program string (via a -c/-e/-r\n * style flag). Running inline code can execute arbitrary shell commands, so a\n * matched destructive/exec keyword inside the program escalates the call to\n * confirm-tier. A plain script-file path stays allow-tier.\n */\nconst INTERPRETER_INLINE_FLAGS: Readonly<Record<string, readonly string[]>> = {\n\tpython: [\"-c\"],\n\tpython2: [\"-c\"],\n\tpython3: [\"-c\"],\n\tnode: [\"-e\", \"--eval\", \"-p\", \"--print\"],\n\tperl: [\"-e\", \"-E\"],\n\truby: [\"-e\", \"-E\"],\n\tphp: [\"-r\", \"--run\"],\n\tlua: [\"-e\"],\n\texpect: [\"-c\"],\n};\n\n/**\n * Patterns that indicate an inline program invokes a shell command or\n * performs a destructive filesystem operation. Matching any of these inside\n * a `-c`/`-e`/`-r` program string escalates the call to confirm-tier.\n */\nconst INLINE_DESTRUCTIVE_PATTERNS: readonly RegExp[] = [\n\t/\\bos\\.system\\s*\\(/,\n\t/\\bos\\.remove\\s*\\(/,\n\t/\\bos\\.unlink\\s*\\(/,\n\t/\\bshutil\\.rmtree\\s*\\(/,\n\t/\\bsubprocess\\.(?:call|run|check_call|check_output|Popen)\\s*\\(/,\n\t/\\bexec(?:Sync|File)?\\s*\\(/,\n\t/\\bspawn(?:Sync)?\\s*\\(/,\n\t/\\bchild_process\\b/,\n\t/\\brequire\\s*\\(\\s*['\"]child_process['\"]\\s*\\)/,\n\t/\\b(?:system|popen)\\s*\\(/,\n\t/`[^`]*\\brm\\s+-/m,\n\t/\\$\\(\\s*[^)]*\\brm\\s+-/,\n];\n\n/** xargs options that consume a following value token before the wrapped command begins. */\nconst XARGS_OPTIONS_WITH_VALUE = new Set([\n\t\"-I\",\n\t\"-i\",\n\t\"-n\",\n\t\"-L\",\n\t\"-P\",\n\t\"-d\",\n\t\"-E\",\n\t\"-s\",\n\t\"-a\",\n\t\"--max-args\",\n\t\"--max-procs\",\n\t\"--delimiter\",\n\t\"--arg-file\",\n\t\"--max-lines\",\n\t\"--replace\",\n]);\n\n/**\n * Credential / secret file patterns. A bash argv token matching one of these is\n * treated as a `confirm`-tier access: headless callers (LLM tool calls, RPC bash)\n * deny by default, while an interactive user can still approve reading their own\n * files. These complement the §0.1 freedom safety floor, which hard-denies the\n * same paths on the entry points it guards.\n */\nconst SECRET_FILE_STRICT_PATTERNS: readonly RegExp[] = [\n\t/(^|\\/)\\.env(\\.|$)/i,\n\t/(^|\\/)\\.npmrc$/i,\n\t/(^|\\/)\\.netrc$/i,\n\t/(^|\\/)\\.pgpass$/i,\n\t/(^|\\/)\\.aws\\/credentials$/i,\n\t/(^|\\/)credentials$/i,\n\t/(^|\\/)auth\\.json$/i,\n\t/(^|\\/)id_(rsa|dsa|ecdsa|ed25519)$/i,\n\t/\\.(pem|key|p12|pfx|keystore|jks)$/i,\n\t/(^|\\/)secrets?(\\.[^/]+)?$/i,\n];\n\n/** Benign sibling files that look secret-ish but never hold credentials. */\nconst SECRET_FILE_ALLOW_PATTERNS: readonly RegExp[] = [/(^|\\/)\\.env\\.(example|sample|template|dist|defaults?)$/i];\nconst SEARCH_COMMANDS = new Set([\"grep\", \"egrep\", \"fgrep\", \"rg\"]);\nconst HOME_VARIABLE_RM_TARGETS = new Set([\"$home\", \"$\" + \"{home}\", \"$home/\", \"$\" + \"{home}/\"]);\n\nfunction verdict(risk: CommandRisk, rule: string, reason: string): CommandVerdict {\n\treturn { risk, rule, reason };\n}\n\nfunction allowVerdict(): CommandVerdict {\n\treturn verdict(\"allow\", \"command.allow\", \"No destructive or protected command pattern matched.\");\n}\n\nfunction isEnvAssignment(token: string | undefined): boolean {\n\treturn token !== undefined && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(token);\n}\n\nfunction tokenizeShellSegment(segment: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (const character of segment.trim()) {\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tif (character === quote) {\n\t\t\t\tquote = undefined;\n\t\t\t} else {\n\t\t\t\tcurrent += character;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/.test(character)) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (escaped) current += \"\\\\\";\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n\nfunction splitShellCommands(command: string): string[] {\n\tconst segments: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (let index = 0; index < command.length; index += 1) {\n\t\tconst character = command[index];\n\t\tconst next = command[index + 1];\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tcurrent += character;\n\t\t\tif (character === quote) quote = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \";\" || character === \"|\" || character === \"\\n\" || character === \"\\r\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"&\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tif (next === \"&\") index += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (current.trim()) segments.push(current.trim());\n\treturn segments;\n}\n\nfunction skipEnvironmentPrefix(tokens: readonly string[], startIndex: number): number {\n\tlet index = startIndex;\n\twhile (isEnvAssignment(tokens[index])) index += 1;\n\tif (tokens[index]?.toLowerCase() !== \"env\") return index;\n\n\tindex += 1;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (isEnvAssignment(token)) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (token?.startsWith(\"-\")) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn index;\n}\n\nfunction optionConsumesValue(option: string, optionsWithValue: ReadonlySet<string>): boolean {\n\tif (option.includes(\"=\")) return false;\n\treturn optionsWithValue.has(option);\n}\n\nfunction skipPrivilegeOptions(tokens: readonly string[], startIndex: number, privilege: string): number {\n\tif (privilege !== \"sudo\" && privilege !== \"doas\") return startIndex;\n\n\tlet index = startIndex;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (!token?.startsWith(\"-\")) break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (optionConsumesValue(token, SUDO_OPTIONS_WITH_VALUE)) index += 1;\n\t}\n\treturn index;\n}\n\nfunction stripCommandPrefixes(tokens: readonly string[]): CommandPrefixResult {\n\tlet index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) {\n\t\treturn { tokens: tokens.slice(index), privilege: undefined };\n\t}\n\n\tindex += 1;\n\tindex = skipPrivilegeOptions(tokens, index, privilege ?? \"\");\n\tindex = skipEnvironmentPrefix(tokens, index);\n\treturn { tokens: tokens.slice(index), privilege };\n}\n\nfunction hasForkBomb(command: string): boolean {\n\treturn command.replace(/\\s/g, \"\").includes(\":(){:|:&};:\");\n}\n\nfunction normalizePathTarget(target: string): string {\n\treturn target.replace(/\\/+$/g, (match) => (target === match ? \"/\" : \"\")).replace(/\\/+/g, \"/\");\n}\n\nfunction classifyRmTarget(target: string): CommandVerdict | null {\n\tconst normalized = normalizePathTarget(target);\n\tif (normalized === \"/\" || normalized === \"/*\" || normalized === \"/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_root\", \"Recursive forced rm targets filesystem root.\");\n\t}\n\tif (normalized === \"~\" || normalized === \"~/\" || normalized === \"~/*\" || normalized === \"~/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_home\", \"Recursive forced rm targets the home directory.\");\n\t}\n\t// $HOME / ${HOME} expands to the user home directory at runtime.\n\tconst lower = normalized.toLowerCase();\n\tif (HOME_VARIABLE_RM_TARGETS.has(lower)) {\n\t\treturn verdict(\n\t\t\t\"confirm\",\n\t\t\t\"fs.rm_rf_home_var\",\n\t\t\t\"Recursive forced rm targets $HOME, which expands to the user home directory.\",\n\t\t);\n\t}\n\t// ~username targets another user home directory.\n\tif (/^~[A-Za-z0-9._-]/.test(normalized)) {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_user_home\", \"Recursive forced rm targets another user home directory.\");\n\t}\n\t// \".\" or \"./\" — current working directory (project root deletion risk).\n\tif (normalized === \".\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_cwd\", \"Recursive forced rm targets the current working directory.\");\n\t}\n\t// \"..\" — parent directory.\n\tif (normalized === \"..\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_parent\", \"Recursive forced rm targets the parent directory.\");\n\t}\n\treturn null;\n}\n\nfunction classifyRm(tokens: readonly string[]): CommandVerdict | null {\n\tlet hasRecursive = false;\n\tlet hasForce = false;\n\tlet endOfOptions = false;\n\tconst targets: string[] = [];\n\n\tfor (const token of tokens.slice(1)) {\n\t\tif (!endOfOptions && token === \"--\") {\n\t\t\tendOfOptions = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!endOfOptions && token.startsWith(\"-\") && token !== \"-\") {\n\t\t\tif (token === \"--recursive\") hasRecursive = true;\n\t\t\tif (token === \"--force\") hasForce = true;\n\t\t\tif (!token.startsWith(\"--\")) {\n\t\t\t\tconst flagCharacters = token.slice(1).split(\"\");\n\t\t\t\tif (flagCharacters.some((flag) => flag === \"r\" || flag === \"R\")) hasRecursive = true;\n\t\t\t\tif (flagCharacters.includes(\"f\")) hasForce = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\ttargets.push(token);\n\t}\n\n\tif (!hasRecursive || !hasForce) return null;\n\tfor (const target of targets) {\n\t\tconst targetVerdict = classifyRmTarget(target);\n\t\tif (targetVerdict) return targetVerdict;\n\t}\n\treturn null;\n}\n\nfunction isBlockDeviceOutput(token: string): boolean {\n\treturn /^of=\\/dev\\/(?:sd[a-z][a-z0-9]*|nvme[0-9a-z]+|disk(?:[0-9].*|\\/.*|$))/i.test(token);\n}\n\nfunction classifyDestructiveFilesystemCommand(command: string): CommandVerdict | null {\n\tif (hasForkBomb(command)) {\n\t\treturn verdict(\"block\", \"process.fork_bomb\", \"Shell fork bomb pattern would exhaust process resources.\");\n\t}\n\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\tif (executable === \"rm\") return classifyRm(tokens);\n\tif (executable.startsWith(\"mkfs\")) {\n\t\treturn verdict(\"block\", \"fs.mkfs\", \"Filesystem formatting commands are blocked.\");\n\t}\n\tif (executable === \"dd\" && tokens.slice(1).some(isBlockDeviceOutput)) {\n\t\treturn verdict(\"block\", \"fs.dd_block_device\", \"dd writes directly to a block-device output path.\");\n\t}\n\treturn null;\n}\n\nfunction findGitCommand(tokens: readonly string[]): { command: string; args: string[]; originalArgs: string[] } | null {\n\tif (tokens[0]?.toLowerCase() !== \"git\") return null;\n\tlet index = 1;\n\twhile (index < tokens.length && tokens[index]?.startsWith(\"-\")) {\n\t\tconst option = tokens[index];\n\t\tconst normalizedOption = option.toLowerCase();\n\t\tindex += 1;\n\t\tif (option === \"--\") break;\n\t\tif (\n\t\t\toptionConsumesValue(option, GIT_GLOBAL_OPTIONS_WITH_VALUE) ||\n\t\t\toptionConsumesValue(normalizedOption, GIT_GLOBAL_OPTIONS_WITH_VALUE)\n\t\t) {\n\t\t\tindex += 1;\n\t\t}\n\t}\n\tconst command = tokens[index]?.toLowerCase();\n\tif (!command) return null;\n\tconst originalArgs = tokens.slice(index + 1);\n\treturn {\n\t\tcommand,\n\t\targs: originalArgs.map((argument) => argument.toLowerCase()),\n\t\toriginalArgs,\n\t};\n}\n\nfunction hasGitCleanForceDirectory(args: readonly string[]): boolean {\n\tlet hasForce = false;\n\tlet hasDirectory = false;\n\tfor (const arg of args) {\n\t\tif (arg === \"--force\") hasForce = true;\n\t\tif (arg === \"-f\") hasForce = true;\n\t\tif (arg === \"-d\") hasDirectory = true;\n\t\tif (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tconst flags = arg.slice(1).split(\"\");\n\t\t\tif (flags.includes(\"f\")) hasForce = true;\n\t\t\tif (flags.includes(\"d\")) hasDirectory = true;\n\t\t}\n\t}\n\treturn hasForce && hasDirectory;\n}\n\nfunction classifyProtectedGitCommand(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst gitCommand = findGitCommand(tokens);\n\tif (!gitCommand) return null;\n\n\tif (gitCommand.command === \"reset\" && gitCommand.args.includes(\"--hard\")) {\n\t\treturn verdict(\"confirm\", \"git.reset_hard\", \"git reset --hard can discard worktree changes.\");\n\t}\n\tif (gitCommand.command === \"restore\") {\n\t\tconst targetAll = gitCommand.originalArgs.includes(\".\") || gitCommand.originalArgs.includes(\"*\");\n\t\tconst rewritesWorktree = gitCommand.args.includes(\"--worktree\") || gitCommand.args.includes(\"--source\");\n\t\tif (targetAll || rewritesWorktree) {\n\t\t\treturn verdict(\"confirm\", \"git.restore\", \"git restore can discard or overwrite local worktree changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"checkout\" && gitCommand.originalArgs.includes(\".\")) {\n\t\treturn verdict(\"confirm\", \"git.checkout_dot\", \"git checkout . can overwrite local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"clean\" && hasGitCleanForceDirectory(gitCommand.args)) {\n\t\treturn verdict(\"confirm\", \"git.clean_force\", \"git clean -fd can delete untracked files and directories.\");\n\t}\n\tif (gitCommand.command === \"stash\" && gitCommand.originalArgs.length === 0) {\n\t\treturn verdict(\"confirm\", \"git.stash_bare\", \"Bare git stash can hide local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"add\") {\n\t\tif (gitCommand.originalArgs.includes(\".\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_dot\", \"git add . can stage unrelated local changes.\");\n\t\t}\n\t\tif (gitCommand.originalArgs.includes(\"-A\") || gitCommand.args.includes(\"--all\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_all\", \"git add -A can stage unrelated local changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"commit\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.no_verify\", \"git commit --no-verify bypasses repository verification hooks.\");\n\t}\n\tif (gitCommand.command === \"push\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.push_no_verify\", \"git push --no-verify bypasses remote verification hooks.\");\n\t}\n\tif (\n\t\tgitCommand.command === \"push\" &&\n\t\t(gitCommand.originalArgs.includes(\"-f\") ||\n\t\t\tgitCommand.args.includes(\"--force\") ||\n\t\t\tgitCommand.args.includes(\"--force-with-lease\"))\n\t) {\n\t\treturn verdict(\"confirm\", \"git.force_push\", \"Force-pushing can rewrite remote history.\");\n\t}\n\treturn null;\n}\n\nfunction classifyPrivilegeCommand(command: string): CommandVerdict | null {\n\tconst tokens = tokenizeShellSegment(command);\n\tconst index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) return null;\n\treturn verdict(\"confirm\", `priv.${privilege}`, `${privilege} requires explicit per-command confirmation.`);\n}\n\n/**\n * Locate the inline command-string argument for a `shell -c \"<cmd>\"` invocation.\n * Returns the token index of the command string, or -1 when the shell runs a\n * script file (no inline command to classify).\n */\nfunction findShellCommandStringIndex(tokens: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (/^-[a-z]*c$/i.test(token)) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Locate the inline program-string argument for an interpreter invocation\n * (`python -c`, `node -e`, `perl -e`, ...). Returns the token index of the\n * program string, or -1 when the interpreter runs a script file (no inline\n * program to classify).\n */\nfunction findInterpreterInlineIndex(tokens: readonly string[], flags: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (token === \"--\") return -1;\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (flags.includes(token) || flags.includes(token.toLowerCase())) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/** Drop leading xargs options (and their values) to expose the wrapped command. */\nfunction stripXargsOptions(args: readonly string[]): string[] {\n\tlet index = 0;\n\twhile (index < args.length) {\n\t\tconst token = args[index];\n\t\tif (!token.startsWith(\"-\") || token === \"-\") break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (!token.includes(\"=\") && XARGS_OPTIONS_WITH_VALUE.has(token)) index += 1;\n\t}\n\treturn args.slice(index);\n}\n\n/** Extract the command tokens of a `find ... -exec <cmd> {} ;|+` clause, if present. */\nfunction extractFindExec(tokens: readonly string[]): string[] | null {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index].toLowerCase();\n\t\tif (token === \"-exec\" || token === \"-execdir\" || token === \"-ok\" || token === \"-okdir\") {\n\t\t\tconst collected: string[] = [];\n\t\t\tfor (let inner = index + 1; inner < tokens.length; inner += 1) {\n\t\t\t\tconst piece = tokens[inner];\n\t\t\t\tif (piece === \";\" || piece === \"+\") break;\n\t\t\t\tif (piece === \"{}\") continue;\n\t\t\t\tcollected.push(piece);\n\t\t\t}\n\t\t\treturn collected.length > 0 ? collected : null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Classify the effective command hidden inside a wrapper executable so a\n * destructive or protected command cannot be smuggled past the classifier via\n * `bash -c`, `eval`, `xargs`, or `find -exec`. Returns null when the segment is\n * not a recognized wrapper.\n */\nfunction classifyWrappedCommand(command: string, depth: number): CommandVerdict | null {\n\tif (depth >= MAX_WRAP_DEPTH) return null;\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\n\tif (SHELL_WRAPPERS.has(executable)) {\n\t\tconst innerIndex = findShellCommandStringIndex(tokens);\n\t\tconst inner = innerIndex === -1 ? undefined : tokens[innerIndex];\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"eval\") {\n\t\tconst inner = tokens.slice(1).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"xargs\") {\n\t\tconst inner = stripXargsOptions(tokens.slice(1)).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"find\") {\n\t\tconst lowered = tokens.map((token) => token.toLowerCase());\n\t\tconst hasDelete = lowered.includes(\"-delete\");\n\t\tconst execTokens = extractFindExec(tokens);\n\t\tconst inner = execTokens?.join(\" \").trim();\n\t\tconst innerVerdict = inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t\tif (hasDelete) {\n\t\t\tconst deleteVerdict = verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t\"fs.find_delete\",\n\t\t\t\t\"find -delete removes matched files and directories.\",\n\t\t\t);\n\t\t\tif (!innerVerdict || RISK_RANK[deleteVerdict.risk] > RISK_RANK[innerVerdict.risk]) {\n\t\t\t\treturn deleteVerdict;\n\t\t\t}\n\t\t}\n\t\treturn innerVerdict ?? null;\n\t}\n\tconst inlineFlags = INTERPRETER_INLINE_FLAGS[executable];\n\tif (inlineFlags) {\n\t\tconst innerIndex = findInterpreterInlineIndex(tokens, inlineFlags);\n\t\tif (innerIndex === -1) return null; // script file path — allow\n\t\tconst program = tokens[innerIndex];\n\t\tif (INLINE_DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(program))) {\n\t\t\treturn verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t`interp.${executable}_inline_destructive`,\n\t\t\t\t`${executable} inline program can execute destructive shell commands.`,\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn null;\n}\n\nfunction isSearchPatternArgument(tokens: readonly string[], index: number): boolean {\n\tconst executable = tokens[0]?.toLowerCase();\n\treturn SEARCH_COMMANDS.has(executable ?? \"\") && tokens[index - 1] === \"--\";\n}\n\n/** Flag commands that read, copy, or transmit a credential / secret file path. */\nfunction classifySecretAccess(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tif (tokens.length === 0) return null;\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token || token.startsWith(\"-\")) continue;\n\t\tif (isSearchPatternArgument(tokens, index)) continue;\n\t\tif (/\\s/.test(token)) continue;\n\t\tif (SECRET_FILE_ALLOW_PATTERNS.some((pattern) => pattern.test(token))) continue;\n\t\tif (SECRET_FILE_STRICT_PATTERNS.some((pattern) => pattern.test(token))) {\n\t\t\treturn verdict(\"confirm\", \"secret.read_path\", \"Command references a credential or secret file path.\");\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction classifySingleCommand(command: string, depth: number): CommandVerdict {\n\tconst trimmed = command.trim();\n\tif (!trimmed) return allowVerdict();\n\n\tconst destructiveFilesystem = classifyDestructiveFilesystemCommand(trimmed);\n\tif (destructiveFilesystem) return destructiveFilesystem;\n\n\tconst wrapped = classifyWrappedCommand(trimmed, depth);\n\tif (wrapped && wrapped.risk === \"block\") return wrapped;\n\n\tconst protectedGit = classifyProtectedGitCommand(trimmed);\n\tif (protectedGit) return protectedGit;\n\n\tconst privilege = classifyPrivilegeCommand(trimmed);\n\tif (privilege) return privilege;\n\n\tconst secret = classifySecretAccess(trimmed);\n\tif (secret) return secret;\n\n\tif (wrapped && RISK_RANK[wrapped.risk] > RISK_RANK.allow) return wrapped;\n\n\treturn allowVerdict();\n}\n\nfunction classifyShellCommandInternal(command: string, depth: number): CommandVerdict {\n\tlet selected = classifySingleCommand(command, depth);\n\tfor (const segment of splitShellCommands(command)) {\n\t\tconst candidate = classifySingleCommand(segment, depth);\n\t\tif (RISK_RANK[candidate.risk] > RISK_RANK[selected.risk]) selected = candidate;\n\t\tif (selected.risk === \"block\") break;\n\t}\n\treturn selected;\n}\n\nexport function classifyShellCommand(command: string): CommandVerdict {\n\treturn classifyShellCommandInternal(command, 0);\n}\n\nexport function isDestructiveFilesystem(command: string): boolean {\n\treturn classifyDestructiveFilesystemCommand(command) !== null;\n}\n\nexport function isProtectedGitOperation(command: string): boolean {\n\treturn classifyProtectedGitCommand(command) !== null;\n}\n\nexport function isPrivilegeEscalation(command: string): boolean {\n\treturn classifyPrivilegeCommand(command) !== null;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-headroom.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,gCAAgC,EAGrC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,MAAM,oCAAoC,CAAC;AAqB5C,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,2BAA2B,EACnC,MAAM,GAAE,uBAAyD,GAC/D,gCAAgC,
|
|
1
|
+
{"version":3,"file":"context-budget-headroom.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,gCAAgC,EAGrC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,MAAM,oCAAoC,CAAC;AAqB5C,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,2BAA2B,EACnC,MAAM,GAAE,uBAAyD,GAC/D,gCAAgC,CAwClC;AAgGD,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AACzF,OAAO,EACN,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,oCAAoC,EACzC,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACpC,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACvB,+BAA+B,EAC/B,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,wBAAwB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,mBAAmB,EACnB,KAAK,2BAA2B,GAChC,MAAM,oCAAoC,CAAC","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = representationPreference(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\nfunction representationPreference(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2,\n\ttight: boolean,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
|
|
@@ -22,6 +22,10 @@ export function chooseHeadroomRepresentation(item, budget, policy = DEFAULT_HEAD
|
|
|
22
22
|
const tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;
|
|
23
23
|
const globalTight = budget.remainingGlobalTokens < full;
|
|
24
24
|
const tight = tierOver || globalTight;
|
|
25
|
+
// A candidate must fit BOTH the global remainder and the tier remainder, so a
|
|
26
|
+
// smaller representation is chosen instead of over-selecting full text and
|
|
27
|
+
// having the caller drop the whole item at the tier ceiling.
|
|
28
|
+
const spendable = Math.min(budget.remainingGlobalTokens, Math.max(0, budget.tierCeilingTokens - budget.tierUsedTokens));
|
|
25
29
|
if (item.priority === "hard" || item.required) {
|
|
26
30
|
const fullCandidate = candidates.find((candidate) => candidate.kind === "full");
|
|
27
31
|
if (fullCandidate) {
|
|
@@ -34,7 +38,7 @@ export function chooseHeadroomRepresentation(item, budget, policy = DEFAULT_HEAD
|
|
|
34
38
|
if (isMoreExpensiveThanFull(candidate, full)) {
|
|
35
39
|
continue;
|
|
36
40
|
}
|
|
37
|
-
if (candidate.kind !== "omit" && candidate.estimatedTokens >
|
|
41
|
+
if (candidate.kind !== "omit" && candidate.estimatedTokens > spendable) {
|
|
38
42
|
continue;
|
|
39
43
|
}
|
|
40
44
|
const score = representationPreference(candidate, item, policy, tight);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-headroom.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"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","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > budget.remainingGlobalTokens) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = representationPreference(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\nfunction representationPreference(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2,\n\ttight: boolean,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget-headroom.js","sourceRoot":"","sources":["../../src/core/context-budget-headroom.ts"],"names":[],"mappings":"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","sourcesContent":["import { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nimport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextRepresentationCandidateV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\tfullTextTokens,\n\ttype HeadroomQualityPolicyV2,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n\nconst PRIORITY_WEIGHT: Record<ContextBudgetPriorityV2, number> = {\n\thard: 1_000_000,\n\thigh: 120,\n\tmedium: 60,\n\tlow: 15,\n};\n\nconst FIDELITY_BONUS: Record<ContextRepresentationCandidateV2[\"fidelity\"], number> = {\n\texact: 6,\n\tbounded: 3,\n\treversible: 4,\n\tlossy: 0,\n};\n\nconst CACHE_HIT_BONUS = 8;\nconst TIGHT_CACHE_HIT_BONUS = 20;\nconst MATERIALIZED_CACHE_HIT_BONUS = 8;\nconst TIGHT_MATERIALIZED_CACHE_HIT_BONUS = 35;\n\nexport function chooseHeadroomRepresentation(\n\titem: ContextBudgetItemV2,\n\tbudget: RepresentationBudgetContext,\n\tpolicy: HeadroomQualityPolicyV2 = DEFAULT_HEADROOM_QUALITY_POLICY,\n): ContextRepresentationCandidateV2 {\n\tconst candidates = item.representations ?? deriveRepresentationCandidates(item, policy);\n\tconst full = fullTextTokens(item);\n\tconst tierOver = budget.tierUsedTokens >= budget.tierCeilingTokens;\n\tconst globalTight = budget.remainingGlobalTokens < full;\n\tconst tight = tierOver || globalTight;\n\t// A candidate must fit BOTH the global remainder and the tier remainder, so a\n\t// smaller representation is chosen instead of over-selecting full text and\n\t// having the caller drop the whole item at the tier ceiling.\n\tconst spendable = Math.min(\n\t\tbudget.remainingGlobalTokens,\n\t\tMath.max(0, budget.tierCeilingTokens - budget.tierUsedTokens),\n\t);\n\n\tif (item.priority === \"hard\" || item.required) {\n\t\tconst fullCandidate = candidates.find((candidate) => candidate.kind === \"full\");\n\t\tif (fullCandidate) {\n\t\t\treturn fullCandidate;\n\t\t}\n\t}\n\n\tlet best: ContextRepresentationCandidateV2 | undefined;\n\tlet bestScore = Number.NEGATIVE_INFINITY;\n\tfor (const candidate of candidates) {\n\t\tif (isMoreExpensiveThanFull(candidate, full)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (candidate.kind !== \"omit\" && candidate.estimatedTokens > spendable) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = representationPreference(candidate, item, policy, tight);\n\t\tif (\n\t\t\tscore > bestScore ||\n\t\t\t(score === bestScore && best !== undefined && compareCandidatesForChoice(candidate, best) < 0)\n\t\t) {\n\t\t\tbest = candidate;\n\t\t\tbestScore = score;\n\t\t}\n\t}\n\treturn best ?? candidates.find((candidate) => candidate.kind === \"omit\") ?? omitCandidate(item);\n}\n\nfunction omitCandidate(item: ContextBudgetItemV2): ContextRepresentationCandidateV2 {\n\treturn {\n\t\tkind: \"omit\",\n\t\ttext: \"\",\n\t\testimatedTokens: 0,\n\t\tfidelity: \"lossy\",\n\t\tsourceRef: item.sourceRef,\n\t};\n}\n\nfunction isMoreExpensiveThanFull(candidate: ContextRepresentationCandidateV2, fullTokens: number): boolean {\n\treturn candidate.kind !== \"full\" && candidate.kind !== \"omit\" && candidate.estimatedTokens >= fullTokens;\n}\n\nfunction representationPreference(\n\tcandidate: ContextRepresentationCandidateV2,\n\titem: ContextBudgetItemV2,\n\tpolicy: HeadroomQualityPolicyV2,\n\ttight: boolean,\n): number {\n\tconst priority = PRIORITY_WEIGHT[item.priority];\n\tconst cost = candidate.estimatedTokens;\n\tconst fidelity = FIDELITY_BONUS[candidate.fidelity];\n\tconst retrievable = item.sourceRef?.retrievable === true;\n\tconst cacheBonus =\n\t\tcandidate.cache?.hit === true\n\t\t\t? candidate.cache.keyKind === \"materialized\"\n\t\t\t\t? tight\n\t\t\t\t\t? TIGHT_MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t\t: MATERIALIZED_CACHE_HIT_BONUS\n\t\t\t\t: tight\n\t\t\t\t\t? TIGHT_CACHE_HIT_BONUS\n\t\t\t\t\t: CACHE_HIT_BONUS\n\t\t\t: 0;\n\n\tswitch (candidate.kind) {\n\t\tcase \"full\": {\n\t\t\tlet score = priority + fidelity + cacheBonus - cost * 0.05;\n\t\t\tif (tight) score -= 50;\n\t\t\tif (isHistoryLike(item) && (item.ageTurns ?? 0) >= policy.summaryMaxAgeTurns) {\n\t\t\t\tscore -= 40;\n\t\t\t}\n\t\t\treturn score;\n\t\t}\n\t\tcase \"pointer\": {\n\t\t\tif (!policy.preferPointerForRetrievable || !retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tlet score = 40 + fidelity + cacheBonus - cost * 0.2;\n\t\t\tif (tight) score += 20;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"summary\": {\n\t\t\tconst base = isHistoryLike(item) ? 30 : 10;\n\t\t\tlet score = base + ageBonus(item) + fidelity + cacheBonus - cost * 0.3;\n\t\t\tif (tight) score += 15;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"headroom-compressed\": {\n\t\t\tif (!retrievable) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tconst large = fullTextTokens(item) > policy.headroomThresholdTokens ? 35 : 5;\n\t\t\tlet score = large + 10 + fidelity + cacheBonus - cost * 0.5;\n\t\t\tif (tight) score += 18;\n\t\t\treturn score;\n\t\t}\n\t\tcase \"omit\": {\n\t\t\tif (!policy.allowOmit) {\n\t\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t\t}\n\t\t\tif (tight && item.priority === \"low\" && retrievable) {\n\t\t\t\treturn 12;\n\t\t\t}\n\t\t\treturn Number.NEGATIVE_INFINITY;\n\t\t}\n\t}\n}\n\nfunction ageBonus(item: ContextBudgetItemV2): number {\n\treturn Math.min(20, (item.ageTurns ?? 0) * 3);\n}\n\nfunction isHistoryLike(item: ContextBudgetItemV2): boolean {\n\treturn item.tier === \"history\" || item.tier === \"evidence\" || item.tier === \"scratch\";\n}\n\nfunction compareCandidatesForChoice(a: ContextRepresentationCandidateV2, b: ContextRepresentationCandidateV2): number {\n\tif (a.estimatedTokens !== b.estimatedTokens) {\n\t\treturn a.estimatedTokens - b.estimatedTokens;\n\t}\n\treturn a.kind.localeCompare(b.kind);\n}\n\nexport { deriveRepresentationCandidates } from \"./context-budget-headroom-candidates.ts\";\nexport {\n\ttype ContextBudgetItemV2,\n\ttype ContextBudgetPriorityV2,\n\ttype ContextBudgetTierV2,\n\ttype ContextEvidenceKindV2,\n\ttype ContextRepresentationCacheMetadataV2,\n\ttype ContextRepresentationCandidateV2,\n\ttype ContextRepresentationFidelityV2,\n\ttype ContextRepresentationKindV2,\n\ttype ContextSourceRefV2,\n\tDEFAULT_HEADROOM_QUALITY_POLICY,\n\testimatePointerTokens,\n\tfnv1aHex,\n\tfullTextTokens,\n\tgetHeadroomRuntimeStatus,\n\ttype HeadroomQualityPolicyV2,\n\ttype HeadroomRuntimeStatusV2,\n\theuristicTokenCount,\n\ttype RepresentationBudgetContext,\n} from \"./context-budget-headroom-types.ts\";\n"]}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { ContextBudgetCacheProviderV2, ContextBudgetNegativeCacheEntryV2, ContextBudgetPlanCacheEntryV2, ContextBudgetPlanCacheReadV2, ContextBudgetRepresentationCacheEntryV2, ContextBudgetRepresentationCacheReadV2 } from "./context-budget-v2-types.ts";
|
|
2
|
-
export declare function createMemoryContextBudgetCacheProviderV2(): ContextBudgetCacheProviderV2;
|
|
1
|
+
import type { ContextBudgetCacheLayerV2, ContextBudgetCacheProviderV2, ContextBudgetNegativeCacheEntryV2, ContextBudgetPlanCacheEntryV2, ContextBudgetPlanCacheReadV2, ContextBudgetRepresentationCacheEntryV2, ContextBudgetRepresentationCacheReadV2 } from "./context-budget-v2-types.ts";
|
|
2
|
+
export declare function createMemoryContextBudgetCacheProviderV2(layer?: ContextBudgetCacheLayerV2): ContextBudgetCacheProviderV2;
|
|
3
3
|
export declare class MemoryContextBudgetCacheProviderV2 implements ContextBudgetCacheProviderV2 {
|
|
4
4
|
private readonly representations;
|
|
5
5
|
private readonly negatives;
|
|
6
6
|
private readonly plans;
|
|
7
|
+
private readonly layer;
|
|
8
|
+
constructor(layer: ContextBudgetCacheLayerV2);
|
|
7
9
|
readRepresentation(key: string): ContextBudgetRepresentationCacheReadV2 | undefined;
|
|
8
10
|
writeRepresentation(input: {
|
|
9
11
|
readonly key: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-v2-cache-provider.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-cache-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,4BAA4B,EAC5B,iCAAiC,EACjC,6BAA6B,EAC7B,4BAA4B,EAC5B,uCAAuC,EACvC,sCAAsC,EACtC,MAAM,8BAA8B,CAAC;AAEtC,wBAAgB,wCAAwC,
|
|
1
|
+
{"version":3,"file":"context-budget-v2-cache-provider.d.ts","sourceRoot":"","sources":["../../src/core/context-budget-v2-cache-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,yBAAyB,EACzB,4BAA4B,EAC5B,iCAAiC,EACjC,6BAA6B,EAC7B,4BAA4B,EAC5B,uCAAuC,EACvC,sCAAsC,EACtC,MAAM,8BAA8B,CAAC;AAEtC,wBAAgB,wCAAwC,CACvD,KAAK,GAAE,yBAAkC,GACvC,4BAA4B,CAE9B;AAED,qBAAa,kCAAmC,YAAW,4BAA4B;IACtF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA8D;IAC9F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwD;IAClF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoD;IAC1E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAElD,YAAY,KAAK,EAAE,yBAAyB,EAE3C;IAED,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,sCAAsC,GAAG,SAAS,CAGlF;IAED,mBAAmB,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,uCAAuC,CAAA;KAAE,GAAG,IAAI,CAElH;IAED,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,iCAAiC,GAAG,SAAS,CAErF;IAED,2BAA2B,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAE1F;IAED,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,4BAA4B,GAAG,SAAS,CAG9D;IAED,SAAS,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,6BAA6B,CAAA;KAAE,GAAG,IAAI,CAE9F;CACD","sourcesContent":["import type {\n\tContextBudgetCacheLayerV2,\n\tContextBudgetCacheProviderV2,\n\tContextBudgetNegativeCacheEntryV2,\n\tContextBudgetPlanCacheEntryV2,\n\tContextBudgetPlanCacheReadV2,\n\tContextBudgetRepresentationCacheEntryV2,\n\tContextBudgetRepresentationCacheReadV2,\n} from \"./context-budget-v2-types.ts\";\n\nexport function createMemoryContextBudgetCacheProviderV2(\n\tlayer: ContextBudgetCacheLayerV2 = \"turn\",\n): ContextBudgetCacheProviderV2 {\n\treturn new MemoryContextBudgetCacheProviderV2(layer);\n}\n\nexport class MemoryContextBudgetCacheProviderV2 implements ContextBudgetCacheProviderV2 {\n\tprivate readonly representations = new Map<string, ContextBudgetRepresentationCacheEntryV2>();\n\tprivate readonly negatives = new Map<string, ContextBudgetNegativeCacheEntryV2>();\n\tprivate readonly plans = new Map<string, ContextBudgetPlanCacheEntryV2>();\n\tprivate readonly layer: ContextBudgetCacheLayerV2;\n\n\tconstructor(layer: ContextBudgetCacheLayerV2) {\n\t\tthis.layer = layer;\n\t}\n\n\treadRepresentation(key: string): ContextBudgetRepresentationCacheReadV2 | undefined {\n\t\tconst entry = this.representations.get(key);\n\t\treturn entry ? { entry, layer: this.layer } : undefined;\n\t}\n\n\twriteRepresentation(input: { readonly key: string; readonly entry: ContextBudgetRepresentationCacheEntryV2 }): void {\n\t\tthis.representations.set(input.key, input.entry);\n\t}\n\n\treadNegativeRepresentation(key: string): ContextBudgetNegativeCacheEntryV2 | undefined {\n\t\treturn this.negatives.get(key);\n\t}\n\n\twriteNegativeRepresentation(input: { readonly key: string; readonly reason: string }): void {\n\t\tthis.negatives.set(input.key, { reason: input.reason, layer: this.layer });\n\t}\n\n\treadPlan(key: string): ContextBudgetPlanCacheReadV2 | undefined {\n\t\tconst entry = this.plans.get(key);\n\t\treturn entry ? { entry, layer: this.layer } : undefined;\n\t}\n\n\twritePlan(input: { readonly key: string; readonly entry: ContextBudgetPlanCacheEntryV2 }): void {\n\t\tthis.plans.set(input.key, input.entry);\n\t}\n}\n"]}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
export function createMemoryContextBudgetCacheProviderV2() {
|
|
2
|
-
return new MemoryContextBudgetCacheProviderV2();
|
|
1
|
+
export function createMemoryContextBudgetCacheProviderV2(layer = "turn") {
|
|
2
|
+
return new MemoryContextBudgetCacheProviderV2(layer);
|
|
3
3
|
}
|
|
4
4
|
export class MemoryContextBudgetCacheProviderV2 {
|
|
5
5
|
representations = new Map();
|
|
6
6
|
negatives = new Map();
|
|
7
7
|
plans = new Map();
|
|
8
|
+
layer;
|
|
9
|
+
constructor(layer) {
|
|
10
|
+
this.layer = layer;
|
|
11
|
+
}
|
|
8
12
|
readRepresentation(key) {
|
|
9
13
|
const entry = this.representations.get(key);
|
|
10
|
-
return entry ? { entry, layer:
|
|
14
|
+
return entry ? { entry, layer: this.layer } : undefined;
|
|
11
15
|
}
|
|
12
16
|
writeRepresentation(input) {
|
|
13
17
|
this.representations.set(input.key, input.entry);
|
|
@@ -16,11 +20,11 @@ export class MemoryContextBudgetCacheProviderV2 {
|
|
|
16
20
|
return this.negatives.get(key);
|
|
17
21
|
}
|
|
18
22
|
writeNegativeRepresentation(input) {
|
|
19
|
-
this.negatives.set(input.key, { reason: input.reason, layer:
|
|
23
|
+
this.negatives.set(input.key, { reason: input.reason, layer: this.layer });
|
|
20
24
|
}
|
|
21
25
|
readPlan(key) {
|
|
22
26
|
const entry = this.plans.get(key);
|
|
23
|
-
return entry ? { entry, layer:
|
|
27
|
+
return entry ? { entry, layer: this.layer } : undefined;
|
|
24
28
|
}
|
|
25
29
|
writePlan(input) {
|
|
26
30
|
this.plans.set(input.key, input.entry);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget-v2-cache-provider.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-cache-provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"context-budget-v2-cache-provider.js","sourceRoot":"","sources":["../../src/core/context-budget-v2-cache-provider.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,wCAAwC,CACvD,KAAK,GAA8B,MAAM,EACV;IAC/B,OAAO,IAAI,kCAAkC,CAAC,KAAK,CAAC,CAAC;AAAA,CACrD;AAED,MAAM,OAAO,kCAAkC;IAC7B,eAAe,GAAG,IAAI,GAAG,EAAmD,CAAC;IAC7E,SAAS,GAAG,IAAI,GAAG,EAA6C,CAAC;IACjE,KAAK,GAAG,IAAI,GAAG,EAAyC,CAAC;IACzD,KAAK,CAA4B;IAElD,YAAY,KAAgC,EAAE;QAC7C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAAA,CACnB;IAED,kBAAkB,CAAC,GAAW,EAAsD;QACnF,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5C,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAAA,CACxD;IAED,mBAAmB,CAAC,KAAwF,EAAQ;QACnH,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAAA,CACjD;IAED,0BAA0B,CAAC,GAAW,EAAiD;QACtF,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAA,CAC/B;IAED,2BAA2B,CAAC,KAAwD,EAAQ;QAC3F,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAAA,CAC3E;IAED,QAAQ,CAAC,GAAW,EAA4C;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAAA,CACxD;IAED,SAAS,CAAC,KAA8E,EAAQ;QAC/F,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAAA,CACvC;CACD","sourcesContent":["import type {\n\tContextBudgetCacheLayerV2,\n\tContextBudgetCacheProviderV2,\n\tContextBudgetNegativeCacheEntryV2,\n\tContextBudgetPlanCacheEntryV2,\n\tContextBudgetPlanCacheReadV2,\n\tContextBudgetRepresentationCacheEntryV2,\n\tContextBudgetRepresentationCacheReadV2,\n} from \"./context-budget-v2-types.ts\";\n\nexport function createMemoryContextBudgetCacheProviderV2(\n\tlayer: ContextBudgetCacheLayerV2 = \"turn\",\n): ContextBudgetCacheProviderV2 {\n\treturn new MemoryContextBudgetCacheProviderV2(layer);\n}\n\nexport class MemoryContextBudgetCacheProviderV2 implements ContextBudgetCacheProviderV2 {\n\tprivate readonly representations = new Map<string, ContextBudgetRepresentationCacheEntryV2>();\n\tprivate readonly negatives = new Map<string, ContextBudgetNegativeCacheEntryV2>();\n\tprivate readonly plans = new Map<string, ContextBudgetPlanCacheEntryV2>();\n\tprivate readonly layer: ContextBudgetCacheLayerV2;\n\n\tconstructor(layer: ContextBudgetCacheLayerV2) {\n\t\tthis.layer = layer;\n\t}\n\n\treadRepresentation(key: string): ContextBudgetRepresentationCacheReadV2 | undefined {\n\t\tconst entry = this.representations.get(key);\n\t\treturn entry ? { entry, layer: this.layer } : undefined;\n\t}\n\n\twriteRepresentation(input: { readonly key: string; readonly entry: ContextBudgetRepresentationCacheEntryV2 }): void {\n\t\tthis.representations.set(input.key, input.entry);\n\t}\n\n\treadNegativeRepresentation(key: string): ContextBudgetNegativeCacheEntryV2 | undefined {\n\t\treturn this.negatives.get(key);\n\t}\n\n\twriteNegativeRepresentation(input: { readonly key: string; readonly reason: string }): void {\n\t\tthis.negatives.set(input.key, { reason: input.reason, layer: this.layer });\n\t}\n\n\treadPlan(key: string): ContextBudgetPlanCacheReadV2 | undefined {\n\t\tconst entry = this.plans.get(key);\n\t\treturn entry ? { entry, layer: this.layer } : undefined;\n\t}\n\n\twritePlan(input: { readonly key: string; readonly entry: ContextBudgetPlanCacheEntryV2 }): void {\n\t\tthis.plans.set(input.key, input.entry);\n\t}\n}\n"]}
|