@caupulican/pi-adaptative 0.93.4 → 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.
- package/CHANGELOG.md +37 -0
- package/dist/bundled-resources/skills/n-plus-2-architecture/SKILL.md +13 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +5 -20
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/envelope-enforcement.d.ts +3 -1
- package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
- package/dist/core/autonomy/envelope-enforcement.js +23 -12
- package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
- package/dist/core/autonomy/gates.d.ts.map +1 -1
- package/dist/core/autonomy/gates.js +24 -26
- package/dist/core/autonomy/gates.js.map +1 -1
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +128 -3
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/session-shutdown.d.ts +9 -0
- package/dist/core/session-shutdown.d.ts.map +1 -0
- package/dist/core/session-shutdown.js +44 -0
- package/dist/core/session-shutdown.js.map +1 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skill-vault.d.ts +24 -16
- package/dist/core/skill-vault.d.ts.map +1 -1
- package/dist/core/skill-vault.js +159 -82
- package/dist/core/skill-vault.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +7 -18
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +3 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +15 -7
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +86 -33
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-intent.d.ts +16 -0
- package/dist/core/tools/file-mutation-intent.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-intent.js +51 -2
- package/dist/core/tools/file-mutation-intent.js.map +1 -1
- package/dist/core/tools/persistent-process-coordinator.d.ts +8 -2
- package/dist/core/tools/persistent-process-coordinator.d.ts.map +1 -1
- package/dist/core/tools/persistent-process-coordinator.js +52 -4
- package/dist/core/tools/persistent-process-coordinator.js.map +1 -1
- package/dist/core/tools/shell-execution-session.d.ts +8 -1
- package/dist/core/tools/shell-execution-session.d.ts.map +1 -1
- package/dist/core/tools/shell-execution-session.js +54 -3
- package/dist/core/tools/shell-execution-session.js.map +1 -1
- package/dist/core/tools/shell-session.d.ts +5 -2
- package/dist/core/tools/shell-session.d.ts.map +1 -1
- package/dist/core/tools/shell-session.js +112 -17
- package/dist/core/tools/shell-session.js.map +1 -1
- package/dist/core/tools/skill.d.ts +2 -1
- package/dist/core/tools/skill.d.ts.map +1 -1
- package/dist/core/tools/skill.js +26 -12
- package/dist/core/tools/skill.js.map +1 -1
- package/dist/core/tools/windows-shell-engine.d.ts +1 -1
- package/dist/core/tools/windows-shell-engine.d.ts.map +1 -1
- package/dist/core/tools/windows-shell-engine.js +6 -2
- package/dist/core/tools/windows-shell-engine.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +2 -2
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -5
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +4 -7
- package/dist/utils/shell.js.map +1 -1
- package/docs/settings.md +7 -7
- 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/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
|
@@ -17,7 +17,9 @@ export interface EnvelopeScopedTool {
|
|
|
17
17
|
execute: (...args: unknown[]) => unknown;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Wrap a tool so
|
|
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":"
|
|
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
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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,
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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: "
|
|
144
|
-
message: `Path '${targetPath}' is
|
|
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":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAO9D,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oEAAoE;IACpE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sFAAsF;IACtF,cAAc,CAAC,EAAE,wBAAwB,CAAC;CAC1C;AAED,MAAM,WAAW,2BAA4B,SAAQ,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;IACvG,cAAc,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACtC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAED,MAAM,WAAW,gBAAiB,SAAQ,2BAA2B;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,uBAAuB,CAAC;IAC1E,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;yCAEqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;CACxB;AA4BD,eAAO,MAAM,2BAA2B,EAAE,2BAiCzC,CAAC;AAwCF,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,2BAA2B,CAE9F;
|
|
1
|
+
{"version":3,"file":"context-gc.d.ts","sourceRoot":"","sources":["../../src/core/context-gc.ts"],"names":[],"mappings":"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"}
|
package/dist/core/context-gc.js
CHANGED
|
@@ -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:
|
|
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
|
|
551
|
+
command,
|
|
427
552
|
key,
|
|
428
553
|
};
|
|
429
554
|
commitPackedMessage(options, baseReport, nextMessages, index, message, originalText, key, record, makePackedToolResult);
|