@caupulican/pi-agent-core 0.85.0 → 0.85.4
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 +148 -37
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent.d.ts +4 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +10 -3
- package/dist/agent.js.map +1 -1
- package/dist/compaction/loop.d.ts +1 -1
- package/dist/compaction/loop.d.ts.map +1 -1
- package/dist/compaction/loop.js +7 -2
- package/dist/compaction/loop.js.map +1 -1
- package/dist/tool-failure-memory.d.ts +17 -2
- package/dist/tool-failure-memory.d.ts.map +1 -1
- package/dist/tool-failure-memory.js +100 -10
- package/dist/tool-failure-memory.js.map +1 -1
- package/package.json +2 -2
package/dist/agent-loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAyB7D,OAAO,KAAK,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EACf,YAAY,EAEZ,aAAa,EAGb,uBAAuB,EACvB,sBAAsB,EACtB,QAAQ,EACR,kBAAkB,EAClB,MAAM,YAAY,CAAC;AAIpB,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAEzC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAIzC;AAuBD,wBAAsB,YAAY,CACjC,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAgBzB;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAWzB;AAyFD;;;;GAIG;AACH,wBAAsB,gCAAgC,CAAC,OAAO,EAAE;IAC/D,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE,uBAAuB,EAChC,MAAM,CAAC,EAAE,WAAW,KAChB,sBAAsB,GAAG,SAAS,GAAG,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC,CAAC;IACtF,KAAK,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACxC,OAAO,EAAE,uBAAuB,CAAC,SAAS,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmB9B;AAmLD;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CA8CxC;AA47BD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,kBAAkB,GAAG,SAAS,CAO7F"}
|
package/dist/agent-loop.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { EventStream } from "@caupulican/pi-ai/event-stream";
|
|
6
6
|
import { streamSimple } from "@caupulican/pi-ai/stream";
|
|
7
|
-
import { formatToolRepairStandingRule } from "@caupulican/pi-ai/tool-repair-registry";
|
|
7
|
+
import { formatToolRepairStandingRule, REPEATED_SUCCESSFUL_TOOL_CALL_FAILURE, } from "@caupulican/pi-ai/tool-repair-registry";
|
|
8
8
|
import { ToolArgumentValidationError, validateToolArguments, } from "@caupulican/pi-ai/validation";
|
|
9
9
|
import { assessToolFailure, clearToolFailure, createToolFailureMemoryTracker, createToolFailureResult, normalizeToolSignature, rememberToolFailure, sanitizeToolFailureContext, toolFailureCorrection, } from "./tool-failure-memory.js";
|
|
10
10
|
import { DEFAULT_MAX_STALL_TURNS } from "./types.js";
|
|
@@ -98,6 +98,51 @@ function createAgentStream() {
|
|
|
98
98
|
* enough that it's indistinguishable from legitimate varied work, so we don't chase it.
|
|
99
99
|
*/
|
|
100
100
|
const STALL_WINDOW_PERIODS = 4;
|
|
101
|
+
function textProtocolOperationArguments(toolCall) {
|
|
102
|
+
const args = toolCall.arguments;
|
|
103
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
104
|
+
return args ?? null;
|
|
105
|
+
const record = args;
|
|
106
|
+
if ((toolCall.name === "write" || toolCall.name === "edit") && typeof record.path === "string") {
|
|
107
|
+
return { path: record.path };
|
|
108
|
+
}
|
|
109
|
+
return args;
|
|
110
|
+
}
|
|
111
|
+
function textProtocolBatchSignature(toolCalls) {
|
|
112
|
+
return normalizeToolSignature(toolCalls.map((toolCall) => [toolCall.name, textProtocolOperationArguments(toolCall)]));
|
|
113
|
+
}
|
|
114
|
+
function toolCallRecord(toolCall) {
|
|
115
|
+
const args = toolCall.arguments;
|
|
116
|
+
return args && typeof args === "object" && !Array.isArray(args) ? args : undefined;
|
|
117
|
+
}
|
|
118
|
+
function toolResultPhase(result) {
|
|
119
|
+
if (!result.details || typeof result.details !== "object" || Array.isArray(result.details))
|
|
120
|
+
return undefined;
|
|
121
|
+
const phase = result.details.phase;
|
|
122
|
+
return typeof phase === "string" ? phase : undefined;
|
|
123
|
+
}
|
|
124
|
+
function repeatsSuccessfulTextProtocolBatch(incomingSignature, toolCalls, previous) {
|
|
125
|
+
if (!previous)
|
|
126
|
+
return false;
|
|
127
|
+
if (incomingSignature === previous.signature)
|
|
128
|
+
return true;
|
|
129
|
+
if (toolCalls.length !== previous.calls.length || toolCalls.length !== previous.messages.length)
|
|
130
|
+
return false;
|
|
131
|
+
return toolCalls.every((toolCall, index) => {
|
|
132
|
+
const previousCall = previous.calls[index];
|
|
133
|
+
const previousResult = previous.messages[index];
|
|
134
|
+
if (!previousCall || !previousResult)
|
|
135
|
+
return false;
|
|
136
|
+
const currentArgs = toolCallRecord(toolCall);
|
|
137
|
+
const previousArgs = toolCallRecord(previousCall);
|
|
138
|
+
if (currentArgs?.path !== previousArgs?.path)
|
|
139
|
+
return false;
|
|
140
|
+
const phase = toolResultPhase(previousResult);
|
|
141
|
+
if (phase === "written")
|
|
142
|
+
return toolCall.name === "write" && previousCall.name === "write";
|
|
143
|
+
return phase === "edited" && toolCall.name === "edit" && previousCall.name === "edit";
|
|
144
|
+
});
|
|
145
|
+
}
|
|
101
146
|
/**
|
|
102
147
|
* Apply one request-local preflight without mutating persistent loop configuration.
|
|
103
148
|
* Shared with isolated tool-free provider calls so every transport boundary has identical
|
|
@@ -144,6 +189,7 @@ async function runLoop(initialContext, newMessages, initialConfig, signal, emit,
|
|
|
144
189
|
const validationFailureTracker = { repeats: 0 };
|
|
145
190
|
const repairTeachTracker = new Map();
|
|
146
191
|
let toolFailureMemory = createToolFailureMemoryTracker(currentContext.messages);
|
|
192
|
+
let lastSuccessfulTextProtocolBatch;
|
|
147
193
|
// Check for steering messages at start (user may have typed while waiting)
|
|
148
194
|
let pendingMessages = (await config.getSteeringMessages?.()) || [];
|
|
149
195
|
// Outer loop: continues when queued follow-up messages arrive after agent would stop
|
|
@@ -159,6 +205,7 @@ async function runLoop(initialContext, newMessages, initialConfig, signal, emit,
|
|
|
159
205
|
}
|
|
160
206
|
// Process pending messages (inject before next assistant response)
|
|
161
207
|
if (pendingMessages.length > 0) {
|
|
208
|
+
lastSuccessfulTextProtocolBatch = undefined;
|
|
162
209
|
for (const message of pendingMessages) {
|
|
163
210
|
await emit({ type: "message_start", message });
|
|
164
211
|
await emit({ type: "message_end", message });
|
|
@@ -180,13 +227,34 @@ async function runLoop(initialContext, newMessages, initialConfig, signal, emit,
|
|
|
180
227
|
const toolResults = [];
|
|
181
228
|
hasMoreToolCalls = false;
|
|
182
229
|
if (toolCalls.length > 0) {
|
|
183
|
-
const
|
|
230
|
+
const textProtocolBatch = toolCalls.every((toolCall) => toolCall.source === "text-protocol");
|
|
231
|
+
const incomingBatchSignature = textProtocolBatchSignature(toolCalls);
|
|
232
|
+
const previousSuccessfulTextProtocolResults = textProtocolBatch &&
|
|
233
|
+
repeatsSuccessfulTextProtocolBatch(incomingBatchSignature, toolCalls, lastSuccessfulTextProtocolBatch)
|
|
234
|
+
? lastSuccessfulTextProtocolBatch?.messages
|
|
235
|
+
: undefined;
|
|
236
|
+
const executedToolBatch = await executeToolCalls(currentContext, message, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit);
|
|
184
237
|
toolResults.push(...executedToolBatch.messages);
|
|
185
238
|
hasMoreToolCalls = !executedToolBatch.terminate;
|
|
186
239
|
for (const result of toolResults) {
|
|
187
240
|
currentContext.messages.push(result);
|
|
188
241
|
newMessages.push(result);
|
|
189
242
|
}
|
|
243
|
+
if (!previousSuccessfulTextProtocolResults) {
|
|
244
|
+
lastSuccessfulTextProtocolBatch =
|
|
245
|
+
textProtocolBatch &&
|
|
246
|
+
toolResults.length === toolCalls.length &&
|
|
247
|
+
toolResults.every((result) => !result.isError)
|
|
248
|
+
? {
|
|
249
|
+
signature: textProtocolBatchSignature(toolCalls),
|
|
250
|
+
messages: toolResults,
|
|
251
|
+
calls: toolCalls,
|
|
252
|
+
}
|
|
253
|
+
: undefined;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
lastSuccessfulTextProtocolBatch = undefined;
|
|
190
258
|
}
|
|
191
259
|
await emit({ type: "turn_end", message, toolResults });
|
|
192
260
|
// Runaway-loop backstop (cost guard): detect a model stuck repeating one action.
|
|
@@ -358,27 +426,36 @@ async function streamAssistantResponse(context, config, signal, emit, streamFn)
|
|
|
358
426
|
/**
|
|
359
427
|
* Execute tool calls from an assistant message.
|
|
360
428
|
*/
|
|
361
|
-
async function executeToolCalls(currentContext, assistantMessage, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, signal, emit) {
|
|
429
|
+
async function executeToolCalls(currentContext, assistantMessage, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit) {
|
|
362
430
|
const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall");
|
|
363
431
|
const hasSequentialToolCall = toolCalls.some((tc) => currentContext.tools?.find((t) => t.name === tc.name)?.executionMode === "sequential");
|
|
364
432
|
if (config.toolExecution === "sequential" || hasSequentialToolCall) {
|
|
365
|
-
return executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, signal, emit);
|
|
433
|
+
return executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit);
|
|
434
|
+
}
|
|
435
|
+
return executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit);
|
|
436
|
+
}
|
|
437
|
+
async function prepareAndStartToolCall(currentContext, assistantMessage, toolCall, index, config, validationFailureTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit) {
|
|
438
|
+
const preparation = previousSuccessfulTextProtocolResults
|
|
439
|
+
? createRepeatedSuccessfulToolCallOutcome(previousSuccessfulTextProtocolResults[index])
|
|
440
|
+
: await prepareToolCall(currentContext, assistantMessage, toolCall, config, validationFailureTracker, signal);
|
|
441
|
+
await emitToolExecutionStart(toolCall, emit);
|
|
442
|
+
if (preparation.kind === "immediate") {
|
|
443
|
+
emitToolArgumentValidationTelemetry(config, preparation.validationEvent, "not_run", "none");
|
|
444
|
+
return { kind: "finalized", finalized: finalizeRejectedToolCall(toolCall, preparation, toolFailureMemory) };
|
|
366
445
|
}
|
|
367
|
-
return
|
|
446
|
+
return { kind: "prepared", preparation };
|
|
368
447
|
}
|
|
369
|
-
async function executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, signal, emit) {
|
|
448
|
+
async function executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit) {
|
|
370
449
|
const finalizedCalls = [];
|
|
371
450
|
const messages = [];
|
|
372
|
-
for (const toolCall of toolCalls) {
|
|
373
|
-
const
|
|
374
|
-
await emitToolExecutionStart(toolCall, emit);
|
|
451
|
+
for (const [index, toolCall] of toolCalls.entries()) {
|
|
452
|
+
const started = await prepareAndStartToolCall(currentContext, assistantMessage, toolCall, index, config, validationFailureTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit);
|
|
375
453
|
let finalized;
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
finalized = finalizeRejectedToolCall(toolCall, preparation, toolFailureMemory);
|
|
454
|
+
if (started.kind === "finalized") {
|
|
455
|
+
finalized = started.finalized;
|
|
379
456
|
}
|
|
380
457
|
else {
|
|
381
|
-
finalized = await executeAndFinalizePreparedToolCall(currentContext, assistantMessage, preparation, config, repairTeachTracker, toolFailureMemory, signal, emit);
|
|
458
|
+
finalized = await executeAndFinalizePreparedToolCall(currentContext, assistantMessage, started.preparation, config, repairTeachTracker, toolFailureMemory, signal, emit);
|
|
382
459
|
}
|
|
383
460
|
await emitToolExecutionEnd(finalized, emit);
|
|
384
461
|
const toolResultMessage = createToolResultMessage(finalized);
|
|
@@ -394,23 +471,20 @@ async function executeToolCallsSequential(currentContext, assistantMessage, tool
|
|
|
394
471
|
terminate: shouldTerminateToolBatch(finalizedCalls),
|
|
395
472
|
};
|
|
396
473
|
}
|
|
397
|
-
async function executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, signal, emit) {
|
|
474
|
+
async function executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, validationFailureTracker, repairTeachTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit) {
|
|
398
475
|
const finalizedCalls = [];
|
|
399
|
-
for (const toolCall of toolCalls) {
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const finalized = finalizeRejectedToolCall(toolCall, preparation, toolFailureMemory);
|
|
405
|
-
await emitToolExecutionEnd(finalized, emit);
|
|
406
|
-
finalizedCalls.push(finalized);
|
|
476
|
+
for (const [index, toolCall] of toolCalls.entries()) {
|
|
477
|
+
const started = await prepareAndStartToolCall(currentContext, assistantMessage, toolCall, index, config, validationFailureTracker, toolFailureMemory, previousSuccessfulTextProtocolResults, signal, emit);
|
|
478
|
+
if (started.kind === "finalized") {
|
|
479
|
+
await emitToolExecutionEnd(started.finalized, emit);
|
|
480
|
+
finalizedCalls.push(started.finalized);
|
|
407
481
|
if (signal?.aborted) {
|
|
408
482
|
break;
|
|
409
483
|
}
|
|
410
484
|
continue;
|
|
411
485
|
}
|
|
412
486
|
finalizedCalls.push(async () => {
|
|
413
|
-
const finalized = await executeAndFinalizePreparedToolCall(currentContext, assistantMessage, preparation, config, repairTeachTracker, toolFailureMemory, signal, emit);
|
|
487
|
+
const finalized = await executeAndFinalizePreparedToolCall(currentContext, assistantMessage, started.preparation, config, repairTeachTracker, toolFailureMemory, signal, emit);
|
|
414
488
|
await emitToolExecutionEnd(finalized, emit);
|
|
415
489
|
return finalized;
|
|
416
490
|
});
|
|
@@ -432,6 +506,27 @@ async function executeToolCallsParallel(currentContext, assistantMessage, toolCa
|
|
|
432
506
|
}
|
|
433
507
|
const DEFAULT_TOOL_VALIDATION_ESCALATION_THRESHOLD = 3;
|
|
434
508
|
const TOOL_REPAIR_TEACH_EVERY = 5;
|
|
509
|
+
const REPEATED_SUCCESS_RESULT_MAX_CHARS = 2_048;
|
|
510
|
+
function createRepeatedSuccessfulToolCallOutcome(previousResult) {
|
|
511
|
+
const previousText = previousResult?.content
|
|
512
|
+
.filter((block) => block.type === "text")
|
|
513
|
+
.map((block) => block.text)
|
|
514
|
+
.join("\n")
|
|
515
|
+
.slice(0, REPEATED_SUCCESS_RESULT_MAX_CHARS);
|
|
516
|
+
const diagnostic = previousText
|
|
517
|
+
? `${REPEATED_SUCCESSFUL_TOOL_CALL_FAILURE.diagnostic} Previous successful result: ${previousText}`
|
|
518
|
+
: REPEATED_SUCCESSFUL_TOOL_CALL_FAILURE.diagnostic;
|
|
519
|
+
return {
|
|
520
|
+
kind: "immediate",
|
|
521
|
+
result: createErrorToolResult(diagnostic),
|
|
522
|
+
isError: true,
|
|
523
|
+
phase: "execution",
|
|
524
|
+
failureCode: REPEATED_SUCCESSFUL_TOOL_CALL_FAILURE.failureCode,
|
|
525
|
+
correction: REPEATED_SUCCESSFUL_TOOL_CALL_FAILURE.guidance,
|
|
526
|
+
diagnostic,
|
|
527
|
+
...(previousResult ? { repeatedSuccessfulCall: { previousToolCallId: previousResult.toolCallId } } : {}),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
435
530
|
function shouldTerminateToolBatch(finalizedCalls) {
|
|
436
531
|
return finalizedCalls.length > 0 && finalizedCalls.every((finalized) => finalized.result.terminate === true);
|
|
437
532
|
}
|
|
@@ -527,25 +622,27 @@ function handleValidationFailure(error, config, tracker) {
|
|
|
527
622
|
return `${error.message}\n\nRepeated validation failure (${tracker.repeats} identical attempts). Use this full schema and example before retrying:\n${error.enrichment}`;
|
|
528
623
|
}
|
|
529
624
|
async function prepareToolCall(currentContext, assistantMessage, toolCall, config, validationFailureTracker, signal) {
|
|
530
|
-
|
|
625
|
+
const tool = currentContext.tools?.find((candidate) => candidate.name === toolCall.name);
|
|
626
|
+
if (!tool) {
|
|
531
627
|
return {
|
|
532
628
|
kind: "immediate",
|
|
533
|
-
result: createErrorToolResult(toolCall.
|
|
629
|
+
result: createErrorToolResult(`Tool ${toolCall.name} not found`),
|
|
534
630
|
isError: true,
|
|
535
|
-
|
|
536
|
-
|
|
631
|
+
phase: "validation",
|
|
632
|
+
failureCode: "unknown_tool",
|
|
633
|
+
correction: "Choose a tool from the currently available tool list.",
|
|
537
634
|
validationEvent: createValidationBounceTelemetry(config, toolCall, "unknown_tool"),
|
|
538
635
|
};
|
|
539
636
|
}
|
|
540
|
-
|
|
541
|
-
if (!tool) {
|
|
637
|
+
if (toolCall.errorMessage) {
|
|
542
638
|
return {
|
|
543
639
|
kind: "immediate",
|
|
544
|
-
result: createErrorToolResult(
|
|
640
|
+
result: createErrorToolResult(toolCall.errorMessage),
|
|
545
641
|
isError: true,
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
642
|
+
phase: "validation",
|
|
643
|
+
failureCode: "malformed_call",
|
|
644
|
+
correction: "Resend one complete JSON argument object matching the current tool schema.",
|
|
645
|
+
validationEvent: createValidationBounceTelemetry(config, toolCall, "malformed_call"),
|
|
549
646
|
};
|
|
550
647
|
}
|
|
551
648
|
let validationEvent;
|
|
@@ -579,6 +676,7 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
|
|
|
579
676
|
kind: "immediate",
|
|
580
677
|
result: createErrorToolResult("Operation aborted"),
|
|
581
678
|
isError: true,
|
|
679
|
+
phase: "cancelled",
|
|
582
680
|
failureCode: "aborted",
|
|
583
681
|
correction: "Retry only if the operation is still required.",
|
|
584
682
|
validationEvent,
|
|
@@ -590,6 +688,7 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
|
|
|
590
688
|
kind: "immediate",
|
|
591
689
|
result: createErrorToolResult(reason),
|
|
592
690
|
isError: true,
|
|
691
|
+
phase: "policy",
|
|
593
692
|
failureCode: "blocked",
|
|
594
693
|
correction: "Choose an allowed approach or request the required authority before retrying.",
|
|
595
694
|
diagnostic: reason,
|
|
@@ -602,6 +701,7 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
|
|
|
602
701
|
kind: "immediate",
|
|
603
702
|
result: createErrorToolResult("Operation aborted"),
|
|
604
703
|
isError: true,
|
|
704
|
+
phase: "cancelled",
|
|
605
705
|
failureCode: "aborted",
|
|
606
706
|
correction: "Retry only if the operation is still required.",
|
|
607
707
|
validationEvent,
|
|
@@ -625,19 +725,30 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
|
|
|
625
725
|
kind: "immediate",
|
|
626
726
|
result: createErrorToolResult(message),
|
|
627
727
|
isError: true,
|
|
728
|
+
phase: isToolArgumentValidationError(error) ? "validation" : "preflight",
|
|
628
729
|
failureCode: isToolArgumentValidationError(error) ? "invalid_arguments" : "preflight_error",
|
|
629
730
|
correction: isToolArgumentValidationError(error)
|
|
630
731
|
? validationFailureCorrection(validationEvent, toolCall.name)
|
|
631
|
-
: toolFailureCorrection(message, "rejected"),
|
|
732
|
+
: toolFailureCorrection(message, "rejected", "preflight"),
|
|
733
|
+
diagnostic: isToolArgumentValidationError(error) ? undefined : message,
|
|
632
734
|
validationEvent,
|
|
633
735
|
};
|
|
634
736
|
}
|
|
635
737
|
}
|
|
636
738
|
function finalizeRejectedToolCall(toolCall, outcome, tracker) {
|
|
637
|
-
const record = rememberToolFailure(tracker, toolCall.name, toolCall.arguments, "rejected", outcome.failureCode, outcome.correction, outcome.diagnostic);
|
|
739
|
+
const record = rememberToolFailure(tracker, toolCall.name, toolCall.arguments, "rejected", outcome.failureCode, outcome.correction, outcome.diagnostic, outcome.phase);
|
|
740
|
+
const failureResult = createToolFailureResult(record, outcome.result.terminate);
|
|
638
741
|
return {
|
|
639
742
|
toolCall,
|
|
640
|
-
result:
|
|
743
|
+
result: outcome.repeatedSuccessfulCall
|
|
744
|
+
? {
|
|
745
|
+
...failureResult,
|
|
746
|
+
details: {
|
|
747
|
+
...failureResult.details,
|
|
748
|
+
piRepeatedSuccessfulCall: outcome.repeatedSuccessfulCall,
|
|
749
|
+
},
|
|
750
|
+
}
|
|
751
|
+
: failureResult,
|
|
641
752
|
isError: true,
|
|
642
753
|
};
|
|
643
754
|
}
|
|
@@ -838,7 +949,7 @@ async function finalizeExecutedToolCall(currentContext, assistantMessage, prepar
|
|
|
838
949
|
const usage = result.usage;
|
|
839
950
|
const effectiveFailureMessage = failureMessage || result.content.find((block) => block.type === "text")?.text || "Tool execution failed";
|
|
840
951
|
const assessment = assessToolFailure(effectiveFailureMessage, "failed", errorClass);
|
|
841
|
-
const record = rememberToolFailure(toolFailureMemory, prepared.toolCall.name, prepared.args, "failed", assessment.failureCode, assessment.guidance, assessment.diagnostic);
|
|
952
|
+
const record = rememberToolFailure(toolFailureMemory, prepared.toolCall.name, prepared.args, "failed", assessment.failureCode, assessment.guidance, assessment.diagnostic, assessment.phase);
|
|
842
953
|
result = { ...createToolFailureResult(record, result.terminate), usage };
|
|
843
954
|
}
|
|
844
955
|
else {
|