@botbotgo/agent-harness 0.0.99 → 0.0.101

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.
Files changed (74) hide show
  1. package/README.md +3 -6
  2. package/README.zh.md +2 -2
  3. package/dist/benchmark/upstream-runtime-ab-benchmark.d.ts +1 -1
  4. package/dist/benchmark/upstream-runtime-ab-benchmark.js +1 -2
  5. package/dist/contracts/core.d.ts +2 -2
  6. package/dist/contracts/runtime.d.ts +1 -5
  7. package/dist/package-version.d.ts +1 -1
  8. package/dist/package-version.js +1 -1
  9. package/dist/resource/resource-impl.js +78 -76
  10. package/dist/runtime/adapter/deepagent-runnable-config.d.ts +30 -0
  11. package/dist/runtime/adapter/deepagent-runnable-config.js +22 -0
  12. package/dist/runtime/adapter/index.d.ts +0 -2
  13. package/dist/runtime/adapter/index.js +0 -2
  14. package/dist/runtime/adapter/invocation-result.d.ts +13 -0
  15. package/dist/runtime/adapter/invocation-result.js +40 -0
  16. package/dist/runtime/adapter/langchain-runnable-config.d.ts +25 -0
  17. package/dist/runtime/adapter/langchain-runnable-config.js +19 -0
  18. package/dist/runtime/adapter/local-tool-invocation.d.ts +23 -0
  19. package/dist/runtime/adapter/local-tool-invocation.js +64 -0
  20. package/dist/runtime/adapter/runtime-adapter-support.d.ts +18 -0
  21. package/dist/runtime/adapter/runtime-adapter-support.js +54 -0
  22. package/dist/runtime/adapter/stream-event-projection.d.ts +19 -0
  23. package/dist/runtime/adapter/stream-event-projection.js +79 -0
  24. package/dist/runtime/adapter/stream-text-consumption.d.ts +4 -0
  25. package/dist/runtime/adapter/stream-text-consumption.js +18 -0
  26. package/dist/runtime/adapter/tool/builtin-middleware-tools.d.ts +64 -0
  27. package/dist/runtime/adapter/tool/builtin-middleware-tools.js +144 -0
  28. package/dist/runtime/adapter/tool/tool-replay.d.ts +18 -0
  29. package/dist/runtime/adapter/tool/tool-replay.js +26 -0
  30. package/dist/runtime/agent-runtime-adapter.d.ts +2 -54
  31. package/dist/runtime/agent-runtime-adapter.js +122 -1568
  32. package/dist/runtime/harness/run/helpers.js +2 -8
  33. package/dist/runtime/harness/run/recovery.d.ts +42 -0
  34. package/dist/runtime/harness/run/recovery.js +139 -0
  35. package/dist/runtime/harness/run/routing.d.ts +1 -3
  36. package/dist/runtime/harness/run/routing.js +2 -25
  37. package/dist/runtime/harness/run/run-lifecycle.d.ts +0 -11
  38. package/dist/runtime/harness/run/run-lifecycle.js +7 -50
  39. package/dist/runtime/harness/runtime-defaults.d.ts +4 -0
  40. package/dist/runtime/harness/runtime-defaults.js +39 -0
  41. package/dist/runtime/harness/system/inventory.js +2 -1
  42. package/dist/runtime/harness/system/skill-requirements.d.ts +1 -0
  43. package/dist/runtime/harness.d.ts +5 -24
  44. package/dist/runtime/harness.js +356 -536
  45. package/dist/runtime/index.d.ts +1 -12
  46. package/dist/runtime/index.js +1 -12
  47. package/dist/runtime/support/compiled-binding.d.ts +0 -2
  48. package/dist/runtime/support/compiled-binding.js +3 -22
  49. package/dist/runtime/support/harness-support.d.ts +0 -11
  50. package/dist/runtime/support/harness-support.js +1 -44
  51. package/dist/runtime/support/index.d.ts +1 -1
  52. package/dist/runtime/support/index.js +1 -1
  53. package/dist/runtime/support/runtime-factories.js +2 -2
  54. package/dist/workspace/agent-binding-compiler.js +9 -93
  55. package/dist/workspace/index.d.ts +0 -5
  56. package/dist/workspace/index.js +0 -5
  57. package/dist/workspace/object-loader.js +44 -99
  58. package/dist/workspace/support/agent-capabilities.js +2 -2
  59. package/dist/workspace/support/workspace-ref-utils.d.ts +0 -2
  60. package/dist/workspace/support/workspace-ref-utils.js +0 -17
  61. package/dist/workspace/validate.js +1 -1
  62. package/package.json +1 -1
  63. package/dist/config/workflows/langgraph-workflows.yaml +0 -570
  64. package/dist/config/workflows/runtime-profiles.yaml +0 -94
  65. package/dist/runtime/adapter/langgraph/presets.d.ts +0 -25
  66. package/dist/runtime/adapter/langgraph/presets.js +0 -165
  67. package/dist/runtime/adapter/langgraph/profiles.d.ts +0 -6
  68. package/dist/runtime/adapter/langgraph/profiles.js +0 -206
  69. package/dist/runtime/checkpoint-maintenance.d.ts +0 -1
  70. package/dist/runtime/checkpoint-maintenance.js +0 -1
  71. package/dist/runtime/file-checkpoint-saver.d.ts +0 -1
  72. package/dist/runtime/file-checkpoint-saver.js +0 -1
  73. package/dist/runtime/sqlite-maintained-checkpoint-saver.d.ts +0 -1
  74. package/dist/runtime/sqlite-maintained-checkpoint-saver.js +0 -1
@@ -1,36 +1,34 @@
1
1
  import path from "node:path";
2
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
2
  import { Command, MemorySaver } from "@langchain/langgraph";
4
- import { HumanMessage, ToolMessage } from "@langchain/core/messages";
5
- import { DEFAULT_SUBAGENT_PROMPT, createDeepAgent, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, isSandboxBackend, } from "deepagents";
3
+ import { HumanMessage } from "@langchain/core/messages";
4
+ import { DEFAULT_SUBAGENT_PROMPT, createDeepAgent, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, } from "deepagents";
6
5
  import { createAgent, humanInTheLoopMiddleware } from "langchain";
7
- import { z } from "zod";
8
- import { extractEmptyAssistantMessageFailure, extractContentBlocks, extractOutputContent, extractReasoningText, extractToolFallbackContext, extractVisibleOutput, isLikelyToolArgsObject, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
9
- import { computeIncrementalOutput, extractAgentStep, extractInterruptPayload, normalizeUpstreamRuntimeEvent, extractReasoningStreamOutput, extractStateStreamOutput, extractVisibleStreamOutput, extractTerminalStreamOutput, extractToolResult, normalizeTerminalOutputKey, readStreamDelta, } from "./parsing/stream-event-parsing.js";
6
+ import { extractToolFallbackContext, extractVisibleOutput, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, wrapResolvedModel, } from "./parsing/output-parsing.js";
7
+ import { readStreamDelta, } from "./parsing/stream-event-parsing.js";
10
8
  import { wrapToolForExecution } from "./adapter/tool/tool-hitl.js";
11
9
  import { resolveDeclaredMiddleware } from "./adapter/tool/declared-middleware.js";
12
- import { extractMessageText } from "../utils/message-content.js";
13
10
  import { applyDeepAgentDelegationPromptCompatibility, materializeDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
14
- import { buildToolNameMapping, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, } from "./adapter/tool/tool-name-mapping.js";
11
+ import { buildToolNameMapping, } from "./adapter/tool/tool-name-mapping.js";
12
+ import { createBuiltinMiddlewareTools } from "./adapter/tool/builtin-middleware-tools.js";
13
+ import { finalizeInvocationResult } from "./adapter/invocation-result.js";
14
+ import { runLocalToolInvocationLoop } from "./adapter/local-tool-invocation.js";
15
+ import { createStreamEventProjectionState, projectRuntimeStreamEvent } from "./adapter/stream-event-projection.js";
16
+ import { projectTextStreamChunks } from "./adapter/stream-text-consumption.js";
17
+ import { buildDeepAgentRunnableConfig } from "./adapter/deepagent-runnable-config.js";
18
+ import { buildLangChainRunnableConfig } from "./adapter/langchain-runnable-config.js";
15
19
  import { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, } from "./adapter/resilience.js";
16
20
  import { createResolvedModel } from "./adapter/model/model-providers.js";
17
- import { buildInvocationRequest, buildSlashCommandSkillInstruction, } from "./adapter/model/invocation-request.js";
21
+ import { buildInvocationRequest, } from "./adapter/model/invocation-request.js";
18
22
  import { compileInterruptOn } from "./adapter/tool/interrupt-policy.js";
19
- import { buildRawModelMessages, buildStateSnapshot } from "./adapter/model/message-assembly.js";
20
- import { asRecord, asStructuredExecutableTool, hasCallableToolHandler, normalizeResolvedToolSchema, wrapResolvedToolWithModelFacingName, } from "./adapter/tool/resolved-tool.js";
23
+ import { buildRawModelMessages } from "./adapter/model/message-assembly.js";
24
+ import { asStructuredExecutableTool, hasCallableToolHandler, normalizeResolvedToolSchema, wrapResolvedToolWithModelFacingName, } from "./adapter/tool/resolved-tool.js";
21
25
  import { instantiateProviderTool } from "./adapter/tool/provider-tool.js";
22
- import { extractToolCallsFromResult, normalizeToolArgsForSchema, stringifyToolOutput } from "./adapter/tool/tool-arguments.js";
26
+ import { countConfiguredTools, hasConfiguredMiddlewareKind, hasConfiguredSubagentSupport, isObject, isRecord, sleep, } from "./adapter/runtime-adapter-support.js";
23
27
  export { applyDeepAgentDelegationPromptCompatibility, materializeDeepAgentSkillSourcePaths, relativizeDeepAgentSkillSourcePaths, shouldRelaxDeepAgentDelegationPrompt, } from "./adapter/compat/deepagent-compat.js";
24
28
  export { buildAuthOmittingFetch, normalizeOpenAICompatibleInit } from "./adapter/compat/openai-compatible.js";
25
29
  export { buildToolNameMapping, createModelFacingToolNameCandidates, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, sanitizeToolNameForModel, } from "./adapter/tool/tool-name-mapping.js";
26
30
  export { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, resolveTimeoutMs, } from "./adapter/resilience.js";
27
- import { getBindingAdapterKind, getBindingDeepAgentParams, getBindingAdapterConfig, getBindingInterruptCompatibilityRules, getBindingLangChainParams, getBindingLangGraphPreset, getBindingLangGraphWorkflow, getBindingMiddlewareConfigs, getBindingPrimaryModel, getBindingRuntimeModel, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
28
- import { resolveLangGraphProfileWorkflow } from "./adapter/langgraph/profiles.js";
29
- import { resolveLangGraphPresetWorkflow } from "./adapter/langgraph/presets.js";
30
- const SUPPORTED_LANGGRAPH_WORKFLOW_NODE_KINDS = new Set(["llm", "agent", "tool", "approval", "condition"]);
31
- function countConfiguredTools(binding) {
32
- return getBindingPrimaryTools(binding).length;
33
- }
31
+ import { getBindingAdapterKind, getBindingDeepAgentParams, getBindingInterruptCompatibilityRules, getBindingLangChainParams, getBindingMiddlewareConfigs, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
34
32
  const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
35
33
  const UPSTREAM_BUILTIN_MIDDLEWARE_TOOL_NAMES = Object.freeze([
36
34
  "write_todos",
@@ -55,61 +53,10 @@ class RuntimeOperationTimeoutError extends Error {
55
53
  this.name = "RuntimeOperationTimeoutError";
56
54
  }
57
55
  }
58
- function asObject(value) {
59
- return typeof value === "object" && value ? value : undefined;
60
- }
61
- function sleep(ms) {
62
- return new Promise((resolve) => setTimeout(resolve, ms));
63
- }
64
- function hasConfiguredSubagentSupport(binding) {
65
- const params = getBindingLangChainParams(binding);
66
- if (!params) {
67
- return false;
68
- }
69
- return (params.subagents?.length ?? 0) > 0 || params.generalPurposeAgent === true || Boolean(params.taskDescription?.trim());
70
- }
71
- function hasConfiguredMiddlewareKind(binding, kind) {
72
- return getBindingMiddlewareConfigs(binding)?.some((entry) => entry.kind === kind) ?? false;
73
- }
74
- function isRecord(value) {
75
- return typeof value === "object" && value !== null && !Array.isArray(value);
76
- }
77
- function isObject(value) {
78
- return isRecord(value);
79
- }
80
- function truncateLines(lines, maxChars = 12_000) {
81
- const joined = lines.join("\n");
82
- if (joined.length <= maxChars) {
83
- return joined;
84
- }
85
- return `${joined.slice(0, maxChars - 18)}\n...[truncated]`;
86
- }
87
- function summarizeBuiltinWriteTodosArgs(args) {
88
- const todos = Array.isArray(args.todos) ? args.todos : [];
89
- const items = todos.flatMap((todo) => {
90
- if (!isObject(todo)) {
91
- return [];
92
- }
93
- const content = typeof todo.content === "string" && todo.content.trim().length > 0
94
- ? todo.content.trim()
95
- : typeof todo.description === "string" && todo.description.trim().length > 0
96
- ? todo.description.trim()
97
- : "";
98
- const status = typeof todo.status === "string" && todo.status.trim().length > 0 ? todo.status.trim() : "pending";
99
- return content ? [{ content, status }] : [];
100
- });
101
- return {
102
- total: items.length,
103
- pending: items.filter((item) => item.status !== "completed").length,
104
- completed: items.filter((item) => item.status === "completed").length,
105
- items,
106
- };
107
- }
108
56
  export class AgentRuntimeAdapter {
109
57
  options;
110
58
  modelCache = new Map();
111
59
  runnableCache = new WeakMap();
112
- langGraphSessions = new Map();
113
60
  constructor(options = {}) {
114
61
  this.options = options;
115
62
  }
@@ -279,21 +226,9 @@ export class AgentRuntimeAdapter {
279
226
  throw error;
280
227
  }
281
228
  }
282
- buildToolNameMapping(tools) {
283
- return buildToolNameMapping(tools);
284
- }
285
- buildSlashCommandSkillInstruction(binding, input) {
286
- return buildSlashCommandSkillInstruction(binding, input);
287
- }
288
- buildInvocationRequest(binding, history, input, options = {}) {
289
- return buildInvocationRequest(binding, history, input, options);
290
- }
291
- buildRawModelMessages(binding, systemPrompt, history, input) {
292
- return buildRawModelMessages(binding, systemPrompt, history, input);
293
- }
294
229
  resolveTools(tools, binding) {
295
230
  const resolved = this.options.toolResolver ? this.options.toolResolver(tools.map((tool) => tool.id), binding) : [];
296
- const toolNameMapping = this.buildToolNameMapping(tools);
231
+ const toolNameMapping = buildToolNameMapping(tools);
297
232
  return tools.flatMap((compiledTool, index) => {
298
233
  const resolvedTool = resolved[index] ?? (compiledTool.type === "provider" ? instantiateProviderTool(compiledTool) : undefined);
299
234
  if (resolvedTool === undefined) {
@@ -308,12 +243,6 @@ export class AgentRuntimeAdapter {
308
243
  return modelFacingName === compiledTool.name ? wrappedTool : wrapResolvedToolWithModelFacingName(wrappedTool, modelFacingName);
309
244
  });
310
245
  }
311
- compileInterruptOn(tools, compatibilityRules) {
312
- return compileInterruptOn(tools, compatibilityRules);
313
- }
314
- resolveInterruptOn(binding) {
315
- return this.compileInterruptOn(getBindingPrimaryTools(binding), getBindingInterruptCompatibilityRules(binding));
316
- }
317
246
  resolveFilesystemBackend(binding) {
318
247
  const filesystemConfig = getBindingLangChainParams(binding)?.filesystem;
319
248
  const configuredRootDir = typeof filesystemConfig?.rootDir === "string" && filesystemConfig.rootDir.trim().length > 0
@@ -351,6 +280,21 @@ export class AgentRuntimeAdapter {
351
280
  }
352
281
  return new StateBackend(runtimeLike);
353
282
  }
283
+ createDeclaredMiddlewareResolverOptions(binding) {
284
+ return {
285
+ resolveModel: (model) => this.resolveModel(model),
286
+ resolveBackend: (resolvedBinding) => {
287
+ const targetBinding = resolvedBinding ?? binding;
288
+ return targetBinding ? this.options.backendResolver?.(targetBinding) : undefined;
289
+ },
290
+ resolveFilesystemBackend: (resolvedBinding) => {
291
+ const targetBinding = resolvedBinding ?? binding;
292
+ return targetBinding ? this.resolveFilesystemBackend(targetBinding) : undefined;
293
+ },
294
+ resolveCustom: this.options.declaredMiddlewareResolver,
295
+ binding,
296
+ };
297
+ }
354
298
  async invokeBuiltinTaskTool(binding, input, options = {}) {
355
299
  if (!isDeepAgentBinding(binding)) {
356
300
  throw new Error("The built-in task tool is only available for deepagent bindings.");
@@ -392,7 +336,7 @@ export class AgentRuntimeAdapter {
392
336
  ]),
393
337
  ...(selectedSubagent.interruptOn
394
338
  ? [humanInTheLoopMiddleware({
395
- interruptOn: this.compileInterruptOn(selectedSubagent.tools ?? [], selectedSubagent.interruptOn),
339
+ interruptOn: compileInterruptOn(selectedSubagent.tools ?? [], selectedSubagent.interruptOn),
396
340
  })]
397
341
  : []),
398
342
  ];
@@ -412,169 +356,12 @@ export class AgentRuntimeAdapter {
412
356
  return visibleOutput || fallbackOutput || JSON.stringify(result);
413
357
  }
414
358
  async resolveBuiltinMiddlewareTools(binding, options = {}) {
415
- const tools = new Map();
416
359
  const backend = this.resolveBuiltinMiddlewareBackend(binding, options);
417
- tools.set("write_todos", {
418
- name: "write_todos",
419
- schema: z.object({
420
- todos: z.array(z.object({}).passthrough()).optional(),
421
- }).passthrough(),
422
- invoke: async (input) => {
423
- const args = isObject(input) ? input : {};
424
- const summary = summarizeBuiltinWriteTodosArgs(args);
425
- return {
426
- ok: true,
427
- tool: "write_todos",
428
- message: `Tracked ${summary.total} todo item(s).`,
429
- summary,
430
- };
431
- },
432
- });
433
- tools.set("ls", {
434
- name: "ls",
435
- schema: z.object({ path: z.string().optional().default("/") }).passthrough(),
436
- invoke: async (input) => {
437
- const targetPath = isObject(input) && typeof input.path === "string" ? input.path : "/";
438
- const infos = (await Promise.resolve(backend.lsInfo?.(targetPath))) ?? [];
439
- if (infos.length === 0) {
440
- return `No files found in ${targetPath}`;
441
- }
442
- return truncateLines(infos.map((info) => info.is_dir ? `${info.path} (directory)` : `${info.path}${info.size ? ` (${info.size} bytes)` : ""}`));
443
- },
444
- });
445
- tools.set("read_file", {
446
- name: "read_file",
447
- schema: z.object({
448
- file_path: z.string(),
449
- offset: z.number().optional(),
450
- limit: z.number().optional(),
451
- }).passthrough(),
452
- invoke: async (input) => {
453
- const typed = isObject(input) ? input : {};
454
- const filePath = typeof typed.file_path === "string" ? typed.file_path : "";
455
- const offset = typeof typed.offset === "number" ? typed.offset : 0;
456
- const limit = typeof typed.limit === "number" ? typed.limit : 500;
457
- return Promise.resolve(backend.read?.(filePath, offset, limit)) ?? "";
458
- },
459
- });
460
- tools.set("write_file", {
461
- name: "write_file",
462
- schema: z.object({ file_path: z.string(), content: z.string().optional() }).passthrough(),
463
- invoke: async (input) => {
464
- const typed = isObject(input) ? input : {};
465
- const result = await Promise.resolve(backend.write?.(typeof typed.file_path === "string" ? typed.file_path : "", typeof typed.content === "string" ? typed.content : ""));
466
- return result?.error ?? `Successfully wrote to '${result?.path ?? (typed.file_path ?? "")}'`;
467
- },
468
- });
469
- tools.set("edit_file", {
470
- name: "edit_file",
471
- schema: z.object({
472
- file_path: z.string(),
473
- old_string: z.string(),
474
- new_string: z.string(),
475
- replace_all: z.boolean().optional(),
476
- }).passthrough(),
477
- invoke: async (input) => {
478
- const typed = isObject(input) ? input : {};
479
- const result = await Promise.resolve(backend.edit?.(typeof typed.file_path === "string" ? typed.file_path : "", typeof typed.old_string === "string" ? typed.old_string : "", typeof typed.new_string === "string" ? typed.new_string : "", typed.replace_all === true));
480
- return result?.error ?? `Successfully replaced ${result?.occurrences ?? 0} occurrence(s) in '${result?.path ?? (typed.file_path ?? "")}'`;
481
- },
482
- });
483
- tools.set("glob", {
484
- name: "glob",
485
- schema: z.object({ pattern: z.string(), path: z.string().optional().default("/") }).passthrough(),
486
- invoke: async (input) => {
487
- const typed = isObject(input) ? input : {};
488
- const pattern = typeof typed.pattern === "string" ? typed.pattern : "";
489
- const targetPath = typeof typed.path === "string" ? typed.path : "/";
490
- const infos = (await Promise.resolve(backend.globInfo?.(pattern, targetPath))) ?? [];
491
- if (infos.length === 0) {
492
- return `No files found matching pattern '${pattern}'`;
493
- }
494
- return truncateLines(infos.map((info) => info.path));
495
- },
496
- });
497
- tools.set("grep", {
498
- name: "grep",
499
- schema: z.object({
500
- pattern: z.string(),
501
- path: z.string().optional().default("/"),
502
- glob: z.string().nullable().optional(),
503
- }).passthrough(),
504
- invoke: async (input) => {
505
- const typed = isObject(input) ? input : {};
506
- const result = await Promise.resolve(backend.grepRaw?.(typeof typed.pattern === "string" ? typed.pattern : "", typeof typed.path === "string" ? typed.path : "/", typeof typed.glob === "string" ? typed.glob : null));
507
- if (typeof result === "string") {
508
- return result;
509
- }
510
- if (!result || result.length === 0) {
511
- return `No matches found for pattern '${typeof typed.pattern === "string" ? typed.pattern : ""}'`;
512
- }
513
- const lines = [];
514
- let currentFile = "";
515
- for (const match of result) {
516
- if (match.path !== currentFile) {
517
- currentFile = match.path;
518
- lines.push(`\n${currentFile}:`);
519
- }
520
- lines.push(` ${match.line}: ${match.text}`);
521
- }
522
- return truncateLines(lines);
523
- },
524
- });
525
- tools.set("execute", {
526
- name: "execute",
527
- schema: z.object({ command: z.string() }).passthrough(),
528
- invoke: async (input) => {
529
- if (!isSandboxBackend(backend) || typeof backend.execute !== "function") {
530
- return "Error: Execution not available. This agent's backend does not support command execution (SandboxBackendProtocol).";
531
- }
532
- const typed = isObject(input) ? input : {};
533
- const result = await Promise.resolve(backend.execute(typeof typed.command === "string" ? typed.command : ""));
534
- const parts = [result.output];
535
- if (result.exitCode !== null) {
536
- parts.push(`\n[Command ${result.exitCode === 0 ? "succeeded" : "failed"} with exit code ${result.exitCode}]`);
537
- }
538
- if (result.truncated) {
539
- parts.push("\n[Output was truncated due to size limits]");
540
- }
541
- return parts.join("");
542
- },
543
- });
544
- if (isDeepAgentBinding(binding)) {
545
- tools.set("task", {
546
- name: "task",
547
- schema: z.object({
548
- description: z.string(),
549
- subagent_type: z.string(),
550
- }).passthrough(),
551
- invoke: async (input) => this.invokeBuiltinTaskTool(binding, input, options),
552
- });
553
- }
554
- return tools;
555
- }
556
- canReplayToolCallsLocally(binding, toolCalls, primaryTools, toolNameMapping, executableTools, builtinExecutableTools) {
557
- if (toolCalls.length === 0) {
558
- return false;
559
- }
560
- if (isLangChainBinding(binding)) {
561
- return true;
562
- }
563
- return toolCalls.every((toolCall) => {
564
- const resolvedToolName = resolveModelFacingToolName(toolCall.name, toolNameMapping, primaryTools);
565
- if (resolvedToolName === "task" || toolCall.name === "task") {
566
- return false;
567
- }
568
- const executable = executableTools.get(toolCall.name) ?? executableTools.get(resolvedToolName);
569
- if (executable) {
570
- return false;
571
- }
572
- const builtinExecutable = builtinExecutableTools.get(toolCall.name) ??
573
- builtinExecutableTools.get(resolvedToolName) ??
574
- createModelFacingToolNameLookupCandidates(toolCall.name)
575
- .map((candidate) => builtinExecutableTools.get(candidate))
576
- .find((candidate) => candidate !== undefined);
577
- return builtinExecutable !== undefined;
360
+ return createBuiltinMiddlewareTools(backend, {
361
+ includeTaskTool: isDeepAgentBinding(binding),
362
+ invokeTaskTool: isDeepAgentBinding(binding)
363
+ ? async (input) => this.invokeBuiltinTaskTool(binding, input, options)
364
+ : undefined,
578
365
  });
579
366
  }
580
367
  async resolveAutomaticSummarizationMiddleware(binding) {
@@ -585,13 +372,7 @@ export class AgentRuntimeAdapter {
585
372
  if (!primaryModel) {
586
373
  return [];
587
374
  }
588
- return resolveDeclaredMiddleware([{ kind: "summarization", model: primaryModel }], {
589
- resolveModel: (model) => this.resolveModel(model),
590
- resolveBackend: (resolvedBinding) => this.options.backendResolver?.(resolvedBinding ?? binding),
591
- resolveFilesystemBackend: (resolvedBinding) => this.resolveFilesystemBackend(resolvedBinding ?? binding),
592
- resolveCustom: this.options.declaredMiddlewareResolver,
593
- binding,
594
- });
375
+ return resolveDeclaredMiddleware([{ kind: "summarization", model: primaryModel }], this.createDeclaredMiddlewareResolverOptions(binding));
595
376
  }
596
377
  async resolveLangChainAutomaticMiddleware(binding) {
597
378
  const params = getBindingLangChainParams(binding);
@@ -626,23 +407,11 @@ export class AgentRuntimeAdapter {
626
407
  }
627
408
  return automaticMiddleware;
628
409
  }
629
- async resolveDeepAgentAutomaticMiddleware(binding) {
630
- if (!isDeepAgentBinding(binding)) {
631
- return [];
632
- }
633
- return [];
634
- }
635
410
  async resolveMiddleware(binding, interruptOn) {
636
- const declarativeMiddleware = await resolveDeclaredMiddleware(getBindingMiddlewareConfigs(binding), {
637
- resolveModel: (model) => this.resolveModel(model),
638
- resolveBackend: (resolvedBinding) => this.options.backendResolver?.(resolvedBinding ?? binding),
639
- resolveFilesystemBackend: (resolvedBinding) => this.resolveFilesystemBackend(resolvedBinding ?? binding),
640
- resolveCustom: this.options.declaredMiddlewareResolver,
641
- binding,
642
- });
411
+ const declarativeMiddleware = await resolveDeclaredMiddleware(getBindingMiddlewareConfigs(binding), this.createDeclaredMiddlewareResolverOptions(binding));
643
412
  const automaticMiddleware = isLangChainBinding(binding)
644
413
  ? await this.resolveLangChainAutomaticMiddleware(binding)
645
- : await this.resolveDeepAgentAutomaticMiddleware(binding);
414
+ : [];
646
415
  const middleware = [
647
416
  ...declarativeMiddleware,
648
417
  ...automaticMiddleware,
@@ -653,35 +422,6 @@ export class AgentRuntimeAdapter {
653
422
  }
654
423
  return middleware;
655
424
  }
656
- resolveCheckpointer(binding) {
657
- return this.options.checkpointerResolver ? this.options.checkpointerResolver(binding) : new MemorySaver();
658
- }
659
- resolveLangGraphWorkflowCheckpointer(binding) {
660
- const checkpointer = this.resolveCheckpointer(binding);
661
- return typeof checkpointer === "object" && checkpointer
662
- ? checkpointer
663
- : undefined;
664
- }
665
- buildRouteSystemPrompt(primaryBinding, secondaryBinding, overridePrompt) {
666
- const defaultPrompt = `You are a routing classifier for an agent harness. Reply with exactly one agent id: ${primaryBinding.agent.id} or ${secondaryBinding.agent.id}.\n\n` +
667
- `Choose ${primaryBinding.agent.id} only for lightweight conversational turns that can be answered directly in one step ` +
668
- "without tool use, repository inspection, file lookup, external checkout, or orchestration.\n\n" +
669
- `Choose ${secondaryBinding.agent.id} for tasks that need tools, multi-step execution, external research, repository or ` +
670
- "file analysis, downloading or cloning content, codebase exploration, verification, or any task where the agent " +
671
- "should inspect the workspace or another repository before answering.\n\n" +
672
- `If the request asks to download, clone, fetch, inspect, analyze, trace, or locate implementation in a repo or codebase, choose ${secondaryBinding.agent.id}.\n\n` +
673
- `When uncertain, prefer ${secondaryBinding.agent.id}.\n\n` +
674
- `Agent ${primaryBinding.agent.id}: ${primaryBinding.agent.description}\n` +
675
- `Agent ${secondaryBinding.agent.id}: ${secondaryBinding.agent.description}`;
676
- if (!overridePrompt?.trim()) {
677
- return defaultPrompt;
678
- }
679
- return overridePrompt
680
- .replaceAll("{{primaryAgentId}}", primaryBinding.agent.id)
681
- .replaceAll("{{primaryDescription}}", primaryBinding.agent.description)
682
- .replaceAll("{{secondaryAgentId}}", secondaryBinding.agent.id)
683
- .replaceAll("{{secondaryDescription}}", secondaryBinding.agent.description);
684
- }
685
425
  async resolveSubagents(subagents, binding) {
686
426
  return Promise.all(subagents.map(async (subagent) => ({
687
427
  ...subagent,
@@ -694,1006 +434,63 @@ export class AgentRuntimeAdapter {
694
434
  ownerId: `${binding?.agent.id ?? "agent"}-${subagent.name}`,
695
435
  skillPaths: subagent.skills,
696
436
  }),
697
- interruptOn: this.compileInterruptOn(subagent.tools ?? [], subagent.interruptOn),
437
+ interruptOn: compileInterruptOn(subagent.tools ?? [], subagent.interruptOn),
698
438
  responseFormat: subagent.responseFormat,
699
439
  contextSchema: subagent.contextSchema,
700
- middleware: (await resolveDeclaredMiddleware(subagent.middleware, {
701
- resolveModel: (model) => this.resolveModel(model),
702
- resolveBackend: (resolvedBinding) => {
703
- const targetBinding = resolvedBinding ?? binding;
704
- return targetBinding ? this.options.backendResolver?.(targetBinding) : undefined;
705
- },
706
- resolveFilesystemBackend: (resolvedBinding) => {
707
- const targetBinding = resolvedBinding ?? binding;
708
- return targetBinding ? this.resolveFilesystemBackend(targetBinding) : undefined;
709
- },
710
- resolveCustom: this.options.declaredMiddlewareResolver,
711
- binding,
712
- })),
440
+ middleware: (await resolveDeclaredMiddleware(subagent.middleware, this.createDeclaredMiddlewareResolverOptions(binding))),
713
441
  })));
714
442
  }
715
443
  async createLangChainRunnable(binding, options = {}) {
716
444
  const params = getBindingLangChainParams(binding);
717
- const interruptOn = this.resolveInterruptOn(binding);
445
+ const interruptOn = compileInterruptOn(getBindingPrimaryTools(binding), getBindingInterruptCompatibilityRules(binding));
718
446
  const model = (await this.resolveModel(params.model));
719
447
  const tools = this.resolveTools(params.tools, binding);
720
448
  if (tools.length > 0 && typeof model.bindTools !== "function") {
721
449
  throw new Error(`Agent ${binding.agent.id} configures ${tools.length} tool(s), but resolved model ${params.model.id} does not support tool binding.`);
722
450
  }
723
- return createAgent({
724
- ...(options.passthroughOverride ?? params.passthrough ?? {}),
725
- model: model,
726
- tools: tools,
727
- systemPrompt: options.systemPromptOverride ?? params.systemPrompt,
728
- stateSchema: params.stateSchema,
729
- responseFormat: params.responseFormat,
730
- contextSchema: params.contextSchema,
731
- middleware: (await this.resolveMiddleware(binding, interruptOn)),
732
- checkpointer: this.resolveCheckpointer(binding),
733
- store: this.options.storeResolver?.(binding),
734
- includeAgentName: params.includeAgentName,
735
- version: params.version,
736
- name: params.name,
737
- description: params.description,
738
- });
739
- }
740
- normalizeLangGraphWorkflowNode(raw) {
741
- if (!isRecord(raw)) {
742
- return null;
743
- }
744
- const id = typeof raw.id === "string" ? raw.id.trim() : "";
745
- const rawKind = typeof raw.kind === "string" ? raw.kind.trim() : "";
746
- const role = typeof raw.role === "string" && raw.role.trim() ? raw.role.trim() : undefined;
747
- const agent = typeof raw.agent === "string" && raw.agent.trim() ? raw.agent.trim() : undefined;
748
- const tool = typeof raw.tool === "string" && raw.tool.trim() ? raw.tool.trim() : undefined;
749
- const args = isRecord(raw.args) ? { ...raw.args } : undefined;
750
- if (!id || !rawKind) {
751
- return null;
752
- }
753
- if (!SUPPORTED_LANGGRAPH_WORKFLOW_NODE_KINDS.has(rawKind)) {
754
- throw new Error(`Unsupported LangGraph workflow node kind ${rawKind}. Supported node kinds: ${Array.from(SUPPORTED_LANGGRAPH_WORKFLOW_NODE_KINDS).join(", ")}`);
755
- }
756
- return {
757
- id,
758
- kind: rawKind,
759
- ...(typeof raw.prompt === "string" && raw.prompt.trim() ? { prompt: raw.prompt.trim() } : {}),
760
- ...(role ? { role } : {}),
761
- ...(agent ? { agent } : {}),
762
- ...(tool ? { tool } : {}),
763
- ...(args ? { args } : {}),
764
- };
765
- }
766
- async invokeLangGraphToolNode(binding, toolName, userInputText, config, args) {
767
- const primaryTools = getBindingPrimaryTools(binding);
768
- const resolvedTools = this.resolveTools(primaryTools, binding);
769
- const toolNameMapping = this.buildToolNameMapping(primaryTools);
770
- const resolvedToolName = resolveModelFacingToolName(toolName, toolNameMapping, primaryTools);
771
- const executableTool = resolvedTools.find((candidate) => {
772
- if (!hasCallableToolHandler(candidate)) {
773
- return false;
774
- }
775
- const candidateName = typeof candidate.name === "string" ? candidate.name : undefined;
776
- return candidateName === toolName || candidateName === resolvedToolName;
777
- });
778
- if (!executableTool) {
779
- throw new Error(`LangGraph agent ${binding.agent.id} workflow references unknown tool ${toolName}`);
780
- }
781
- const invokeMethod = typeof executableTool.invoke === "function"
782
- ? executableTool.invoke.bind(executableTool)
783
- : typeof executableTool.call === "function"
784
- ? executableTool.call.bind(executableTool)
785
- : typeof executableTool.func === "function"
786
- ? executableTool.func.bind(executableTool)
787
- : undefined;
788
- if (typeof invokeMethod !== "function") {
789
- throw new Error(`LangGraph workflow tool ${toolName} is not executable`);
790
- }
791
- const input = args && Object.keys(args).length > 0 ? args : { input: userInputText };
792
- const output = await invokeMethod(input, config);
793
- const finalText = stringifyToolOutput(output);
794
- return {
795
- output: finalText,
796
- messages: [{ role: "assistant", content: finalText }],
797
- metadata: {
798
- executedToolResults: [{
799
- toolName: resolvedToolName,
800
- output,
801
- }],
802
- },
803
- };
804
- }
805
- listLangGraphWorkflowNodes(workflow) {
806
- return Array.isArray(workflow.nodes)
807
- ? workflow.nodes.map((node) => this.normalizeLangGraphWorkflowNode(node)).filter((node) => node !== null)
808
- : [];
809
- }
810
- normalizeLangGraphWorkflowEdge(raw) {
811
- if (!isRecord(raw)) {
812
- return null;
813
- }
814
- const from = typeof raw.from === "string" ? raw.from.trim() : "";
815
- const to = typeof raw.to === "string" ? raw.to.trim() : "";
816
- if (!from || !to) {
817
- return null;
818
- }
819
- return {
820
- from,
821
- to,
822
- ...(typeof raw.when === "string" && raw.when.trim() ? { when: raw.when.trim() } : {}),
823
- };
824
- }
825
- listLangGraphWorkflowEdges(workflow) {
826
- if (!Array.isArray(workflow.edges)) {
827
- return [];
828
- }
829
- return workflow.edges
830
- .map((edge) => this.normalizeLangGraphWorkflowEdge(edge))
831
- .filter((edge) => edge !== null);
832
- }
833
- shouldFollowLangGraphEdge(edge, state, currentResult) {
834
- const condition = edge.when?.toLowerCase().trim();
835
- if (!condition || condition === "always") {
836
- return true;
837
- }
838
- const review = state.review?.toLowerCase() ?? "";
839
- const hasResult = Boolean(currentResult);
840
- switch (condition) {
841
- case "has_plan":
842
- return Boolean(state.plan?.trim());
843
- case "has_result":
844
- return hasResult;
845
- case "needs_review":
846
- return hasResult;
847
- case "review_ok":
848
- case "review_sufficient":
849
- return review.length > 0 && !/(incomplete|insufficient|missing|risky|risk|follow-up|followup|unclear)/i.test(review);
850
- case "review_incomplete":
851
- case "review_retry":
852
- return /(incomplete|insufficient|missing|follow-up|followup|retry|replan|unclear)/i.test(review);
853
- case "can_replan":
854
- return state.replans < 4;
855
- case "approval_approved":
856
- return state.approvalDecision === "approve";
857
- case "approval_rejected":
858
- return state.approvalDecision === "reject";
859
- case "approval_edited":
860
- return state.approvalDecision === "edit";
861
- default:
862
- return false;
863
- }
864
- }
865
- listLangGraphWorkflowNextNodes(workflow, nodeId, state, currentResult) {
866
- return this.listLangGraphWorkflowEdges(workflow)
867
- .filter((edge) => edge.from === nodeId && this.shouldFollowLangGraphEdge(edge, state, currentResult))
868
- .map((edge) => edge.to);
869
- }
870
- extractInvocationRequestText(request) {
871
- if (!isRecord(request) || !Array.isArray(request.messages)) {
872
- return "";
873
- }
874
- for (let index = request.messages.length - 1; index >= 0; index -= 1) {
875
- const message = request.messages[index];
876
- if (!isRecord(message)) {
877
- continue;
878
- }
879
- const role = typeof message.role === "string" ? message.role : undefined;
880
- if (role !== "user") {
881
- continue;
882
- }
883
- return extractMessageText(message.content);
884
- }
885
- return "";
886
- }
887
- prependSystemMessage(request, content) {
888
- if (!content.trim() || !isRecord(request) || !Array.isArray(request.messages)) {
889
- return request;
890
- }
891
- return {
892
- ...request,
893
- messages: [{ role: "system", content }, ...request.messages],
894
- };
895
- }
896
- replaceLastUserMessage(request, content) {
897
- if (!content.trim() || !isRecord(request) || !Array.isArray(request.messages)) {
898
- return request;
899
- }
900
- const messages = [...request.messages];
901
- for (let index = messages.length - 1; index >= 0; index -= 1) {
902
- const message = messages[index];
903
- if (!isRecord(message) || message.role !== "user") {
904
- continue;
905
- }
906
- messages[index] = {
907
- ...message,
908
- content,
909
- };
910
- return {
911
- ...request,
912
- messages,
913
- };
914
- }
915
- return {
916
- ...request,
917
- messages: [...messages, { role: "user", content }],
918
- };
919
- }
920
- resolveLangGraphSessionKey(binding, config) {
921
- const configurable = isRecord(config?.configurable) ? config?.configurable : undefined;
922
- const threadId = typeof configurable?.thread_id === "string" ? configurable.thread_id : undefined;
923
- return threadId ? `${binding.agent.id}:${threadId}` : undefined;
924
- }
925
- resolveLangGraphSessionIdentity(binding, config, options = {}) {
926
- const configurable = isRecord(config?.configurable) ? config?.configurable : undefined;
927
- const threadId = typeof configurable?.thread_id === "string" ? configurable.thread_id : undefined;
928
- if (!threadId) {
929
- return undefined;
930
- }
931
- const configuredRunId = typeof configurable?.run_id === "string" ? configurable.run_id : undefined;
932
- const runId = configuredRunId ?? options.fallbackRunId ?? threadId;
933
- return {
934
- sessionKey: `${binding.agent.id}:${threadId}:${runId}`,
935
- legacySessionKey: `${binding.agent.id}:${threadId}`,
936
- threadId,
937
- runId,
938
- };
939
- }
940
- langGraphCheckpointNamespace(binding, identity) {
941
- return `langgraph-workflow:${binding.agent.id}:${identity.runId}`;
942
- }
943
- langGraphCheckpointConfig(binding, identity, checkpointId) {
944
- return {
945
- configurable: {
946
- thread_id: identity.threadId,
947
- checkpoint_ns: this.langGraphCheckpointNamespace(binding, identity),
948
- ...(checkpointId ? { checkpoint_id: checkpointId } : {}),
949
- },
950
- };
951
- }
952
- buildLangGraphWorkflowCheckpoint(session) {
953
- const checkpointId = `langgraph-workflow-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
954
- return {
955
- v: 1,
956
- id: checkpointId,
957
- ts: new Date().toISOString(),
958
- channel_values: {
959
- workflow_session: session,
960
- },
961
- channel_versions: {
962
- workflow_session: 1,
963
- },
964
- versions_seen: {},
965
- pending_sends: [],
966
- };
967
- }
968
- async saveLangGraphSessionToCheckpointer(binding, identity, session) {
969
- const checkpointer = this.resolveLangGraphWorkflowCheckpointer(binding);
970
- if (typeof checkpointer?.put !== "function") {
971
- return false;
972
- }
973
- await checkpointer.put(this.langGraphCheckpointConfig(binding, identity), this.buildLangGraphWorkflowCheckpoint(session), {
974
- source: "agent-harness",
975
- step: 0,
976
- writes: {
977
- workflow_session: true,
978
- },
979
- });
980
- return true;
981
- }
982
- async loadLangGraphSessionFromCheckpointer(binding, identity) {
983
- const checkpointer = this.resolveLangGraphWorkflowCheckpointer(binding);
984
- if (typeof checkpointer?.getTuple !== "function") {
985
- return undefined;
986
- }
987
- const tuple = await checkpointer.getTuple(this.langGraphCheckpointConfig(binding, identity));
988
- const checkpoint = asObject(asObject(tuple)?.checkpoint);
989
- const channelValues = asObject(checkpoint?.channel_values);
990
- const session = channelValues?.workflow_session;
991
- if (session === null || session === undefined) {
992
- return undefined;
993
- }
994
- return session;
995
- }
996
- async clearLangGraphSessionInCheckpointer(binding, identity) {
997
- const checkpointer = this.resolveLangGraphWorkflowCheckpointer(binding);
998
- if (typeof checkpointer?.put !== "function") {
999
- return;
1000
- }
1001
- await checkpointer.put(this.langGraphCheckpointConfig(binding, identity), {
1002
- v: 1,
1003
- id: `langgraph-workflow-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
1004
- ts: new Date().toISOString(),
1005
- channel_values: {
1006
- workflow_session: null,
1007
- },
1008
- channel_versions: {
1009
- workflow_session: 1,
1010
- },
1011
- versions_seen: {},
1012
- pending_sends: [],
1013
- }, {
1014
- source: "agent-harness",
1015
- step: 0,
1016
- writes: {
1017
- workflow_session: true,
1018
- },
1019
- });
1020
- }
1021
- langGraphSessionFilePath(binding, identity) {
1022
- return path.join(binding.harnessRuntime.runRoot, "threads", identity.threadId, "runs", identity.runId, "backend", "langgraph", "session.json");
1023
- }
1024
- artifactLangGraphSessionFilePath(binding, identity) {
1025
- return path.join(binding.harnessRuntime.runRoot, "threads", identity.threadId, "runs", identity.runId, "artifacts", "langgraph-workflow-session.json");
1026
- }
1027
- legacyLangGraphSessionFilePath(binding, sessionKey) {
1028
- return path.join(binding.harnessRuntime.runRoot, "langgraph-sessions", `${sessionKey.replace(/[^a-zA-Z0-9._-]+/g, "_")}.json`);
1029
- }
1030
- async saveLangGraphSession(binding, identity, session) {
1031
- this.langGraphSessions.set(identity.sessionKey, session);
1032
- const persistedToCheckpointer = await this.saveLangGraphSessionToCheckpointer(binding, identity, session);
1033
- if (!persistedToCheckpointer) {
1034
- const filePath = this.langGraphSessionFilePath(binding, identity);
1035
- await mkdir(path.dirname(filePath), { recursive: true });
1036
- await writeFile(filePath, JSON.stringify(session, null, 2), "utf8");
1037
- }
1038
- }
1039
- async loadLangGraphSession(binding, identity) {
1040
- const cached = this.langGraphSessions.get(identity.sessionKey);
1041
- if (cached) {
1042
- return cached;
1043
- }
1044
- const checkpointerSession = await this.loadLangGraphSessionFromCheckpointer(binding, identity);
1045
- if (checkpointerSession) {
1046
- this.langGraphSessions.set(identity.sessionKey, checkpointerSession);
1047
- return checkpointerSession;
1048
- }
1049
- try {
1050
- const filePath = this.langGraphSessionFilePath(binding, identity);
1051
- const content = await readFile(filePath, "utf8");
1052
- const parsed = JSON.parse(content);
1053
- this.langGraphSessions.set(identity.sessionKey, parsed);
1054
- return parsed;
1055
- }
1056
- catch {
1057
- try {
1058
- const artifactPath = this.artifactLangGraphSessionFilePath(binding, identity);
1059
- const content = await readFile(artifactPath, "utf8");
1060
- const parsed = JSON.parse(content);
1061
- this.langGraphSessions.set(identity.sessionKey, parsed);
1062
- return parsed;
1063
- }
1064
- catch {
1065
- try {
1066
- const legacyPath = this.legacyLangGraphSessionFilePath(binding, identity.legacySessionKey);
1067
- const content = await readFile(legacyPath, "utf8");
1068
- const parsed = JSON.parse(content);
1069
- this.langGraphSessions.set(identity.sessionKey, parsed);
1070
- return parsed;
1071
- }
1072
- catch {
1073
- return undefined;
1074
- }
1075
- }
1076
- }
1077
- }
1078
- async clearLangGraphSession(binding, identity) {
1079
- this.langGraphSessions.delete(identity.sessionKey);
1080
- await this.clearLangGraphSessionInCheckpointer(binding, identity);
1081
- const filePath = this.langGraphSessionFilePath(binding, identity);
1082
- await rm(filePath, { force: true });
1083
- await rm(this.artifactLangGraphSessionFilePath(binding, identity), { force: true });
1084
- await rm(this.legacyLangGraphSessionFilePath(binding, identity.legacySessionKey), { force: true });
1085
- }
1086
- async invokeWorkflowNodeModel(model, systemPrompt, userContent) {
1087
- const resolved = (await this.resolveModel(model));
1088
- if (!resolved.invoke) {
1089
- throw new Error(`Workflow model ${model.id} does not support invoke()`);
1090
- }
1091
- const result = await resolved.invoke([
1092
- { role: "system", content: systemPrompt },
1093
- { role: "user", content: userContent },
1094
- ]);
1095
- return sanitizeVisibleText(typeof result === "string"
1096
- ? result
1097
- : typeof result?.content === "string"
1098
- ? result.content
1099
- : JSON.stringify(result));
1100
- }
1101
- async invokeLangGraphAgentNode(binding, agentName, userInputText, workflowState, activeResult, config) {
1102
- const params = getBindingLangChainParams(binding);
1103
- const resolvedSubagents = await this.resolveSubagents(params.subagents ?? [], binding);
1104
- const delegatedAgent = resolvedSubagents.find((candidate) => candidate.name === agentName);
1105
- if (!delegatedAgent) {
1106
- throw new Error(`LangGraph agent ${binding.agent.id} workflow references unknown subagent ${agentName}`);
1107
- }
1108
- const model = (delegatedAgent.model
1109
- ? await this.resolveModel(delegatedAgent.model)
1110
- : await this.resolveModel(params.model));
1111
- const tools = delegatedAgent.tools ? this.resolveTools(delegatedAgent.tools, binding) : [];
1112
- if (tools.length > 0 && typeof model.bindTools !== "function") {
1113
- throw new Error(`Subagent ${delegatedAgent.name} configures ${tools.length} tool(s), but its resolved model does not support tool binding.`);
1114
- }
1115
- const delegatedRunnable = createAgent({
1116
- ...(delegatedAgent.passthrough ?? {}),
1117
- model: model,
1118
- tools: tools,
1119
- systemPrompt: delegatedAgent.systemPrompt,
1120
- responseFormat: delegatedAgent.responseFormat,
1121
- contextSchema: delegatedAgent.contextSchema,
1122
- middleware: (delegatedAgent.middleware ?? []),
1123
- includeAgentName: "inline",
1124
- name: delegatedAgent.name,
1125
- description: delegatedAgent.description,
1126
- });
1127
- const delegatedPrompt = [
1128
- `User request:\n${userInputText}`,
1129
- ...(workflowState.plan ? ["", `Workflow plan:\n${workflowState.plan}`] : []),
1130
- ...(workflowState.review ? ["", `Workflow review:\n${workflowState.review}`] : []),
1131
- ...(activeResult ? ["", `Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`] : []),
1132
- "",
1133
- "Complete the delegated subagent work and return concise results.",
1134
- ].join("\n");
1135
- return delegatedRunnable.invoke({
1136
- messages: [{ role: "user", content: delegatedPrompt }],
1137
- }, config);
1138
- }
1139
- async createLangGraphRunnable(binding) {
1140
- const adapterConfig = getBindingAdapterConfig(binding);
1141
- const workflow = getBindingLangGraphWorkflow(binding) ??
1142
- resolveLangGraphProfileWorkflow(typeof adapterConfig.profile === "string" ? adapterConfig.profile : undefined, typeof adapterConfig.with === "object" && adapterConfig.with ? adapterConfig.with : {}) ??
1143
- resolveLangGraphPresetWorkflow(getBindingLangGraphPreset(binding), adapterConfig);
1144
- if (!workflow) {
1145
- throw new Error(`LangGraph agent ${binding.agent.id} requires execution.config.workflow, execution.config.profile, or execution.config.preset`);
1146
- }
1147
- const entryNode = typeof workflow.entryNode === "string" ? workflow.entryNode.trim() : "";
1148
- const nodes = this.listLangGraphWorkflowNodes(workflow);
1149
- if (!entryNode || nodes.length === 0) {
1150
- throw new Error(`LangGraph agent ${binding.agent.id} workflow must define entryNode and nodes`);
1151
- }
1152
- const nodeMap = new Map(nodes.map((node) => [node.id, node]));
1153
- if (!nodeMap.has(entryNode)) {
1154
- throw new Error(`LangGraph agent ${binding.agent.id} workflow entryNode ${entryNode} does not match any node id`);
1155
- }
1156
- const baseParams = getBindingLangChainParams(binding);
1157
- const passthroughWithoutWorkflow = {
1158
- ...(baseParams.passthrough ?? {}),
1159
- };
1160
- delete passthroughWithoutWorkflow.workflow;
1161
- delete passthroughWithoutWorkflow.langgraph;
1162
- const executorRunnable = await this.createLangChainRunnable(binding, {
1163
- passthroughOverride: passthroughWithoutWorkflow,
1164
- });
1165
- return {
1166
- invoke: async (request, config) => {
1167
- const sessionIdentity = this.resolveLangGraphSessionIdentity(binding, config);
1168
- const resumedSession = request instanceof Command && sessionIdentity ? await this.loadLangGraphSession(binding, sessionIdentity) : undefined;
1169
- const resumePayload = request instanceof Command ? request.resume : undefined;
1170
- const userInputText = resumedSession ? this.extractInvocationRequestText(resumedSession.request) : this.extractInvocationRequestText(request);
1171
- let activeRequest = resumedSession?.request ?? request;
1172
- let activeResult = resumedSession?.result;
1173
- const workflowState = resumedSession?.state
1174
- ? { ...resumedSession.state }
1175
- : {
1176
- iterations: 0,
1177
- replans: 0,
1178
- };
1179
- const visited = new Set();
1180
- let currentNodeId = resumedSession?.nextNodeId ?? entryNode;
1181
- if (resumePayload !== undefined) {
1182
- if (typeof resumePayload === "string" && (resumePayload === "approve" || resumePayload === "reject")) {
1183
- workflowState.approvalDecision = resumePayload;
1184
- }
1185
- else if (isRecord(resumePayload) && resumePayload.decision === "edit" && resumePayload.editedInput) {
1186
- workflowState.approvalDecision = "edit";
1187
- activeRequest = this.replaceLastUserMessage(activeRequest, String(resumePayload.editedInput));
1188
- }
1189
- if (sessionIdentity) {
1190
- await this.clearLangGraphSession(binding, sessionIdentity);
1191
- }
1192
- }
1193
- for (let iteration = 0; currentNodeId; iteration += 1) {
1194
- workflowState.iterations = iteration + 1;
1195
- if (iteration >= 24) {
1196
- break;
1197
- }
1198
- const loopKey = `${currentNodeId}:${workflowState.replans}:${Boolean(activeResult)}`;
1199
- if (visited.has(loopKey)) {
1200
- break;
1201
- }
1202
- visited.add(loopKey);
1203
- const node = nodeMap.get(currentNodeId);
1204
- if (!node) {
1205
- break;
1206
- }
1207
- workflowState.lastNodeId = currentNodeId;
1208
- if (node.kind === "llm") {
1209
- const nodeRole = node.role?.toLowerCase() ?? "llm";
1210
- if (nodeRole === "planner") {
1211
- const plannerModel = getBindingRuntimeModel(binding, "planning") ?? baseParams.model;
1212
- const plannerPrompt = node.prompt ?? [
1213
- "You are a LangGraph workflow planner.",
1214
- "Write a concise execution plan for the user request.",
1215
- "Keep it brief and actionable.",
1216
- ].join(" ");
1217
- workflowState.plan = await this.invokeWorkflowNodeModel(plannerModel, plannerPrompt, userInputText);
1218
- activeRequest = this.prependSystemMessage(activeRequest, `Workflow plan:\n${workflowState.plan}`);
1219
- }
1220
- else if (nodeRole === "replanner") {
1221
- const replannerModel = getBindingRuntimeModel(binding, "planning") ?? baseParams.model;
1222
- const replannerPrompt = node.prompt ?? [
1223
- "You are a LangGraph workflow replanner.",
1224
- "Refine the execution plan based on the current result and review feedback.",
1225
- "Return an updated concise plan only.",
1226
- ].join(" ");
1227
- workflowState.plan = await this.invokeWorkflowNodeModel(replannerModel, replannerPrompt, [
1228
- `User request:\n${userInputText}`,
1229
- ...(workflowState.plan ? ["", `Current plan:\n${workflowState.plan}`] : []),
1230
- ...(workflowState.review ? ["", `Review feedback:\n${workflowState.review}`] : []),
1231
- ...(activeResult ? ["", `Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`] : []),
1232
- ].join("\n"));
1233
- workflowState.replans += 1;
1234
- activeRequest = this.prependSystemMessage(activeRequest, `Updated workflow plan:\n${workflowState.plan}`);
1235
- }
1236
- else if (nodeRole === "reviewer" && activeResult) {
1237
- const reviewerModel = getBindingRuntimeModel(binding, "review") ?? baseParams.model;
1238
- const reviewerPrompt = node.prompt ?? [
1239
- "You are a LangGraph workflow reviewer.",
1240
- "Review the executor result and state whether it appears sufficient.",
1241
- "Call out missing verification or obvious risks briefly.",
1242
- ].join(" ");
1243
- workflowState.review = await this.invokeWorkflowNodeModel(reviewerModel, reviewerPrompt, [
1244
- `User request:\n${userInputText}`,
1245
- "",
1246
- `Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`,
1247
- ].join("\n"));
1248
- }
1249
- else if ((nodeRole === "finalizer" || nodeRole === "final") && activeResult) {
1250
- const finalModel = getBindingRuntimeModel(binding, "final") ?? baseParams.model;
1251
- const finalPrompt = node.prompt ?? [
1252
- "You are a LangGraph workflow finalizer.",
1253
- "Rewrite the current result into a concise user-facing answer.",
1254
- "Preserve facts and caveats. Do not invent work that was not completed.",
1255
- ].join(" ");
1256
- const finalized = await this.invokeWorkflowNodeModel(finalModel, finalPrompt, [
1257
- `User request:\n${userInputText}`,
1258
- ...(workflowState.plan ? ["", `Plan:\n${workflowState.plan}`] : []),
1259
- ...(workflowState.review ? ["", `Review:\n${workflowState.review}`] : []),
1260
- "",
1261
- `Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`,
1262
- ].join("\n"));
1263
- activeResult = {
1264
- ...activeResult,
1265
- output: finalized,
1266
- messages: [{ role: "assistant", content: finalized }],
1267
- workflow: {
1268
- ...workflowState,
1269
- },
1270
- };
1271
- }
1272
- else {
1273
- const llmModel = getBindingRuntimeModel(binding, "execution") ?? baseParams.model;
1274
- const llmPrompt = node.prompt ?? "Produce the next response for this workflow node.";
1275
- const llmOutput = await this.invokeWorkflowNodeModel(llmModel, llmPrompt, userInputText);
1276
- activeResult = {
1277
- output: llmOutput,
1278
- messages: [{ role: "assistant", content: llmOutput }],
1279
- };
1280
- }
1281
- }
1282
- else if (node.kind === "agent") {
1283
- if (node.agent) {
1284
- activeResult = await this.invokeLangGraphAgentNode(binding, node.agent, userInputText, workflowState, activeResult, config);
1285
- }
1286
- else {
1287
- activeResult = await executorRunnable.invoke(activeRequest, config);
1288
- }
1289
- }
1290
- else if (node.kind === "tool") {
1291
- if (!node.tool) {
1292
- throw new Error(`LangGraph agent ${binding.agent.id} tool node ${node.id} requires tool`);
1293
- }
1294
- activeResult = await this.invokeLangGraphToolNode(binding, node.tool, userInputText, config, node.args);
1295
- }
1296
- else if (node.kind === "condition") {
1297
- // Condition nodes are routing-only. Edge conditions decide the next node.
1298
- }
1299
- else if (node.kind === "approval") {
1300
- const nextNodes = this.listLangGraphWorkflowNextNodes(workflow, currentNodeId, workflowState, activeResult);
1301
- const nextNodeId = nextNodes[0];
1302
- if (resumePayload === undefined) {
1303
- if (sessionIdentity) {
1304
- await this.saveLangGraphSession(binding, sessionIdentity, {
1305
- request: activeRequest,
1306
- result: activeResult,
1307
- state: { ...workflowState },
1308
- nextNodeId,
1309
- });
1310
- }
1311
- return {
1312
- __interrupt__: [{
1313
- toolName: "workflow_approval",
1314
- toolId: `workflow-approval-${binding.agent.id}-${node.id}`,
1315
- allowedDecisions: ["approve", "edit", "reject"],
1316
- inputPreview: {
1317
- nodeId: node.id,
1318
- agentId: binding.agent.id,
1319
- ...(workflowState.plan ? { plan: workflowState.plan } : {}),
1320
- ...(workflowState.review ? { review: workflowState.review } : {}),
1321
- },
1322
- }],
1323
- messages: activeResult?.messages ?? [],
1324
- workflow: {
1325
- ...workflowState,
1326
- pendingApprovalNodeId: node.id,
1327
- },
1328
- };
1329
- }
1330
- }
1331
- const nextNodes = this.listLangGraphWorkflowNextNodes(workflow, currentNodeId, workflowState, activeResult);
1332
- currentNodeId = nextNodes[0];
1333
- }
1334
- if (!activeResult) {
1335
- throw new Error(`LangGraph agent ${binding.agent.id} workflow did not execute an agent or tool node`);
1336
- }
1337
- if (sessionIdentity) {
1338
- await this.clearLangGraphSession(binding, sessionIdentity);
1339
- }
1340
- if ((workflowState.plan || workflowState.review || workflowState.replans > 0) && !isRecord(activeResult.workflow)) {
1341
- activeResult = {
1342
- ...activeResult,
1343
- workflow: {
1344
- ...workflowState,
1345
- },
1346
- };
1347
- }
1348
- return activeResult;
1349
- },
1350
- };
1351
- }
1352
- extractExecutedToolResults(result) {
1353
- const metadata = asObject(result?.metadata);
1354
- if (Array.isArray(metadata?.executedToolResults)) {
1355
- return metadata.executedToolResults;
1356
- }
1357
- const messages = Array.isArray(result?.messages) ? result.messages : [];
1358
- return messages.flatMap((message) => {
1359
- const typed = asObject(message);
1360
- const kwargs = asObject(typed?.kwargs);
1361
- const typeId = Array.isArray(typed?.id) ? typed.id.at(-1) : undefined;
1362
- const runtimeType = typeof typed?.type === "string" ? typed.type : undefined;
1363
- if (typeId !== "ToolMessage" && runtimeType !== "tool") {
1364
- return [];
1365
- }
1366
- const toolName = typeof typed?.name === "string"
1367
- ? typed.name
1368
- : typeof kwargs?.name === "string"
1369
- ? kwargs.name
1370
- : "tool";
1371
- const output = typed?.content ??
1372
- kwargs?.content ??
1373
- "";
1374
- return [{
1375
- toolName,
1376
- output,
1377
- }];
1378
- });
1379
- }
1380
- extractLangGraphResultOutput(result) {
1381
- if (!result) {
1382
- return "";
1383
- }
1384
- return sanitizeVisibleText(extractVisibleOutput(result) ||
1385
- extractToolFallbackContext(result) ||
1386
- (typeof result.output === "string" ? result.output : ""));
1387
- }
1388
- async *streamLangGraphWorkflow(binding, input, threadId, history = [], options = {}) {
1389
- const adapterConfig = getBindingAdapterConfig(binding);
1390
- const workflow = getBindingLangGraphWorkflow(binding) ??
1391
- resolveLangGraphProfileWorkflow(typeof adapterConfig.profile === "string" ? adapterConfig.profile : undefined, typeof adapterConfig.with === "object" && adapterConfig.with ? adapterConfig.with : {}) ??
1392
- resolveLangGraphPresetWorkflow(getBindingLangGraphPreset(binding), adapterConfig);
1393
- if (!workflow) {
1394
- throw new Error(`LangGraph agent ${binding.agent.id} requires execution.config.workflow, execution.config.profile, or execution.config.preset`);
1395
- }
1396
- const entryNode = typeof workflow.entryNode === "string" ? workflow.entryNode.trim() : "";
1397
- const nodes = this.listLangGraphWorkflowNodes(workflow);
1398
- if (!entryNode || nodes.length === 0) {
1399
- throw new Error(`LangGraph agent ${binding.agent.id} workflow must define entryNode and nodes`);
1400
- }
1401
- const nodeMap = new Map(nodes.map((node) => [node.id, node]));
1402
- if (!nodeMap.has(entryNode)) {
1403
- throw new Error(`LangGraph agent ${binding.agent.id} workflow entryNode ${entryNode} does not match any node id`);
1404
- }
1405
- const baseParams = getBindingLangChainParams(binding);
1406
- const passthroughWithoutWorkflow = {
1407
- ...(baseParams.passthrough ?? {}),
1408
- };
1409
- delete passthroughWithoutWorkflow.workflow;
1410
- delete passthroughWithoutWorkflow.langgraph;
1411
- const executorRunnable = await this.createLangChainRunnable(binding, {
1412
- passthroughOverride: passthroughWithoutWorkflow,
1413
- });
1414
- const request = this.buildInvocationRequest(binding, history, input, options);
1415
- const sessionIdentity = this.resolveLangGraphSessionIdentity(binding, { configurable: { thread_id: threadId, run_id: options.runId } }, {
1416
- fallbackRunId: options.runId,
1417
- });
1418
- const userInputText = this.extractInvocationRequestText(request);
1419
- let activeRequest = request;
1420
- let activeResult;
1421
- const workflowState = {
1422
- iterations: 0,
1423
- replans: 0,
1424
- };
1425
- const visited = new Set();
1426
- let currentNodeId = entryNode;
1427
- let emittedOutput = "";
1428
- for (let iteration = 0; currentNodeId; iteration += 1) {
1429
- workflowState.iterations = iteration + 1;
1430
- if (iteration >= 24) {
1431
- break;
1432
- }
1433
- const loopKey = `${currentNodeId}:${workflowState.replans}:${Boolean(activeResult)}`;
1434
- if (visited.has(loopKey)) {
1435
- break;
1436
- }
1437
- visited.add(loopKey);
1438
- const node = nodeMap.get(currentNodeId);
1439
- if (!node) {
1440
- break;
1441
- }
1442
- workflowState.lastNodeId = currentNodeId;
1443
- yield { kind: "step", content: `langgraph node ${node.id} (${node.kind})` };
1444
- if (node.kind === "llm") {
1445
- const nodeRole = node.role?.toLowerCase() ?? "llm";
1446
- if (nodeRole === "planner") {
1447
- const plannerModel = getBindingRuntimeModel(binding, "planning") ?? baseParams.model;
1448
- const plannerPrompt = node.prompt ?? [
1449
- "You are a LangGraph workflow planner.",
1450
- "Write a concise execution plan for the user request.",
1451
- "Keep it brief and actionable.",
1452
- ].join(" ");
1453
- workflowState.plan = await this.invokeWorkflowNodeModel(plannerModel, plannerPrompt, userInputText);
1454
- activeRequest = this.prependSystemMessage(activeRequest, `Workflow plan:\n${workflowState.plan}`);
1455
- if (workflowState.plan) {
1456
- yield { kind: "reasoning", content: workflowState.plan };
1457
- }
1458
- }
1459
- else if (nodeRole === "replanner") {
1460
- const replannerModel = getBindingRuntimeModel(binding, "planning") ?? baseParams.model;
1461
- const replannerPrompt = node.prompt ?? [
1462
- "You are a LangGraph workflow replanner.",
1463
- "Refine the execution plan based on the current result and review feedback.",
1464
- "Return an updated concise plan only.",
1465
- ].join(" ");
1466
- workflowState.plan = await this.invokeWorkflowNodeModel(replannerModel, replannerPrompt, [
1467
- `User request:\n${userInputText}`,
1468
- ...(workflowState.plan ? ["", `Current plan:\n${workflowState.plan}`] : []),
1469
- ...(workflowState.review ? ["", `Review feedback:\n${workflowState.review}`] : []),
1470
- ...(activeResult ? ["", `Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`] : []),
1471
- ].join("\n"));
1472
- workflowState.replans += 1;
1473
- activeRequest = this.prependSystemMessage(activeRequest, `Updated workflow plan:\n${workflowState.plan}`);
1474
- if (workflowState.plan) {
1475
- yield { kind: "reasoning", content: workflowState.plan };
1476
- }
1477
- }
1478
- else if (nodeRole === "reviewer" && activeResult) {
1479
- const reviewerModel = getBindingRuntimeModel(binding, "review") ?? baseParams.model;
1480
- const reviewerPrompt = node.prompt ?? [
1481
- "You are a LangGraph workflow reviewer.",
1482
- "Review the executor result and state whether it appears sufficient.",
1483
- "Call out missing verification or obvious risks briefly.",
1484
- ].join(" ");
1485
- workflowState.review = await this.invokeWorkflowNodeModel(reviewerModel, reviewerPrompt, [
1486
- `User request:\n${userInputText}`,
1487
- "",
1488
- `Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`,
1489
- ].join("\n"));
1490
- if (workflowState.review) {
1491
- yield { kind: "reasoning", content: workflowState.review };
1492
- }
1493
- }
1494
- else if ((nodeRole === "finalizer" || nodeRole === "final") && activeResult) {
1495
- const finalModel = getBindingRuntimeModel(binding, "final") ?? baseParams.model;
1496
- const finalPrompt = node.prompt ?? [
1497
- "You are a LangGraph workflow finalizer.",
1498
- "Rewrite the current result into a concise user-facing answer.",
1499
- "Preserve facts and caveats. Do not invent work that was not completed.",
1500
- ].join(" ");
1501
- const finalized = await this.invokeWorkflowNodeModel(finalModel, finalPrompt, [
1502
- `User request:\n${userInputText}`,
1503
- ...(workflowState.plan ? ["", `Plan:\n${workflowState.plan}`] : []),
1504
- ...(workflowState.review ? ["", `Review:\n${workflowState.review}`] : []),
1505
- "",
1506
- `Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`,
1507
- ].join("\n"));
1508
- activeResult = {
1509
- ...activeResult,
1510
- output: finalized,
1511
- messages: [{ role: "assistant", content: finalized }],
1512
- workflow: {
1513
- ...workflowState,
1514
- },
1515
- };
1516
- const nextOutput = computeIncrementalOutput(emittedOutput, finalized);
1517
- emittedOutput = nextOutput.accumulated;
1518
- if (nextOutput.delta) {
1519
- yield { kind: "content", content: nextOutput.delta };
1520
- }
1521
- }
1522
- else {
1523
- const llmModel = getBindingRuntimeModel(binding, "execution") ?? baseParams.model;
1524
- const llmPrompt = node.prompt ?? "Produce the next response for this workflow node.";
1525
- const llmOutput = await this.invokeWorkflowNodeModel(llmModel, llmPrompt, userInputText);
1526
- activeResult = {
1527
- output: llmOutput,
1528
- messages: [{ role: "assistant", content: llmOutput }],
1529
- };
1530
- const nextOutput = computeIncrementalOutput(emittedOutput, llmOutput);
1531
- emittedOutput = nextOutput.accumulated;
1532
- if (nextOutput.delta) {
1533
- yield { kind: "content", content: nextOutput.delta };
1534
- }
1535
- }
1536
- }
1537
- else if (node.kind === "agent") {
1538
- if (node.agent) {
1539
- activeResult = await this.invokeLangGraphAgentNode(binding, node.agent, userInputText, workflowState, activeResult, { configurable: { thread_id: threadId, run_id: options.runId }, ...(options.context ? { context: options.context } : {}) });
1540
- }
1541
- else {
1542
- if (typeof executorRunnable.streamEvents === "function") {
1543
- const executorEvents = await executorRunnable.streamEvents(activeRequest, { configurable: { thread_id: threadId, run_id: options.runId }, version: "v2", ...(options.context ? { context: options.context } : {}) });
1544
- let executorOutput = "";
1545
- const seenTerminalOutputs = new Set();
1546
- for await (const event of executorEvents) {
1547
- const reasoning = extractReasoningStreamOutput(event);
1548
- if (reasoning) {
1549
- yield { kind: "reasoning", content: reasoning };
1550
- }
1551
- const toolResult = extractToolResult(event);
1552
- if (toolResult) {
1553
- yield {
1554
- kind: "tool-result",
1555
- toolName: toolResult.toolName,
1556
- output: toolResult.output,
1557
- isError: toolResult.isError,
1558
- };
1559
- }
1560
- const visibleStreamOutput = extractVisibleStreamOutput(event);
1561
- if (visibleStreamOutput) {
1562
- executorOutput = computeIncrementalOutput(executorOutput, visibleStreamOutput).accumulated;
1563
- }
1564
- const terminalOutput = extractTerminalStreamOutput(event);
1565
- if (terminalOutput) {
1566
- const outputKey = normalizeTerminalOutputKey(terminalOutput);
1567
- if (outputKey && seenTerminalOutputs.has(outputKey)) {
1568
- continue;
1569
- }
1570
- if (outputKey) {
1571
- seenTerminalOutputs.add(outputKey);
1572
- }
1573
- executorOutput = computeIncrementalOutput(executorOutput, sanitizeVisibleText(terminalOutput)).accumulated;
1574
- }
1575
- }
1576
- activeResult = executorOutput
1577
- ? {
1578
- output: executorOutput,
1579
- messages: [{ role: "assistant", content: executorOutput }],
1580
- }
1581
- : await executorRunnable.invoke(activeRequest, { configurable: { thread_id: threadId, run_id: options.runId }, ...(options.context ? { context: options.context } : {}) });
1582
- }
1583
- else {
1584
- activeResult = await executorRunnable.invoke(activeRequest, { configurable: { thread_id: threadId, run_id: options.runId }, ...(options.context ? { context: options.context } : {}) });
1585
- }
1586
- }
1587
- for (const toolResult of this.extractExecutedToolResults(activeResult)) {
1588
- yield {
1589
- kind: "tool-result",
1590
- toolName: toolResult.toolName,
1591
- output: toolResult.output,
1592
- isError: toolResult.isError,
1593
- };
1594
- }
1595
- }
1596
- else if (node.kind === "tool") {
1597
- if (!node.tool) {
1598
- throw new Error(`LangGraph agent ${binding.agent.id} tool node ${node.id} requires tool`);
1599
- }
1600
- activeResult = await this.invokeLangGraphToolNode(binding, node.tool, userInputText, { configurable: { thread_id: threadId, run_id: options.runId }, ...(options.context ? { context: options.context } : {}) }, node.args);
1601
- for (const toolResult of this.extractExecutedToolResults(activeResult)) {
1602
- yield {
1603
- kind: "tool-result",
1604
- toolName: toolResult.toolName,
1605
- output: toolResult.output,
1606
- isError: toolResult.isError,
1607
- };
1608
- }
1609
- }
1610
- else if (node.kind === "condition") {
1611
- // Condition nodes are routing-only. Edge conditions decide the next node.
1612
- }
1613
- else if (node.kind === "approval") {
1614
- const nextNodes = this.listLangGraphWorkflowNextNodes(workflow, currentNodeId, workflowState, activeResult);
1615
- const nextNodeId = nextNodes[0];
1616
- const interruptPayload = {
1617
- toolName: "workflow_approval",
1618
- toolId: `workflow-approval-${binding.agent.id}-${node.id}`,
1619
- allowedDecisions: ["approve", "edit", "reject"],
1620
- inputPreview: {
1621
- nodeId: node.id,
1622
- agentId: binding.agent.id,
1623
- ...(workflowState.plan ? { plan: workflowState.plan } : {}),
1624
- ...(workflowState.review ? { review: workflowState.review } : {}),
1625
- },
1626
- };
1627
- if (sessionIdentity) {
1628
- await this.saveLangGraphSession(binding, sessionIdentity, {
1629
- request: activeRequest,
1630
- result: activeResult,
1631
- state: { ...workflowState },
1632
- nextNodeId,
1633
- });
1634
- }
1635
- yield { kind: "interrupt", content: JSON.stringify([interruptPayload]) };
1636
- return;
1637
- }
1638
- const nextNodes = this.listLangGraphWorkflowNextNodes(workflow, currentNodeId, workflowState, activeResult);
1639
- currentNodeId = nextNodes[0];
1640
- }
1641
- if (!activeResult) {
1642
- throw new Error(`LangGraph agent ${binding.agent.id} workflow did not execute an agent or tool node`);
1643
- }
1644
- if (sessionIdentity) {
1645
- await this.clearLangGraphSession(binding, sessionIdentity);
1646
- }
1647
- if ((workflowState.plan || workflowState.review || workflowState.replans > 0) && !isRecord(activeResult.workflow)) {
1648
- activeResult = {
1649
- ...activeResult,
1650
- workflow: {
1651
- ...workflowState,
1652
- },
1653
- };
1654
- }
1655
- const finalOutput = this.extractLangGraphResultOutput(activeResult);
1656
- const nextOutput = computeIncrementalOutput(emittedOutput, finalOutput);
1657
- if (nextOutput.delta) {
1658
- yield { kind: "content", content: nextOutput.delta };
1659
- }
451
+ return createAgent(buildLangChainRunnableConfig({
452
+ langchainParams: params,
453
+ resolvedModel: model,
454
+ resolvedTools: tools,
455
+ resolvedMiddleware: await this.resolveMiddleware(binding, interruptOn),
456
+ resolvedCheckpointer: this.options.checkpointerResolver ? this.options.checkpointerResolver(binding) : new MemorySaver(),
457
+ resolvedStore: this.options.storeResolver?.(binding),
458
+ passthroughOverride: options.passthroughOverride,
459
+ systemPromptOverride: options.systemPromptOverride,
460
+ }));
1660
461
  }
1661
462
  async createRunnable(binding) {
1662
463
  if (getBindingAdapterKind(binding) === "langgraph") {
1663
- return this.createLangGraphRunnable(binding);
464
+ throw new Error(`Agent ${binding.agent.id} uses removed backend langgraph; use langchain-v1 or deepagent`);
1664
465
  }
1665
466
  if (isLangChainBinding(binding)) {
1666
467
  return this.createLangChainRunnable(binding);
1667
468
  }
469
+ return this.createDeepAgentRunnable(binding);
470
+ }
471
+ async createDeepAgentRunnable(binding) {
1668
472
  const params = getBindingDeepAgentParams(binding);
1669
473
  if (!params) {
1670
474
  throw new Error(`Agent ${binding.agent.id} has no runnable params`);
1671
475
  }
1672
476
  const compatibleParams = applyDeepAgentDelegationPromptCompatibility(params.model, params);
1673
- const deepAgentConfig = {
1674
- ...(compatibleParams.passthrough ?? {}),
1675
- model: (await this.resolveModel(compatibleParams.model)),
1676
- tools: this.resolveTools(compatibleParams.tools, binding),
1677
- systemPrompt: compatibleParams.systemPrompt,
1678
- responseFormat: compatibleParams.responseFormat,
1679
- contextSchema: compatibleParams.contextSchema,
1680
- middleware: (await this.resolveMiddleware(binding)),
1681
- subagents: (await this.resolveSubagents(compatibleParams.subagents, binding)),
1682
- checkpointer: this.resolveCheckpointer(binding),
1683
- store: this.options.storeResolver?.(binding),
1684
- backend: this.options.backendResolver?.(binding),
1685
- interruptOn: this.resolveInterruptOn(binding),
1686
- name: compatibleParams.name,
1687
- memory: compatibleParams.memory,
1688
- skills: await materializeDeepAgentSkillSourcePaths({
477
+ const deepAgentConfig = buildDeepAgentRunnableConfig({
478
+ compatibleParams,
479
+ resolvedModel: await this.resolveModel(compatibleParams.model),
480
+ resolvedTools: this.resolveTools(compatibleParams.tools, binding),
481
+ resolvedMiddleware: await this.resolveMiddleware(binding),
482
+ resolvedSubagents: await this.resolveSubagents(compatibleParams.subagents, binding),
483
+ resolvedCheckpointer: this.options.checkpointerResolver ? this.options.checkpointerResolver(binding) : new MemorySaver(),
484
+ resolvedStore: this.options.storeResolver?.(binding),
485
+ resolvedBackend: this.options.backendResolver?.(binding),
486
+ resolvedInterruptOn: compileInterruptOn(getBindingPrimaryTools(binding), getBindingInterruptCompatibilityRules(binding)),
487
+ resolvedSkills: (await materializeDeepAgentSkillSourcePaths({
1689
488
  workspaceRoot: binding.harnessRuntime.workspaceRoot,
1690
489
  runRoot: binding.harnessRuntime.runRoot,
1691
490
  ownerId: binding.agent.id,
1692
491
  skillPaths: compatibleParams.skills,
1693
- }),
1694
- generalPurposeAgent: compatibleParams.generalPurposeAgent,
1695
- taskDescription: compatibleParams.taskDescription,
1696
- };
492
+ })) ?? [],
493
+ });
1697
494
  return createDeepAgent(deepAgentConfig);
1698
495
  }
1699
496
  async create(binding) {
@@ -1711,116 +508,9 @@ export class AgentRuntimeAdapter {
1711
508
  throw error;
1712
509
  }
1713
510
  }
1714
- async route(input, primaryBinding, secondaryBinding, options = {}) {
1715
- const routeModelConfig = getBindingRuntimeModel(primaryBinding, "routing") ??
1716
- getBindingRuntimeModel(secondaryBinding, "routing") ??
1717
- getBindingPrimaryModel(primaryBinding) ??
1718
- getBindingPrimaryModel(secondaryBinding);
1719
- if (!routeModelConfig) {
1720
- throw new Error("No router model configuration available");
1721
- }
1722
- const routerModel = (await this.resolveModel(routeModelConfig));
1723
- if (!routerModel?.invoke) {
1724
- throw new Error("Router model does not support invoke()");
1725
- }
1726
- const result = await routerModel.invoke([
1727
- {
1728
- role: "system",
1729
- content: this.buildRouteSystemPrompt(primaryBinding, secondaryBinding, options.systemPrompt),
1730
- },
1731
- { role: "user", content: extractMessageText(input) },
1732
- ]);
1733
- const content = typeof result === "string"
1734
- ? result
1735
- : typeof result?.content === "string"
1736
- ? result.content
1737
- : JSON.stringify(result);
1738
- const normalized = content.trim().toLowerCase();
1739
- return normalized === secondaryBinding.agent.id.toLowerCase() || normalized.includes(secondaryBinding.agent.id.toLowerCase())
1740
- ? secondaryBinding.agent.id
1741
- : primaryBinding.agent.id;
1742
- }
1743
- async reviewRunResult(binding, input, result) {
1744
- const reviewModelConfig = getBindingRuntimeModel(binding, "review");
1745
- if (!reviewModelConfig) {
1746
- return null;
1747
- }
1748
- const reviewModel = (await this.resolveModel(reviewModelConfig));
1749
- if (!reviewModel?.invoke) {
1750
- throw new Error("Review model does not support invoke()");
1751
- }
1752
- const reviewPrompt = [
1753
- "You are a runtime completion reviewer.",
1754
- "Review the completed agent result against the user request.",
1755
- "Respond with a concise assessment that states whether the result looks sufficient, incomplete, or risky.",
1756
- "Call out missing verification or likely follow-up work when relevant.",
1757
- "Keep the response brief and operator-readable.",
1758
- ].join(" ");
1759
- const reviewResult = await reviewModel.invoke([
1760
- { role: "system", content: reviewPrompt },
1761
- {
1762
- role: "user",
1763
- content: [
1764
- `User request:\n${extractMessageText(input)}`,
1765
- "",
1766
- `Run state: ${result.state}`,
1767
- "",
1768
- `Agent result:\n${result.finalMessageText ?? result.output}`,
1769
- ].join("\n"),
1770
- },
1771
- ]);
1772
- const assessment = typeof reviewResult === "string"
1773
- ? reviewResult
1774
- : typeof reviewResult?.content === "string"
1775
- ? reviewResult.content
1776
- : JSON.stringify(reviewResult);
1777
- return {
1778
- assessment: sanitizeVisibleText(assessment),
1779
- modelId: reviewModelConfig.id,
1780
- };
1781
- }
1782
- async synthesizeFinalResult(binding, input, result) {
1783
- const finalModelConfig = getBindingRuntimeModel(binding, "final");
1784
- if (!finalModelConfig) {
1785
- return null;
1786
- }
1787
- const finalModel = (await this.resolveModel(finalModelConfig));
1788
- if (!finalModel?.invoke) {
1789
- throw new Error("Final synthesis model does not support invoke()");
1790
- }
1791
- const synthesisPrompt = [
1792
- "You are a final response synthesizer for a runtime-managed agent system.",
1793
- "Rewrite the agent result into a concise, user-facing final answer.",
1794
- "Preserve important facts and caveats.",
1795
- "Do not invent work that was not completed.",
1796
- "If the result is already concise, keep it concise.",
1797
- ].join(" ");
1798
- const synthesisResult = await finalModel.invoke([
1799
- { role: "system", content: synthesisPrompt },
1800
- {
1801
- role: "user",
1802
- content: [
1803
- `User request:\n${extractMessageText(input)}`,
1804
- "",
1805
- `Agent result:\n${result.finalMessageText ?? result.output}`,
1806
- ].join("\n"),
1807
- },
1808
- ]);
1809
- const synthesized = typeof synthesisResult === "string"
1810
- ? synthesisResult
1811
- : typeof synthesisResult?.content === "string"
1812
- ? synthesisResult.content
1813
- : JSON.stringify(synthesisResult);
1814
- const finalMessageText = sanitizeVisibleText(synthesized);
1815
- return {
1816
- output: finalMessageText,
1817
- finalMessageText,
1818
- modelId: finalModelConfig.id,
1819
- };
1820
- }
1821
511
  async invoke(binding, input, threadId, runId, resumePayload, history = [], options = {}) {
1822
512
  const request = resumePayload === undefined
1823
- ? this.buildInvocationRequest(binding, history, input, options)
513
+ ? buildInvocationRequest(binding, history, input, options)
1824
514
  : new Command({ resume: resumePayload });
1825
515
  let result;
1826
516
  const callRuntime = async (activeBinding, activeRequest) => {
@@ -1852,7 +542,7 @@ export class AgentRuntimeAdapter {
1852
542
  }
1853
543
  else {
1854
544
  const resolvedTools = this.resolveTools(primaryTools, binding);
1855
- const toolNameMapping = this.buildToolNameMapping(primaryTools);
545
+ const toolNameMapping = buildToolNameMapping(primaryTools);
1856
546
  const executableTools = new Map();
1857
547
  const builtinExecutableTools = await this.resolveBuiltinMiddlewareTools(binding, options);
1858
548
  for (let index = 0; index < primaryTools.length; index += 1) {
@@ -1885,117 +575,41 @@ export class AgentRuntimeAdapter {
1885
575
  invoke: handler,
1886
576
  });
1887
577
  }
1888
- let activeRequest = request;
1889
- let currentMessages = Array.isArray(activeRequest.messages) ? [...activeRequest.messages] : [];
1890
- const maxToolIterations = 8;
1891
- let pendingResult;
1892
- for (let iteration = 0; iteration < maxToolIterations; iteration += 1) {
1893
- result = pendingResult ?? await callRuntimeWithToolParseRecovery(activeRequest);
1894
- pendingResult = undefined;
1895
- const toolCalls = extractToolCallsFromResult(result);
1896
- if (toolCalls.length === 0) {
1897
- break;
1898
- }
1899
- if (!this.canReplayToolCallsLocally(binding, toolCalls, primaryTools, toolNameMapping, executableTools, builtinExecutableTools)) {
1900
- break;
1901
- }
1902
- if (iteration + 1 === maxToolIterations) {
1903
- throw new Error(`Tool-calling loop exceeded the maximum of ${maxToolIterations} iterations`);
1904
- }
1905
- const resultMessages = result.messages;
1906
- const nextMessages = Array.isArray(resultMessages)
1907
- ? [...resultMessages]
1908
- : [...currentMessages];
1909
- for (let toolIndex = 0; toolIndex < toolCalls.length; toolIndex += 1) {
1910
- const toolCall = toolCalls[toolIndex];
1911
- const resolvedToolName = resolveModelFacingToolName(toolCall.name, toolNameMapping, primaryTools);
1912
- const executable = executableTools.get(toolCall.name) ?? executableTools.get(resolvedToolName);
1913
- const builtinExecutable = builtinExecutableTools.get(toolCall.name) ??
1914
- builtinExecutableTools.get(resolvedToolName) ??
1915
- createModelFacingToolNameLookupCandidates(toolCall.name)
1916
- .map((candidate) => builtinExecutableTools.get(candidate))
1917
- .find((candidate) => candidate !== undefined);
1918
- const activeExecutable = executable ?? builtinExecutable;
1919
- if (!activeExecutable) {
1920
- throw new Error(`Tool ${toolCall.name} is not configured for this agent.`);
1921
- }
1922
- const normalizedArgs = normalizeToolArgsForSchema(toolCall.args, activeExecutable.schema);
1923
- const toolResult = await activeExecutable.invoke(normalizedArgs);
1924
- executedToolResults.push({
1925
- toolName: activeExecutable.name,
1926
- output: toolResult,
1927
- });
1928
- nextMessages.push(new ToolMessage({
1929
- name: activeExecutable.name,
1930
- tool_call_id: toolCall.id ?? `tool-${iteration + 1}-${toolIndex + 1}`,
1931
- content: stringifyToolOutput(toolResult),
1932
- }));
1933
- }
1934
- currentMessages = nextMessages;
1935
- activeRequest = {
1936
- ...activeRequest,
1937
- messages: currentMessages,
1938
- };
1939
- }
578
+ const localInvocation = await runLocalToolInvocationLoop({
579
+ binding,
580
+ request,
581
+ primaryTools,
582
+ toolNameMapping,
583
+ executableTools: executableTools,
584
+ builtinExecutableTools: builtinExecutableTools,
585
+ callRuntimeWithToolParseRecovery,
586
+ });
587
+ result = localInvocation.result;
588
+ executedToolResults.push(...localInvocation.executedToolResults);
1940
589
  }
1941
590
  }
1942
591
  if (!result) {
1943
592
  throw new Error("Agent invocation returned no result");
1944
593
  }
1945
- const interruptContent = Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? JSON.stringify(result.__interrupt__) : undefined;
1946
- const extractedOutput = extractVisibleOutput(result);
1947
- const visibleOutput = extractedOutput && !isLikelyToolArgsObject(tryParseJson(extractedOutput)) ? extractedOutput : "";
1948
- const emptyAssistantMessageFailure = extractEmptyAssistantMessageFailure(result);
1949
- const toolFallback = extractToolFallbackContext(result);
1950
- if (!visibleOutput && !toolFallback && emptyAssistantMessageFailure) {
1951
- throw new Error(emptyAssistantMessageFailure);
1952
- }
1953
- const output = visibleOutput || toolFallback || JSON.stringify(result, null, 2);
1954
- const finalMessageText = sanitizeVisibleText(output);
1955
- const outputContent = extractOutputContent(result);
1956
- const contentBlocks = extractContentBlocks(result);
1957
- const structuredResponse = result.structuredResponse;
1958
- return {
594
+ return finalizeInvocationResult({
595
+ bindingAgentId: binding.agent.id,
1959
596
  threadId,
1960
597
  runId,
1961
- agentId: binding.agent.id,
1962
- state: Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? "waiting_for_approval" : "completed",
1963
- interruptContent,
1964
- output: finalMessageText,
1965
- finalMessageText,
1966
- ...(outputContent !== undefined ? { outputContent } : {}),
1967
- ...(contentBlocks.length > 0 ? { contentBlocks } : {}),
1968
- ...(structuredResponse !== undefined ? { structuredResponse } : {}),
1969
- metadata: {
1970
- ...(executedToolResults.length > 0 ? { executedToolResults } : {}),
1971
- ...(structuredResponse !== undefined ? { structuredResponse } : {}),
1972
- ...(outputContent !== undefined ? { outputContent } : {}),
1973
- ...(contentBlocks.length > 0 ? { contentBlocks } : {}),
1974
- ...(asRecord(result.files) ? { files: asRecord(result.files) } : {}),
1975
- ...(buildStateSnapshot(result) ? { stateSnapshot: buildStateSnapshot(result) } : {}),
1976
- upstreamResult: result,
1977
- },
1978
- };
598
+ result,
599
+ executedToolResults,
600
+ });
1979
601
  }
1980
602
  async *stream(binding, input, threadId, history = [], options = {}) {
1981
603
  try {
1982
- const adapterKind = getBindingAdapterKind(binding);
1983
604
  const invokeTimeoutMs = resolveBindingTimeout(binding);
1984
605
  const streamIdleTimeoutMs = resolveStreamIdleTimeout(binding);
1985
606
  const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
1986
607
  const primaryTools = getBindingPrimaryTools(binding);
1987
- const toolNameMapping = this.buildToolNameMapping(primaryTools);
608
+ const toolNameMapping = buildToolNameMapping(primaryTools);
1988
609
  const primaryModel = getBindingPrimaryModel(binding);
1989
- const forceInvokeFallback = adapterKind !== "langgraph" &&
1990
- isLangChainBinding(binding) &&
610
+ const forceInvokeFallback = isLangChainBinding(binding) &&
1991
611
  primaryTools.length > 0 &&
1992
612
  primaryModel?.provider === "openai-compatible";
1993
- if (adapterKind === "langgraph") {
1994
- for await (const chunk of this.streamLangGraphWorkflow(binding, input, threadId, history, options)) {
1995
- yield chunk;
1996
- }
1997
- return;
1998
- }
1999
613
  if (isLangChainBinding(binding)) {
2000
614
  const langchainParams = getBindingLangChainParams(binding);
2001
615
  const resolvedModel = (await this.resolveModel(langchainParams.model));
@@ -2009,121 +623,61 @@ export class AgentRuntimeAdapter {
2009
623
  // For tool-using langchain agents, a raw model.stream pass cannot execute the
2010
624
  // agent loop and only adds an extra model round-trip before the runnable path.
2011
625
  if (canUseDirectModelStream && typeof model.stream === "function") {
626
+ const stream = await this.withTimeout(() => model.stream(buildRawModelMessages(binding, getBindingSystemPrompt(binding), history, input)), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "model stream start", "stream");
2012
627
  let emitted = false;
2013
- const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(binding, getBindingSystemPrompt(binding), history, input)), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "model stream start", "stream");
2014
- for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "model stream", streamDeadlineAt, invokeTimeoutMs)) {
2015
- const delta = readStreamDelta(chunk);
2016
- if (delta) {
628
+ const projected = projectTextStreamChunks(this.iterateWithTimeout(stream, streamIdleTimeoutMs, "model stream", streamDeadlineAt, invokeTimeoutMs));
629
+ let nextChunk = await projected.next();
630
+ while (!nextChunk.done) {
631
+ if (nextChunk.value.kind === "content") {
2017
632
  emitted = true;
2018
- yield { kind: "content", content: delta };
2019
- continue;
2020
- }
2021
- const reasoning = extractReasoningText(chunk);
2022
- if (reasoning) {
2023
- yield { kind: "reasoning", content: reasoning };
2024
633
  }
634
+ yield nextChunk.value;
635
+ nextChunk = await projected.next();
2025
636
  }
2026
- if (emitted) {
637
+ if (nextChunk.value.emittedContent || emitted) {
2027
638
  return;
2028
639
  }
2029
640
  }
2030
641
  }
2031
642
  const runnable = await this.create(binding);
2032
- const request = this.buildInvocationRequest(binding, history, input, options);
643
+ const request = buildInvocationRequest(binding, history, input, options);
2033
644
  if (!forceInvokeFallback && typeof runnable.streamEvents === "function") {
2034
645
  const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId, run_id: options.runId }, version: "v2", ...(options.context ? { context: options.context } : {}) }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent streamEvents start", "stream");
2035
- const allowVisibleStreamDeltas = isLangChainBinding(binding);
2036
- let emittedOutput = "";
2037
- let emittedToolError = false;
2038
- const seenTerminalOutputs = new Set();
2039
- let lastStep = "";
646
+ const projectionState = createStreamEventProjectionState();
2040
647
  for await (const event of this.iterateWithTimeout(events, streamIdleTimeoutMs, "agent streamEvents", streamDeadlineAt, invokeTimeoutMs)) {
2041
- yield { kind: "upstream-event", event: normalizeUpstreamRuntimeEvent(event) };
2042
- const interruptPayload = extractInterruptPayload(event);
2043
- if (interruptPayload) {
2044
- yield { kind: "interrupt", content: interruptPayload };
2045
- continue;
2046
- }
2047
- const reasoning = extractReasoningStreamOutput(event);
2048
- if (reasoning) {
2049
- yield { kind: "reasoning", content: reasoning };
2050
- }
2051
- if (allowVisibleStreamDeltas) {
2052
- const visibleStreamOutput = extractVisibleStreamOutput(event);
2053
- if (visibleStreamOutput) {
2054
- const nextOutput = computeIncrementalOutput(emittedOutput, visibleStreamOutput);
2055
- emittedOutput = nextOutput.accumulated;
2056
- if (nextOutput.delta) {
2057
- yield { kind: "content", content: nextOutput.delta };
2058
- }
2059
- }
2060
- }
2061
- if (isDeepAgentBinding(binding)) {
2062
- const stateStreamOutput = extractStateStreamOutput(event);
2063
- if (stateStreamOutput) {
2064
- const nextOutput = computeIncrementalOutput(emittedOutput, sanitizeVisibleText(stateStreamOutput));
2065
- emittedOutput = nextOutput.accumulated;
2066
- if (nextOutput.delta) {
2067
- yield { kind: "content", content: nextOutput.delta };
2068
- }
2069
- }
2070
- }
2071
- const agentStep = extractAgentStep(event);
2072
- if (agentStep && agentStep !== lastStep) {
2073
- lastStep = agentStep;
2074
- yield { kind: "step", content: agentStep };
2075
- }
2076
- const toolResult = extractToolResult(event);
2077
- if (toolResult) {
2078
- emittedToolError = emittedToolError || toolResult.isError === true;
2079
- const resolvedToolName = resolveModelFacingToolName(toolResult.toolName, toolNameMapping, primaryTools);
2080
- yield {
2081
- kind: "tool-result",
2082
- toolName: resolvedToolName,
2083
- output: toolResult.output,
2084
- isError: toolResult.isError,
2085
- };
2086
- }
2087
- const output = extractTerminalStreamOutput(event);
2088
- if (output) {
2089
- const outputKey = normalizeTerminalOutputKey(output);
2090
- if (outputKey && seenTerminalOutputs.has(outputKey)) {
2091
- continue;
2092
- }
2093
- if (outputKey) {
2094
- seenTerminalOutputs.add(outputKey);
2095
- }
2096
- const nextOutput = computeIncrementalOutput(emittedOutput, sanitizeVisibleText(output));
2097
- emittedOutput = nextOutput.accumulated;
2098
- if (nextOutput.delta) {
2099
- yield { kind: "content", content: nextOutput.delta };
2100
- }
648
+ const projectedChunks = projectRuntimeStreamEvent({
649
+ event,
650
+ allowVisibleStreamDeltas: isLangChainBinding(binding),
651
+ includeStateStreamOutput: isDeepAgentBinding(binding),
652
+ toolNameMapping,
653
+ primaryTools,
654
+ state: projectionState,
655
+ });
656
+ for (const chunk of projectedChunks) {
657
+ yield chunk;
2101
658
  }
2102
659
  }
2103
- if (emittedOutput || emittedToolError) {
660
+ if (projectionState.emittedOutput || projectionState.emittedToolResult || projectionState.emittedToolError) {
2104
661
  return;
2105
662
  }
2106
663
  }
2107
664
  if (!forceInvokeFallback && isLangChainBinding(binding) && typeof runnable.stream === "function") {
2108
665
  const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId, run_id: options.runId } }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent stream start", "stream");
2109
666
  let emitted = false;
2110
- for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "agent stream", streamDeadlineAt, invokeTimeoutMs)) {
2111
- const delta = readStreamDelta(chunk);
2112
- if (delta) {
667
+ const projected = projectTextStreamChunks(this.iterateWithTimeout(stream, streamIdleTimeoutMs, "agent stream", streamDeadlineAt, invokeTimeoutMs));
668
+ let nextChunk = await projected.next();
669
+ while (!nextChunk.done) {
670
+ if (nextChunk.value.kind === "content") {
2113
671
  emitted = true;
2114
- yield { kind: "content", content: delta };
2115
- continue;
2116
- }
2117
- const reasoning = extractReasoningText(chunk);
2118
- if (reasoning) {
2119
- yield { kind: "reasoning", content: reasoning };
2120
672
  }
673
+ yield nextChunk.value;
674
+ nextChunk = await projected.next();
2121
675
  }
2122
- if (emitted) {
676
+ if (nextChunk.value.emittedContent || emitted) {
2123
677
  return;
2124
678
  }
2125
679
  }
2126
- const result = await this.invoke(binding, input, threadId, threadId);
680
+ const result = await this.invoke(binding, input, threadId, options.runId ?? threadId, undefined, history, options);
2127
681
  const executedToolResults = Array.isArray(result.metadata?.executedToolResults)
2128
682
  ? result.metadata.executedToolResults
2129
683
  : [];
@@ -2148,7 +702,7 @@ export class AgentRuntimeAdapter {
2148
702
  if (!isToolCallParseFailure(error)) {
2149
703
  throw error;
2150
704
  }
2151
- const retried = await this.invoke(this.applyStrictToolJsonInstruction(binding), input, threadId, threadId, undefined, history);
705
+ const retried = await this.invoke(this.applyStrictToolJsonInstruction(binding), input, threadId, options.runId ?? threadId, undefined, history, options);
2152
706
  if (retried.output) {
2153
707
  yield { kind: "content", content: sanitizeVisibleText(retried.output) };
2154
708
  }