@caupulican/pi-agent-core 0.93.8 → 0.93.10
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 +160 -154
- package/dist/agent-loop.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/tool-failure-memory.d.ts +8 -4
- package/dist/tool-failure-memory.d.ts.map +1 -1
- package/dist/tool-failure-memory.js +87 -79
- package/dist/tool-failure-memory.js.map +1 -1
- package/dist/tool-failure-recovery-gate.d.ts +32 -42
- package/dist/tool-failure-recovery-gate.d.ts.map +1 -1
- package/dist/tool-failure-recovery-gate.js +161 -287
- package/dist/tool-failure-recovery-gate.js.map +1 -1
- package/dist/tool-failure-recovery-protocol.d.ts +2 -0
- package/dist/tool-failure-recovery-protocol.d.ts.map +1 -1
- package/dist/tool-failure-recovery-protocol.js +7 -6
- package/dist/tool-failure-recovery-protocol.js.map +1 -1
- package/dist/tool-protocol-residue.d.ts +9 -0
- package/dist/tool-protocol-residue.d.ts.map +1 -1
- package/dist/tool-protocol-residue.js +28 -1
- package/dist/tool-protocol-residue.js.map +1 -1
- package/dist/types.d.ts +41 -20
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +5 -2
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -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;AAChD,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;
|
|
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;AAChD,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAMnF,OAAO,KAAK,EAAE,YAAY,EAAiB,eAAe,EAAE,MAAM,YAAY,CAAC;AAG/E,QAAA,MAAM,2BAA2B,IAAI,CAAC;AACtC,QAAA,MAAM,8BAA8B,IAAI,CAAC;AACzC,QAAA,MAAM,0BAA0B,eAAoC,CAAC;AAkBrE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,OAAO,2BAA2B,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,QAAQ,CAAC,CAAC,0BAA0B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,mHAAmH;IACnH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mHAAmH;IACnH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,yBAAyB;IACzC,wGAAwG;IACxG,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACxC,mBAAmB,EAAE,uBAAuB,CAAC;CAC7C;AAED,MAAM,WAAW,2BAA2B;IAC3C,sBAAsB,EAAE;QACvB,OAAO,EAAE,OAAO,8BAA8B,CAAC;QAC/C,KAAK,EAAE,gBAAgB,CAAC;QACxB,KAAK,EAAE,gBAAgB,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;KACnB,CAAC;CACF;AAED,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,GAAG,2BAA2B,CAAC;AAE9F,MAAM,MAAM,wBAAwB,GAAG,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;AA6B5E,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI9E;AA6SD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAK9E;AA8BD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAEvE;AAED,2FAA2F;AAC3F,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAY5G;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAEpG;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,wBAAwB,EACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACX,uBAAuB,GAAG,SAAS,CAErC;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAOxF;AAED,uGAAuG;AACvG,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE3E;AAED,wBAAgB,wBAAwB,CACvC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACX,uBAAuB,CA0BzB;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,iBAAiB,CAAC;CAC1B;AAED,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,KAAK,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,OAAO,GAAG,SAAS,GACpD,IAAI,CAyBN;AAgDD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAOhF;AAqDD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,EACvB,KAAK,GAAE,gBAAoE,GACzE,MAAM,CAKR;AA4ED,MAAM,WAAW,qBAAqB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,kGAAkG;IAClG,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,SAAS,CAAC;CAC1B;AAED,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,EACvB,UAAU,CAAC,EAAE,MAAM,GACjB,qBAAqB,CA0BvB;AA+FD,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,gHAAgH;AAChH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,oBAAoB,GAAG,SAAS,CAoB3F;AAgOD,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,wBAAwB,CAEjG;AAED,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,wBAAwB,EACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,gBAAgB,EACvB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,KAAK,GAAE,gBAA4D,EACnE,QAAQ,CAAC,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE,yBAAyB,GACxC,uBAAuB,CA8CzB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC5B,uBAAuB,CAezB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,wBAAwB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAKrG;AAiCD,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,uBAAuB,EAC/B,SAAS,CAAC,EAAE,OAAO,GACjB,eAAe,CAAC,wBAAwB,CAAC,CAuB3C;AAED,wBAAgB,+BAA+B,CAC9C,MAAM,EAAE,uBAAuB,GAC7B,eAAe,CAAC,wBAAwB,CAAC,CA0B3C;AAcD,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,YAAY,EAAE,EACxB,YAAY,EAAE,MAAM,GAClB;IAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAuCpD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getToolExecutionAttemptMemory, getToolExecutionErrorPolicy, } from "@caupulican/pi-ai/tool-repair-registry";
|
|
2
|
-
import { MANDATORY_TOOL_FAILURE_RECOVERY_PROTOCOL_PROMPT, mandatoryToolFailureRecoveryMetadata, } from "./tool-failure-recovery-protocol.js";
|
|
2
|
+
import { MANDATORY_TOOL_FAILURE_RECOVERY_PROTOCOL_PROMPT, mandatoryToolFailureRecoveryMetadata, TOOL_FAILURE_READMISSION_RULE, } from "./tool-failure-recovery-protocol.js";
|
|
3
3
|
import { sanitizeBinaryOutput } from "./utils/shell-output.js";
|
|
4
4
|
const TOOL_FAILURE_MEMORY_VERSION = 1;
|
|
5
5
|
const TOOL_FAILURE_DIRECTIVE_VERSION = 1;
|
|
@@ -9,7 +9,6 @@ const MAX_FAILURE_CODE_CHARS = 48;
|
|
|
9
9
|
const MAX_DIAGNOSTIC_CHARS = 240;
|
|
10
10
|
const MAX_CORRECTION_CHARS = 480;
|
|
11
11
|
const MAX_TOOL_FAILURE_EVIDENCE_CHARS = 1_600;
|
|
12
|
-
const MAX_ACTIVE_FAILURE_EVIDENCE_CHARS = 2_400;
|
|
13
12
|
const MAX_TOOL_NAME_CHARS = 64;
|
|
14
13
|
const TOOL_SIGNATURE_HEX_CHARS = 32;
|
|
15
14
|
const TOOL_FALLBACK_OUTPUT_SIGNATURE_BASE62_CHARS = 22;
|
|
@@ -19,8 +18,7 @@ const MAX_ACTIVE_FAILURES = 8;
|
|
|
19
18
|
const MAX_TRACKED_FAILURES = 64;
|
|
20
19
|
const REPAIRABLE_REJECTION_CODES = new Set(["invalid_arguments", "malformed_call", "unknown_tool"]);
|
|
21
20
|
const LEGACY_GENERIC_EXECUTION_CORRECTION = "Change the arguments or approach before retrying; do not resend the unchanged operation.";
|
|
22
|
-
const
|
|
23
|
-
const CLOSED_OPERATION_CAVEMAN_CORRECTION = "Closed: this exact operation was not executed and will not run again this session — use a different operation or continue other work.";
|
|
21
|
+
const BLOCKED_REPLAY_CORRECTION = `Not executed: its last result is already above. Do corrective work or use a different operation. ${TOOL_FAILURE_READMISSION_RULE}`;
|
|
24
22
|
function truncate(value, maxChars) {
|
|
25
23
|
if (value.length <= maxChars)
|
|
26
24
|
return value;
|
|
@@ -373,9 +371,6 @@ export function readVisibleToolFailureCode(result) {
|
|
|
373
371
|
}
|
|
374
372
|
return undefined;
|
|
375
373
|
}
|
|
376
|
-
export function isClosedOperationFailureCode(code) {
|
|
377
|
-
return code === "operation_recovery_exhausted" || code === "recovery_exhausted";
|
|
378
|
-
}
|
|
379
374
|
/** Failures that only a new owner prompt can clear. Do not restore their circuit across user turns. */
|
|
380
375
|
export function isPromptScopedFailureCode(code) {
|
|
381
376
|
return code === "owner_authorization_required";
|
|
@@ -390,6 +385,10 @@ export function restoreToolFailureRecord(result, tool, args) {
|
|
|
390
385
|
};
|
|
391
386
|
}
|
|
392
387
|
const identity = operationIdentity(tool, args);
|
|
388
|
+
// A completed operation that reported a negative status keeps its own raw output, so there is no
|
|
389
|
+
// harness record to read back. Recover its terminal status from that output rather than flattening
|
|
390
|
+
// every such result to a generic `tool_error`.
|
|
391
|
+
const visibleCode = readVisibleToolFailureCode(result);
|
|
393
392
|
return {
|
|
394
393
|
version: TOOL_FAILURE_MEMORY_VERSION,
|
|
395
394
|
failureKey: identity.failureKey,
|
|
@@ -399,7 +398,7 @@ export function restoreToolFailureRecord(result, tool, args) {
|
|
|
399
398
|
occurrence: 1,
|
|
400
399
|
state: "failed",
|
|
401
400
|
phase: "execution",
|
|
402
|
-
failureCode: boundedFailureCode(
|
|
401
|
+
failureCode: boundedFailureCode(visibleCode ?? classifyToolFailure(firstText(result))),
|
|
403
402
|
correction: fallbackFailureGuidance("failed", false, "execution"),
|
|
404
403
|
};
|
|
405
404
|
}
|
|
@@ -429,19 +428,6 @@ export function forEachPairedToolResult(messages, visit) {
|
|
|
429
428
|
}
|
|
430
429
|
}
|
|
431
430
|
}
|
|
432
|
-
export function transcriptHasClosedToolOperation(messages) {
|
|
433
|
-
const closedByExecutionKey = new Map();
|
|
434
|
-
forEachPairedToolResult(messages, ({ executionKey, result }) => {
|
|
435
|
-
if (!result.isError) {
|
|
436
|
-
closedByExecutionKey.delete(executionKey);
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
if (isClosedOperationFailureCode(readVisibleToolFailureCode(result))) {
|
|
440
|
-
closedByExecutionKey.set(executionKey, true);
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
return closedByExecutionKey.size > 0;
|
|
444
|
-
}
|
|
445
431
|
function boundedFailureCode(value) {
|
|
446
432
|
const normalized = value
|
|
447
433
|
.trim()
|
|
@@ -762,8 +748,18 @@ function fastTextSignature(text) {
|
|
|
762
748
|
}
|
|
763
749
|
function analyzeToolFailureContext(messages) {
|
|
764
750
|
const callById = new Map();
|
|
751
|
+
/**
|
|
752
|
+
* A call the agent made is erased only in two cases: a superseded success, whose newer identical
|
|
753
|
+
* call is still present, and a discard-attempt directive, where the harness has taken ownership of
|
|
754
|
+
* the attempt and the original arguments must not come back — either because it holds the payload
|
|
755
|
+
* by reference (a retarget) or because replaying those bytes is itself the hazard (an encoding
|
|
756
|
+
* corruption). Never for an ordinary failure: being told to change an operation is unactionable
|
|
757
|
+
* once the operation itself is gone.
|
|
758
|
+
*/
|
|
765
759
|
const omittedCallIds = new Set();
|
|
766
|
-
const
|
|
760
|
+
const omittedResults = new Set();
|
|
761
|
+
/** Unbounded failure results to replace in place with their bounded record. */
|
|
762
|
+
const boundedReplacements = new Map();
|
|
767
763
|
const active = new Map();
|
|
768
764
|
const activeDirectives = new Map();
|
|
769
765
|
let sequence = 0;
|
|
@@ -788,8 +784,18 @@ function analyzeToolFailureContext(messages) {
|
|
|
788
784
|
const call = callById.get(message.toolCallId);
|
|
789
785
|
callById.delete(message.toolCallId);
|
|
790
786
|
const textPayload = firstText(message);
|
|
791
|
-
|
|
792
|
-
|
|
787
|
+
// A completed operation reporting its own negative status is evidence, not a mistake: it never
|
|
788
|
+
// enters the ledger and its output stays exactly as the tool wrote it. It does resolve any
|
|
789
|
+
// earlier failure recorded for the same operation, though — a tool that could not run before
|
|
790
|
+
// has now run — so a transcript that predates this classification cannot keep an obsolete
|
|
791
|
+
// record active across the boundary.
|
|
792
|
+
if (message.errorKind === "operation_outcome") {
|
|
793
|
+
if (call)
|
|
794
|
+
active.delete(getToolFailureKey(call.name, call.arguments));
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
const isHarnessFailure = message.isError === true || textPayload.startsWith("[harness] ");
|
|
798
|
+
if (isHarnessFailure) {
|
|
793
799
|
const toolName = call?.name ?? message.toolName;
|
|
794
800
|
const kindCount = (kindMistakesMap.get(toolName) ?? 0) + 1;
|
|
795
801
|
kindMistakesMap.set(toolName, kindCount);
|
|
@@ -797,10 +803,13 @@ function analyzeToolFailureContext(messages) {
|
|
|
797
803
|
if (directive) {
|
|
798
804
|
activeDirectives.delete(directive.failureCode);
|
|
799
805
|
activeDirectives.set(directive.failureCode, directive);
|
|
806
|
+
// The harness has taken ownership of this attempt: the directive replaces it, and the
|
|
807
|
+
// original arguments must not reappear — a retarget keeps the payload by reference, and a
|
|
808
|
+
// corrupt payload must never be replayed at all. Either way the call comes out.
|
|
800
809
|
if (call)
|
|
801
810
|
omittedCallIds.add(call.id);
|
|
802
811
|
else
|
|
803
|
-
|
|
812
|
+
omittedResults.add(message);
|
|
804
813
|
continue;
|
|
805
814
|
}
|
|
806
815
|
const retained = readFailureRecord(message.details);
|
|
@@ -854,10 +863,12 @@ function analyzeToolFailureContext(messages) {
|
|
|
854
863
|
break;
|
|
855
864
|
active.delete(oldest);
|
|
856
865
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
866
|
+
// A failure result written before this harness bounded its own output — a legacy transcript,
|
|
867
|
+
// or a host that set isError directly — can carry unbounded raw text. Bounding it in place
|
|
868
|
+
// is the same treatment createToolFailureResult gives a fresh failure, applied late; the
|
|
869
|
+
// call that produced it still stands.
|
|
870
|
+
if (!retained)
|
|
871
|
+
boundedReplacements.set(message, record);
|
|
861
872
|
continue;
|
|
862
873
|
}
|
|
863
874
|
if (call) {
|
|
@@ -878,16 +889,26 @@ function analyzeToolFailureContext(messages) {
|
|
|
878
889
|
}
|
|
879
890
|
}
|
|
880
891
|
const kindMistakesSummary = Object.fromEntries(kindMistakesMap);
|
|
881
|
-
|
|
882
|
-
|
|
892
|
+
const activeRecords = [...active.values()]
|
|
893
|
+
.sort((left, right) => left.sequence - right.sequence)
|
|
894
|
+
.map(({ record }) => record);
|
|
895
|
+
if (omittedCallIds.size === 0 && omittedResults.size === 0 && boundedReplacements.size === 0) {
|
|
896
|
+
return { messages, activeRecords, activeDirectives: [...activeDirectives.values()], kindMistakesSummary };
|
|
883
897
|
}
|
|
884
898
|
const filteredMessages = [];
|
|
885
899
|
for (let index = 0; index < messages.length; index++) {
|
|
886
900
|
const message = messages[index];
|
|
887
901
|
if (message.role === "toolResult") {
|
|
888
|
-
if (omittedCallIds.has(message.toolCallId) ||
|
|
902
|
+
if (omittedCallIds.has(message.toolCallId) || omittedResults.has(message))
|
|
889
903
|
continue;
|
|
890
|
-
|
|
904
|
+
const bounded = boundedReplacements.get(message);
|
|
905
|
+
filteredMessages.push(bounded
|
|
906
|
+
? {
|
|
907
|
+
...message,
|
|
908
|
+
content: [{ type: "text", text: `[harness] ${formatRecordJson(bounded, false)}` }],
|
|
909
|
+
details: { piToolFailureMemory: bounded },
|
|
910
|
+
}
|
|
911
|
+
: message);
|
|
891
912
|
continue;
|
|
892
913
|
}
|
|
893
914
|
if (message.role !== "assistant") {
|
|
@@ -919,9 +940,6 @@ function analyzeToolFailureContext(messages) {
|
|
|
919
940
|
filteredMessages.push({ ...message, content: retainedContent });
|
|
920
941
|
}
|
|
921
942
|
}
|
|
922
|
-
const activeRecords = [...active.values()]
|
|
923
|
-
.sort((left, right) => left.sequence - right.sequence)
|
|
924
|
-
.map(({ record }) => record);
|
|
925
943
|
return {
|
|
926
944
|
messages: filteredMessages,
|
|
927
945
|
activeRecords,
|
|
@@ -982,6 +1000,29 @@ export function rememberToolFailure(tracker, tool, args, state, failureCode, cor
|
|
|
982
1000
|
}
|
|
983
1001
|
return record;
|
|
984
1002
|
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Describe a completed operation that reported a negative status, without recording it as a mistake.
|
|
1005
|
+
*
|
|
1006
|
+
* The tool did its job, so nothing enters failure memory, nothing rewrites the tool's output, and no
|
|
1007
|
+
* correction is owed. The record exists only so the repetition governor can name the operation if the
|
|
1008
|
+
* agent replays it while nothing has changed.
|
|
1009
|
+
*/
|
|
1010
|
+
export function describeOperationOutcome(tool, args, failureCode, diagnostic) {
|
|
1011
|
+
const identity = operationIdentity(tool, args);
|
|
1012
|
+
return {
|
|
1013
|
+
version: TOOL_FAILURE_MEMORY_VERSION,
|
|
1014
|
+
failureKey: identity.failureKey,
|
|
1015
|
+
[TOOL_FAILURE_EXECUTION_KEY]: identity.executionKey,
|
|
1016
|
+
tool: identity.tool,
|
|
1017
|
+
operation: identity.operation,
|
|
1018
|
+
occurrence: 1,
|
|
1019
|
+
state: "failed",
|
|
1020
|
+
phase: "execution",
|
|
1021
|
+
failureCode: boundedFailureCode(failureCode),
|
|
1022
|
+
...(diagnostic ? { diagnostic: truncate(diagnostic, MAX_DIAGNOSTIC_CHARS) } : {}),
|
|
1023
|
+
correction: BLOCKED_REPLAY_CORRECTION,
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
985
1026
|
export function clearToolFailure(tracker, tool, args) {
|
|
986
1027
|
const failureKey = getToolFailureKey(tool, args);
|
|
987
1028
|
const record = tracker.get(failureKey);
|
|
@@ -1040,14 +1081,19 @@ export function createToolFailureResult(record, terminate) {
|
|
|
1040
1081
|
}
|
|
1041
1082
|
export function createRepeatedToolFailureResult(record) {
|
|
1042
1083
|
const retainedRecord = retainBlockedToolFailure(record);
|
|
1043
|
-
|
|
1084
|
+
// The note states why this call did not run and what makes it runnable again; the retained
|
|
1085
|
+
// root-cause correction stays the next_action, because that is the actionable half. Evidence is
|
|
1086
|
+
// left out: nothing executed, so there is none — the prior run's evidence is still in the
|
|
1087
|
+
// transcript, and repeating it here would only pay for the same bytes twice.
|
|
1088
|
+
const replayNotice = truncateMiddle(`Not executed: unchanged. ${TOOL_FAILURE_READMISSION_RULE}`, MAX_DIAGNOSTIC_CHARS);
|
|
1044
1089
|
const blockedResult = createToolFailureResult({
|
|
1045
1090
|
...retainedRecord,
|
|
1091
|
+
evidence: undefined,
|
|
1046
1092
|
state: "rejected",
|
|
1047
1093
|
failureCode: "repeated_failed_operation",
|
|
1048
1094
|
...(retainedRecord.diagnostic
|
|
1049
1095
|
? { note: replayNotice }
|
|
1050
|
-
: { diagnostic: replayNotice, correction:
|
|
1096
|
+
: { diagnostic: replayNotice, correction: BLOCKED_REPLAY_CORRECTION }),
|
|
1051
1097
|
});
|
|
1052
1098
|
return {
|
|
1053
1099
|
...blockedResult,
|
|
@@ -1057,36 +1103,6 @@ export function createRepeatedToolFailureResult(record) {
|
|
|
1057
1103
|
details: { piToolFailureMemory: retainedRecord },
|
|
1058
1104
|
};
|
|
1059
1105
|
}
|
|
1060
|
-
export function createToolFailureRecoveryExhaustedResult(record, note) {
|
|
1061
|
-
const retainedRecord = retainBlockedToolFailure(record);
|
|
1062
|
-
const exhaustedResult = createToolFailureResult({
|
|
1063
|
-
...retainedRecord,
|
|
1064
|
-
state: "rejected",
|
|
1065
|
-
failureCode: "recovery_exhausted",
|
|
1066
|
-
note: truncateMiddle(note, MAX_DIAGNOSTIC_CHARS),
|
|
1067
|
-
correction: "Stop retrying tools in this run. Report the unresolved failure and the user or environment action required to continue.",
|
|
1068
|
-
}, true);
|
|
1069
|
-
return {
|
|
1070
|
-
...exhaustedResult,
|
|
1071
|
-
details: { piToolFailureMemory: retainedRecord },
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
|
-
export function createToolFailureOperationExhaustedResult(record, note) {
|
|
1075
|
-
const retainedRecord = {
|
|
1076
|
-
...retainBlockedToolFailure(record),
|
|
1077
|
-
correction: CLOSED_OPERATION_CAVEMAN_CORRECTION,
|
|
1078
|
-
};
|
|
1079
|
-
const exhaustedResult = createToolFailureResult({
|
|
1080
|
-
...retainedRecord,
|
|
1081
|
-
state: "rejected",
|
|
1082
|
-
failureCode: "operation_recovery_exhausted",
|
|
1083
|
-
note: truncateMiddle(note, MAX_DIAGNOSTIC_CHARS),
|
|
1084
|
-
}, false);
|
|
1085
|
-
return {
|
|
1086
|
-
...exhaustedResult,
|
|
1087
|
-
details: { piToolFailureMemory: retainedRecord },
|
|
1088
|
-
};
|
|
1089
|
-
}
|
|
1090
1106
|
function retainBlockedToolFailure(record) {
|
|
1091
1107
|
return {
|
|
1092
1108
|
...record,
|
|
@@ -1107,17 +1123,9 @@ export function sanitizeToolFailureContext(messages, systemPrompt) {
|
|
|
1107
1123
|
const kindSummary = Object.entries(analysis.kindMistakesSummary)
|
|
1108
1124
|
.map(([kind, count]) => `${kind}:${count}`)
|
|
1109
1125
|
.join(", ");
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
const record = records[index];
|
|
1114
|
-
if (!record.evidence)
|
|
1115
|
-
continue;
|
|
1116
|
-
const retained = truncate(record.evidence, remainingEvidenceChars);
|
|
1117
|
-
evidenceByFailureKey.set(record.failureKey, retained);
|
|
1118
|
-
remainingEvidenceChars -= retained.length;
|
|
1119
|
-
}
|
|
1120
|
-
const lines = records.map((record) => escapePromptData(formatRecordJson(record, true, evidenceByFailureKey.get(record.failureKey) ?? null)));
|
|
1126
|
+
// Evidence is not repeated here: the failed call and its bounded record both remain in the
|
|
1127
|
+
// transcript, so the ledger only has to name what is still unresolved and what to do about it.
|
|
1128
|
+
const lines = records.map((record) => escapePromptData(formatRecordJson(record, true, null)));
|
|
1121
1129
|
for (const directive of analysis.activeDirectives) {
|
|
1122
1130
|
lines.push(escapePromptData(JSON.stringify({
|
|
1123
1131
|
...mandatoryToolFailureRecoveryMetadata(),
|