@botbotgo/agent-harness 0.0.345 → 0.0.347

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.
@@ -17,6 +17,7 @@ export type RequestResult = {
17
17
  artifacts?: ArtifactRecord[];
18
18
  metadata?: Record<string, unknown>;
19
19
  };
20
+ export type TerminalExecutionStatus = "completed" | "blocked" | "failed" | "refused";
20
21
  export type UpstreamRuntimeEvent = unknown;
21
22
  export type UpstreamRuntimeEventItem = {
22
23
  sessionId: string;
@@ -203,6 +203,9 @@ export type CompiledBuiltinToolsConfig = {
203
203
  todos?: boolean;
204
204
  modelExposed?: boolean | string[];
205
205
  };
206
+ export type CompiledExecutionContract = {
207
+ requiresPlan?: boolean;
208
+ };
206
209
  export type LangChainAgentParams = {
207
210
  model: CompiledModel;
208
211
  tools: CompiledTool[];
@@ -287,6 +290,7 @@ export type CompiledAgentBinding = {
287
290
  resilience?: Record<string, unknown>;
288
291
  governance?: Record<string, unknown>;
289
292
  observability?: Record<string, unknown>;
293
+ executionContract?: CompiledExecutionContract;
290
294
  deepagent?: {
291
295
  description?: string;
292
296
  passthrough?: Record<string, unknown>;
@@ -1,2 +1,2 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.345";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.347";
2
2
  export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.345";
1
+ export const AGENT_HARNESS_VERSION = "0.0.347";
2
2
  export const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
@@ -0,0 +1,8 @@
1
+ The delegated task failed. You are the routing/delegation parent agent, so you must not switch into local execution or start a new local plan.
2
+
3
+ Your next response has only two valid forms:
4
+
5
+ 1. Call the `task` tool again, preserving the user's original request and delegating to the same specialist or another explicit specialist whose configured responsibility clearly matches the original request.
6
+ 2. Return a final blocker report to the user explaining that delegated execution failed.
7
+
8
+ Do not call local execution tools, repository tools, web tools, shell tools, or `write_todos` from the parent agent after this delegated failure. Do not invent a new topic or downgrade the original request. If you continue execution, it must be through `task`.
@@ -1,4 +1,5 @@
1
1
  import { extractVisibleOutput, isToolCallRecoveryFailure, isRetrySafeInvalidToolSelectionError, resolveMissingPlanRecoveryInstruction, resolveExecutionWithoutToolEvidenceTextInstruction, shouldValidateExecutionWithoutToolEvidence, resolveToolCallRecoveryInstruction, sanitizeVisibleText, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION, INVALID_TOOL_SELECTION_RECOVERY_INSTRUCTION, } from "../../parsing/output-parsing.js";
2
+ import { DELEGATED_TASK_FAILURE_RECOVERY_INSTRUCTION } from "../../prompts/runtime-prompts.js";
2
3
  import { buildInvocationRequest } from "../model/invocation-request.js";
3
4
  import { buildRawModelMessages } from "../model/message-assembly.js";
4
5
  import { projectRuntimeStreamEvent, createStreamEventProjectionState } from "../stream-event-projection.js";
@@ -55,6 +56,18 @@ function hasSuccessfulNonTodoToolEvidence(executedToolResults) {
55
56
  function hasSuccessfulTaskToolEvidence(executedToolResults) {
56
57
  return executedToolResults.some((item) => item.isError !== true && item.toolName === "task");
57
58
  }
59
+ function requiresPlanEvidence(binding) {
60
+ return binding.harnessRuntime?.executionContract?.requiresPlan === true;
61
+ }
62
+ function hasParentLocalToolExecutionAfterDelegationFailure(originalEvidence, executedToolResults) {
63
+ return originalEvidence.hasFailedTaskDelegation
64
+ && executedToolResults.some((item) => item.toolName !== "task");
65
+ }
66
+ function isDelegationFailureFinalReport(originalEvidence, executedToolResults, visibleOutput) {
67
+ return originalEvidence.hasFailedTaskDelegation
68
+ && executedToolResults.length === 0
69
+ && visibleOutput.trim().length > 0;
70
+ }
58
71
  function buildExecutionRecoveryEvidence(params) {
59
72
  const { projectionState, executedToolResults = [] } = params;
60
73
  return {
@@ -65,7 +78,8 @@ function buildExecutionRecoveryEvidence(params) {
65
78
  hasIncompletePlanState: projectionState.hasIncompletePlanState || hasIncompletePlanStateInExecutedToolResults(executedToolResults),
66
79
  hasPlanStateEvidence: projectionState.sawPlanState || hasIncompletePlanStateInExecutedToolResults(executedToolResults),
67
80
  hasOpenTaskDelegation: projectionState.openTaskDelegations > 0,
68
- hasFailedTaskDelegation: projectionState.hasFailedTaskDelegation,
81
+ hasFailedTaskDelegation: projectionState.hasFailedTaskDelegation
82
+ || executedToolResults.some((item) => item.toolName === "task" && item.isError === true),
69
83
  hasDelegatedAgentWithConfiguredTools: projectionState.sawDelegatedAgentWithConfiguredTools,
70
84
  hasDelegatedExecutionToolEvidence: projectionState.emittedDelegatedExecutionToolResult,
71
85
  hasOnlyPlaceholderTaskCompletion: projectionState.emittedSuccessfulTaskResult
@@ -98,6 +112,9 @@ function resolveStreamedRuntimeFailureRecoveryInstruction(output, evidence) {
98
112
  return hasExecutionEvidence ? null : EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION;
99
113
  }
100
114
  function resolveDelegatedExecutionRecoveryInstruction(evidence) {
115
+ if (evidence.hasFailedTaskDelegation) {
116
+ return DELEGATED_TASK_FAILURE_RECOVERY_INSTRUCTION;
117
+ }
101
118
  if (hasMissingDelegatedFindings(evidence)
102
119
  || (evidence.hasOpenTaskDelegation
103
120
  && evidence.hasDelegatedAgentWithConfiguredTools
@@ -400,6 +417,7 @@ export async function* streamRuntimeExecution(options) {
400
417
  ? resolveMissingPlanRecoveryInstruction({
401
418
  request,
402
419
  assistantText: terminalVisibleOutput,
420
+ requiresPlan: requiresPlanEvidence(options.binding),
403
421
  hasPlanStateEvidence: terminalExecutionEvidence.hasPlanStateEvidence,
404
422
  hasWriteTodosEvidence: terminalExecutionEvidence.hasPlanStateEvidence,
405
423
  hasToolResultEvidence: terminalExecutionEvidence.hasSuccessfulNonTodoToolResultEvidence,
@@ -443,8 +461,9 @@ export async function* streamRuntimeExecution(options) {
443
461
  throw error;
444
462
  }
445
463
  const streamedExecutionEvidence = buildExecutionRecoveryEvidence({ projectionState });
446
- const delegatedExecutionRecoveryInstruction = !emittedUnsafeStreamSideEffects
447
- ? resolveDelegatedExecutionRecoveryInstruction(streamedExecutionEvidence)
464
+ const streamedDelegatedRecoveryInstruction = resolveDelegatedExecutionRecoveryInstruction(streamedExecutionEvidence);
465
+ const delegatedExecutionRecoveryInstruction = !emittedUnsafeStreamSideEffects || streamedDelegatedRecoveryInstruction
466
+ ? streamedDelegatedRecoveryInstruction
448
467
  : null;
449
468
  if (hasUnresolvedExecution(streamedExecutionEvidence) && !delegatedExecutionRecoveryInstruction) {
450
469
  throw createUnresolvedExecutionError(streamedExecutionEvidence);
@@ -453,6 +472,7 @@ export async function* streamRuntimeExecution(options) {
453
472
  ? resolveExecutionWithoutToolEvidenceTextInstruction(request, projectionState.emittedOutput, false, {
454
473
  ...streamedExecutionEvidence,
455
474
  hasMissingDelegatedExecutionEvidence: hasMissingDelegatedExecutionEvidence(streamedExecutionEvidence),
475
+ requiresPlan: requiresPlanEvidence(options.binding),
456
476
  })
457
477
  : null;
458
478
  const streamedRuntimeFailureRecoveryInstruction = projectionState.emittedOutput
@@ -462,6 +482,7 @@ export async function* streamRuntimeExecution(options) {
462
482
  ? resolveMissingPlanRecoveryInstruction({
463
483
  request,
464
484
  assistantText: projectionState.emittedOutput,
485
+ requiresPlan: requiresPlanEvidence(options.binding),
465
486
  hasPlanStateEvidence: streamedExecutionEvidence.hasPlanStateEvidence,
466
487
  hasWriteTodosEvidence: streamedExecutionEvidence.hasPlanStateEvidence,
467
488
  hasToolResultEvidence: streamedExecutionEvidence.hasSuccessfulNonTodoToolResultEvidence,
@@ -484,17 +505,22 @@ export async function* streamRuntimeExecution(options) {
484
505
  projectionState: createStreamEventProjectionState(),
485
506
  executedToolResults,
486
507
  });
508
+ if (hasParentLocalToolExecutionAfterDelegationFailure(originalExecutionEvidence, executedToolResults)) {
509
+ throw new ExecutionReconciliationError("Agent attempted parent-local tool execution after delegated task failure; it must report a blocker or re-delegate with task.");
510
+ }
487
511
  const retriedVisibleOutput = retried.output ? toVisibleContent(retried.output) : "";
512
+ const retriedIsDelegationFailureFinalReport = isDelegationFailureFinalReport(originalExecutionEvidence, executedToolResults, retriedVisibleOutput);
488
513
  const retriedCarriesExecutionEvidence = retriedExecutionEvidence.hasToolResultEvidence
489
514
  || retriedExecutionEvidence.hasOpenTaskDelegation
490
515
  || retriedExecutionEvidence.hasDelegatedExecutionToolEvidence;
491
- const retriedHasUnresolvedExecution = hasUnresolvedExecution(retriedExecutionEvidence)
492
- || hasMissingDelegatedExecutionEvidence(retriedExecutionEvidence)
493
- || hasMissingDelegatedFindings(retriedExecutionEvidence)
494
- || (!retriedCarriesExecutionEvidence
495
- && (hasUnresolvedExecution(originalExecutionEvidence)
496
- || hasMissingDelegatedExecutionEvidence(originalExecutionEvidence)
497
- || hasMissingDelegatedFindings(originalExecutionEvidence)));
516
+ const retriedHasUnresolvedExecution = !retriedIsDelegationFailureFinalReport
517
+ && (hasUnresolvedExecution(retriedExecutionEvidence)
518
+ || hasMissingDelegatedExecutionEvidence(retriedExecutionEvidence)
519
+ || hasMissingDelegatedFindings(retriedExecutionEvidence)
520
+ || (!retriedCarriesExecutionEvidence
521
+ && (hasUnresolvedExecution(originalExecutionEvidence)
522
+ || hasMissingDelegatedExecutionEvidence(originalExecutionEvidence)
523
+ || hasMissingDelegatedFindings(originalExecutionEvidence))));
498
524
  const effectiveRecoveryEvidence = retriedCarriesExecutionEvidence
499
525
  ? retriedExecutionEvidence
500
526
  : {
@@ -666,12 +692,14 @@ export async function* streamRuntimeExecution(options) {
666
692
  ? resolveExecutionWithoutToolEvidenceTextInstruction(request, result.output, false, {
667
693
  ...invokeExecutionEvidence,
668
694
  hasMissingDelegatedExecutionEvidence: hasMissingDelegatedExecutionEvidence(invokeExecutionEvidence),
695
+ requiresPlan: requiresPlanEvidence(options.binding),
669
696
  })
670
697
  : resolveDelegatedExecutionRecoveryInstruction(invokeExecutionEvidence);
671
698
  const invokeFallbackMissingPlanRecoveryInstruction = !hasUnresolvedExecution(invokeExecutionEvidence) && !invokeFallbackRecoveryInstruction
672
699
  ? resolveMissingPlanRecoveryInstruction({
673
700
  request,
674
701
  assistantText: typeof result.output === "string" ? result.output : "",
702
+ requiresPlan: requiresPlanEvidence(options.binding),
675
703
  hasPlanStateEvidence: invokeExecutionEvidence.hasPlanStateEvidence,
676
704
  hasWriteTodosEvidence: invokeExecutionEvidence.hasPlanStateEvidence,
677
705
  hasToolResultEvidence: invokeExecutionEvidence.hasSuccessfulNonTodoToolResultEvidence,
@@ -688,17 +716,22 @@ export async function* streamRuntimeExecution(options) {
688
716
  projectionState: createStreamEventProjectionState(),
689
717
  executedToolResults: recoveredToolResults,
690
718
  });
719
+ if (hasParentLocalToolExecutionAfterDelegationFailure(originalExecutionEvidence, recoveredToolResults)) {
720
+ throw new ExecutionReconciliationError("Agent attempted parent-local tool execution after delegated task failure; it must report a blocker or re-delegate with task.");
721
+ }
691
722
  const recoveredVisibleOutput = recovered.output ? toVisibleContent(recovered.output) : "";
723
+ const recoveredIsDelegationFailureFinalReport = isDelegationFailureFinalReport(originalExecutionEvidence, recoveredToolResults, recoveredVisibleOutput);
692
724
  const recoveredCarriesExecutionEvidence = recoveredExecutionEvidence.hasToolResultEvidence
693
725
  || recoveredExecutionEvidence.hasOpenTaskDelegation
694
726
  || recoveredExecutionEvidence.hasDelegatedExecutionToolEvidence;
695
- const recoveredHasUnresolvedExecution = hasUnresolvedExecution(recoveredExecutionEvidence)
696
- || hasMissingDelegatedExecutionEvidence(recoveredExecutionEvidence)
697
- || hasMissingDelegatedFindings(recoveredExecutionEvidence)
698
- || (!recoveredCarriesExecutionEvidence
699
- && (hasUnresolvedExecution(originalExecutionEvidence)
700
- || hasMissingDelegatedExecutionEvidence(originalExecutionEvidence)
701
- || hasMissingDelegatedFindings(originalExecutionEvidence)));
727
+ const recoveredHasUnresolvedExecution = !recoveredIsDelegationFailureFinalReport
728
+ && (hasUnresolvedExecution(recoveredExecutionEvidence)
729
+ || hasMissingDelegatedExecutionEvidence(recoveredExecutionEvidence)
730
+ || hasMissingDelegatedFindings(recoveredExecutionEvidence)
731
+ || (!recoveredCarriesExecutionEvidence
732
+ && (hasUnresolvedExecution(originalExecutionEvidence)
733
+ || hasMissingDelegatedExecutionEvidence(originalExecutionEvidence)
734
+ || hasMissingDelegatedFindings(originalExecutionEvidence))));
702
735
  const effectiveRecoveredEvidence = recoveredCarriesExecutionEvidence
703
736
  ? recoveredExecutionEvidence
704
737
  : {
@@ -3,6 +3,7 @@ import { salvageFunctionLikeToolCall } from "../parsing/output-tool-args.js";
3
3
  import { buildStateSnapshot } from "./model/message-assembly.js";
4
4
  import { asRecord } from "./tool/resolved-tool.js";
5
5
  import { renderToolFailure } from "../support/harness-support.js";
6
+ import { mapTerminalStatusToRequestState, readTerminalExecutionStatus } from "./terminal-status.js";
6
7
  function looksLikeLeakedToolCallText(value) {
7
8
  const normalized = sanitizeVisibleText(value).trim();
8
9
  if (!normalized) {
@@ -168,6 +169,29 @@ function extractDeterministicToolFailureReport(executedToolResults) {
168
169
  "- none",
169
170
  ].join("\n");
170
171
  }
172
+ function hasEmptyFinalMessage(result) {
173
+ const messages = Array.isArray(result.messages) ? result.messages : [];
174
+ const lastMessage = messages.at(-1);
175
+ if (!lastMessage || typeof lastMessage !== "object") {
176
+ return false;
177
+ }
178
+ const direct = lastMessage;
179
+ return direct.content === "" || direct.kwargs?.content === "" || direct.lc_kwargs?.content === "";
180
+ }
181
+ function hasFinalMessageToolCalls(result) {
182
+ const messages = Array.isArray(result.messages) ? result.messages : [];
183
+ const lastMessage = messages.at(-1);
184
+ if (!lastMessage || typeof lastMessage !== "object") {
185
+ return false;
186
+ }
187
+ const direct = lastMessage;
188
+ return Array.isArray(direct.tool_calls) && direct.tool_calls.length > 0
189
+ || Array.isArray(direct.invalid_tool_calls) && direct.invalid_tool_calls.length > 0
190
+ || Array.isArray(direct.kwargs?.tool_calls) && direct.kwargs.tool_calls.length > 0
191
+ || Array.isArray(direct.kwargs?.invalid_tool_calls) && direct.kwargs.invalid_tool_calls.length > 0
192
+ || Array.isArray(direct.lc_kwargs?.tool_calls) && direct.lc_kwargs.tool_calls.length > 0
193
+ || Array.isArray(direct.lc_kwargs?.invalid_tool_calls) && direct.lc_kwargs.invalid_tool_calls.length > 0;
194
+ }
171
195
  export function resolveDeterministicFinalOutput(params) {
172
196
  const visibleOutput = params.visibleOutput ?? "";
173
197
  const toolFallback = params.toolFallback ?? "";
@@ -178,6 +202,9 @@ export function resolveDeterministicFinalOutput(params) {
178
202
  const deterministicFailureReport = extractDeterministicToolFailureReport(executedToolResults);
179
203
  const delegatedTaskOutput = extractLatestSuccessfulTaskResultText(executedToolResults);
180
204
  const successfulToolOutput = extractLatestSuccessfulNonTodoToolResultText(executedToolResults);
205
+ if (sanitizedVisibleOutput && deterministicFailureReport && hasDelegationBlocker(executedToolResults) && !successfulToolOutput) {
206
+ return deterministicFailureReport;
207
+ }
181
208
  if (sanitizedVisibleOutput && successfulToolOutput && hasDelegationBlocker(executedToolResults)) {
182
209
  return deterministicFailureReport || delegatedTaskOutput || successfulToolOutput;
183
210
  }
@@ -215,9 +242,26 @@ export function finalizeRequestResult(params) {
215
242
  const visibleOutput = extractedOutput && !isLikelyToolArgsObject(tryParseJson(extractedOutput)) ? extractedOutput : "";
216
243
  const emptyAssistantMessageFailure = extractEmptyAssistantMessageFailure(result);
217
244
  const toolFallback = extractToolFallbackContext(result);
245
+ const outputContent = extractOutputContent(result);
246
+ const contentBlocks = extractContentBlocks(result);
247
+ const structuredResponse = result.structuredResponse;
248
+ const structuredTerminalStatus = readTerminalExecutionStatus(structuredResponse) ?? readTerminalExecutionStatus(result);
249
+ const files = asRecord(result.files);
218
250
  if (!visibleOutput && !toolFallback && emptyAssistantMessageFailure) {
219
251
  throw new Error(emptyAssistantMessageFailure);
220
252
  }
253
+ if (!visibleOutput
254
+ && !toolFallback
255
+ && interruptContent === undefined
256
+ && outputContent === undefined
257
+ && contentBlocks.length === 0
258
+ && structuredResponse === undefined
259
+ && !files
260
+ && executedToolResults.length === 0
261
+ && hasEmptyFinalMessage(result)
262
+ && !hasFinalMessageToolCalls(result)) {
263
+ throw new Error("empty_final_output");
264
+ }
221
265
  const serializedResult = JSON.stringify(result, null, 2);
222
266
  const output = resolveDeterministicFinalOutput({
223
267
  visibleOutput,
@@ -226,17 +270,16 @@ export function finalizeRequestResult(params) {
226
270
  })
227
271
  || (containsLikelySkillDocument(result) ? "" : serializedResult);
228
272
  const finalMessageText = sanitizeVisibleText(output);
229
- const outputContent = extractOutputContent(result);
230
- const contentBlocks = extractContentBlocks(result);
231
- const structuredResponse = result.structuredResponse;
232
- const files = asRecord(result.files);
273
+ const terminalStatus = structuredTerminalStatus ?? readTerminalExecutionStatus(finalMessageText);
233
274
  const stateSnapshot = buildStateSnapshot(result);
234
275
  const memoryCandidates = executedToolResults.flatMap((toolResult) => toolResult.memoryCandidates ?? []);
235
276
  return {
236
277
  sessionId,
237
278
  requestId,
238
279
  agentId: bindingAgentId,
239
- state: Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? "waiting_for_approval" : "completed",
280
+ state: Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0
281
+ ? "waiting_for_approval"
282
+ : mapTerminalStatusToRequestState(terminalStatus),
240
283
  interruptContent,
241
284
  output: finalMessageText,
242
285
  finalMessageText,
@@ -247,6 +290,7 @@ export function finalizeRequestResult(params) {
247
290
  ...(executedToolResults.length > 0 ? { executedToolResults } : {}),
248
291
  ...(memoryCandidates.length > 0 ? { memoryCandidates } : {}),
249
292
  ...(structuredResponse !== undefined ? { structuredResponse } : {}),
293
+ ...(terminalStatus ? { terminalStatus } : {}),
250
294
  ...(outputContent !== undefined ? { outputContent } : {}),
251
295
  ...(contentBlocks.length > 0 ? { contentBlocks } : {}),
252
296
  ...(files ? { files } : {}),
@@ -43,6 +43,9 @@ function hasNonTodoToolEvidence(executedToolResults) {
43
43
  function hasPlanStateEvidence(executedToolResults) {
44
44
  return executedToolResults.some((item) => item.toolName === "write_todos" || item.toolName === "read_todos" || readPlanStateSummary(item.output) !== null);
45
45
  }
46
+ function requiresPlanEvidence(binding) {
47
+ return binding.harnessRuntime.executionContract?.requiresPlan === true;
48
+ }
46
49
  function extractLatestUserInput(request) {
47
50
  const typedRequest = request;
48
51
  const messages = Array.isArray(typedRequest.messages) ? typedRequest.messages : [];
@@ -87,6 +90,7 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
87
90
  hasToolResultEvidence: hasExecutionBeyondTodoPlanning,
88
91
  hasPlanStateEvidence: hasPlanStateEvidence(executedToolResults),
89
92
  hasIncompletePlanState: hasExecutionBeyondTodoPlanning && hasIncompletePlanState,
93
+ requiresPlan: requiresPlanEvidence(binding),
90
94
  })
91
95
  : hasIncompletePlanState && hasExecutionBeyondTodoPlanning
92
96
  ? AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION
@@ -102,6 +106,7 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
102
106
  }
103
107
  const missingPlanRecoveryInstruction = resolveMissingPlanRecoveryInstruction({
104
108
  request: activeRequest,
109
+ requiresPlan: requiresPlanEvidence(binding),
105
110
  hasPlanStateEvidence: hasPlanStateEvidence(executedToolResults),
106
111
  hasWriteTodosEvidence: executedToolResults.some((item) => item.toolName === "write_todos"),
107
112
  hasToolResultEvidence: executedToolResults.length > 0 || toolCalls.length > 0,
@@ -350,7 +350,9 @@ export function projectRuntimeStreamEvent(params) {
350
350
  ? state.lastCompletedTaskDelegationFindings
351
351
  : "";
352
352
  const effectiveToolOutput = salvagedTaskErrorFindings || toolResult.output;
353
- const effectiveToolIsError = salvagedTaskErrorFindings ? false : toolResult.isError;
353
+ const effectiveToolIsError = salvagedTaskErrorFindings
354
+ ? false
355
+ : toolResult.isError === true;
354
356
  const isSuccessfulTaskResult = toolResult.toolName === "task" && effectiveToolIsError !== true;
355
357
  const isDelegatedExecutionTool = (isDelegatedAgentEvent || state.openToolCapableTaskDelegations > 0)
356
358
  && toolResult.toolName !== "write_todos"
@@ -0,0 +1,4 @@
1
+ import type { RequestState, TerminalExecutionStatus } from "../../contracts/types.js";
2
+ export declare function readTerminalExecutionStatus(value: unknown): TerminalExecutionStatus | null;
3
+ export declare function mapTerminalStatusToRequestState(status: TerminalExecutionStatus | null): RequestState;
4
+ export declare function mapTerminalStatusToPlanItemStatus(status: TerminalExecutionStatus): "completed" | "failed";
@@ -0,0 +1,67 @@
1
+ const TERMINAL_STATUSES = new Set(["completed", "blocked", "failed", "refused"]);
2
+ function normalizeTerminalStatus(value) {
3
+ if (typeof value !== "string") {
4
+ return null;
5
+ }
6
+ const normalized = value.trim().toLowerCase();
7
+ return TERMINAL_STATUSES.has(normalized)
8
+ ? normalized
9
+ : null;
10
+ }
11
+ function readStatusLine(value) {
12
+ for (const line of value.split("\n")) {
13
+ const [key, ...rest] = line.split(":");
14
+ if (key?.trim().toLowerCase() !== "status") {
15
+ continue;
16
+ }
17
+ const statusValue = rest.join(":").trim().split(/\s+/)[0];
18
+ const status = normalizeTerminalStatus(statusValue);
19
+ if (status) {
20
+ return status;
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+ export function readTerminalExecutionStatus(value) {
26
+ const direct = normalizeTerminalStatus(value);
27
+ if (direct) {
28
+ return direct;
29
+ }
30
+ if (typeof value === "string") {
31
+ try {
32
+ return readTerminalExecutionStatus(JSON.parse(value));
33
+ }
34
+ catch {
35
+ return readStatusLine(value);
36
+ }
37
+ }
38
+ if (typeof value !== "object" || value === null) {
39
+ return null;
40
+ }
41
+ if (Array.isArray(value)) {
42
+ for (let index = value.length - 1; index >= 0; index -= 1) {
43
+ const status = readTerminalExecutionStatus(value[index]);
44
+ if (status) {
45
+ return status;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ const typed = value;
51
+ return (readTerminalExecutionStatus(typed.status)
52
+ ?? readTerminalExecutionStatus(typed.structuredResponse)
53
+ ?? readTerminalExecutionStatus(typed.messages)
54
+ ?? readTerminalExecutionStatus(typed.content)
55
+ ?? readTerminalExecutionStatus(typed.kwargs?.content)
56
+ ?? readTerminalExecutionStatus(typed.lc_kwargs?.content)
57
+ ?? readTerminalExecutionStatus(typed.output)
58
+ ?? readTerminalExecutionStatus(typed.data));
59
+ }
60
+ export function mapTerminalStatusToRequestState(status) {
61
+ return status === "blocked" || status === "failed" || status === "refused"
62
+ ? "failed"
63
+ : "completed";
64
+ }
65
+ export function mapTerminalStatusToPlanItemStatus(status) {
66
+ return status === "completed" ? "completed" : "failed";
67
+ }
@@ -1,9 +1,10 @@
1
1
  import path from "node:path";
2
- import { GENERAL_PURPOSE_SUBAGENT, createAsyncSubAgentMiddleware, createDeepAgent, createFilesystemMiddleware, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, } from "deepagents";
2
+ import { createAsyncSubAgentMiddleware, createFilesystemMiddleware, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, } from "deepagents";
3
3
  import { createAgent, humanInTheLoopMiddleware, todoListMiddleware } from "langchain";
4
4
  import { wrapResolvedModel, } from "./parsing/output-parsing.js";
5
- import { AGENT_INTERRUPT_SENTINEL_PREFIX, buildDeepAgentCreateParams, buildLangChainCreateParams, DEFAULT_DEEPAGENT_RECURSION_LIMIT, materializeModelExposedBuiltinMiddlewareTools, resolveLangChainInvocationConfig, resolveRunnableCheckpointer, resolveRunnableInterruptOn, shouldAttachDeepAgentBackend, shouldAttachDeepAgentCheckpointer, shouldAttachDeepAgentStore, } from "./agent-runtime-assembly.js";
5
+ import { AGENT_INTERRUPT_SENTINEL_PREFIX, buildDeepAgentCreateParams, buildDeepAgentSystemPromptWithCapabilityHierarchy, buildLangChainCreateParams, DEFAULT_DEEPAGENT_RECURSION_LIMIT, materializeModelExposedBuiltinMiddlewareTools, resolveLangChainInvocationConfig, resolveRunnableCheckpointer, resolveRunnableInterruptOn, shouldAttachDeepAgentBackend, shouldAttachDeepAgentCheckpointer, shouldAttachDeepAgentStore, } from "./agent-runtime-assembly.js";
6
6
  import { resolveDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
7
+ import { EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION } from "./prompts/runtime-prompts.js";
7
8
  import { buildToolNameMapping, } from "./adapter/tool/tool-name-mapping.js";
8
9
  import { executeRequestInvocation } from "./adapter/flow/invocation-flow.js";
9
10
  import { streamRuntimeExecution } from "./adapter/flow/stream-runtime.js";
@@ -21,8 +22,22 @@ export { buildAuthOmittingFetch, normalizeOpenAICompatibleInit } from "./adapter
21
22
  export { buildToolNameMapping, createModelFacingToolNameCandidates, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, sanitizeToolNameForModel, } from "./adapter/tool/tool-name-mapping.js";
22
23
  export { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, resolveTimeoutMs, } from "./adapter/resilience.js";
23
24
  import { getBindingAdapterKind, getBindingBuiltinToolsConfig, getBindingDeepAgentSubagents, getBindingExecutionParams, getBindingExecutionKind, getBindingFilesystemConfig, getBindingMemorySources, getBindingPrimaryModel, getBindingSkills, getBindingToolCount, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
25
+ class DelegatedExecutionNoToolEvidenceError extends Error {
26
+ constructor(agentId) {
27
+ super(`Delegated agent ${agentId} completed without tool execution evidence.`);
28
+ this.name = "DelegatedExecutionNoToolEvidenceError";
29
+ }
30
+ }
31
+ function hasDelegatedExecutionToolEvidence(result) {
32
+ const executedToolResults = Array.isArray(result.metadata?.executedToolResults)
33
+ ? result.metadata.executedToolResults
34
+ : [];
35
+ return executedToolResults.some((toolResult) => (toolResult.isError !== true
36
+ && toolResult.toolName !== "write_todos"
37
+ && toolResult.toolName !== "read_todos"));
38
+ }
24
39
  function shouldUseConfigurableDeepAgentAssembly(binding) {
25
- return getBindingBuiltinToolsConfig(binding) !== undefined;
40
+ return getBindingExecutionKind(binding) === "deepagent";
26
41
  }
27
42
  export class AgentRuntimeAdapter {
28
43
  options;
@@ -319,9 +334,18 @@ export class AgentRuntimeAdapter {
319
334
  const childSessionId = `${sessionId}:delegated:${resolvedSubagent.name}`;
320
335
  const childRequestId = `${requestId}:delegated:${resolvedSubagent.name}:${Date.now().toString(36)}`;
321
336
  try {
322
- const result = await this.invoke(targetBinding, requestText, childSessionId, childRequestId, undefined, [], {
337
+ const invokeOptions = {
323
338
  ...(typeof config?.context === "object" && config.context ? { context: config.context } : {}),
324
- });
339
+ };
340
+ const runDelegatedRequest = (text, requestSuffix = "") => this.invoke(targetBinding, text, childSessionId, `${childRequestId}${requestSuffix}`, undefined, [], invokeOptions);
341
+ let result = await runDelegatedRequest(requestText);
342
+ const targetRequiresExecutionToolEvidence = getBindingPrimaryTools(targetBinding).length > 0;
343
+ if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(result)) {
344
+ result = await runDelegatedRequest([requestText, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":tool-evidence-retry");
345
+ if (!hasDelegatedExecutionToolEvidence(result)) {
346
+ throw new DelegatedExecutionNoToolEvidenceError(targetBinding.agent.id);
347
+ }
348
+ }
325
349
  return wrapRequestResultAsSubagentResponse({
326
350
  output: result.output,
327
351
  structuredResponse: result.structuredResponse,
@@ -331,9 +355,7 @@ export class AgentRuntimeAdapter {
331
355
  const message = error instanceof Error && error.message.trim().length > 0
332
356
  ? error.message.trim()
333
357
  : "delegated execution failed";
334
- return wrapRequestResultAsSubagentResponse({
335
- output: `Blocked: ${message}`,
336
- });
358
+ throw new Error(message);
337
359
  }
338
360
  },
339
361
  },
@@ -422,18 +444,6 @@ export class AgentRuntimeAdapter {
422
444
  ownerId: binding.agent.id,
423
445
  skillPaths: getBindingSkills(binding),
424
446
  }) ?? [];
425
- const deepAgentConfig = buildDeepAgentCreateParams({
426
- binding,
427
- resolvedModel,
428
- resolvedTools: [...resolvedTools, ...builtinMiddlewareTools],
429
- resolvedMiddleware,
430
- resolvedSubagents,
431
- resolvedCheckpointer,
432
- resolvedStore,
433
- resolvedBackend,
434
- resolvedInterruptOn,
435
- resolvedSkills,
436
- });
437
447
  if (shouldUseConfigurableDeepAgentAssembly(binding)) {
438
448
  return this.createConfigurableDeepAgentRunnable(binding, {
439
449
  resolvedModel,
@@ -441,36 +451,33 @@ export class AgentRuntimeAdapter {
441
451
  resolvedMiddleware,
442
452
  resolvedSubagents,
443
453
  resolvedInterruptOn,
454
+ resolvedCheckpointer,
455
+ resolvedStore,
444
456
  resolvedBackend,
445
457
  resolvedSkills,
446
458
  });
447
459
  }
448
- return createDeepAgent(deepAgentConfig);
460
+ throw new Error(`Agent ${binding.agent.id} has no supported deepagent assembly path`);
449
461
  }
450
462
  createConfigurableDeepAgentRunnable(binding, input) {
451
463
  const builtinTools = getBindingBuiltinToolsConfig(binding) ?? {};
452
464
  const backend = (input.resolvedBackend ?? new StateBackend({}));
453
465
  const inlineSubagents = input.resolvedSubagents.filter((subagent) => !("graphId" in subagent));
454
466
  const asyncSubagents = input.resolvedSubagents.filter((subagent) => "graphId" in subagent);
455
- const subagents = inlineSubagents.some((subagent) => subagent.name === GENERAL_PURPOSE_SUBAGENT.name)
456
- ? inlineSubagents
457
- : [{
458
- ...GENERAL_PURPOSE_SUBAGENT,
459
- model: input.resolvedModel,
460
- tools: input.resolvedTools,
461
- skills: input.resolvedSkills,
462
- }, ...inlineSubagents];
467
+ const subagents = inlineSubagents;
463
468
  const middleware = [
464
469
  ...(builtinTools.todos === false ? [] : [todoListMiddleware()]),
465
470
  ...(input.resolvedSkills.length > 0 ? [createSkillsMiddleware({ backend, sources: input.resolvedSkills })] : []),
466
471
  ...(builtinTools.filesystem === false ? [] : [createFilesystemMiddleware({ backend })]),
467
- createSubAgentMiddleware({
468
- defaultModel: input.resolvedModel,
469
- defaultTools: input.resolvedTools,
470
- defaultInterruptOn: input.resolvedInterruptOn,
471
- subagents: subagents,
472
- generalPurposeAgent: false,
473
- }),
472
+ ...(subagents.length > 0
473
+ ? [createSubAgentMiddleware({
474
+ defaultModel: input.resolvedModel,
475
+ defaultTools: input.resolvedTools,
476
+ defaultInterruptOn: input.resolvedInterruptOn,
477
+ subagents: subagents,
478
+ generalPurposeAgent: false,
479
+ })]
480
+ : []),
474
481
  createSummarizationMiddleware({
475
482
  model: input.resolvedModel,
476
483
  backend,
@@ -487,10 +494,17 @@ export class AgentRuntimeAdapter {
487
494
  : undefined;
488
495
  return createAgent({
489
496
  model: input.resolvedModel,
490
- systemPrompt: getBindingSystemPrompt(binding),
497
+ systemPrompt: buildDeepAgentSystemPromptWithCapabilityHierarchy({
498
+ systemPrompt: getBindingSystemPrompt(binding),
499
+ subagents: input.resolvedSubagents,
500
+ skills: input.resolvedSkills,
501
+ tools: getBindingPrimaryTools(binding),
502
+ }),
491
503
  tools: input.resolvedTools,
492
504
  middleware: middleware,
493
505
  name: binding.agent.id,
506
+ ...(input.resolvedCheckpointer !== undefined ? { checkpointer: input.resolvedCheckpointer } : {}),
507
+ ...(input.resolvedStore !== undefined ? { store: input.resolvedStore } : {}),
494
508
  ...(responseFormat !== undefined ? { responseFormat: responseFormat } : {}),
495
509
  });
496
510
  }
@@ -8,6 +8,16 @@ export declare function materializeModelExposedBuiltinMiddlewareTools(input: {
8
8
  explicitToolNames?: string[];
9
9
  modelExposed?: boolean | string[];
10
10
  }): unknown[];
11
+ export declare function buildDeepAgentSystemPromptWithCapabilityHierarchy(input: {
12
+ systemPrompt?: unknown;
13
+ subagents: Array<Pick<UpstreamSubagentConfig, "name" | "description"> | Pick<CompiledAsyncSubAgent, "name" | "description">>;
14
+ skills?: string[];
15
+ tools?: Array<{
16
+ name: string;
17
+ description?: string;
18
+ }>;
19
+ }): unknown;
20
+ export declare const buildDeepAgentSystemPromptWithSubagentCatalog: typeof buildDeepAgentSystemPromptWithCapabilityHierarchy;
11
21
  export declare function resolveRunnableCheckpointer(options: RuntimeAdapterOptions, binding: CompiledAgentBinding): unknown;
12
22
  export declare function resolveRunnableInterruptOn(binding: CompiledAgentBinding): Record<string, {
13
23
  allowedDecisions: import("./adapter/tool/interrupt-policy.js").InterruptDecision[];