@caupulican/pi-agent-core 0.90.11 → 0.91.2
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/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +39 -3
- package/dist/agent-loop.js.map +1 -1
- package/dist/degenerate-assistant-text.d.ts +18 -0
- package/dist/degenerate-assistant-text.d.ts.map +1 -0
- package/dist/degenerate-assistant-text.js +110 -0
- package/dist/degenerate-assistant-text.js.map +1 -0
- package/dist/tool-failure-memory.d.ts +14 -4
- package/dist/tool-failure-memory.d.ts.map +1 -1
- package/dist/tool-failure-memory.js +124 -3
- package/dist/tool-failure-memory.js.map +1 -1
- package/dist/tool-failure-recovery-gate.d.ts +9 -2
- package/dist/tool-failure-recovery-gate.d.ts.map +1 -1
- package/dist/tool-failure-recovery-gate.js +63 -2
- package/dist/tool-failure-recovery-gate.js.map +1 -1
- package/dist/utils/truncate.d.ts +19 -0
- package/dist/utils/truncate.d.ts.map +1 -1
- package/dist/utils/truncate.js +124 -0
- package/dist/utils/truncate.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AssistantMessage } from "@caupulican/pi-ai/types";
|
|
2
|
+
/** Consecutive identical non-empty units (lines or sentences) at or above this run count are degeneration. */
|
|
3
|
+
export declare const DEGENERATE_REPEATED_LINE_MIN = 4;
|
|
4
|
+
/** Mid-stream abort once a generation loop is already this long; cheaper than waiting for `done`. */
|
|
5
|
+
export declare const DEGENERATE_STREAM_ABORT_RUN = 8;
|
|
6
|
+
/**
|
|
7
|
+
* Collapse consecutive identical assistant lines, period-2 ABAB line runs, and the same
|
|
8
|
+
* sentence repeated inside one paragraph. Session 01a0016f stored 551 copies of one
|
|
9
|
+
* sentence as a single line; newline-only collapse cannot see that.
|
|
10
|
+
*/
|
|
11
|
+
export declare function collapseRepeatedLines(text: string): string;
|
|
12
|
+
export declare function isDegenerateRepeatedText(text: string): boolean;
|
|
13
|
+
export declare function shouldAbortDegenerateStream(text: string): boolean;
|
|
14
|
+
export declare function collapseDegenerateAssistantMessage(message: AssistantMessage): AssistantMessage;
|
|
15
|
+
/** True when this message is (or was collapsed from) a generation loop. */
|
|
16
|
+
export declare function isCollapsedDegenerateAssistantMessage(message: AssistantMessage): boolean;
|
|
17
|
+
export declare function assistantMessageText(message: AssistantMessage): string;
|
|
18
|
+
//# sourceMappingURL=degenerate-assistant-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"degenerate-assistant-text.d.ts","sourceRoot":"","sources":["../src/degenerate-assistant-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,8GAA8G;AAC9G,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,qGAAqG;AACrG,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAyD7C;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKjE;AAID,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAa9F;AAED,2EAA2E;AAC3E,wBAAgB,qCAAqC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAExF;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAKtE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Consecutive identical non-empty units (lines or sentences) at or above this run count are degeneration. */
|
|
2
|
+
export const DEGENERATE_REPEATED_LINE_MIN = 4;
|
|
3
|
+
/** Mid-stream abort once a generation loop is already this long; cheaper than waiting for `done`. */
|
|
4
|
+
export const DEGENERATE_STREAM_ABORT_RUN = 8;
|
|
5
|
+
function collapseRepeatedUnits(units, joinWith) {
|
|
6
|
+
if (units.length < DEGENERATE_REPEATED_LINE_MIN)
|
|
7
|
+
return units.join(joinWith);
|
|
8
|
+
const out = [];
|
|
9
|
+
let index = 0;
|
|
10
|
+
while (index < units.length) {
|
|
11
|
+
const unit = units[index];
|
|
12
|
+
let run = 1;
|
|
13
|
+
while (index + run < units.length && units[index + run] === unit)
|
|
14
|
+
run++;
|
|
15
|
+
if (unit.trim() !== "" && run >= DEGENERATE_REPEATED_LINE_MIN) {
|
|
16
|
+
out.push(unit);
|
|
17
|
+
index += run;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (index + 1 < units.length) {
|
|
21
|
+
const next = units[index + 1];
|
|
22
|
+
if (unit.trim() !== "" && next.trim() !== "" && unit !== next) {
|
|
23
|
+
let pairs = 1;
|
|
24
|
+
while (index + (pairs + 1) * 2 <= units.length &&
|
|
25
|
+
units[index + pairs * 2] === unit &&
|
|
26
|
+
units[index + pairs * 2 + 1] === next) {
|
|
27
|
+
pairs++;
|
|
28
|
+
}
|
|
29
|
+
if (pairs >= DEGENERATE_REPEATED_LINE_MIN) {
|
|
30
|
+
out.push(unit, next);
|
|
31
|
+
index += pairs * 2;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
out.push(unit);
|
|
37
|
+
index++;
|
|
38
|
+
}
|
|
39
|
+
return out.join(joinWith);
|
|
40
|
+
}
|
|
41
|
+
function maxRepeatedUnitRun(units) {
|
|
42
|
+
let maxRun = 1;
|
|
43
|
+
let run = 1;
|
|
44
|
+
for (let index = 1; index < units.length; index++) {
|
|
45
|
+
if (units[index] === units[index - 1] && units[index].trim() !== "") {
|
|
46
|
+
run++;
|
|
47
|
+
if (run > maxRun)
|
|
48
|
+
maxRun = run;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
run = 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return units.length === 0 ? 0 : maxRun;
|
|
55
|
+
}
|
|
56
|
+
function splitSentences(text) {
|
|
57
|
+
return text.split(/(?<=[.!?])\s+/);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Collapse consecutive identical assistant lines, period-2 ABAB line runs, and the same
|
|
61
|
+
* sentence repeated inside one paragraph. Session 01a0016f stored 551 copies of one
|
|
62
|
+
* sentence as a single line; newline-only collapse cannot see that.
|
|
63
|
+
*/
|
|
64
|
+
export function collapseRepeatedLines(text) {
|
|
65
|
+
const lineCollapsed = collapseRepeatedUnits(text.split("\n"), "\n");
|
|
66
|
+
return lineCollapsed
|
|
67
|
+
.split("\n")
|
|
68
|
+
.map((line) => collapseRepeatedUnits(splitSentences(line), " "))
|
|
69
|
+
.join("\n");
|
|
70
|
+
}
|
|
71
|
+
export function isDegenerateRepeatedText(text) {
|
|
72
|
+
return collapseRepeatedLines(text) !== text;
|
|
73
|
+
}
|
|
74
|
+
export function shouldAbortDegenerateStream(text) {
|
|
75
|
+
if (text.length < 400)
|
|
76
|
+
return false;
|
|
77
|
+
const sentences = splitSentences(text);
|
|
78
|
+
if (maxRepeatedUnitRun(sentences) >= DEGENERATE_STREAM_ABORT_RUN)
|
|
79
|
+
return true;
|
|
80
|
+
return maxRepeatedUnitRun(text.split("\n")) >= DEGENERATE_STREAM_ABORT_RUN;
|
|
81
|
+
}
|
|
82
|
+
const collapsedDegenerateMessages = new WeakSet();
|
|
83
|
+
export function collapseDegenerateAssistantMessage(message) {
|
|
84
|
+
let changed = false;
|
|
85
|
+
const content = message.content.map((block) => {
|
|
86
|
+
if (block.type !== "text")
|
|
87
|
+
return block;
|
|
88
|
+
const collapsed = collapseRepeatedLines(block.text);
|
|
89
|
+
if (collapsed === block.text)
|
|
90
|
+
return block;
|
|
91
|
+
changed = true;
|
|
92
|
+
return { ...block, text: collapsed };
|
|
93
|
+
});
|
|
94
|
+
if (!changed)
|
|
95
|
+
return message;
|
|
96
|
+
const next = { ...message, content };
|
|
97
|
+
collapsedDegenerateMessages.add(next);
|
|
98
|
+
return next;
|
|
99
|
+
}
|
|
100
|
+
/** True when this message is (or was collapsed from) a generation loop. */
|
|
101
|
+
export function isCollapsedDegenerateAssistantMessage(message) {
|
|
102
|
+
return collapsedDegenerateMessages.has(message);
|
|
103
|
+
}
|
|
104
|
+
export function assistantMessageText(message) {
|
|
105
|
+
return message.content
|
|
106
|
+
.filter((block) => block.type === "text")
|
|
107
|
+
.map((block) => block.text)
|
|
108
|
+
.join("\n");
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=degenerate-assistant-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"degenerate-assistant-text.js","sourceRoot":"","sources":["../src/degenerate-assistant-text.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@caupulican/pi-ai/types\";\n\n/** Consecutive identical non-empty units (lines or sentences) at or above this run count are degeneration. */\nexport const DEGENERATE_REPEATED_LINE_MIN = 4;\n\n/** Mid-stream abort once a generation loop is already this long; cheaper than waiting for `done`. */\nexport const DEGENERATE_STREAM_ABORT_RUN = 8;\n\nfunction collapseRepeatedUnits(units: readonly string[], joinWith: string): string {\n\tif (units.length < DEGENERATE_REPEATED_LINE_MIN) return units.join(joinWith);\n\tconst out: string[] = [];\n\tlet index = 0;\n\twhile (index < units.length) {\n\t\tconst unit = units[index];\n\t\tlet run = 1;\n\t\twhile (index + run < units.length && units[index + run] === unit) run++;\n\t\tif (unit.trim() !== \"\" && run >= DEGENERATE_REPEATED_LINE_MIN) {\n\t\t\tout.push(unit);\n\t\t\tindex += run;\n\t\t\tcontinue;\n\t\t}\n\t\tif (index + 1 < units.length) {\n\t\t\tconst next = units[index + 1];\n\t\t\tif (unit.trim() !== \"\" && next.trim() !== \"\" && unit !== next) {\n\t\t\t\tlet pairs = 1;\n\t\t\t\twhile (\n\t\t\t\t\tindex + (pairs + 1) * 2 <= units.length &&\n\t\t\t\t\tunits[index + pairs * 2] === unit &&\n\t\t\t\t\tunits[index + pairs * 2 + 1] === next\n\t\t\t\t) {\n\t\t\t\t\tpairs++;\n\t\t\t\t}\n\t\t\t\tif (pairs >= DEGENERATE_REPEATED_LINE_MIN) {\n\t\t\t\t\tout.push(unit, next);\n\t\t\t\t\tindex += pairs * 2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tout.push(unit);\n\t\tindex++;\n\t}\n\treturn out.join(joinWith);\n}\n\nfunction maxRepeatedUnitRun(units: readonly string[]): number {\n\tlet maxRun = 1;\n\tlet run = 1;\n\tfor (let index = 1; index < units.length; index++) {\n\t\tif (units[index] === units[index - 1] && units[index].trim() !== \"\") {\n\t\t\trun++;\n\t\t\tif (run > maxRun) maxRun = run;\n\t\t} else {\n\t\t\trun = 1;\n\t\t}\n\t}\n\treturn units.length === 0 ? 0 : maxRun;\n}\n\nfunction splitSentences(text: string): string[] {\n\treturn text.split(/(?<=[.!?])\\s+/);\n}\n\n/**\n * Collapse consecutive identical assistant lines, period-2 ABAB line runs, and the same\n * sentence repeated inside one paragraph. Session 01a0016f stored 551 copies of one\n * sentence as a single line; newline-only collapse cannot see that.\n */\nexport function collapseRepeatedLines(text: string): string {\n\tconst lineCollapsed = collapseRepeatedUnits(text.split(\"\\n\"), \"\\n\");\n\treturn lineCollapsed\n\t\t.split(\"\\n\")\n\t\t.map((line) => collapseRepeatedUnits(splitSentences(line), \" \"))\n\t\t.join(\"\\n\");\n}\n\nexport function isDegenerateRepeatedText(text: string): boolean {\n\treturn collapseRepeatedLines(text) !== text;\n}\n\nexport function shouldAbortDegenerateStream(text: string): boolean {\n\tif (text.length < 400) return false;\n\tconst sentences = splitSentences(text);\n\tif (maxRepeatedUnitRun(sentences) >= DEGENERATE_STREAM_ABORT_RUN) return true;\n\treturn maxRepeatedUnitRun(text.split(\"\\n\")) >= DEGENERATE_STREAM_ABORT_RUN;\n}\n\nconst collapsedDegenerateMessages = new WeakSet<AssistantMessage>();\n\nexport function collapseDegenerateAssistantMessage(message: AssistantMessage): AssistantMessage {\n\tlet changed = false;\n\tconst content = message.content.map((block) => {\n\t\tif (block.type !== \"text\") return block;\n\t\tconst collapsed = collapseRepeatedLines(block.text);\n\t\tif (collapsed === block.text) return block;\n\t\tchanged = true;\n\t\treturn { ...block, text: collapsed };\n\t});\n\tif (!changed) return message;\n\tconst next = { ...message, content };\n\tcollapsedDegenerateMessages.add(next);\n\treturn next;\n}\n\n/** True when this message is (or was collapsed from) a generation loop. */\nexport function isCollapsedDegenerateAssistantMessage(message: AssistantMessage): boolean {\n\treturn collapsedDegenerateMessages.has(message);\n}\n\nexport function assistantMessageText(message: AssistantMessage): string {\n\treturn message.content\n\t\t.filter((block) => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ToolFailurePhase } from "@caupulican/pi-ai/tool-repair-registry";
|
|
2
|
+
import type { ToolResultMessage } from "@caupulican/pi-ai/types";
|
|
2
3
|
import type { AgentMessage, AgentToolResult } from "./types.ts";
|
|
3
4
|
declare const TOOL_FAILURE_MEMORY_VERSION = 1;
|
|
4
5
|
declare const TOOL_FAILURE_DIRECTIVE_VERSION = 1;
|
|
@@ -36,14 +37,23 @@ export interface ToolFailureDirectiveDetails {
|
|
|
36
37
|
}
|
|
37
38
|
export type ToolFailureResultDetails = ToolFailureMemoryDetails | ToolFailureDirectiveDetails;
|
|
38
39
|
export type ToolFailureMemoryTracker = Map<string, ToolFailureMemoryRecord>;
|
|
39
|
-
/**
|
|
40
|
-
* Fingerprint a tool operation without materializing or retaining its serialized payload.
|
|
41
|
-
* Volatile identifiers normalize before hashing; short numbers and ordinary paths remain significant.
|
|
42
|
-
*/
|
|
43
40
|
export declare function normalizeToolSignature(pairs: Array<[string, unknown]>): string;
|
|
44
41
|
export declare function getToolExecutionKey(tool: string, args: unknown): string;
|
|
45
42
|
export declare function getToolFailureRecordExecutionKey(record: ToolFailureMemoryRecord): string | undefined;
|
|
46
43
|
export declare function getUnresolvedToolFailure(tracker: ToolFailureMemoryTracker, tool: string, args: unknown): ToolFailureMemoryRecord | undefined;
|
|
44
|
+
export declare function readVisibleToolFailureCode(result: ToolResultMessage): string | undefined;
|
|
45
|
+
export declare function isClosedOperationFailureCode(code: string | undefined): boolean;
|
|
46
|
+
/** Failures that only a new owner prompt can clear. Do not restore their circuit across user turns. */
|
|
47
|
+
export declare function isPromptScopedFailureCode(code: string | undefined): boolean;
|
|
48
|
+
export declare function restoreToolFailureRecord(result: ToolResultMessage, tool: string, args: unknown): ToolFailureMemoryRecord;
|
|
49
|
+
export interface PairedToolResult {
|
|
50
|
+
tool: string;
|
|
51
|
+
args: unknown;
|
|
52
|
+
executionKey: string;
|
|
53
|
+
result: ToolResultMessage;
|
|
54
|
+
}
|
|
55
|
+
export declare function forEachPairedToolResult(messages: readonly AgentMessage[], visit: (pair: PairedToolResult) => boolean | undefined): void;
|
|
56
|
+
export declare function transcriptHasClosedToolOperation(messages: readonly AgentMessage[]): boolean;
|
|
47
57
|
export declare function classifyToolFailure(message: string, errorClass?: string): string;
|
|
48
58
|
export declare function toolFailureCorrection(message: string, state: ToolFailureState, phase?: ToolFailurePhase): string;
|
|
49
59
|
export interface ToolFailureAssessment {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-failure-memory.d.ts","sourceRoot":"","sources":["../src/tool-failure-memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,gBAAgB,EACrB,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-failure-memory.d.ts","sourceRoot":"","sources":["../src/tool-failure-memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -243,10 +243,49 @@ function boundedJsonPreview(value, maxChars) {
|
|
|
243
243
|
* Fingerprint a tool operation without materializing or retaining its serialized payload.
|
|
244
244
|
* Volatile identifiers normalize before hashing; short numbers and ordinary paths remain significant.
|
|
245
245
|
*/
|
|
246
|
+
/**
|
|
247
|
+
* Resource-envelope fields are not the operation. Changing `timeout` (or a sibling wait bound)
|
|
248
|
+
* must not mint a new execution identity — that is how a failed command is replayed forever.
|
|
249
|
+
*/
|
|
250
|
+
const OPERATION_ENVELOPE_KEYS = new Set([
|
|
251
|
+
"timeout",
|
|
252
|
+
"timeoutms",
|
|
253
|
+
"timeout_ms",
|
|
254
|
+
"timeoutsec",
|
|
255
|
+
"timeout_sec",
|
|
256
|
+
"timeoutseconds",
|
|
257
|
+
"timeout_seconds",
|
|
258
|
+
"maxwait",
|
|
259
|
+
"max_wait",
|
|
260
|
+
"maxwaitms",
|
|
261
|
+
"max_wait_ms",
|
|
262
|
+
"waitms",
|
|
263
|
+
"wait_ms",
|
|
264
|
+
"waitsec",
|
|
265
|
+
"wait_sec",
|
|
266
|
+
"waitseconds",
|
|
267
|
+
"wait_seconds",
|
|
268
|
+
]);
|
|
269
|
+
function omitOperationEnvelopeFields(value) {
|
|
270
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
271
|
+
return value;
|
|
272
|
+
const record = value;
|
|
273
|
+
let changed = false;
|
|
274
|
+
const next = {};
|
|
275
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
276
|
+
if (OPERATION_ENVELOPE_KEYS.has(key.toLowerCase())) {
|
|
277
|
+
changed = true;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
next[key] = entry;
|
|
281
|
+
}
|
|
282
|
+
return changed ? next : value;
|
|
283
|
+
}
|
|
246
284
|
export function normalizeToolSignature(pairs) {
|
|
247
|
-
return structuredHash(pairs, true);
|
|
285
|
+
return structuredHash(pairs.map(([name, args]) => [name, omitOperationEnvelopeFields(args)]), true);
|
|
248
286
|
}
|
|
249
287
|
function toolOperationKey(tool, args, normalizeVolatile) {
|
|
288
|
+
const identityArgs = omitOperationEnvelopeFields(args);
|
|
250
289
|
const boundedTool = truncate(tool, MAX_TOOL_NAME_CHARS);
|
|
251
290
|
const hash = createSignatureHash();
|
|
252
291
|
const updateHashString = normalizeVolatile ? updateNormalizedHashString : updateExactHashString;
|
|
@@ -254,7 +293,7 @@ function toolOperationKey(tool, args, normalizeVolatile) {
|
|
|
254
293
|
// Preserve the stable structured-hash wire identity without allocating the two synthetic arrays.
|
|
255
294
|
updateHashRange(hash, "array:1[array:2[");
|
|
256
295
|
updateStructuredHash(hash, tool, active, 2, updateHashString);
|
|
257
|
-
updateStructuredHash(hash,
|
|
296
|
+
updateStructuredHash(hash, identityArgs, active, 2, updateHashString);
|
|
258
297
|
updateHashRange(hash, "];];");
|
|
259
298
|
const signature = renderSignatureHash(hash);
|
|
260
299
|
return `${boundedTool}:${signature}`;
|
|
@@ -279,6 +318,85 @@ export function getToolFailureRecordExecutionKey(record) {
|
|
|
279
318
|
export function getUnresolvedToolFailure(tracker, tool, args) {
|
|
280
319
|
return tracker.get(getToolFailureKey(tool, args));
|
|
281
320
|
}
|
|
321
|
+
export function readVisibleToolFailureCode(result) {
|
|
322
|
+
for (const block of result.content) {
|
|
323
|
+
if (block.type !== "text")
|
|
324
|
+
continue;
|
|
325
|
+
const match = /"failure_code"\s*:\s*"([^"]+)"/.exec(block.text);
|
|
326
|
+
if (match)
|
|
327
|
+
return match[1];
|
|
328
|
+
}
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
export function isClosedOperationFailureCode(code) {
|
|
332
|
+
return code === "operation_recovery_exhausted" || code === "recovery_exhausted";
|
|
333
|
+
}
|
|
334
|
+
/** Failures that only a new owner prompt can clear. Do not restore their circuit across user turns. */
|
|
335
|
+
export function isPromptScopedFailureCode(code) {
|
|
336
|
+
return code === "owner_authorization_required";
|
|
337
|
+
}
|
|
338
|
+
export function restoreToolFailureRecord(result, tool, args) {
|
|
339
|
+
const executionKey = getToolExecutionKey(tool, args);
|
|
340
|
+
const persisted = readFailureRecord(result.details);
|
|
341
|
+
if (persisted) {
|
|
342
|
+
return {
|
|
343
|
+
...persisted,
|
|
344
|
+
[TOOL_FAILURE_EXECUTION_KEY]: getToolFailureRecordExecutionKey(persisted) ?? executionKey,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
const identity = operationIdentity(tool, args);
|
|
348
|
+
return {
|
|
349
|
+
version: TOOL_FAILURE_MEMORY_VERSION,
|
|
350
|
+
failureKey: identity.failureKey,
|
|
351
|
+
[TOOL_FAILURE_EXECUTION_KEY]: executionKey,
|
|
352
|
+
tool: identity.tool,
|
|
353
|
+
operation: identity.operation,
|
|
354
|
+
occurrence: 1,
|
|
355
|
+
state: "failed",
|
|
356
|
+
phase: "execution",
|
|
357
|
+
failureCode: boundedFailureCode(readVisibleToolFailureCode(result) ?? "tool_error"),
|
|
358
|
+
correction: fallbackFailureGuidance("failed", false, "execution"),
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
export function forEachPairedToolResult(messages, visit) {
|
|
362
|
+
const callsById = new Map();
|
|
363
|
+
for (const message of messages) {
|
|
364
|
+
if (message.role === "assistant") {
|
|
365
|
+
for (const block of message.content) {
|
|
366
|
+
if (block.type === "toolCall") {
|
|
367
|
+
callsById.set(block.id, { name: block.name, args: block.arguments });
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
if (message.role !== "toolResult")
|
|
373
|
+
continue;
|
|
374
|
+
const call = callsById.get(message.toolCallId);
|
|
375
|
+
if (!call)
|
|
376
|
+
continue;
|
|
377
|
+
if (visit({
|
|
378
|
+
tool: call.name,
|
|
379
|
+
args: call.args,
|
|
380
|
+
executionKey: getToolExecutionKey(call.name, call.args),
|
|
381
|
+
result: message,
|
|
382
|
+
}) === false) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
export function transcriptHasClosedToolOperation(messages) {
|
|
388
|
+
const closedByExecutionKey = new Map();
|
|
389
|
+
forEachPairedToolResult(messages, ({ executionKey, result }) => {
|
|
390
|
+
if (!result.isError) {
|
|
391
|
+
closedByExecutionKey.delete(executionKey);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (isClosedOperationFailureCode(readVisibleToolFailureCode(result))) {
|
|
395
|
+
closedByExecutionKey.set(executionKey, true);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
return closedByExecutionKey.size > 0;
|
|
399
|
+
}
|
|
282
400
|
function boundedFailureCode(value) {
|
|
283
401
|
const normalized = value
|
|
284
402
|
.trim()
|
|
@@ -329,8 +447,11 @@ function inferToolFailurePhase(state, failureCode) {
|
|
|
329
447
|
if (failureCode === "malformed_call" || failureCode === "unknown_tool" || failureCode === "invalid_arguments") {
|
|
330
448
|
return "validation";
|
|
331
449
|
}
|
|
332
|
-
if (failureCode === "blocked" ||
|
|
450
|
+
if (failureCode === "blocked" ||
|
|
451
|
+
failureCode === "permission_denied" ||
|
|
452
|
+
failureCode === "owner_authorization_required") {
|
|
333
453
|
return "policy";
|
|
454
|
+
}
|
|
334
455
|
if (failureCode === "preflight_error")
|
|
335
456
|
return "preflight";
|
|
336
457
|
if (failureCode === "aborted" || failureCode === "cancelled")
|