@botbotgo/agent-harness 0.0.326 → 0.0.328
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/cli/chat-stream.js +33 -27
- package/dist/cli/main.js +30 -3
- package/dist/contracts/runtime-requests.d.ts +1 -2
- package/dist/contracts/runtime-scheduling.d.ts +1 -1
- package/dist/flow/flow-graph-upstream.js +3 -7
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/projections/request-events.js +0 -1
- package/dist/resource/isolation.js +51 -10
- package/dist/resources/toolkit.mjs +183 -0
- package/dist/resources/tools/cancel_request.mjs +1 -1
- package/dist/resources/tools/fetch_url.mjs +1 -1
- package/dist/resources/tools/http_request.mjs +1 -1
- package/dist/resources/tools/inspect_approvals.mjs +1 -1
- package/dist/resources/tools/inspect_artifacts.mjs +1 -1
- package/dist/resources/tools/inspect_events.mjs +1 -1
- package/dist/resources/tools/inspect_requests.mjs +1 -1
- package/dist/resources/tools/inspect_sessions.mjs +1 -1
- package/dist/resources/tools/list_files.mjs +1 -1
- package/dist/resources/tools/read_artifact.mjs +1 -1
- package/dist/resources/tools/request_approval.mjs +1 -1
- package/dist/resources/tools/run_command.mjs +1 -1
- package/dist/resources/tools/schedule_task.mjs +1 -1
- package/dist/resources/tools/search_files.mjs +1 -1
- package/dist/resources/tools/send_message.mjs +1 -1
- package/dist/runtime/adapter/compat/deepagent-compat.d.ts +0 -9
- package/dist/runtime/adapter/compat/deepagent-compat.js +0 -22
- package/dist/runtime/adapter/flow/stream-runtime.d.ts +4 -0
- package/dist/runtime/adapter/flow/stream-runtime.js +239 -8
- package/dist/runtime/adapter/local-tool-invocation.js +53 -0
- package/dist/runtime/adapter/middleware-assembly.js +174 -29
- package/dist/runtime/adapter/runtime-adapter-support.js +1 -2
- package/dist/runtime/adapter/stream-event-projection.d.ts +17 -0
- package/dist/runtime/adapter/stream-event-projection.js +217 -4
- package/dist/runtime/adapter/tool/builtin-middleware-tools.d.ts +0 -3
- package/dist/runtime/adapter/tool/builtin-middleware-tools.js +37 -17
- package/dist/runtime/adapter/tool/resolved-tool.js +29 -3
- package/dist/runtime/agent-runtime-adapter.d.ts +3 -3
- package/dist/runtime/agent-runtime-adapter.js +12 -33
- package/dist/runtime/agent-runtime-assembly.d.ts +3 -21
- package/dist/runtime/agent-runtime-assembly.js +4 -56
- package/dist/runtime/harness/run/inspection.js +21 -5
- package/dist/runtime/harness/run/run-operations.js +2 -1
- package/dist/runtime/harness/run/stream-run.d.ts +3 -1
- package/dist/runtime/harness/run/stream-run.js +206 -30
- package/dist/runtime/harness.js +3 -0
- package/dist/runtime/parsing/output-content.js +11 -4
- package/dist/runtime/parsing/output-recovery.d.ts +3 -0
- package/dist/runtime/parsing/output-recovery.js +57 -11
- package/dist/runtime/parsing/output-tool-args.d.ts +4 -0
- package/dist/runtime/parsing/output-tool-args.js +122 -0
- package/dist/runtime/parsing/stream-event-parsing.js +37 -3
- package/dist/runtime/support/harness-support.d.ts +1 -0
- package/dist/runtime/support/harness-support.js +44 -2
- package/dist/tools.js +34 -4
- package/package.json +8 -8
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import type { CompiledModel, CompiledSubAgent } from "../../../contracts/types.js";
|
|
2
|
-
type DelegationPromptCompatibilityParams = {
|
|
3
|
-
subagents?: CompiledSubAgent[];
|
|
4
|
-
generalPurposeAgent?: boolean;
|
|
5
|
-
taskDescription?: string;
|
|
6
|
-
};
|
|
7
1
|
export declare function relativizeDeepAgentSkillSourcePaths(workspaceRoot: string | undefined, skillPaths: string[] | undefined): string[] | undefined;
|
|
8
2
|
export declare function materializeDeepAgentSkillSourcePaths(options: {
|
|
9
3
|
workspaceRoot?: string;
|
|
@@ -17,6 +11,3 @@ export declare function resolveDeepAgentSkillSourcePaths(options: {
|
|
|
17
11
|
ownerId: string;
|
|
18
12
|
skillPaths?: string[];
|
|
19
13
|
}): string[] | undefined;
|
|
20
|
-
export declare function shouldRelaxDeepAgentDelegationPrompt(model: CompiledModel | undefined, params: DelegationPromptCompatibilityParams): boolean;
|
|
21
|
-
export declare function applyDeepAgentDelegationPromptCompatibility<T extends DelegationPromptCompatibilityParams>(model: CompiledModel | undefined, params: T): T;
|
|
22
|
-
export {};
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
function isOpenAICompatibleGptOssModel(model) {
|
|
3
|
-
return model?.provider === "openai-compatible" && model.model.trim().toLowerCase().startsWith("gpt-oss");
|
|
4
|
-
}
|
|
5
2
|
export function relativizeDeepAgentSkillSourcePaths(workspaceRoot, skillPaths) {
|
|
6
3
|
if (!workspaceRoot || !skillPaths) {
|
|
7
4
|
return skillPaths;
|
|
@@ -27,22 +24,3 @@ export function resolveDeepAgentSkillSourcePaths(options) {
|
|
|
27
24
|
}
|
|
28
25
|
return relativizeDeepAgentSkillSourcePaths(workspaceRoot, skillPaths) ?? skillPaths;
|
|
29
26
|
}
|
|
30
|
-
export function shouldRelaxDeepAgentDelegationPrompt(model, params) {
|
|
31
|
-
if (!isOpenAICompatibleGptOssModel(model)) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
if ((params.subagents?.length ?? 0) === 0) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
return params.generalPurposeAgent === true || Boolean(params.taskDescription?.trim());
|
|
38
|
-
}
|
|
39
|
-
export function applyDeepAgentDelegationPromptCompatibility(model, params) {
|
|
40
|
-
if (!shouldRelaxDeepAgentDelegationPrompt(model, params)) {
|
|
41
|
-
return params;
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
...params,
|
|
45
|
-
generalPurposeAgent: undefined,
|
|
46
|
-
taskDescription: undefined,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
@@ -5,6 +5,9 @@ type RunnableLike = {
|
|
|
5
5
|
stream?: (input: unknown, config?: Record<string, unknown>) => Promise<AsyncIterable<unknown>>;
|
|
6
6
|
streamEvents?: (input: unknown, config?: Record<string, unknown>) => Promise<AsyncIterable<unknown>>;
|
|
7
7
|
};
|
|
8
|
+
export declare class ExecutionReconciliationError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
8
11
|
export declare function streamRuntimeExecution(options: {
|
|
9
12
|
binding: CompiledAgentBinding;
|
|
10
13
|
input: MessageContent;
|
|
@@ -48,5 +51,6 @@ export declare function streamRuntimeExecution(options: {
|
|
|
48
51
|
isLangChainBinding: (binding: CompiledAgentBinding) => boolean;
|
|
49
52
|
isDeepAgentBinding: (binding: CompiledAgentBinding) => boolean;
|
|
50
53
|
countConfiguredTools: (binding: CompiledAgentBinding) => number;
|
|
54
|
+
countConfiguredToolsForAgentId?: (agentId: string) => number;
|
|
51
55
|
}): AsyncGenerator<RuntimeStreamChunk>;
|
|
52
56
|
export {};
|
|
@@ -1,14 +1,109 @@
|
|
|
1
|
-
import { extractVisibleOutput, isToolCallRecoveryFailure, isRetrySafeInvalidToolSelectionError, shouldValidateExecutionWithoutToolEvidence, resolveToolCallRecoveryInstruction, sanitizeVisibleText, INVALID_TOOL_SELECTION_RECOVERY_INSTRUCTION, } from "../../parsing/output-parsing.js";
|
|
1
|
+
import { extractVisibleOutput, isToolCallRecoveryFailure, isRetrySafeInvalidToolSelectionError, resolveExecutionWithoutToolEvidenceTextInstruction, shouldValidateExecutionWithoutToolEvidence, resolveToolCallRecoveryInstruction, sanitizeVisibleText, INVALID_TOOL_SELECTION_RECOVERY_INSTRUCTION, } from "../../parsing/output-parsing.js";
|
|
2
2
|
import { buildInvocationRequest } from "../model/invocation-request.js";
|
|
3
3
|
import { buildRawModelMessages } from "../model/message-assembly.js";
|
|
4
4
|
import { projectRuntimeStreamEvent, createStreamEventProjectionState } from "../stream-event-projection.js";
|
|
5
5
|
import { projectTextStreamChunks } from "../stream-text-consumption.js";
|
|
6
6
|
import { computeRemainingTimeoutMs } from "../resilience.js";
|
|
7
7
|
import { UPSTREAM_REQUEST_CONFIG_KEY, UPSTREAM_SESSION_CONFIG_KEY } from "../upstream-configurable-keys.js";
|
|
8
|
+
export class ExecutionReconciliationError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "ExecutionReconciliationError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
8
14
|
function toVisibleContent(value) {
|
|
9
15
|
const extracted = extractVisibleOutput(value);
|
|
10
16
|
return extracted ? sanitizeVisibleText(extracted) : "";
|
|
11
17
|
}
|
|
18
|
+
function readTerminalEventVisibleOutput(event) {
|
|
19
|
+
if (typeof event !== "object" || event === null) {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
const typed = event;
|
|
23
|
+
const eventName = typeof typed.event === "string" ? typed.event : "";
|
|
24
|
+
if (eventName !== "on_chat_model_end" && eventName !== "on_chain_end") {
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
return toVisibleContent(typed.data?.output);
|
|
28
|
+
}
|
|
29
|
+
function hasIncompletePlanStateInExecutedToolResults(executedToolResults) {
|
|
30
|
+
for (const latest of [...executedToolResults].reverse()) {
|
|
31
|
+
if (typeof latest.output !== "object" || latest.output === null) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const summaryContainer = latest.output.summary;
|
|
35
|
+
if (typeof summaryContainer !== "object" || summaryContainer === null) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const summary = summaryContainer.summary;
|
|
39
|
+
if (typeof summary !== "object" || summary === null) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const typedSummary = summary;
|
|
43
|
+
const pending = typeof typedSummary.pending === "number" ? typedSummary.pending : 0;
|
|
44
|
+
const inProgress = typeof typedSummary.inProgress === "number" ? typedSummary.inProgress : 0;
|
|
45
|
+
return pending > 0 || inProgress > 0;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function hasNonTodoToolEvidence(executedToolResults) {
|
|
50
|
+
return executedToolResults.some((item) => item.toolName !== "write_todos" && item.toolName !== "read_todos");
|
|
51
|
+
}
|
|
52
|
+
function hasSuccessfulNonTodoToolEvidence(executedToolResults) {
|
|
53
|
+
return executedToolResults.some((item) => item.isError !== true && item.toolName !== "write_todos" && item.toolName !== "read_todos");
|
|
54
|
+
}
|
|
55
|
+
function hasSuccessfulTaskToolEvidence(executedToolResults) {
|
|
56
|
+
return executedToolResults.some((item) => item.isError !== true && item.toolName === "task");
|
|
57
|
+
}
|
|
58
|
+
function buildExecutionRecoveryEvidence(params) {
|
|
59
|
+
const { projectionState, executedToolResults = [] } = params;
|
|
60
|
+
return {
|
|
61
|
+
hasToolResultEvidence: executedToolResults.length > 0 || projectionState.emittedToolResult || projectionState.emittedToolError,
|
|
62
|
+
hasSuccessfulToolResultEvidence: executedToolResults.some((item) => item.isError !== true) || projectionState.emittedSuccessfulToolResult,
|
|
63
|
+
hasNonTodoToolResultEvidence: hasNonTodoToolEvidence(executedToolResults) || projectionState.emittedNonTodoToolResult || projectionState.emittedToolError,
|
|
64
|
+
hasSuccessfulNonTodoToolResultEvidence: hasSuccessfulNonTodoToolEvidence(executedToolResults) || projectionState.emittedSuccessfulNonTodoToolResult,
|
|
65
|
+
hasIncompletePlanState: projectionState.hasIncompletePlanState || hasIncompletePlanStateInExecutedToolResults(executedToolResults),
|
|
66
|
+
hasPlanStateEvidence: projectionState.sawPlanState || hasIncompletePlanStateInExecutedToolResults(executedToolResults),
|
|
67
|
+
hasOpenTaskDelegation: projectionState.openTaskDelegations > 0,
|
|
68
|
+
hasFailedTaskDelegation: projectionState.hasFailedTaskDelegation,
|
|
69
|
+
hasDelegatedAgentWithConfiguredTools: projectionState.sawDelegatedAgentWithConfiguredTools,
|
|
70
|
+
hasDelegatedExecutionToolEvidence: projectionState.emittedDelegatedExecutionToolResult,
|
|
71
|
+
hasOnlyPlaceholderTaskCompletion: projectionState.emittedSuccessfulTaskResult
|
|
72
|
+
&& projectionState.emittedPlaceholderTaskResult
|
|
73
|
+
&& !projectionState.emittedDelegatedTerminalOutput,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function hasUnresolvedExecution(evidence) {
|
|
77
|
+
return (evidence.hasIncompletePlanState
|
|
78
|
+
|| evidence.hasFailedTaskDelegation
|
|
79
|
+
|| evidence.hasOpenTaskDelegation);
|
|
80
|
+
}
|
|
81
|
+
function hasMissingDelegatedExecutionEvidence(evidence) {
|
|
82
|
+
return evidence.hasDelegatedAgentWithConfiguredTools && !evidence.hasDelegatedExecutionToolEvidence;
|
|
83
|
+
}
|
|
84
|
+
function hasMissingDelegatedFindings(evidence) {
|
|
85
|
+
return evidence.hasDelegatedAgentWithConfiguredTools && evidence.hasOnlyPlaceholderTaskCompletion;
|
|
86
|
+
}
|
|
87
|
+
function createUnresolvedExecutionError(evidence) {
|
|
88
|
+
const reasons = [];
|
|
89
|
+
if (evidence.hasIncompletePlanState) {
|
|
90
|
+
reasons.push("plan state still has unfinished work");
|
|
91
|
+
}
|
|
92
|
+
if (evidence.hasFailedTaskDelegation) {
|
|
93
|
+
reasons.push("delegated task failed before surfacing final findings");
|
|
94
|
+
}
|
|
95
|
+
if (evidence.hasOpenTaskDelegation) {
|
|
96
|
+
reasons.push("delegated task has not finished");
|
|
97
|
+
}
|
|
98
|
+
if (hasMissingDelegatedExecutionEvidence(evidence)) {
|
|
99
|
+
reasons.push("delegated agent ended without surfacing any real tool execution evidence");
|
|
100
|
+
}
|
|
101
|
+
if (hasMissingDelegatedFindings(evidence)) {
|
|
102
|
+
reasons.push("delegated task returned only the upstream placeholder result without surfaced final findings");
|
|
103
|
+
}
|
|
104
|
+
const detail = reasons.length > 0 ? `: ${reasons.join("; ")}` : "";
|
|
105
|
+
return new ExecutionReconciliationError(`Agent ended before execution was fully reconciled${detail}.`);
|
|
106
|
+
}
|
|
12
107
|
function createProfileStep(id, kind, name, action, status, detail) {
|
|
13
108
|
return {
|
|
14
109
|
kind: "profile",
|
|
@@ -49,6 +144,7 @@ export async function* streamRuntimeExecution(options) {
|
|
|
49
144
|
const shouldValidateStreamOutput = shouldValidateExecutionWithoutToolEvidence(request);
|
|
50
145
|
const deferredStreamContent = [];
|
|
51
146
|
let sawRetrySafeInvalidToolSelectionError = false;
|
|
147
|
+
const projectionState = createStreamEventProjectionState();
|
|
52
148
|
const shouldDeferStreamContent = () => shouldValidateStreamOutput && !emittedUnsafeStreamSideEffects;
|
|
53
149
|
const flushDeferredStreamContent = async function* () {
|
|
54
150
|
while (deferredStreamContent.length > 0) {
|
|
@@ -212,7 +308,6 @@ export async function* streamRuntimeExecution(options) {
|
|
|
212
308
|
});
|
|
213
309
|
throw error;
|
|
214
310
|
}
|
|
215
|
-
const projectionState = createStreamEventProjectionState();
|
|
216
311
|
const streamEventsConsume = startProfileStep({
|
|
217
312
|
id: "profile:agent:stream-events-consume",
|
|
218
313
|
kind: "agent",
|
|
@@ -225,8 +320,12 @@ export async function* streamRuntimeExecution(options) {
|
|
|
225
320
|
for await (const event of options.iterateWithTimeout(events, options.streamIdleTimeoutMs, "agent streamEvents", options.streamDeadlineAt, options.invokeTimeoutMs)) {
|
|
226
321
|
const projectedChunks = projectRuntimeStreamEvent({
|
|
227
322
|
event,
|
|
228
|
-
allowVisibleStreamDeltas:
|
|
323
|
+
allowVisibleStreamDeltas: true,
|
|
229
324
|
includeStateStreamOutput: options.isDeepAgentBinding(options.binding),
|
|
325
|
+
rootAgentId: typeof options.binding.agent?.id === "string"
|
|
326
|
+
? options.binding.agent.id
|
|
327
|
+
: undefined,
|
|
328
|
+
countConfiguredToolsForAgentId: options.countConfiguredToolsForAgentId,
|
|
230
329
|
toolNameMapping: options.toolNameMapping,
|
|
231
330
|
primaryTools: options.primaryTools,
|
|
232
331
|
state: projectionState,
|
|
@@ -248,12 +347,27 @@ export async function* streamRuntimeExecution(options) {
|
|
|
248
347
|
if (eventContainsNonTodoToolResult || eventContainsNonRetrySafeChunk) {
|
|
249
348
|
emittedUnsafeStreamSideEffects = true;
|
|
250
349
|
}
|
|
251
|
-
if (chunk.kind === "content" && shouldDeferStreamContent()) {
|
|
350
|
+
if (chunk.kind === "content" && (shouldDeferStreamContent() || projectionState.hasFailedTaskDelegation)) {
|
|
252
351
|
deferredStreamContent.push(chunk);
|
|
253
352
|
continue;
|
|
254
353
|
}
|
|
255
354
|
yield chunk;
|
|
256
355
|
}
|
|
356
|
+
const terminalVisibleOutput = readTerminalEventVisibleOutput(event);
|
|
357
|
+
if (terminalVisibleOutput) {
|
|
358
|
+
const terminalExecutionEvidence = buildExecutionRecoveryEvidence({ projectionState });
|
|
359
|
+
if (!shouldDeferStreamContent()
|
|
360
|
+
&& !terminalExecutionEvidence.hasIncompletePlanState
|
|
361
|
+
&& !terminalExecutionEvidence.hasFailedTaskDelegation
|
|
362
|
+
&& !terminalExecutionEvidence.hasOpenTaskDelegation
|
|
363
|
+
&& !hasMissingDelegatedExecutionEvidence(terminalExecutionEvidence)
|
|
364
|
+
&& !hasMissingDelegatedFindings(terminalExecutionEvidence)) {
|
|
365
|
+
if (deferredStreamContent.length > 0) {
|
|
366
|
+
yield* flushDeferredStreamContent();
|
|
367
|
+
}
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
257
371
|
}
|
|
258
372
|
if (shouldProfile)
|
|
259
373
|
yield finishProfileStep({
|
|
@@ -278,13 +392,55 @@ export async function* streamRuntimeExecution(options) {
|
|
|
278
392
|
});
|
|
279
393
|
throw error;
|
|
280
394
|
}
|
|
281
|
-
const
|
|
282
|
-
if (
|
|
395
|
+
const streamedExecutionEvidence = buildExecutionRecoveryEvidence({ projectionState });
|
|
396
|
+
if (hasUnresolvedExecution(streamedExecutionEvidence)) {
|
|
397
|
+
throw createUnresolvedExecutionError(streamedExecutionEvidence);
|
|
398
|
+
}
|
|
399
|
+
const executionWithoutToolEvidenceInstruction = projectionState.emittedOutput
|
|
400
|
+
? resolveExecutionWithoutToolEvidenceTextInstruction(request, projectionState.emittedOutput, false, {
|
|
401
|
+
...streamedExecutionEvidence,
|
|
402
|
+
hasMissingDelegatedExecutionEvidence: hasMissingDelegatedExecutionEvidence(streamedExecutionEvidence),
|
|
403
|
+
})
|
|
404
|
+
: null;
|
|
405
|
+
const retryInstruction = !emittedUnsafeStreamSideEffects && sawRetrySafeInvalidToolSelectionError
|
|
406
|
+
? INVALID_TOOL_SELECTION_RECOVERY_INSTRUCTION
|
|
407
|
+
: executionWithoutToolEvidenceInstruction;
|
|
408
|
+
if (retryInstruction) {
|
|
283
409
|
let retried;
|
|
284
|
-
retried = await options.invoke(options.applyToolRecoveryInstruction(options.binding,
|
|
410
|
+
retried = await options.invoke(options.applyToolRecoveryInstruction(options.binding, retryInstruction), options.input, options.sessionId, options.runtimeOptions.requestId ?? options.sessionId, undefined, options.history, options.runtimeOptions);
|
|
285
411
|
const executedToolResults = Array.isArray(retried.metadata?.executedToolResults)
|
|
286
412
|
? retried.metadata.executedToolResults
|
|
287
413
|
: [];
|
|
414
|
+
const originalExecutionEvidence = buildExecutionRecoveryEvidence({ projectionState });
|
|
415
|
+
const retriedExecutionEvidence = buildExecutionRecoveryEvidence({
|
|
416
|
+
projectionState: createStreamEventProjectionState(),
|
|
417
|
+
executedToolResults,
|
|
418
|
+
});
|
|
419
|
+
const retriedVisibleOutput = retried.output ? toVisibleContent(retried.output) : "";
|
|
420
|
+
const retriedCarriesExecutionEvidence = retriedExecutionEvidence.hasToolResultEvidence
|
|
421
|
+
|| retriedExecutionEvidence.hasOpenTaskDelegation
|
|
422
|
+
|| retriedExecutionEvidence.hasDelegatedExecutionToolEvidence;
|
|
423
|
+
const retriedHasUnresolvedExecution = hasUnresolvedExecution(retriedExecutionEvidence)
|
|
424
|
+
|| hasMissingDelegatedExecutionEvidence(retriedExecutionEvidence)
|
|
425
|
+
|| hasMissingDelegatedFindings(retriedExecutionEvidence)
|
|
426
|
+
|| (!retriedCarriesExecutionEvidence
|
|
427
|
+
&& (hasUnresolvedExecution(originalExecutionEvidence)
|
|
428
|
+
|| hasMissingDelegatedExecutionEvidence(originalExecutionEvidence)
|
|
429
|
+
|| hasMissingDelegatedFindings(originalExecutionEvidence)));
|
|
430
|
+
const effectiveRecoveryEvidence = retriedCarriesExecutionEvidence
|
|
431
|
+
? retriedExecutionEvidence
|
|
432
|
+
: {
|
|
433
|
+
...retriedExecutionEvidence,
|
|
434
|
+
hasIncompletePlanState: originalExecutionEvidence.hasIncompletePlanState,
|
|
435
|
+
hasFailedTaskDelegation: originalExecutionEvidence.hasFailedTaskDelegation,
|
|
436
|
+
hasOpenTaskDelegation: originalExecutionEvidence.hasOpenTaskDelegation,
|
|
437
|
+
hasDelegatedAgentWithConfiguredTools: originalExecutionEvidence.hasDelegatedAgentWithConfiguredTools,
|
|
438
|
+
hasDelegatedExecutionToolEvidence: originalExecutionEvidence.hasDelegatedExecutionToolEvidence,
|
|
439
|
+
};
|
|
440
|
+
if (retriedHasUnresolvedExecution
|
|
441
|
+
|| (retriedHasUnresolvedExecution && retriedExecutionEvidence.hasToolResultEvidence && !retriedVisibleOutput)) {
|
|
442
|
+
throw createUnresolvedExecutionError(effectiveRecoveryEvidence);
|
|
443
|
+
}
|
|
288
444
|
for (const toolResult of executedToolResults) {
|
|
289
445
|
yield {
|
|
290
446
|
kind: "tool-result",
|
|
@@ -303,7 +459,15 @@ export async function* streamRuntimeExecution(options) {
|
|
|
303
459
|
if (deferredStreamContent.length > 0) {
|
|
304
460
|
yield* flushDeferredStreamContent();
|
|
305
461
|
}
|
|
306
|
-
if (
|
|
462
|
+
if (hasMissingDelegatedExecutionEvidence(streamedExecutionEvidence)) {
|
|
463
|
+
throw createUnresolvedExecutionError(streamedExecutionEvidence);
|
|
464
|
+
}
|
|
465
|
+
if (hasMissingDelegatedFindings(streamedExecutionEvidence)) {
|
|
466
|
+
throw createUnresolvedExecutionError(streamedExecutionEvidence);
|
|
467
|
+
}
|
|
468
|
+
const hasUnresolvedStreamExecution = hasUnresolvedExecution(streamedExecutionEvidence);
|
|
469
|
+
if (projectionState.emittedOutput
|
|
470
|
+
|| ((projectionState.emittedToolResult || projectionState.emittedToolError) && !hasUnresolvedStreamExecution)) {
|
|
307
471
|
return;
|
|
308
472
|
}
|
|
309
473
|
}
|
|
@@ -429,6 +593,73 @@ export async function* streamRuntimeExecution(options) {
|
|
|
429
593
|
const executedToolResults = Array.isArray(result.metadata?.executedToolResults)
|
|
430
594
|
? result.metadata.executedToolResults
|
|
431
595
|
: [];
|
|
596
|
+
const invokeExecutionEvidence = buildExecutionRecoveryEvidence({ projectionState, executedToolResults });
|
|
597
|
+
if (hasUnresolvedExecution(invokeExecutionEvidence)) {
|
|
598
|
+
throw createUnresolvedExecutionError(invokeExecutionEvidence);
|
|
599
|
+
}
|
|
600
|
+
const invokeFallbackRecoveryInstruction = result.output
|
|
601
|
+
? resolveExecutionWithoutToolEvidenceTextInstruction(request, result.output, false, {
|
|
602
|
+
...invokeExecutionEvidence,
|
|
603
|
+
hasMissingDelegatedExecutionEvidence: hasMissingDelegatedExecutionEvidence(invokeExecutionEvidence),
|
|
604
|
+
})
|
|
605
|
+
: null;
|
|
606
|
+
if (invokeFallbackRecoveryInstruction) {
|
|
607
|
+
const recovered = await options.invoke(options.applyToolRecoveryInstruction(options.binding, invokeFallbackRecoveryInstruction), options.input, options.sessionId, options.runtimeOptions.requestId ?? options.sessionId, undefined, options.history, options.runtimeOptions);
|
|
608
|
+
const recoveredToolResults = Array.isArray(recovered.metadata?.executedToolResults)
|
|
609
|
+
? recovered.metadata.executedToolResults
|
|
610
|
+
: [];
|
|
611
|
+
const originalExecutionEvidence = buildExecutionRecoveryEvidence({ projectionState, executedToolResults });
|
|
612
|
+
const recoveredExecutionEvidence = buildExecutionRecoveryEvidence({
|
|
613
|
+
projectionState: createStreamEventProjectionState(),
|
|
614
|
+
executedToolResults: recoveredToolResults,
|
|
615
|
+
});
|
|
616
|
+
const recoveredVisibleOutput = recovered.output ? toVisibleContent(recovered.output) : "";
|
|
617
|
+
const recoveredCarriesExecutionEvidence = recoveredExecutionEvidence.hasToolResultEvidence
|
|
618
|
+
|| recoveredExecutionEvidence.hasOpenTaskDelegation
|
|
619
|
+
|| recoveredExecutionEvidence.hasDelegatedExecutionToolEvidence;
|
|
620
|
+
const recoveredHasUnresolvedExecution = hasUnresolvedExecution(recoveredExecutionEvidence)
|
|
621
|
+
|| hasMissingDelegatedExecutionEvidence(recoveredExecutionEvidence)
|
|
622
|
+
|| hasMissingDelegatedFindings(recoveredExecutionEvidence)
|
|
623
|
+
|| (!recoveredCarriesExecutionEvidence
|
|
624
|
+
&& (hasUnresolvedExecution(originalExecutionEvidence)
|
|
625
|
+
|| hasMissingDelegatedExecutionEvidence(originalExecutionEvidence)
|
|
626
|
+
|| hasMissingDelegatedFindings(originalExecutionEvidence)));
|
|
627
|
+
const effectiveRecoveredEvidence = recoveredCarriesExecutionEvidence
|
|
628
|
+
? recoveredExecutionEvidence
|
|
629
|
+
: {
|
|
630
|
+
...recoveredExecutionEvidence,
|
|
631
|
+
hasIncompletePlanState: originalExecutionEvidence.hasIncompletePlanState,
|
|
632
|
+
hasFailedTaskDelegation: originalExecutionEvidence.hasFailedTaskDelegation,
|
|
633
|
+
hasOpenTaskDelegation: originalExecutionEvidence.hasOpenTaskDelegation,
|
|
634
|
+
hasDelegatedAgentWithConfiguredTools: originalExecutionEvidence.hasDelegatedAgentWithConfiguredTools,
|
|
635
|
+
hasDelegatedExecutionToolEvidence: originalExecutionEvidence.hasDelegatedExecutionToolEvidence,
|
|
636
|
+
};
|
|
637
|
+
if (recoveredHasUnresolvedExecution
|
|
638
|
+
|| (recoveredHasUnresolvedExecution && recoveredExecutionEvidence.hasToolResultEvidence && !recoveredVisibleOutput)) {
|
|
639
|
+
throw createUnresolvedExecutionError(effectiveRecoveredEvidence);
|
|
640
|
+
}
|
|
641
|
+
for (const toolResult of recoveredToolResults) {
|
|
642
|
+
yield {
|
|
643
|
+
kind: "tool-result",
|
|
644
|
+
toolName: toolResult.toolName,
|
|
645
|
+
output: toolResult.output,
|
|
646
|
+
isError: toolResult.isError,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
if (recovered.output) {
|
|
650
|
+
const visible = toVisibleContent(recovered.output);
|
|
651
|
+
if (visible) {
|
|
652
|
+
yield { kind: "content", content: visible };
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (hasMissingDelegatedExecutionEvidence(invokeExecutionEvidence)) {
|
|
658
|
+
throw createUnresolvedExecutionError(invokeExecutionEvidence);
|
|
659
|
+
}
|
|
660
|
+
if (hasMissingDelegatedFindings(invokeExecutionEvidence)) {
|
|
661
|
+
throw createUnresolvedExecutionError(invokeExecutionEvidence);
|
|
662
|
+
}
|
|
432
663
|
for (const toolResult of executedToolResults) {
|
|
433
664
|
yield {
|
|
434
665
|
kind: "tool-result",
|
|
@@ -4,7 +4,42 @@ import { canReplayToolCallsLocally } from "./tool/tool-replay.js";
|
|
|
4
4
|
import { extractToolCallsFromResult, normalizeToolArgsForSchema, stringifyToolOutput } from "./tool/tool-arguments.js";
|
|
5
5
|
import { extractMemoryCandidatesFromToolOutput } from "../harness/system/runtime-memory-candidates.js";
|
|
6
6
|
import { maybePersistLargeToolOutput } from "./tool/tool-output-artifacts.js";
|
|
7
|
+
import { appendToolRecoveryInstruction, extractVisibleOutput, resolveExecutionWithoutToolEvidenceTextInstruction, sanitizeVisibleText, } from "../parsing/output-parsing.js";
|
|
8
|
+
import { AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION } from "../prompts/runtime-prompts.js";
|
|
7
9
|
const TOOL_FOLLOW_UP_INSTRUCTION = "One or more tool results are already available in this conversation. Answer the user's current request directly from the existing context and tool results. Do not ask the user to repeat inputs that are already present above.";
|
|
10
|
+
function readPlanStateSummary(output) {
|
|
11
|
+
if (typeof output !== "object" || output === null) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const typed = output;
|
|
15
|
+
const summaryContainer = typed.summary;
|
|
16
|
+
if (typeof summaryContainer !== "object" || summaryContainer === null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const nested = summaryContainer;
|
|
20
|
+
const counts = nested.summary;
|
|
21
|
+
if (typeof counts !== "object" || counts === null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const typedCounts = counts;
|
|
25
|
+
return {
|
|
26
|
+
pending: typeof typedCounts.pending === "number" ? typedCounts.pending : 0,
|
|
27
|
+
inProgress: typeof typedCounts.inProgress === "number" ? typedCounts.inProgress : 0,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function hasIncompleteExecutedPlan(executedToolResults) {
|
|
31
|
+
for (const latest of [...executedToolResults].reverse()) {
|
|
32
|
+
const summary = readPlanStateSummary(latest.output);
|
|
33
|
+
if (!summary) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
return summary.pending > 0 || summary.inProgress > 0;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
function hasNonTodoToolEvidence(executedToolResults) {
|
|
41
|
+
return executedToolResults.some((item) => item.toolName !== "write_todos" && item.toolName !== "read_todos");
|
|
42
|
+
}
|
|
8
43
|
function extractLatestUserInput(request) {
|
|
9
44
|
const typedRequest = request;
|
|
10
45
|
const messages = Array.isArray(typedRequest.messages) ? typedRequest.messages : [];
|
|
@@ -40,6 +75,24 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
40
75
|
pendingResult = undefined;
|
|
41
76
|
const toolCalls = extractToolCallsFromResult(result);
|
|
42
77
|
if (toolCalls.length === 0) {
|
|
78
|
+
const terminalText = sanitizeVisibleText(extractVisibleOutput(result) || "");
|
|
79
|
+
const hasIncompletePlanState = hasIncompleteExecutedPlan(executedToolResults);
|
|
80
|
+
const hasExecutionBeyondTodoPlanning = hasNonTodoToolEvidence(executedToolResults);
|
|
81
|
+
const recoveryInstruction = terminalText
|
|
82
|
+
? resolveExecutionWithoutToolEvidenceTextInstruction(activeRequest, terminalText, false, {
|
|
83
|
+
hasToolResultEvidence: hasExecutionBeyondTodoPlanning,
|
|
84
|
+
hasIncompletePlanState: hasExecutionBeyondTodoPlanning && hasIncompletePlanState,
|
|
85
|
+
})
|
|
86
|
+
: hasIncompletePlanState && hasExecutionBeyondTodoPlanning
|
|
87
|
+
? AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION
|
|
88
|
+
: null;
|
|
89
|
+
if (recoveryInstruction) {
|
|
90
|
+
if (iteration + 1 === maxToolIterations) {
|
|
91
|
+
throw new Error(`Tool-calling loop exceeded the maximum of ${maxToolIterations} iterations`);
|
|
92
|
+
}
|
|
93
|
+
activeRequest = appendToolRecoveryInstruction(activeRequest, recoveryInstruction);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
43
96
|
break;
|
|
44
97
|
}
|
|
45
98
|
if (!canReplayToolCallsLocally(binding, toolCalls, primaryTools, toolNameMapping, executableTools, builtinExecutableTools)) {
|