@caupulican/pi-adaptative 0.93.5 → 0.93.7

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/bundled-resources/skills/n-plus-2-architecture/SKILL.md +13 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +5 -20
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/autonomy/envelope-enforcement.d.ts +3 -1
  7. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  8. package/dist/core/autonomy/envelope-enforcement.js +23 -12
  9. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  10. package/dist/core/autonomy/gates.d.ts.map +1 -1
  11. package/dist/core/autonomy/gates.js +24 -26
  12. package/dist/core/autonomy/gates.js.map +1 -1
  13. package/dist/core/context-gc.d.ts.map +1 -1
  14. package/dist/core/context-gc.js +128 -3
  15. package/dist/core/context-gc.js.map +1 -1
  16. package/dist/core/session-shutdown.d.ts +9 -0
  17. package/dist/core/session-shutdown.d.ts.map +1 -0
  18. package/dist/core/session-shutdown.js +44 -0
  19. package/dist/core/session-shutdown.js.map +1 -0
  20. package/dist/core/settings-manager.js.map +1 -1
  21. package/dist/core/skill-vault.d.ts +6 -2
  22. package/dist/core/skill-vault.d.ts.map +1 -1
  23. package/dist/core/skill-vault.js +30 -12
  24. package/dist/core/skill-vault.js.map +1 -1
  25. package/dist/core/system-prompt.d.ts.map +1 -1
  26. package/dist/core/system-prompt.js +7 -18
  27. package/dist/core/system-prompt.js.map +1 -1
  28. package/dist/core/tools/bash.d.ts.map +1 -1
  29. package/dist/core/tools/bash.js +2 -3
  30. package/dist/core/tools/bash.js.map +1 -1
  31. package/dist/core/tools/edit.d.ts.map +1 -1
  32. package/dist/core/tools/edit.js +86 -33
  33. package/dist/core/tools/edit.js.map +1 -1
  34. package/dist/core/tools/file-mutation-intent.d.ts +16 -0
  35. package/dist/core/tools/file-mutation-intent.d.ts.map +1 -1
  36. package/dist/core/tools/file-mutation-intent.js +51 -2
  37. package/dist/core/tools/file-mutation-intent.js.map +1 -1
  38. package/dist/core/tools/persistent-process-coordinator.d.ts +8 -2
  39. package/dist/core/tools/persistent-process-coordinator.d.ts.map +1 -1
  40. package/dist/core/tools/persistent-process-coordinator.js +52 -4
  41. package/dist/core/tools/persistent-process-coordinator.js.map +1 -1
  42. package/dist/core/tools/shell-execution-session.d.ts +8 -1
  43. package/dist/core/tools/shell-execution-session.d.ts.map +1 -1
  44. package/dist/core/tools/shell-execution-session.js +54 -3
  45. package/dist/core/tools/shell-execution-session.js.map +1 -1
  46. package/dist/core/tools/shell-session.d.ts +2 -1
  47. package/dist/core/tools/shell-session.d.ts.map +1 -1
  48. package/dist/core/tools/shell-session.js +22 -2
  49. package/dist/core/tools/shell-session.js.map +1 -1
  50. package/dist/core/tools/skill.d.ts +1 -0
  51. package/dist/core/tools/skill.d.ts.map +1 -1
  52. package/dist/core/tools/skill.js +11 -6
  53. package/dist/core/tools/skill.js.map +1 -1
  54. package/dist/core/tools/windows-shell-engine.d.ts +1 -1
  55. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -1
  56. package/dist/core/tools/windows-shell-engine.js +6 -2
  57. package/dist/core/tools/windows-shell-engine.js.map +1 -1
  58. package/dist/index.d.ts +1 -1
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +1 -1
  61. package/dist/index.js.map +1 -1
  62. package/dist/modes/interactive/components/settings-selector.js +2 -2
  63. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  64. package/dist/utils/shell.d.ts +3 -5
  65. package/dist/utils/shell.d.ts.map +1 -1
  66. package/dist/utils/shell.js +4 -7
  67. package/dist/utils/shell.js.map +1 -1
  68. package/docs/settings.md +7 -7
  69. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  70. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  71. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  72. package/examples/extensions/sandbox/package-lock.json +2 -2
  73. package/examples/extensions/sandbox/package.json +1 -1
  74. package/examples/extensions/with-deps/package-lock.json +2 -2
  75. package/examples/extensions/with-deps/package.json +1 -1
  76. package/npm-shrinkwrap.json +12 -12
  77. package/package.json +4 -4
@@ -17,7 +17,9 @@ export interface EnvelopeScopedTool {
17
17
  execute: (...args: unknown[]) => unknown;
18
18
  }
19
19
  /**
20
- * Wrap a tool so every path-bearing argument is scope-checked when it RUNS. The wrapped tool is
20
+ * Wrap a tool so path-bearing arguments are scope-checked when it RUNS, consulting canonical
21
+ * tool capability policy so only tools whose policy declares path-scope access
22
+ * (resolveToolCallPathAccess !== "none") are checked against envelope boundaries. The wrapped tool is
21
23
  * shape-identical; params are conventionally the second execute argument (toolCallId, params, …).
22
24
  */
23
25
  export declare function wrapToolWithEnvelopeScope<T extends EnvelopeScopedTool>(tool: T, envelope: CapabilityEnvelope, cwd: string): T;
@@ -1 +1 @@
1
- {"version":3,"file":"envelope-enforcement.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/envelope-enforcement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAezD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAiB9D;AAED,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAuBpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAoCxG;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,kBAAkB,EACrE,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,MAAM,GACT,CAAC,CAsBH"}
1
+ {"version":3,"file":"envelope-enforcement.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/envelope-enforcement.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAezD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAiB9D;AAED,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAuBpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAoCxG;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CACzC;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,kBAAkB,EACrE,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,MAAM,GACT,CAAC,CA8BH"}
@@ -1,4 +1,5 @@
1
1
  import { resolve } from "node:path";
2
+ import { resolveToolCallPathAccess } from "../tool-capability-policy.js";
2
3
  import { isPathWithinScope, safeRealpathSync } from "./path-scope.js";
3
4
  /**
4
5
  * Tool-level envelope enforcement (G2 prerequisite for code-writing workers): the capability
@@ -111,7 +112,9 @@ export function isPathWithinEnvelope(envelope, rawPath, cwd) {
111
112
  });
112
113
  }
113
114
  /**
114
- * Wrap a tool so every path-bearing argument is scope-checked when it RUNS. The wrapped tool is
115
+ * Wrap a tool so path-bearing arguments are scope-checked when it RUNS, consulting canonical
116
+ * tool capability policy so only tools whose policy declares path-scope access
117
+ * (resolveToolCallPathAccess !== "none") are checked against envelope boundaries. The wrapped tool is
115
118
  * shape-identical; params are conventionally the second execute argument (toolCallId, params, …).
116
119
  */
117
120
  export function wrapToolWithEnvelopeScope(tool, envelope, cwd) {
@@ -119,18 +122,26 @@ export function wrapToolWithEnvelopeScope(tool, envelope, cwd) {
119
122
  ...tool,
120
123
  execute: (...args) => {
121
124
  const params = args[1];
122
- for (const rawPath of extractToolPathArguments(tool.name, params)) {
123
- if (!isPathWithinEnvelope(envelope, rawPath, cwd)) {
124
- return {
125
- content: [
126
- {
127
- type: "text",
128
- text: `envelope_path_denied: "${rawPath}" is outside envelope ${envelope.id}'s path scope. The tool was NOT run.`,
125
+ const pathAccess = resolveToolCallPathAccess(envelope.capabilities, tool.name, params);
126
+ if (pathAccess !== "none") {
127
+ for (const rawPath of extractToolPathArguments(tool.name, params)) {
128
+ if (!isPathWithinEnvelope(envelope, rawPath, cwd)) {
129
+ return {
130
+ content: [
131
+ {
132
+ type: "text",
133
+ text: `envelope_path_denied: "${rawPath}" is outside envelope ${envelope.id}'s path scope. The tool was NOT run.`,
134
+ },
135
+ ],
136
+ details: {
137
+ outcome: "envelope_path_denied",
138
+ tool: tool.name,
139
+ path: rawPath,
140
+ envelopeId: envelope.id,
129
141
  },
130
- ],
131
- details: { outcome: "envelope_path_denied", tool: tool.name, path: rawPath, envelopeId: envelope.id },
132
- isError: true,
133
- };
142
+ isError: true,
143
+ };
144
+ }
134
145
  }
135
146
  }
136
147
  return tool.execute(...args);
@@ -1 +1 @@
1
- {"version":3,"file":"envelope-enforcement.js","sourceRoot":"","sources":["../../../src/core/autonomy/envelope-enforcement.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"./path-scope.ts\";\n\n/**\n * Tool-level envelope enforcement (G2 prerequisite for code-writing workers): the capability\n * envelope's `allowedPaths`/`deniedPaths` were previously VALIDATION-ONLY — recorded on the\n * envelope but never checked when a tool actually ran. This module wraps tools so path-bearing\n * arguments are checked AT EXECUTION TIME, structurally refusing out-of-scope paths the same way\n * a failed script can never look like success: the refusal is an isError result with a stable\n * outcome code, never a silent no-op.\n */\n\nconst PATH_ARGUMENT_KEYS = [\"path\", \"file_path\", \"filePath\", \"cwd\", \"directory\", \"dir\", \"target\"] as const;\nconst PATH_LIST_ARGUMENT_KEYS = [\"paths\", \"files\"] as const;\n\nexport function extractPathArguments(params: unknown): string[] {\n\tif (!params || typeof params !== \"object\") return [];\n\tconst record = params as Record<string, unknown>;\n\tconst found: string[] = [];\n\tfor (const key of PATH_ARGUMENT_KEYS) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.trim()) found.push(value.trim());\n\t}\n\tfor (const key of PATH_LIST_ARGUMENT_KEYS) {\n\t\tconst value = record[key];\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const entry of value) {\n\t\t\t\tif (typeof entry === \"string\" && entry.trim()) found.push(entry.trim());\n\t\t\t}\n\t\t}\n\t}\n\treturn found;\n}\n\n/** Tool-aware path projection shared by every envelope/gateway enforcement boundary. */\nexport function extractToolPathArguments(toolName: string, params: unknown): string[] {\n\tconst normalizedToolName = toolName.toLowerCase();\n\tconst paths = extractPathArguments(params);\n\tif (normalizedToolName === \"secret_store\" && params && typeof params === \"object\") {\n\t\tconst record = params as Record<string, unknown>;\n\t\tif (record.action === \"migrate\" && Array.isArray(record.sources)) {\n\t\t\tfor (const source of record.sources) {\n\t\t\t\tif (!source || typeof source !== \"object\" || !(\"path\" in source)) continue;\n\t\t\t\tconst sourcePath = source.path;\n\t\t\t\tif (typeof sourcePath === \"string\" && sourcePath.trim()) paths.push(sourcePath.trim());\n\t\t\t}\n\t\t}\n\t}\n\tif (\n\t\tpaths.length === 0 &&\n\t\t(normalizedToolName === \"find\" ||\n\t\t\tnormalizedToolName === \"grep\" ||\n\t\t\tnormalizedToolName === \"ls\" ||\n\t\t\tnormalizedToolName === \"pipeline\")\n\t) {\n\t\tpaths.push(\".\");\n\t}\n\treturn paths;\n}\n\n/**\n * Deny wins over allow; an empty/absent allow list means \"no positive scope restriction\"\n * (only denies apply) — mirroring the resource-profile filter semantics.\n *\n * Both the target and every scope root are resolved through the real filesystem\n * (symlinks expanded in the existing prefix) before comparison: a pre-existing symlink\n * under an allowed root cannot smuggle a write outside the scope, and a shortcut into a\n * denied subtree is still denied. An unresolvable target fails closed.\n */\nexport function isPathWithinEnvelope(envelope: CapabilityEnvelope, rawPath: string, cwd: string): boolean {\n\tconst lexicalTarget = resolve(cwd, rawPath);\n\tconst allowed = envelope.allowedPaths ?? [];\n\tif (allowed.length > 0) {\n\t\tconst couldBeAllowed = allowed.some((root) => {\n\t\t\tconst lexicalRoot = resolve(cwd, root);\n\t\t\tif (isPathWithinScope(lexicalTarget, lexicalRoot)) return true;\n\t\t\ttry {\n\t\t\t\treturn isPathWithinScope(lexicalTarget, safeRealpathSync(lexicalRoot));\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t\tif (!couldBeAllowed) return false;\n\t}\n\tlet target: string;\n\ttry {\n\t\ttarget = safeRealpathSync(lexicalTarget);\n\t} catch {\n\t\treturn false;\n\t}\n\tfor (const denied of envelope.deniedPaths ?? []) {\n\t\ttry {\n\t\t\tif (isPathWithinScope(target, safeRealpathSync(resolve(cwd, denied)))) return false;\n\t\t} catch {\n\t\t\t// Mirror checkPathScope: an unresolvable deny root cannot match anything.\n\t\t}\n\t}\n\tif (allowed.length === 0) return true;\n\treturn allowed.some((root) => {\n\t\ttry {\n\t\t\treturn isPathWithinScope(target, safeRealpathSync(resolve(cwd, root)));\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\nexport interface EnvelopeScopedTool {\n\tname: string;\n\texecute: (...args: unknown[]) => unknown;\n}\n\n/**\n * Wrap a tool so every path-bearing argument is scope-checked when it RUNS. The wrapped tool is\n * shape-identical; params are conventionally the second execute argument (toolCallId, params, …).\n */\nexport function wrapToolWithEnvelopeScope<T extends EnvelopeScopedTool>(\n\ttool: T,\n\tenvelope: CapabilityEnvelope,\n\tcwd: string,\n): T {\n\treturn {\n\t\t...tool,\n\t\texecute: (...args: unknown[]) => {\n\t\t\tconst params = args[1];\n\t\t\tfor (const rawPath of extractToolPathArguments(tool.name, params)) {\n\t\t\t\tif (!isPathWithinEnvelope(envelope, rawPath, cwd)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\ttext: `envelope_path_denied: \"${rawPath}\" is outside envelope ${envelope.id}'s path scope. The tool was NOT run.`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: { outcome: \"envelope_path_denied\", tool: tool.name, path: rawPath, envelopeId: envelope.id },\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn tool.execute(...args);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"envelope-enforcement.js","sourceRoot":"","sources":["../../../src/core/autonomy/envelope-enforcement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAEzE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEtE;;;;;;;GAOG;AAEH,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAU,CAAC;AAC3G,MAAM,uBAAuB,GAAG,CAAC,OAAO,EAAE,OAAO,CAAU,CAAC;AAE5D,MAAM,UAAU,oBAAoB,CAAC,MAAe;IACnD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACrD,MAAM,MAAM,GAAG,MAAiC,CAAC;IACjD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,uBAAuB,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;gBAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACzE,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,wBAAwB,CAAC,QAAgB,EAAE,MAAe;IACzE,MAAM,kBAAkB,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAClD,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,kBAAkB,KAAK,cAAc,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QACnF,MAAM,MAAM,GAAG,MAAiC,CAAC;QACjD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAClE,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC;oBAAE,SAAS;gBAC3E,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;gBAC/B,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;YACxF,CAAC;QACF,CAAC;IACF,CAAC;IACD,IACC,KAAK,CAAC,MAAM,KAAK,CAAC;QAClB,CAAC,kBAAkB,KAAK,MAAM;YAC7B,kBAAkB,KAAK,MAAM;YAC7B,kBAAkB,KAAK,IAAI;YAC3B,kBAAkB,KAAK,UAAU,CAAC,EAClC,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAA4B,EAAE,OAAe,EAAE,GAAW;IAC9F,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC;IAC5C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACvC,IAAI,iBAAiB,CAAC,aAAa,EAAE,WAAW,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC/D,IAAI,CAAC;gBACJ,OAAO,iBAAiB,CAAC,aAAa,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC;YACxE,CAAC;YAAC,MAAM,CAAC;gBACR,OAAO,KAAK,CAAC;YACd,CAAC;QACF,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,cAAc;YAAE,OAAO,KAAK,CAAC;IACnC,CAAC;IACD,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACJ,MAAM,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;QACjD,IAAI,CAAC;YACJ,IAAI,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;QACrF,CAAC;QAAC,MAAM,CAAC;YACR,0EAA0E;QAC3E,CAAC;IACF,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5B,IAAI,CAAC;YACJ,OAAO,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAOD;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CACxC,IAAO,EACP,QAA4B,EAC5B,GAAW;IAEX,OAAO;QACN,GAAG,IAAI;QACP,OAAO,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE;YAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACvB,MAAM,UAAU,GAAG,yBAAyB,CAAC,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACvF,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;gBAC3B,KAAK,MAAM,OAAO,IAAI,wBAAwB,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;oBACnE,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;wBACnD,OAAO;4BACN,OAAO,EAAE;gCACR;oCACC,IAAI,EAAE,MAAM;oCACZ,IAAI,EAAE,0BAA0B,OAAO,yBAAyB,QAAQ,CAAC,EAAE,sCAAsC;iCACjH;6BACD;4BACD,OAAO,EAAE;gCACR,OAAO,EAAE,sBAAsB;gCAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;gCACf,IAAI,EAAE,OAAO;gCACb,UAAU,EAAE,QAAQ,CAAC,EAAE;6BACvB;4BACD,OAAO,EAAE,IAAI;yBACb,CAAC;oBACH,CAAC;gBACF,CAAC;YACF,CAAC;YACD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QAC9B,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { resolve } from \"node:path\";\nimport { resolveToolCallPathAccess } from \"../tool-capability-policy.ts\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"./path-scope.ts\";\n\n/**\n * Tool-level envelope enforcement (G2 prerequisite for code-writing workers): the capability\n * envelope's `allowedPaths`/`deniedPaths` were previously VALIDATION-ONLY — recorded on the\n * envelope but never checked when a tool actually ran. This module wraps tools so path-bearing\n * arguments are checked AT EXECUTION TIME, structurally refusing out-of-scope paths the same way\n * a failed script can never look like success: the refusal is an isError result with a stable\n * outcome code, never a silent no-op.\n */\n\nconst PATH_ARGUMENT_KEYS = [\"path\", \"file_path\", \"filePath\", \"cwd\", \"directory\", \"dir\", \"target\"] as const;\nconst PATH_LIST_ARGUMENT_KEYS = [\"paths\", \"files\"] as const;\n\nexport function extractPathArguments(params: unknown): string[] {\n\tif (!params || typeof params !== \"object\") return [];\n\tconst record = params as Record<string, unknown>;\n\tconst found: string[] = [];\n\tfor (const key of PATH_ARGUMENT_KEYS) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.trim()) found.push(value.trim());\n\t}\n\tfor (const key of PATH_LIST_ARGUMENT_KEYS) {\n\t\tconst value = record[key];\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const entry of value) {\n\t\t\t\tif (typeof entry === \"string\" && entry.trim()) found.push(entry.trim());\n\t\t\t}\n\t\t}\n\t}\n\treturn found;\n}\n\n/** Tool-aware path projection shared by every envelope/gateway enforcement boundary. */\nexport function extractToolPathArguments(toolName: string, params: unknown): string[] {\n\tconst normalizedToolName = toolName.toLowerCase();\n\tconst paths = extractPathArguments(params);\n\tif (normalizedToolName === \"secret_store\" && params && typeof params === \"object\") {\n\t\tconst record = params as Record<string, unknown>;\n\t\tif (record.action === \"migrate\" && Array.isArray(record.sources)) {\n\t\t\tfor (const source of record.sources) {\n\t\t\t\tif (!source || typeof source !== \"object\" || !(\"path\" in source)) continue;\n\t\t\t\tconst sourcePath = source.path;\n\t\t\t\tif (typeof sourcePath === \"string\" && sourcePath.trim()) paths.push(sourcePath.trim());\n\t\t\t}\n\t\t}\n\t}\n\tif (\n\t\tpaths.length === 0 &&\n\t\t(normalizedToolName === \"find\" ||\n\t\t\tnormalizedToolName === \"grep\" ||\n\t\t\tnormalizedToolName === \"ls\" ||\n\t\t\tnormalizedToolName === \"pipeline\")\n\t) {\n\t\tpaths.push(\".\");\n\t}\n\treturn paths;\n}\n\n/**\n * Deny wins over allow; an empty/absent allow list means \"no positive scope restriction\"\n * (only denies apply) — mirroring the resource-profile filter semantics.\n *\n * Both the target and every scope root are resolved through the real filesystem\n * (symlinks expanded in the existing prefix) before comparison: a pre-existing symlink\n * under an allowed root cannot smuggle a write outside the scope, and a shortcut into a\n * denied subtree is still denied. An unresolvable target fails closed.\n */\nexport function isPathWithinEnvelope(envelope: CapabilityEnvelope, rawPath: string, cwd: string): boolean {\n\tconst lexicalTarget = resolve(cwd, rawPath);\n\tconst allowed = envelope.allowedPaths ?? [];\n\tif (allowed.length > 0) {\n\t\tconst couldBeAllowed = allowed.some((root) => {\n\t\t\tconst lexicalRoot = resolve(cwd, root);\n\t\t\tif (isPathWithinScope(lexicalTarget, lexicalRoot)) return true;\n\t\t\ttry {\n\t\t\t\treturn isPathWithinScope(lexicalTarget, safeRealpathSync(lexicalRoot));\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t\tif (!couldBeAllowed) return false;\n\t}\n\tlet target: string;\n\ttry {\n\t\ttarget = safeRealpathSync(lexicalTarget);\n\t} catch {\n\t\treturn false;\n\t}\n\tfor (const denied of envelope.deniedPaths ?? []) {\n\t\ttry {\n\t\t\tif (isPathWithinScope(target, safeRealpathSync(resolve(cwd, denied)))) return false;\n\t\t} catch {\n\t\t\t// Mirror checkPathScope: an unresolvable deny root cannot match anything.\n\t\t}\n\t}\n\tif (allowed.length === 0) return true;\n\treturn allowed.some((root) => {\n\t\ttry {\n\t\t\treturn isPathWithinScope(target, safeRealpathSync(resolve(cwd, root)));\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\nexport interface EnvelopeScopedTool {\n\tname: string;\n\texecute: (...args: unknown[]) => unknown;\n}\n\n/**\n * Wrap a tool so path-bearing arguments are scope-checked when it RUNS, consulting canonical\n * tool capability policy so only tools whose policy declares path-scope access\n * (resolveToolCallPathAccess !== \"none\") are checked against envelope boundaries. The wrapped tool is\n * shape-identical; params are conventionally the second execute argument (toolCallId, params, …).\n */\nexport function wrapToolWithEnvelopeScope<T extends EnvelopeScopedTool>(\n\ttool: T,\n\tenvelope: CapabilityEnvelope,\n\tcwd: string,\n): T {\n\treturn {\n\t\t...tool,\n\t\texecute: (...args: unknown[]) => {\n\t\t\tconst params = args[1];\n\t\t\tconst pathAccess = resolveToolCallPathAccess(envelope.capabilities, tool.name, params);\n\t\t\tif (pathAccess !== \"none\") {\n\t\t\t\tfor (const rawPath of extractToolPathArguments(tool.name, params)) {\n\t\t\t\t\tif (!isPathWithinEnvelope(envelope, rawPath, cwd)) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `envelope_path_denied: \"${rawPath}\" is outside envelope ${envelope.id}'s path scope. The tool was NOT run.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\toutcome: \"envelope_path_denied\",\n\t\t\t\t\t\t\t\ttool: tool.name,\n\t\t\t\t\t\t\t\tpath: rawPath,\n\t\t\t\t\t\t\t\tenvelopeId: envelope.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn tool.execute(...args);\n\t\t},\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/gates.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAmB,MAAM,gBAAgB,CAAC;AAoBvF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,CA+BjF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAWhH;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC9B,GAAG,WAAW,CA4Id"}
1
+ {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/gates.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAmB,MAAM,gBAAgB,CAAC;AAoBvF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,CA+BjF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAWhH;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC9B,GAAG,WAAW,CA0Id"}
@@ -113,37 +113,35 @@ export function evaluateToolGate(input) {
113
113
  // fixed-root control-plane operation; selecting filesystem.read instead keeps the path gate.
114
114
  const pathAccess = resolveToolCallPathAccess(envelope.capabilities, input.toolName, input.args);
115
115
  const paths = pathAccess === "none" ? [] : extractCandidatePaths(input.toolName, input.args);
116
- if (paths.length > 0) {
117
- for (const targetPath of paths) {
118
- if (!isPathWithinEnvelope(envelope, targetPath, input.cwd)) {
119
- let isDenied = false;
120
- try {
121
- const target = safeRealpathSync(path.resolve(input.cwd, targetPath));
122
- isDenied = (envelope.deniedPaths ?? []).some((denied) => {
123
- try {
124
- return isPathWithinScope(target, safeRealpathSync(path.resolve(input.cwd, denied)));
125
- }
126
- catch {
127
- return false;
128
- }
129
- });
130
- }
131
- catch { }
132
- if (isDenied) {
133
- return {
134
- outcome: "block",
135
- gate: "path_scope",
136
- reasonCode: "path_denied",
137
- message: `Path '${targetPath}' is explicitly denied.`,
138
- };
139
- }
116
+ for (const targetPath of paths) {
117
+ if (!isPathWithinEnvelope(envelope, targetPath, input.cwd)) {
118
+ let isDenied = false;
119
+ try {
120
+ const target = safeRealpathSync(path.resolve(input.cwd, targetPath));
121
+ isDenied = (envelope.deniedPaths ?? []).some((denied) => {
122
+ try {
123
+ return isPathWithinScope(target, safeRealpathSync(path.resolve(input.cwd, denied)));
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ });
129
+ }
130
+ catch { }
131
+ if (isDenied) {
140
132
  return {
141
133
  outcome: "block",
142
134
  gate: "path_scope",
143
- reasonCode: "path_outside_allowed_roots",
144
- message: `Path '${targetPath}' is outside all allowed roots.`,
135
+ reasonCode: "path_denied",
136
+ message: `Path '${targetPath}' is explicitly denied.`,
145
137
  };
146
138
  }
139
+ return {
140
+ outcome: "block",
141
+ gate: "path_scope",
142
+ reasonCode: "path_outside_allowed_roots",
143
+ message: `Path '${targetPath}' is outside all allowed roots.`,
144
+ };
147
145
  }
148
146
  }
149
147
  let command = "";
@@ -1 +1 @@
1
- {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../../src/core/autonomy/gates.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport {\n\tresolveToolCallCapabilities,\n\tresolveToolCallPathAccess,\n\ttoolUsesPathScope,\n} from \"../tool-capability-policy.ts\";\nimport { describeCapabilityRequirementForTool, hasCapabilityPolicyForTool } from \"./approval-gate.ts\";\nimport type { CapabilityEnvelope, GateOutcome, GateOutcomeKind } from \"./contracts.ts\";\nimport { extractToolPathArguments, isPathWithinEnvelope } from \"./envelope-enforcement.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"./path-scope.ts\";\nimport { assessOperationRisk } from \"./risk-assessment.ts\";\n\nfunction isGateOutcomeKind(value: unknown): value is GateOutcomeKind {\n\treturn (\n\t\tvalue === \"allow\" || value === \"downgrade\" || value === \"escalate\" || value === \"ask-user\" || value === \"block\"\n\t);\n}\n\nfunction getPrecedence(kind: unknown): number {\n\tif (kind === \"allow\") return 0;\n\tif (kind === \"downgrade\") return 1;\n\tif (kind === \"escalate\") return 2;\n\tif (kind === \"ask-user\") return 3;\n\tif (kind === \"block\") return 4;\n\treturn 4; // Malformed/unknown outcome kind defaults to most restrictive (block)\n}\n\nexport function combineGateOutcomes(outcomes: readonly GateOutcome[]): GateOutcome {\n\tif (outcomes.length === 0) {\n\t\treturn {\n\t\t\toutcome: \"ask-user\",\n\t\t\tgate: \"gate-combiner\",\n\t\t\treasonCode: \"no_gate_outcomes\",\n\t\t\tmessage: \"No gate outcomes to combine\",\n\t\t};\n\t}\n\n\tlet winner = outcomes[0];\n\tlet maxPrecedence = getPrecedence(winner.outcome);\n\n\tfor (let i = 1; i < outcomes.length; i++) {\n\t\tconst current = outcomes[i];\n\t\tconst currentPrecedence = getPrecedence(current.outcome);\n\t\tif (currentPrecedence > maxPrecedence) {\n\t\t\twinner = current;\n\t\t\tmaxPrecedence = currentPrecedence;\n\t\t}\n\t}\n\n\tif (!isGateOutcomeKind(winner.outcome)) {\n\t\treturn {\n\t\t\t...winner,\n\t\t\toutcome: \"block\",\n\t\t\tmessage: winner.message || \"Malformed outcome kind coerced to block\",\n\t\t};\n\t}\n\n\treturn winner;\n}\n\nexport function fallbackGateOutcome(args: { gate: string; reversible: boolean; reasonCode: string }): GateOutcome {\n\tconst gate = (args.gate || \"\").trim() || \"unknown_gate\";\n\tconst reasonCode = (args.reasonCode || \"\").trim() || \"unknown_reason\";\n\tconst outcome: GateOutcomeKind = args.reversible ? \"ask-user\" : \"block\";\n\n\treturn {\n\t\toutcome,\n\t\tgate,\n\t\treasonCode,\n\t\tmessage: `Fallback gate outcome: ${outcome} for gate ${gate} (${reasonCode})`,\n\t};\n}\n\nexport function extractCandidatePaths(toolName: string, args: unknown): string[] {\n\treturn toolUsesPathScope(toolName) ? extractToolPathArguments(toolName, args) : [];\n}\n\nexport function evaluateToolGate(input: {\n\ttoolName: string;\n\targs?: unknown;\n\tcwd: string;\n\tenvelope?: CapabilityEnvelope;\n}): GateOutcome {\n\tif (!input.envelope) {\n\t\treturn {\n\t\t\toutcome: \"allow\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"no_envelope\",\n\t\t\tmessage: \"No envelope active, preserving existing session behavior.\",\n\t\t};\n\t}\n\n\tconst envelope = input.envelope;\n\n\t// 1. Tool allow/deny list overrides\n\tif (envelope.deniedTools?.includes(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"tool_denied\",\n\t\t\tmessage: `Tool '${input.toolName}' is explicitly denied.`,\n\t\t};\n\t}\n\n\tif (envelope.allowedTools && !envelope.allowedTools.includes(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"tool_not_allowed\",\n\t\t\tmessage: `Tool '${input.toolName}' is not in the allowed tools list.`,\n\t\t};\n\t}\n\n\t// 2. Capability checks. Resolve the selected alternatives once so action-sensitive path\n\t// enforcement cannot disagree with capability admission.\n\tif (!hasCapabilityPolicyForTool(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"unknown_tool_capability\",\n\t\t\tmessage: `Tool '${input.toolName}' has no capability policy in the active envelope.`,\n\t\t};\n\t}\n\n\tconst callCapabilities = resolveToolCallCapabilities(envelope.capabilities, input.toolName, input.args);\n\tif (!callCapabilities) {\n\t\tconst requirement = describeCapabilityRequirementForTool(input.toolName, input.args);\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"missing_capability\",\n\t\t\tmessage: `Tool '${input.toolName}' requires ${requirement || \"a classified capability\"}, which is missing from the active envelope.`,\n\t\t};\n\t}\n\n\t// 3. Path scope containment for caller-controlled paths. A workflow.plan pipeline read is a\n\t// fixed-root control-plane operation; selecting filesystem.read instead keeps the path gate.\n\tconst pathAccess = resolveToolCallPathAccess(envelope.capabilities, input.toolName, input.args);\n\tconst paths = pathAccess === \"none\" ? [] : extractCandidatePaths(input.toolName, input.args);\n\tif (paths.length > 0) {\n\t\tfor (const targetPath of paths) {\n\t\t\tif (!isPathWithinEnvelope(envelope, targetPath, input.cwd)) {\n\t\t\t\tlet isDenied = false;\n\t\t\t\ttry {\n\t\t\t\t\tconst target = safeRealpathSync(path.resolve(input.cwd, targetPath));\n\t\t\t\t\tisDenied = (envelope.deniedPaths ?? []).some((denied) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn isPathWithinScope(target, safeRealpathSync(path.resolve(input.cwd, denied)));\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} catch {}\n\n\t\t\t\tif (isDenied) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\toutcome: \"block\",\n\t\t\t\t\t\tgate: \"path_scope\",\n\t\t\t\t\t\treasonCode: \"path_denied\",\n\t\t\t\t\t\tmessage: `Path '${targetPath}' is explicitly denied.`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\toutcome: \"block\",\n\t\t\t\t\tgate: \"path_scope\",\n\t\t\t\t\treasonCode: \"path_outside_allowed_roots\",\n\t\t\t\t\tmessage: `Path '${targetPath}' is outside all allowed roots.`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\tlet command = \"\";\n\tif (\n\t\tinput.toolName === \"bash\" ||\n\t\tinput.toolName === \"powershell\" ||\n\t\tinput.toolName === \"shell\" ||\n\t\tinput.toolName === \"python\"\n\t) {\n\t\tconst argsObj = input.args as Record<string, unknown>;\n\t\tif (argsObj && typeof argsObj.command === \"string\") command = argsObj.command;\n\t\telse if (input.toolName === \"python\" && argsObj && typeof argsObj.code === \"string\") command = argsObj.code;\n\t\telse if (input.toolName === \"python\" && argsObj && typeof argsObj.scriptPath === \"string\") {\n\t\t\tcommand = `python ${argsObj.scriptPath}`;\n\t\t}\n\t} else if (input.toolName === \"secret_store\") {\n\t\tconst argsObj = input.args as Record<string, unknown>;\n\t\tif (argsObj && typeof argsObj.action === \"string\") command = argsObj.action;\n\t}\n\n\tconst riskResult = assessOperationRisk({\n\t\toperation: `Tool ${input.toolName}`,\n\t\ttoolName: input.toolName,\n\t\tcommand,\n\t\tpaths,\n\t\tcapabilities: envelope.capabilities,\n\t});\n\n\tif (riskResult.requiresApproval) {\n\t\treturn {\n\t\t\toutcome: \"ask-user\", // or block, prompt says: ask-user/block\n\t\t\tgate: \"risk_assessment\",\n\t\t\treasonCode: riskResult.reasonCode,\n\t\t\tmessage: `Operation requires approval: ${riskResult.reasons.join(\", \")}`,\n\t\t};\n\t}\n\n\tif (riskResult.risk === \"high-impact\") {\n\t\treturn {\n\t\t\toutcome: \"ask-user\",\n\t\t\tgate: \"risk_assessment\",\n\t\t\treasonCode: riskResult.reasonCode,\n\t\t\tmessage: `High-impact operation requires review: ${riskResult.reasons.join(\", \")}`,\n\t\t};\n\t}\n\n\treturn {\n\t\toutcome: \"allow\",\n\t\tgate: \"tool_gate\",\n\t\treasonCode: \"allowed_by_envelope\",\n\t\tmessage: \"Operation allowed by current capability envelope.\",\n\t};\n}\n"]}
1
+ {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../../src/core/autonomy/gates.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport {\n\tresolveToolCallCapabilities,\n\tresolveToolCallPathAccess,\n\ttoolUsesPathScope,\n} from \"../tool-capability-policy.ts\";\nimport { describeCapabilityRequirementForTool, hasCapabilityPolicyForTool } from \"./approval-gate.ts\";\nimport type { CapabilityEnvelope, GateOutcome, GateOutcomeKind } from \"./contracts.ts\";\nimport { extractToolPathArguments, isPathWithinEnvelope } from \"./envelope-enforcement.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"./path-scope.ts\";\nimport { assessOperationRisk } from \"./risk-assessment.ts\";\n\nfunction isGateOutcomeKind(value: unknown): value is GateOutcomeKind {\n\treturn (\n\t\tvalue === \"allow\" || value === \"downgrade\" || value === \"escalate\" || value === \"ask-user\" || value === \"block\"\n\t);\n}\n\nfunction getPrecedence(kind: unknown): number {\n\tif (kind === \"allow\") return 0;\n\tif (kind === \"downgrade\") return 1;\n\tif (kind === \"escalate\") return 2;\n\tif (kind === \"ask-user\") return 3;\n\tif (kind === \"block\") return 4;\n\treturn 4; // Malformed/unknown outcome kind defaults to most restrictive (block)\n}\n\nexport function combineGateOutcomes(outcomes: readonly GateOutcome[]): GateOutcome {\n\tif (outcomes.length === 0) {\n\t\treturn {\n\t\t\toutcome: \"ask-user\",\n\t\t\tgate: \"gate-combiner\",\n\t\t\treasonCode: \"no_gate_outcomes\",\n\t\t\tmessage: \"No gate outcomes to combine\",\n\t\t};\n\t}\n\n\tlet winner = outcomes[0];\n\tlet maxPrecedence = getPrecedence(winner.outcome);\n\n\tfor (let i = 1; i < outcomes.length; i++) {\n\t\tconst current = outcomes[i];\n\t\tconst currentPrecedence = getPrecedence(current.outcome);\n\t\tif (currentPrecedence > maxPrecedence) {\n\t\t\twinner = current;\n\t\t\tmaxPrecedence = currentPrecedence;\n\t\t}\n\t}\n\n\tif (!isGateOutcomeKind(winner.outcome)) {\n\t\treturn {\n\t\t\t...winner,\n\t\t\toutcome: \"block\",\n\t\t\tmessage: winner.message || \"Malformed outcome kind coerced to block\",\n\t\t};\n\t}\n\n\treturn winner;\n}\n\nexport function fallbackGateOutcome(args: { gate: string; reversible: boolean; reasonCode: string }): GateOutcome {\n\tconst gate = (args.gate || \"\").trim() || \"unknown_gate\";\n\tconst reasonCode = (args.reasonCode || \"\").trim() || \"unknown_reason\";\n\tconst outcome: GateOutcomeKind = args.reversible ? \"ask-user\" : \"block\";\n\n\treturn {\n\t\toutcome,\n\t\tgate,\n\t\treasonCode,\n\t\tmessage: `Fallback gate outcome: ${outcome} for gate ${gate} (${reasonCode})`,\n\t};\n}\n\nexport function extractCandidatePaths(toolName: string, args: unknown): string[] {\n\treturn toolUsesPathScope(toolName) ? extractToolPathArguments(toolName, args) : [];\n}\n\nexport function evaluateToolGate(input: {\n\ttoolName: string;\n\targs?: unknown;\n\tcwd: string;\n\tenvelope?: CapabilityEnvelope;\n}): GateOutcome {\n\tif (!input.envelope) {\n\t\treturn {\n\t\t\toutcome: \"allow\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"no_envelope\",\n\t\t\tmessage: \"No envelope active, preserving existing session behavior.\",\n\t\t};\n\t}\n\n\tconst envelope = input.envelope;\n\n\t// 1. Tool allow/deny list overrides\n\tif (envelope.deniedTools?.includes(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"tool_denied\",\n\t\t\tmessage: `Tool '${input.toolName}' is explicitly denied.`,\n\t\t};\n\t}\n\n\tif (envelope.allowedTools && !envelope.allowedTools.includes(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"tool_not_allowed\",\n\t\t\tmessage: `Tool '${input.toolName}' is not in the allowed tools list.`,\n\t\t};\n\t}\n\n\t// 2. Capability checks. Resolve the selected alternatives once so action-sensitive path\n\t// enforcement cannot disagree with capability admission.\n\tif (!hasCapabilityPolicyForTool(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"unknown_tool_capability\",\n\t\t\tmessage: `Tool '${input.toolName}' has no capability policy in the active envelope.`,\n\t\t};\n\t}\n\n\tconst callCapabilities = resolveToolCallCapabilities(envelope.capabilities, input.toolName, input.args);\n\tif (!callCapabilities) {\n\t\tconst requirement = describeCapabilityRequirementForTool(input.toolName, input.args);\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"missing_capability\",\n\t\t\tmessage: `Tool '${input.toolName}' requires ${requirement || \"a classified capability\"}, which is missing from the active envelope.`,\n\t\t};\n\t}\n\n\t// 3. Path scope containment for caller-controlled paths. A workflow.plan pipeline read is a\n\t// fixed-root control-plane operation; selecting filesystem.read instead keeps the path gate.\n\tconst pathAccess = resolveToolCallPathAccess(envelope.capabilities, input.toolName, input.args);\n\tconst paths = pathAccess === \"none\" ? [] : extractCandidatePaths(input.toolName, input.args);\n\tfor (const targetPath of paths) {\n\t\tif (!isPathWithinEnvelope(envelope, targetPath, input.cwd)) {\n\t\t\tlet isDenied = false;\n\t\t\ttry {\n\t\t\t\tconst target = safeRealpathSync(path.resolve(input.cwd, targetPath));\n\t\t\t\tisDenied = (envelope.deniedPaths ?? []).some((denied) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn isPathWithinScope(target, safeRealpathSync(path.resolve(input.cwd, denied)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} catch {}\n\n\t\t\tif (isDenied) {\n\t\t\t\treturn {\n\t\t\t\t\toutcome: \"block\",\n\t\t\t\t\tgate: \"path_scope\",\n\t\t\t\t\treasonCode: \"path_denied\",\n\t\t\t\t\tmessage: `Path '${targetPath}' is explicitly denied.`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\toutcome: \"block\",\n\t\t\t\tgate: \"path_scope\",\n\t\t\t\treasonCode: \"path_outside_allowed_roots\",\n\t\t\t\tmessage: `Path '${targetPath}' is outside all allowed roots.`,\n\t\t\t};\n\t\t}\n\t}\n\n\tlet command = \"\";\n\tif (\n\t\tinput.toolName === \"bash\" ||\n\t\tinput.toolName === \"powershell\" ||\n\t\tinput.toolName === \"shell\" ||\n\t\tinput.toolName === \"python\"\n\t) {\n\t\tconst argsObj = input.args as Record<string, unknown>;\n\t\tif (argsObj && typeof argsObj.command === \"string\") command = argsObj.command;\n\t\telse if (input.toolName === \"python\" && argsObj && typeof argsObj.code === \"string\") command = argsObj.code;\n\t\telse if (input.toolName === \"python\" && argsObj && typeof argsObj.scriptPath === \"string\") {\n\t\t\tcommand = `python ${argsObj.scriptPath}`;\n\t\t}\n\t} else if (input.toolName === \"secret_store\") {\n\t\tconst argsObj = input.args as Record<string, unknown>;\n\t\tif (argsObj && typeof argsObj.action === \"string\") command = argsObj.action;\n\t}\n\n\tconst riskResult = assessOperationRisk({\n\t\toperation: `Tool ${input.toolName}`,\n\t\ttoolName: input.toolName,\n\t\tcommand,\n\t\tpaths,\n\t\tcapabilities: envelope.capabilities,\n\t});\n\n\tif (riskResult.requiresApproval) {\n\t\treturn {\n\t\t\toutcome: \"ask-user\", // or block, prompt says: ask-user/block\n\t\t\tgate: \"risk_assessment\",\n\t\t\treasonCode: riskResult.reasonCode,\n\t\t\tmessage: `Operation requires approval: ${riskResult.reasons.join(\", \")}`,\n\t\t};\n\t}\n\n\tif (riskResult.risk === \"high-impact\") {\n\t\treturn {\n\t\t\toutcome: \"ask-user\",\n\t\t\tgate: \"risk_assessment\",\n\t\t\treasonCode: riskResult.reasonCode,\n\t\t\tmessage: `High-impact operation requires review: ${riskResult.reasons.join(\", \")}`,\n\t\t};\n\t}\n\n\treturn {\n\t\toutcome: \"allow\",\n\t\tgate: \"tool_gate\",\n\t\treasonCode: \"allowed_by_envelope\",\n\t\tmessage: \"Operation allowed by current capability envelope.\",\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"context-gc.d.ts","sourceRoot":"","sources":["../../src/core/context-gc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context-gc.d.ts","sourceRoot":"","sources":["../../src/core/context-gc.ts"],"names":[],"mappings":"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"}
@@ -32,7 +32,7 @@ const DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS = {
32
32
  };
33
33
  export const DEFAULT_CONTEXT_GC_SETTINGS = {
34
34
  enabled: true,
35
- preserveRecentMessages: 8,
35
+ preserveRecentMessages: 24,
36
36
  minToolResultChars: 1200,
37
37
  tools: [
38
38
  "read",
@@ -230,6 +230,123 @@ function collectContextGcPlan(messages, cwd, semanticSettings) {
230
230
  }
231
231
  return { calls, latestReadByPath, semanticIndexes };
232
232
  }
233
+ const REFERENCE_PROTECTION_ASSISTANT_MESSAGES = 8;
234
+ const REFERENCE_FRAGMENT_MAX_COUNT = 8;
235
+ const REFERENCE_FRAGMENT_MAX_CHARS = 200;
236
+ const REFERENCE_COMMAND_MAX_SEGMENTS = 4;
237
+ function assistantMessageText(message) {
238
+ if (message.role !== "assistant")
239
+ return "";
240
+ const parts = [];
241
+ for (const part of message.content) {
242
+ if (part.type === "text" && part.text)
243
+ parts.push(part.text);
244
+ else if (part.type === "thinking" && part.thinking)
245
+ parts.push(part.thinking);
246
+ }
247
+ return joinTextParts(parts);
248
+ }
249
+ function collectRecentAssistantText(messages) {
250
+ const collected = [];
251
+ for (let index = messages.length - 1; index >= 0; index--) {
252
+ if (collected.length >= REFERENCE_PROTECTION_ASSISTANT_MESSAGES)
253
+ break;
254
+ const message = messages[index];
255
+ if (message.role !== "assistant")
256
+ continue;
257
+ collected.push(assistantMessageText(message));
258
+ }
259
+ return joinTextParts(collected);
260
+ }
261
+ function lastPathSegment(value) {
262
+ return value.slice(Math.max(value.lastIndexOf("/"), value.lastIndexOf("\\")) + 1);
263
+ }
264
+ function stripTokenWrappers(token) {
265
+ return token.replace(/^["'`(]+/, "").replace(/["'`),;:]+$/, "");
266
+ }
267
+ function pushReferenceFragment(fragments, fragment) {
268
+ if (fragments.length >= REFERENCE_FRAGMENT_MAX_COUNT)
269
+ return;
270
+ if (fragment.length === 0 || fragment.length > REFERENCE_FRAGMENT_MAX_CHARS)
271
+ return;
272
+ if (!fragments.includes(fragment))
273
+ fragments.push(fragment);
274
+ }
275
+ function isDistinctiveCommandWord(word) {
276
+ if (word.startsWith("-"))
277
+ return false;
278
+ if (word.length >= 5)
279
+ return true;
280
+ return word.length >= 4 && (word.includes("/") || word.includes("\\") || word.includes("."));
281
+ }
282
+ function collectCommandReferenceFragments(fragments, command) {
283
+ for (const segment of command.split(/&&|\|\||[;|\n]/).slice(0, REFERENCE_COMMAND_MAX_SEGMENTS)) {
284
+ const words = segment
285
+ .split(/\s+/)
286
+ .map(stripTokenWrappers)
287
+ .filter((word) => word.length > 0 && !word.startsWith("-"));
288
+ if (words.length >= 2 && `${words[0]} ${words[1]}`.length >= 6) {
289
+ pushReferenceFragment(fragments, `${words[0]} ${words[1]}`);
290
+ }
291
+ }
292
+ for (const token of command.split(/\s+/)) {
293
+ if (fragments.length >= REFERENCE_FRAGMENT_MAX_COUNT)
294
+ break;
295
+ const word = stripTokenWrappers(token);
296
+ if (isDistinctiveCommandWord(word))
297
+ pushReferenceFragment(fragments, word);
298
+ const base = lastPathSegment(word);
299
+ if (base !== word && !base.startsWith("-") && base.length >= 4)
300
+ pushReferenceFragment(fragments, base);
301
+ }
302
+ }
303
+ /**
304
+ * Review finding (ledger #144 follow-up): `args.path`/`args.command` are not the only citable
305
+ * evidence carriers -- python results carry `scriptPath` (path-shaped) or inline `code`, and
306
+ * run_process results carry `executable` + `args` argv (joined with spaces). Widen ONLY the
307
+ * reference check's inputs through the existing path/command-fragment rules and bounds; the
308
+ * packed record's own `path`/`command` fields stay exactly what the tool args named.
309
+ */
310
+ function referenceEvidenceInputs(cwd, call, path, command) {
311
+ const scriptPath = normalizeToolPath(cwd, call?.args.scriptPath ?? call?.args.script_path);
312
+ const code = typeof call?.args.code === "string" ? call.args.code : undefined;
313
+ const executable = typeof call?.args.executable === "string" ? call.args.executable : undefined;
314
+ const argv = executable !== undefined && Array.isArray(call?.args.args)
315
+ ? [executable, ...call.args.args.filter((entry) => typeof entry === "string")].join(" ")
316
+ : undefined;
317
+ const commandText = [command, code, argv]
318
+ .filter((entry) => entry !== undefined && entry.length > 0)
319
+ .join("\n");
320
+ return { path: path ?? scriptPath, command: commandText.length > 0 ? commandText : undefined };
321
+ }
322
+ function referenceFragmentsFor(path, command) {
323
+ const fragments = [];
324
+ if (path) {
325
+ const base = lastPathSegment(path);
326
+ const fileLike = base.includes(".") && base.length >= 3;
327
+ pushReferenceFragment(fragments, fileLike || base.length >= 5 ? base : path);
328
+ }
329
+ if (command)
330
+ collectCommandReferenceFragments(fragments, command);
331
+ return fragments;
332
+ }
333
+ /**
334
+ * Reference protection (ledger #144): a stale tool result the model is still citing is not
335
+ * packable — packing mid-investigation evidence forces re-derivation of already-paid-for work
336
+ * (field trace: 64 gc stores / 10.1 MB persisted, zero retrievals). "Still citing" is a bounded
337
+ * pure check: the result's `path` (its file-like final segment) or a distinctive fragment of its
338
+ * shell `command` (leading non-flag bigram per segment, non-flag words of >= 5 chars or
339
+ * path-shaped words of >= 4, plus their final segments) appears in the text/thinking of the last
340
+ * REFERENCE_PROTECTION_ASSISTANT_MESSAGES assistant messages. Fragment count and length are
341
+ * capped, so the check stays O(recent assistant text) per candidate. The `superseded-read`
342
+ * reason is exempt at the call site: a newer read of the same file supersedes regardless of
343
+ * references.
344
+ */
345
+ function isReferencedByRecentAssistantText(recentAssistantText, path, command) {
346
+ if (recentAssistantText.length === 0)
347
+ return false;
348
+ return referenceFragmentsFor(path, command).some((fragment) => recentAssistantText.includes(fragment));
349
+ }
233
350
  function storagePathFor(storageDir, key) {
234
351
  if (!storageDir || !isAbsolute(storageDir))
235
352
  return undefined;
@@ -278,7 +395,7 @@ function buildSummary(record) {
278
395
  // provider request and busting prompt caching (BUG E).
279
396
  record.digest ? `digest (machine, never authority): ${record.digest}` : undefined,
280
397
  record.storagePath
281
- ? `exact old text: ${record.storagePath}`
398
+ ? `exact old text: read ${record.storagePath}`
282
399
  : "exact old text retained in the session log, not provider context",
283
400
  semantic
284
401
  ? "Need memory: query same topic/filter or fetch stored drawer pointers."
@@ -356,6 +473,7 @@ export function applyContextGc(messages, rawSettings) {
356
473
  : []);
357
474
  const nextMessages = messages.slice();
358
475
  let changed = false;
476
+ let recentAssistantText;
359
477
  for (let index = 0; index < messages.length; index++) {
360
478
  const message = messages[index];
361
479
  if (semanticIndexSet.has(index) && !preservedSemanticIndexes.has(index) && index < recentStart) {
@@ -397,12 +515,19 @@ export function applyContextGc(messages, rawSettings) {
397
515
  continue;
398
516
  const call = plan.calls.get(message.toolCallId);
399
517
  const path = normalizeToolPath(options.cwd, call?.args.path);
518
+ const command = typeof call?.args.command === "string" ? call.args.command : undefined;
400
519
  let reason = "stale-tool-result";
401
520
  if (message.toolName === "read" && path) {
402
521
  if (plan.latestReadByPath.get(path) === message.toolCallId)
403
522
  continue;
404
523
  reason = "superseded-read";
405
524
  }
525
+ if (reason === "stale-tool-result") {
526
+ recentAssistantText ??= collectRecentAssistantText(messages);
527
+ const reference = referenceEvidenceInputs(options.cwd, call, path, command);
528
+ if (isReferencedByRecentAssistantText(recentAssistantText, reference.path, reference.command))
529
+ continue;
530
+ }
406
531
  const originalTokens = estimateTokens(message);
407
532
  const key = createHash("sha256")
408
533
  .update(message.toolName)
@@ -423,7 +548,7 @@ export function applyContextGc(messages, rawSettings) {
423
548
  packedTokens: 0,
424
549
  storagePath,
425
550
  path,
426
- command: typeof call?.args.command === "string" ? call.args.command : undefined,
551
+ command,
427
552
  key,
428
553
  };
429
554
  commitPackedMessage(options, baseReport, nextMessages, index, message, originalText, key, record, makePackedToolResult);