@botbotgo/agent-harness 0.0.95 → 0.0.97

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 (114) hide show
  1. package/README.md +1 -114
  2. package/README.zh.md +1 -70
  3. package/dist/api.d.ts +5 -5
  4. package/dist/config/workflows/langgraph-workflows.yaml +363 -111
  5. package/dist/config/workflows/runtime-profiles.yaml +94 -0
  6. package/dist/contracts/core.d.ts +9 -0
  7. package/dist/contracts/core.js +1 -0
  8. package/dist/contracts/runtime.d.ts +421 -0
  9. package/dist/contracts/runtime.js +1 -0
  10. package/dist/contracts/types.d.ts +3 -571
  11. package/dist/contracts/types.js +3 -1
  12. package/dist/contracts/workspace.d.ts +229 -0
  13. package/dist/contracts/workspace.js +1 -0
  14. package/dist/package-version.d.ts +1 -1
  15. package/dist/package-version.js +1 -1
  16. package/dist/runtime/adapter/compat/deepagent-compat.d.ts +16 -0
  17. package/dist/runtime/adapter/compat/deepagent-compat.js +45 -0
  18. package/dist/runtime/adapter/compat/openai-compatible.d.ts +2 -0
  19. package/dist/runtime/adapter/compat/openai-compatible.js +43 -0
  20. package/dist/runtime/adapter/index.d.ts +15 -0
  21. package/dist/runtime/adapter/index.js +15 -0
  22. package/dist/runtime/adapter/langgraph/presets.js +165 -0
  23. package/dist/runtime/{langgraph-profiles.d.ts → adapter/langgraph/profiles.d.ts} +1 -1
  24. package/dist/runtime/adapter/langgraph/profiles.js +206 -0
  25. package/dist/runtime/adapter/model/invocation-request.d.ts +10 -0
  26. package/dist/runtime/adapter/model/invocation-request.js +46 -0
  27. package/dist/runtime/adapter/model/message-assembly.d.ts +6 -0
  28. package/dist/runtime/adapter/model/message-assembly.js +21 -0
  29. package/dist/runtime/adapter/model/model-providers.d.ts +2 -0
  30. package/dist/runtime/adapter/model/model-providers.js +27 -0
  31. package/dist/runtime/adapter/resilience.d.ts +12 -0
  32. package/dist/runtime/adapter/resilience.js +60 -0
  33. package/dist/runtime/{declared-middleware.d.ts → adapter/tool/declared-middleware.d.ts} +1 -1
  34. package/dist/runtime/adapter/tool/interrupt-policy.d.ts +8 -0
  35. package/dist/runtime/adapter/tool/interrupt-policy.js +34 -0
  36. package/dist/runtime/adapter/tool/provider-tool.d.ts +2 -0
  37. package/dist/runtime/adapter/tool/provider-tool.js +25 -0
  38. package/dist/runtime/adapter/tool/resolved-tool.d.ts +18 -0
  39. package/dist/runtime/adapter/tool/resolved-tool.js +62 -0
  40. package/dist/runtime/adapter/tool/tool-arguments.d.ts +7 -0
  41. package/dist/runtime/adapter/tool/tool-arguments.js +87 -0
  42. package/dist/runtime/{tool-hitl.d.ts → adapter/tool/tool-hitl.d.ts} +2 -2
  43. package/dist/runtime/adapter/tool/tool-name-mapping.d.ts +13 -0
  44. package/dist/runtime/adapter/tool/tool-name-mapping.js +101 -0
  45. package/dist/runtime/agent-runtime-adapter.d.ts +5 -20
  46. package/dist/runtime/agent-runtime-adapter.js +42 -544
  47. package/dist/runtime/checkpoint-maintenance.d.ts +1 -45
  48. package/dist/runtime/checkpoint-maintenance.js +1 -259
  49. package/dist/runtime/file-checkpoint-saver.d.ts +1 -20
  50. package/dist/runtime/file-checkpoint-saver.js +1 -106
  51. package/dist/runtime/{event-bus.d.ts → harness/events/event-bus.d.ts} +1 -1
  52. package/dist/runtime/{event-sink.d.ts → harness/events/event-sink.d.ts} +1 -1
  53. package/dist/runtime/{event-sink.js → harness/events/event-sink.js} +1 -1
  54. package/dist/runtime/harness/events/events.d.ts +23 -0
  55. package/dist/runtime/harness/events/events.js +61 -0
  56. package/dist/runtime/harness/events/streaming.d.ts +19 -0
  57. package/dist/runtime/harness/events/streaming.js +96 -0
  58. package/dist/runtime/harness/index.d.ts +16 -0
  59. package/dist/runtime/harness/index.js +16 -0
  60. package/dist/runtime/harness/run/helpers.d.ts +33 -0
  61. package/dist/runtime/harness/run/helpers.js +74 -0
  62. package/dist/runtime/harness/run/resources.d.ts +7 -0
  63. package/dist/runtime/harness/run/resources.js +58 -0
  64. package/dist/runtime/harness/run/resume.d.ts +6 -0
  65. package/dist/runtime/harness/run/resume.js +56 -0
  66. package/dist/runtime/harness/run/routing.d.ts +12 -0
  67. package/dist/runtime/harness/run/routing.js +47 -0
  68. package/dist/runtime/harness/run/run-lifecycle.d.ts +37 -0
  69. package/dist/runtime/harness/run/run-lifecycle.js +109 -0
  70. package/dist/runtime/harness/run/run-queue.d.ts +17 -0
  71. package/dist/runtime/harness/run/run-queue.js +43 -0
  72. package/dist/runtime/{health-monitor.d.ts → harness/system/health-monitor.d.ts} +3 -3
  73. package/dist/runtime/{health-monitor.js → harness/system/health-monitor.js} +2 -2
  74. package/dist/runtime/{inventory.d.ts → harness/system/inventory.d.ts} +2 -2
  75. package/dist/runtime/{inventory.js → harness/system/inventory.js} +4 -4
  76. package/dist/runtime/{policy-engine.d.ts → harness/system/policy-engine.d.ts} +1 -1
  77. package/dist/runtime/{policy-engine.js → harness/system/policy-engine.js} +1 -1
  78. package/dist/runtime/{skill-requirements.d.ts → harness/system/skill-requirements.d.ts} +1 -1
  79. package/dist/runtime/{skill-requirements.js → harness/system/skill-requirements.js} +1 -1
  80. package/dist/runtime/{thread-memory-sync.d.ts → harness/system/thread-memory-sync.d.ts} +2 -2
  81. package/dist/runtime/{thread-memory-sync.js → harness/system/thread-memory-sync.js} +1 -1
  82. package/dist/runtime/harness.d.ts +2 -7
  83. package/dist/runtime/harness.js +158 -477
  84. package/dist/runtime/index.d.ts +7 -7
  85. package/dist/runtime/index.js +7 -7
  86. package/dist/runtime/maintenance/checkpoint-maintenance.d.ts +45 -0
  87. package/dist/runtime/maintenance/checkpoint-maintenance.js +259 -0
  88. package/dist/runtime/maintenance/file-checkpoint-saver.d.ts +20 -0
  89. package/dist/runtime/maintenance/file-checkpoint-saver.js +106 -0
  90. package/dist/runtime/maintenance/index.d.ts +4 -0
  91. package/dist/runtime/maintenance/index.js +4 -0
  92. package/dist/runtime/{runtime-record-maintenance.d.ts → maintenance/runtime-record-maintenance.d.ts} +1 -1
  93. package/dist/runtime/{runtime-record-maintenance.js → maintenance/runtime-record-maintenance.js} +2 -2
  94. package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.d.ts +9 -0
  95. package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.js +39 -0
  96. package/dist/runtime/parsing/stream-event-parsing.d.ts +6 -0
  97. package/dist/runtime/parsing/stream-event-parsing.js +231 -0
  98. package/dist/runtime/sqlite-maintained-checkpoint-saver.d.ts +1 -9
  99. package/dist/runtime/sqlite-maintained-checkpoint-saver.js +1 -39
  100. package/dist/runtime/support/harness-support.d.ts +4 -4
  101. package/dist/runtime/support/harness-support.js +14 -3
  102. package/dist/runtime/support/runtime-factories.d.ts +1 -1
  103. package/dist/runtime/support/runtime-factories.js +1 -1
  104. package/dist/workspace/agent-binding-compiler.js +39 -3
  105. package/dist/workspace/object-loader.js +5 -1
  106. package/package.json +4 -4
  107. package/dist/runtime/langgraph-presets.js +0 -165
  108. package/dist/runtime/langgraph-profiles.js +0 -206
  109. /package/dist/runtime/{langgraph-presets.d.ts → adapter/langgraph/presets.d.ts} +0 -0
  110. /package/dist/runtime/{declared-middleware.js → adapter/tool/declared-middleware.js} +0 -0
  111. /package/dist/runtime/{tool-hitl.js → adapter/tool/tool-hitl.js} +0 -0
  112. /package/dist/runtime/{event-bus.js → harness/events/event-bus.js} +0 -0
  113. /package/dist/runtime/{store.d.ts → harness/system/store.d.ts} +0 -0
  114. /package/dist/runtime/{store.js → harness/system/store.js} +0 -0
@@ -2,24 +2,30 @@ import { AUTO_AGENT_ID } from "../contracts/types.js";
2
2
  import { SqlitePersistence } from "../persistence/sqlite-store.js";
3
3
  import { createPersistentId } from "../utils/id.js";
4
4
  import { AGENT_INTERRUPT_SENTINEL_PREFIX, AgentRuntimeAdapter, RuntimeOperationTimeoutError } from "./agent-runtime-adapter.js";
5
+ import { normalizeUpstreamRuntimeEvent } from "./parsing/stream-event-parsing.js";
5
6
  import { createResourceBackendResolver, createResourceToolResolver } from "../resource/resource.js";
6
- import { EventBus } from "./event-bus.js";
7
- import { PolicyEngine } from "./policy-engine.js";
7
+ import { EventBus } from "./harness/events/event-bus.js";
8
+ import { PolicyEngine } from "./harness/system/policy-engine.js";
8
9
  import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getRoutingRules, getRoutingSystemPrompt, isModelRoutingEnabled, matchRoutingRule, } from "../workspace/support/workspace-ref-utils.js";
9
- import { createHarnessEvent, createPendingApproval, heuristicRoute, inferRoutingBindings, renderRuntimeFailure, renderToolFailure, } from "./support/harness-support.js";
10
- import { createCheckpointerForConfig, createStoreForConfig } from "./support/runtime-factories.js";
11
- import { resolveCompiledEmbeddingModel, resolveCompiledEmbeddingModelRef } from "./support/embedding-models.js";
12
- import { resolveCompiledVectorStore, resolveCompiledVectorStoreRef } from "./support/vector-stores.js";
13
- import { ThreadMemorySync } from "./thread-memory-sync.js";
14
- import { FileBackedStore } from "./store.js";
10
+ import { createHarnessEvent, inferRoutingBindings, renderRuntimeFailure, renderToolFailure, } from "./support/harness-support.js";
11
+ import { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
12
+ import { FileBackedStore } from "./harness/system/store.js";
15
13
  import { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
16
- import { RuntimeRecordMaintenanceLoop, discoverRuntimeRecordMaintenanceTargets, readRuntimeRecordMaintenanceConfig, } from "./runtime-record-maintenance.js";
17
- import { HealthMonitor } from "./health-monitor.js";
14
+ import { RuntimeRecordMaintenanceLoop, discoverRuntimeRecordMaintenanceTargets, readRuntimeRecordMaintenanceConfig, } from "./maintenance/runtime-record-maintenance.js";
15
+ import { HealthMonitor } from "./harness/system/health-monitor.js";
18
16
  import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
17
+ import { buildPersistedRunRequest, isTerminalRunState, normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, toPublicApprovalRecord, } from "./harness/run/helpers.js";
18
+ import { emitHarnessEvent, emitRunCreatedEvent, emitSyntheticFallbackEvent, persistApproval, requestApprovalAndEmitEvent, setRunStateAndEmitEvent, } from "./harness/events/events.js";
19
+ import { appendAssistantMessage as appendLifecycleAssistantMessage, checkpointRefForState as getCheckpointRefForRunState, expirePendingApprovals as expireLifecyclePendingApprovals, finalizeCancelledRun as finalizeLifecycleCancelledRun, finalizeContinuedRun as finalizeLifecycleContinuedRun, reviewCompletedRun as reviewLifecycleCompletedRun, synthesizeCompletedRun as synthesizeLifecycleCompletedRun, } from "./harness/run/run-lifecycle.js";
20
+ import { createContentBlocksItem as createStreamingContentBlocksItem, createToolResultKey as createStreamingToolResultKey, dispatchRunListeners as dispatchStreamingRunListeners, emitOutputDeltaAndCreateItem as emitStreamingOutputDeltaAndCreateItem, } from "./harness/events/streaming.js";
21
+ import { buildResumePayload as buildHarnessResumePayload, resolveApprovalRecord as resolveHarnessApprovalRecord, } from "./harness/run/resume.js";
22
+ import { dropPendingRunSlot, enqueuePendingRunSlot, shiftNextPendingRunSlot } from "./harness/run/run-queue.js";
23
+ import { buildRoutingInput, getDefaultHostAgentId, heuristicHostRoute, resolveSelectedAgentId } from "./harness/run/routing.js";
24
+ import { resolveCheckpointer, resolveEmbeddingModel, resolveStore, resolveStoreFromConfig, resolveVectorStore, } from "./harness/run/resources.js";
19
25
  import { createToolMcpServerFromTools, serveToolsOverStdioFromHarness } from "../mcp.js";
20
- import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig, isDeepAgentBinding } from "./support/compiled-binding.js";
26
+ import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig } from "./support/compiled-binding.js";
21
27
  import { isRuntimeEntryBinding } from "./support/runtime-entry.js";
22
- import { describeWorkspaceInventory, listAgentSkills as listWorkspaceAgentSkills, } from "./inventory.js";
28
+ import { describeWorkspaceInventory, listAgentSkills as listWorkspaceAgentSkills, } from "./harness/system/inventory.js";
23
29
  export class AgentHarnessRuntime {
24
30
  workspace;
25
31
  runtimeAdapterOptions;
@@ -59,22 +65,6 @@ export class AgentHarnessRuntime {
59
65
  pendingRunInsertionOrder = 0;
60
66
  pendingRunSlots = [];
61
67
  runtimeEventSequence = 0;
62
- toPublicApprovalRecord(approval) {
63
- const { toolCallId: _toolCallId, checkpointRef: _checkpointRef, eventRefs: _eventRefs, ...publicApproval } = approval;
64
- return publicApproval;
65
- }
66
- normalizeInvocationEnvelope(options) {
67
- const invocation = options.invocation;
68
- return {
69
- context: invocation?.context,
70
- state: invocation?.inputs,
71
- files: invocation?.attachments,
72
- invocation,
73
- };
74
- }
75
- isTerminalRunState(state) {
76
- return state === "completed" || state === "failed";
77
- }
78
68
  listHostBindings() {
79
69
  return inferRoutingBindings(this.workspace).hostBindings;
80
70
  }
@@ -83,90 +73,36 @@ export class AgentHarnessRuntime {
83
73
  `${this.workspace.workspaceRoot}/run-data`);
84
74
  }
85
75
  heuristicRoute(input) {
86
- const { primaryBinding, secondaryBinding } = inferRoutingBindings(this.workspace);
87
- return heuristicRoute(extractMessageText(input), primaryBinding, secondaryBinding);
76
+ return heuristicHostRoute(this.workspace, input);
88
77
  }
89
78
  getDefaultHostAgentId() {
90
- const orchestraBinding = this.workspace.bindings.get(AgentHarnessRuntime.DEFAULT_HOST_AGENT_ID);
91
- if (orchestraBinding && isRuntimeEntryBinding(orchestraBinding)) {
92
- return orchestraBinding.agent.id;
93
- }
94
- return this.heuristicRoute("");
79
+ return getDefaultHostAgentId(this.workspace, AgentHarnessRuntime.DEFAULT_HOST_AGENT_ID);
95
80
  }
96
81
  async buildRoutingInput(input, threadId) {
97
- const inputText = extractMessageText(input);
98
- if (!threadId) {
99
- return inputText;
100
- }
101
- const history = await this.persistence.listThreadMessages(threadId);
102
- const priorHistory = history.filter((message) => extractMessageText(message.content).trim());
103
- if (priorHistory.length === 0) {
104
- return inputText;
105
- }
106
- const recentTurns = priorHistory.slice(-6).map((message) => {
107
- const role = message.role === "assistant" ? "assistant" : "user";
108
- const compact = extractMessageText(message.content).replace(/\s+/g, " ").trim();
109
- return `${role}: ${compact.slice(0, 240)}`;
110
- });
111
- return [
112
- "Recent conversation context:",
113
- ...recentTurns,
114
- "",
115
- `Current user request: ${inputText}`,
116
- ].join("\n");
82
+ const history = threadId ? await this.persistence.listThreadMessages(threadId) : [];
83
+ return buildRoutingInput(input, history);
117
84
  }
118
85
  async resolveSelectedAgentId(input, requestedAgentId, threadId) {
119
- if (!requestedAgentId || requestedAgentId === AUTO_AGENT_ID) {
120
- if (threadId) {
121
- const thread = await this.getSession(threadId);
122
- const threadBinding = thread ? this.workspace.bindings.get(thread.agentId) : undefined;
123
- if (thread?.agentId && threadBinding && isRuntimeEntryBinding(threadBinding)) {
124
- return thread.agentId;
125
- }
126
- }
127
- return this.getDefaultHostAgentId();
128
- }
129
- return requestedAgentId;
86
+ return resolveSelectedAgentId({
87
+ workspace: this.workspace,
88
+ input,
89
+ requestedAgentId,
90
+ threadId,
91
+ preferredHostAgentId: AgentHarnessRuntime.DEFAULT_HOST_AGENT_ID,
92
+ getThreadSummary: (currentThreadId) => this.getSession(currentThreadId),
93
+ });
130
94
  }
131
95
  resolveStore(binding) {
132
- const storeConfig = binding ? getBindingStoreConfig(binding) : undefined;
133
- return this.resolveStoreFromConfig(storeConfig, binding?.harnessRuntime.runRoot ?? this.defaultRunRoot()) ?? this.defaultStore;
96
+ return resolveStore(this.stores, this.defaultStore, this.defaultRunRoot(), (currentBinding) => currentBinding ? getBindingStoreConfig(currentBinding) : undefined, binding);
134
97
  }
135
98
  resolveStoreFromConfig(storeConfig, runRoot) {
136
- const cacheKey = storeConfig ? JSON.stringify(storeConfig) : undefined;
137
- if (!storeConfig || !cacheKey) {
138
- return undefined;
139
- }
140
- const existing = this.stores.get(cacheKey);
141
- if (existing) {
142
- return existing;
143
- }
144
- const created = createStoreForConfig(storeConfig, runRoot);
145
- this.stores.set(cacheKey, created);
146
- return created;
99
+ return resolveStoreFromConfig(this.stores, storeConfig, runRoot);
147
100
  }
148
101
  async resolveEmbeddingModel(embeddingModelRef) {
149
- const compiled = resolveCompiledEmbeddingModelRef(this.workspace, embeddingModelRef);
150
- const existing = this.embeddingModels.get(compiled.id);
151
- if (existing) {
152
- return existing;
153
- }
154
- const resolved = await resolveCompiledEmbeddingModel(compiled, this.runtimeAdapterOptions.embeddingModelResolver);
155
- this.embeddingModels.set(compiled.id, resolved);
156
- return resolved;
102
+ return resolveEmbeddingModel(this.workspace, this.embeddingModels, embeddingModelRef, this.runtimeAdapterOptions);
157
103
  }
158
104
  async resolveVectorStore(vectorStoreRef) {
159
- const compiled = resolveCompiledVectorStoreRef(this.workspace, vectorStoreRef);
160
- const existing = this.vectorStores.get(compiled.id);
161
- if (existing) {
162
- return existing;
163
- }
164
- const resolved = await resolveCompiledVectorStore(this.workspace, compiled, {
165
- embeddingModelResolver: this.runtimeAdapterOptions.embeddingModelResolver,
166
- vectorStoreResolver: this.runtimeAdapterOptions.vectorStoreResolver,
167
- });
168
- this.vectorStores.set(compiled.id, resolved);
169
- return resolved;
105
+ return resolveVectorStore(this.workspace, this.vectorStores, vectorStoreRef, this.runtimeAdapterOptions);
170
106
  }
171
107
  constructor(workspace, runtimeAdapterOptions = {}) {
172
108
  this.workspace = workspace;
@@ -189,21 +125,7 @@ export class AgentHarnessRuntime {
189
125
  getVectorStore: (vectorStoreRef) => this.resolveVectorStore(vectorStoreRef),
190
126
  }),
191
127
  checkpointerResolver: runtimeAdapterOptions.checkpointerResolver ??
192
- ((binding) => {
193
- const key = `${binding.harnessRuntime.runRoot}:${JSON.stringify(binding.harnessRuntime.checkpointer ?? { kind: "FileCheckpointer", path: "checkpoints.json" })}`;
194
- const existing = this.checkpointers.get(key);
195
- if (existing) {
196
- return existing;
197
- }
198
- const resolvedConfig = binding.harnessRuntime.checkpointer ?? { kind: "FileCheckpointer", path: "checkpoints.json" };
199
- if (typeof resolvedConfig === "boolean") {
200
- this.checkpointers.set(key, resolvedConfig);
201
- return resolvedConfig;
202
- }
203
- const saver = createCheckpointerForConfig(resolvedConfig, binding.harnessRuntime.runRoot);
204
- this.checkpointers.set(key, saver);
205
- return saver;
206
- }),
128
+ ((binding) => resolveCheckpointer(this.checkpointers, binding)),
207
129
  storeResolver: runtimeAdapterOptions.storeResolver ??
208
130
  ((binding) => this.resolveStore(binding)),
209
131
  backendResolver: runtimeAdapterOptions.backendResolver ??
@@ -327,11 +249,11 @@ export class AgentHarnessRuntime {
327
249
  }
328
250
  async listApprovals(filter) {
329
251
  const approvals = await this.persistence.listApprovals(filter);
330
- return approvals.map((approval) => this.toPublicApprovalRecord(approval));
252
+ return approvals.map((approval) => toPublicApprovalRecord(approval));
331
253
  }
332
254
  async getApproval(approvalId) {
333
255
  const approval = await this.persistence.getApproval(approvalId);
334
- return approval ? this.toPublicApprovalRecord(approval) : null;
256
+ return approval ? toPublicApprovalRecord(approval) : null;
335
257
  }
336
258
  listAgentSkills(agentId, options = {}) {
337
259
  return listWorkspaceAgentSkills(this.workspace, agentId, options);
@@ -362,7 +284,7 @@ export class AgentHarnessRuntime {
362
284
  if (!thread) {
363
285
  return false;
364
286
  }
365
- const activeRun = thread.runs.find((run) => !this.isTerminalRunState(run.state));
287
+ const activeRun = thread.runs.find((run) => !isTerminalRunState(run.state));
366
288
  if (activeRun) {
367
289
  throw new Error(`Cannot delete thread ${threadId} while run ${activeRun.runId} is ${activeRun.state}`);
368
290
  }
@@ -389,7 +311,8 @@ export class AgentHarnessRuntime {
389
311
  return serveToolsOverStdioFromHarness(tools, options);
390
312
  }
391
313
  async routeAgent(input, options = {}) {
392
- const routingInput = await this.buildRoutingInput(input, options.threadId);
314
+ const routingHistory = options.threadId ? await this.persistence.listThreadMessages(options.threadId) : [];
315
+ const routingInput = buildRoutingInput(input, routingHistory);
393
316
  const rawInput = extractMessageText(input);
394
317
  const { primaryBinding, secondaryBinding } = inferRoutingBindings(this.workspace);
395
318
  const configuredRule = this.routingRules.find((rule) => matchRoutingRule(rawInput, rule, options));
@@ -408,7 +331,7 @@ export class AgentHarnessRuntime {
408
331
  }
409
332
  }
410
333
  if (!primaryBinding || !secondaryBinding) {
411
- return heuristicRoute(rawInput, primaryBinding, secondaryBinding);
334
+ return heuristicHostRoute(this.workspace, rawInput);
412
335
  }
413
336
  try {
414
337
  return await this.runtimeAdapter.route(routingInput, primaryBinding, secondaryBinding, {
@@ -416,21 +339,16 @@ export class AgentHarnessRuntime {
416
339
  });
417
340
  }
418
341
  catch {
419
- return heuristicRoute(rawInput, primaryBinding, secondaryBinding);
342
+ return heuristicHostRoute(this.workspace, rawInput);
420
343
  }
421
344
  }
422
345
  async emit(threadId, runId, sequence, eventType, payload, source = "runtime") {
423
- const event = createHarnessEvent(threadId, runId, sequence, eventType, payload, source);
424
- if (AgentHarnessRuntime.BACKGROUND_EVENT_TYPES.has(event.eventType)) {
425
- this.trackBackgroundTask(this.persistence.appendEvent(event).catch(() => {
426
- // Fail open for telemetry-style event persistence.
427
- }));
428
- }
429
- else {
430
- await this.persistence.appendEvent(event);
431
- }
432
- this.eventBus.publish(event);
433
- return event;
346
+ return emitHarnessEvent({
347
+ persistence: this.persistence,
348
+ publishEvent: (event) => this.eventBus.publish(event),
349
+ trackBackgroundTask: (task) => this.trackBackgroundTask(task),
350
+ backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
351
+ }, threadId, runId, sequence, eventType, payload, source);
434
352
  }
435
353
  trackBackgroundTask(task) {
436
354
  this.backgroundTasks.add(task);
@@ -498,15 +416,7 @@ export class AgentHarnessRuntime {
498
416
  return userTurn?.content ?? "";
499
417
  }
500
418
  async appendAssistantMessage(threadId, runId, content) {
501
- if (!content) {
502
- return;
503
- }
504
- await this.persistence.appendThreadMessage(threadId, {
505
- role: "assistant",
506
- content,
507
- runId,
508
- createdAt: new Date().toISOString(),
509
- });
419
+ return appendLifecycleAssistantMessage(this.persistence, threadId, runId, content);
510
420
  }
511
421
  async getRunCancellation(runId) {
512
422
  const control = await this.persistence.getRunControl(runId);
@@ -516,37 +426,17 @@ export class AgentHarnessRuntime {
516
426
  };
517
427
  }
518
428
  async expirePendingApprovals(threadId, runId) {
519
- const approvals = await this.persistence.getRunApprovals(threadId, runId);
520
- for (const approval of approvals) {
521
- if (approval.status !== "pending") {
522
- continue;
523
- }
524
- await this.persistence.resolveApproval(threadId, runId, approval.approvalId, "expired");
525
- await this.emit(threadId, runId, 6, "approval.resolved", {
526
- approvalId: approval.approvalId,
527
- pendingActionId: approval.pendingActionId,
528
- decision: "cancel",
529
- toolName: approval.toolName,
530
- });
531
- }
429
+ return expireLifecyclePendingApprovals({
430
+ persistence: this.persistence,
431
+ emit: (currentThreadId, currentRunId, sequence, eventType, payload, source) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload, source),
432
+ }, threadId, runId);
532
433
  }
533
434
  async finalizeCancelledRun(threadId, runId, previousState, reason) {
534
- await this.expirePendingApprovals(threadId, runId);
535
- await this.persistence.releaseRunClaim(runId);
536
- await this.persistence.clearRunCancel(runId);
537
- await this.persistence.clearRunRequest(threadId, runId);
538
- await this.setRunStateAndEmit(threadId, runId, 104, "cancelled", {
539
- previousState,
540
- ...(reason ? { error: reason } : {}),
541
- });
542
- const runMeta = await this.persistence.getRunMeta(threadId, runId);
543
- return {
544
- threadId,
545
- runId,
546
- agentId: runMeta.agentId,
547
- state: "cancelled",
548
- output: reason ? `cancelled: ${reason}` : "cancelled",
549
- };
435
+ return finalizeLifecycleCancelledRun({
436
+ persistence: this.persistence,
437
+ emit: (currentThreadId, currentRunId, sequence, eventType, payload, source) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload, source),
438
+ setRunStateAndEmit: (currentThreadId, currentRunId, sequence, state, options) => this.setRunStateAndEmit(currentThreadId, currentRunId, sequence, state, options),
439
+ }, threadId, runId, previousState, reason);
550
440
  }
551
441
  async invokeWithHistory(binding, input, threadId, runId, resumePayload, priorHistory, options = {}) {
552
442
  const history = priorHistory ?? await this.loadPriorHistory(threadId, runId);
@@ -561,67 +451,12 @@ export class AgentHarnessRuntime {
561
451
  throw error;
562
452
  }
563
453
  }
564
- buildPersistedRunRequest(input, invocation, priority) {
565
- const envelope = invocation.invocation ?? {
566
- ...(invocation.context ? { context: invocation.context } : {}),
567
- ...(invocation.state ? { inputs: invocation.state } : {}),
568
- ...(invocation.files ? { attachments: invocation.files } : {}),
569
- };
570
- return {
571
- input: normalizeMessageContent(input),
572
- priority: Number.isFinite(priority) ? Math.trunc(priority) : undefined,
573
- invocation: envelope && Object.keys(envelope).length > 0
574
- ? {
575
- ...(envelope.context ? { context: envelope.context } : {}),
576
- ...(envelope.inputs ? { inputs: envelope.inputs } : {}),
577
- ...(envelope.attachments ? { attachments: envelope.attachments } : {}),
578
- ...(envelope.capabilities ? { capabilities: envelope.capabilities } : {}),
579
- }
580
- : undefined,
581
- savedAt: new Date().toISOString(),
582
- };
583
- }
584
- normalizeRunPriority(priority) {
585
- if (!Number.isFinite(priority)) {
586
- return 0;
587
- }
588
- return Math.trunc(priority);
589
- }
590
454
  async resolvePersistedRunPriority(threadId, runId) {
591
455
  const persisted = await this.persistence.getRunRequest(threadId, runId);
592
- return this.normalizeRunPriority(persisted?.priority);
456
+ return normalizeRunPriority(persisted?.priority);
593
457
  }
594
458
  enqueuePendingRunSlot(entry) {
595
- const previousPositions = new Map(this.pendingRunSlots
596
- .filter((candidate) => Boolean(candidate.threadId && candidate.runId))
597
- .map((candidate, index) => [candidate.runId, index + 1]));
598
- const queuedEntry = {
599
- ...entry,
600
- insertionOrder: this.pendingRunInsertionOrder++,
601
- };
602
- this.pendingRunSlots.push(queuedEntry);
603
- this.pendingRunSlots.sort((left, right) => {
604
- if (right.priority !== left.priority) {
605
- return right.priority - left.priority;
606
- }
607
- return left.insertionOrder - right.insertionOrder;
608
- });
609
- return this.pendingRunSlots.flatMap((candidate, index) => {
610
- if (!candidate.threadId || !candidate.runId) {
611
- return [];
612
- }
613
- const previousPosition = previousPositions.get(candidate.runId);
614
- const queuePosition = index + 1;
615
- if (previousPosition === undefined || previousPosition === queuePosition) {
616
- return [];
617
- }
618
- return [{
619
- threadId: candidate.threadId,
620
- runId: candidate.runId,
621
- priority: candidate.priority,
622
- queuePosition,
623
- }];
624
- });
459
+ return enqueuePendingRunSlot(this.pendingRunSlots, entry, this.pendingRunInsertionOrder++);
625
460
  }
626
461
  async executeQueuedRun(binding, input, threadId, runId, agentId, options = {}) {
627
462
  const previousState = options.previousState ?? "running";
@@ -689,164 +524,80 @@ export class AgentHarnessRuntime {
689
524
  }
690
525
  }
691
526
  checkpointRefForState(threadId, runId, state) {
692
- return state === "waiting_for_approval" ? `checkpoints/${threadId}/${runId}/cp-1` : null;
527
+ return getCheckpointRefForRunState(threadId, runId, state);
693
528
  }
694
529
  async finalizeContinuedRun(binding, threadId, runId, input, actual, options) {
695
- let approval;
696
- const finalizedActual = actual.state === "completed"
697
- ? await this.synthesizeCompletedRun(binding, input, actual)
698
- : actual;
699
- await this.appendAssistantMessage(threadId, runId, finalizedActual.output);
700
- const checkpointRef = this.checkpointRefForState(threadId, runId, actual.state);
701
- await this.setRunStateAndEmit(threadId, runId, options.stateSequence, finalizedActual.state, {
702
- previousState: options.previousState,
703
- checkpointRef,
704
- });
705
- if (finalizedActual.state === "waiting_for_approval" && options.approvalSequence) {
706
- approval = (await this.requestApprovalAndEmit(threadId, runId, input, finalizedActual.interruptContent, checkpointRef, options.approvalSequence)).approval;
707
- }
708
- if (finalizedActual.state === "completed") {
709
- await this.reviewCompletedRun(binding, threadId, runId, input, finalizedActual);
710
- }
711
- return {
712
- ...finalizedActual,
713
- threadId,
714
- runId,
715
- approvalId: approval?.approvalId ?? finalizedActual.approvalId,
716
- pendingActionId: approval?.pendingActionId ?? finalizedActual.pendingActionId,
717
- };
530
+ return finalizeLifecycleContinuedRun({
531
+ persistence: this.persistence,
532
+ emit: (currentThreadId, currentRunId, sequence, eventType, payload, source) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload, source),
533
+ setRunStateAndEmit: (currentThreadId, currentRunId, sequence, state, lifecycleOptions) => this.setRunStateAndEmit(currentThreadId, currentRunId, sequence, state, lifecycleOptions),
534
+ requestApprovalAndEmit: (currentThreadId, currentRunId, lifecycleInput, interruptContent, checkpointRef, sequence) => this.requestApprovalAndEmit(currentThreadId, currentRunId, lifecycleInput, interruptContent, checkpointRef, sequence),
535
+ synthesizeFinalResult: (currentBinding, lifecycleInput, lifecycleActual) => this.runtimeAdapter.synthesizeFinalResult(currentBinding, lifecycleInput, lifecycleActual),
536
+ reviewRunResult: (currentBinding, lifecycleInput, lifecycleActual) => this.runtimeAdapter.reviewRunResult(currentBinding, lifecycleInput, lifecycleActual),
537
+ }, binding, threadId, runId, input, actual, options);
718
538
  }
719
539
  async synthesizeCompletedRun(binding, input, actual) {
720
- try {
721
- const synthesized = await this.runtimeAdapter.synthesizeFinalResult(binding, input, actual);
722
- if (!synthesized) {
723
- return actual;
724
- }
725
- return {
726
- ...actual,
727
- output: synthesized.output,
728
- finalMessageText: synthesized.finalMessageText,
729
- metadata: {
730
- ...(typeof actual.metadata === "object" && actual.metadata ? actual.metadata : {}),
731
- finalSynthesis: {
732
- modelId: synthesized.modelId,
733
- },
734
- },
735
- };
736
- }
737
- catch {
738
- return actual;
739
- }
540
+ return synthesizeLifecycleCompletedRun({
541
+ synthesizeFinalResult: (currentBinding, lifecycleInput, lifecycleActual) => this.runtimeAdapter.synthesizeFinalResult(currentBinding, lifecycleInput, lifecycleActual),
542
+ }, binding, input, actual);
740
543
  }
741
544
  async reviewCompletedRun(binding, threadId, runId, input, actual) {
742
- try {
743
- const review = await this.runtimeAdapter.reviewRunResult(binding, input, actual);
744
- if (!review) {
745
- return;
746
- }
747
- await this.emit(threadId, runId, 7, "run.reviewed", {
748
- modelId: review.modelId,
749
- assessment: review.assessment,
750
- });
751
- }
752
- catch {
753
- // Review is advisory; do not fail the completed run if the review pass fails.
754
- }
545
+ return reviewLifecycleCompletedRun({
546
+ reviewRunResult: (currentBinding, lifecycleInput, lifecycleActual) => this.runtimeAdapter.reviewRunResult(currentBinding, lifecycleInput, lifecycleActual),
547
+ emit: (currentThreadId, currentRunId, sequence, eventType, payload, source) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload, source),
548
+ }, binding, threadId, runId, input, actual);
755
549
  }
756
550
  async emitOutputDeltaAndCreateItem(threadId, runId, agentId, content) {
757
- await this.emit(threadId, runId, 3, "output.delta", {
758
- content,
759
- });
760
- return {
761
- type: "content",
762
- threadId,
763
- runId,
764
- agentId,
765
- content,
766
- };
551
+ return emitStreamingOutputDeltaAndCreateItem((currentThreadId, currentRunId, sequence, eventType, payload) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload), threadId, runId, agentId, content);
767
552
  }
768
553
  createContentBlocksItem(threadId, runId, agentId, contentBlocks) {
769
- return {
770
- type: "content-blocks",
771
- threadId,
772
- runId,
773
- agentId,
774
- contentBlocks,
775
- };
554
+ return createStreamingContentBlocksItem(threadId, runId, agentId, contentBlocks);
776
555
  }
777
556
  createToolResultKey(toolName, output, isError) {
778
- let serializedOutput = "";
779
- try {
780
- serializedOutput = JSON.stringify(output);
781
- }
782
- catch {
783
- serializedOutput = String(output);
784
- }
785
- return JSON.stringify([toolName, serializedOutput, isError === true]);
557
+ return createStreamingToolResultKey(toolName, output, isError);
786
558
  }
787
559
  async emitRunCreated(threadId, runId, payload) {
788
- return this.emit(threadId, runId, 1, "run.created", payload);
560
+ return emitRunCreatedEvent({
561
+ persistence: this.persistence,
562
+ publishEvent: (event) => this.eventBus.publish(event),
563
+ trackBackgroundTask: (task) => this.trackBackgroundTask(task),
564
+ backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
565
+ }, threadId, runId, payload);
789
566
  }
790
567
  async setRunStateAndEmit(threadId, runId, sequence, state, options) {
791
- await this.persistence.setRunState(threadId, runId, state, options.checkpointRef ?? null);
792
- return this.emit(threadId, runId, sequence, "run.state.changed", {
793
- previousState: options.previousState,
794
- state,
795
- checkpointRef: options.checkpointRef ?? null,
796
- ...(options.error ? { error: options.error } : {}),
797
- });
568
+ return setRunStateAndEmitEvent({
569
+ persistence: this.persistence,
570
+ publishEvent: (event) => this.eventBus.publish(event),
571
+ trackBackgroundTask: (task) => this.trackBackgroundTask(task),
572
+ backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
573
+ }, threadId, runId, sequence, state, options);
798
574
  }
799
575
  async requestApprovalAndEmit(threadId, runId, input, interruptContent, checkpointRef, sequence) {
800
- const approval = await this.persistApproval(threadId, runId, checkpointRef, input, interruptContent);
801
- const event = await this.emit(threadId, runId, sequence, "approval.requested", {
802
- approvalId: approval.approvalId,
803
- pendingActionId: approval.pendingActionId,
804
- toolName: approval.toolName,
805
- toolCallId: approval.toolCallId,
806
- allowedDecisions: approval.allowedDecisions,
807
- checkpointRef,
808
- });
809
- return { approval, event };
576
+ return requestApprovalAndEmitEvent({
577
+ persistence: this.persistence,
578
+ publishEvent: (event) => this.eventBus.publish(event),
579
+ trackBackgroundTask: (task) => this.trackBackgroundTask(task),
580
+ backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
581
+ }, threadId, runId, input, interruptContent, checkpointRef, sequence);
810
582
  }
811
583
  async emitSyntheticFallback(threadId, runId, selectedAgentId, error, sequence = 3) {
812
- await this.emit(threadId, runId, sequence, "runtime.synthetic_fallback", {
813
- reason: error instanceof Error ? error.message : String(error),
814
- selectedAgentId,
815
- });
584
+ await emitSyntheticFallbackEvent({
585
+ persistence: this.persistence,
586
+ publishEvent: (event) => this.eventBus.publish(event),
587
+ trackBackgroundTask: (task) => this.trackBackgroundTask(task),
588
+ backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
589
+ }, threadId, runId, selectedAgentId, error, sequence);
816
590
  }
817
591
  async persistApproval(threadId, runId, checkpointRef, input, interruptContent) {
818
- const approval = createPendingApproval(threadId, runId, checkpointRef, extractMessageText(input), interruptContent);
819
- await this.persistence.createApproval(approval);
820
- const artifact = await this.persistence.createArtifact(threadId, runId, {
821
- artifactId: `artifact-approval-${runId}`,
822
- kind: "approval-packet",
823
- path: `artifacts/approval-${runId}.json`,
824
- createdAt: approval.requestedAt,
825
- }, approval);
826
- await this.emit(threadId, runId, 5, "artifact.created", {
827
- artifactId: artifact.artifactId,
828
- kind: artifact.kind,
829
- path: artifact.path,
830
- });
831
- return approval;
592
+ return persistApproval({
593
+ persistence: this.persistence,
594
+ publishEvent: (event) => this.eventBus.publish(event),
595
+ trackBackgroundTask: (task) => this.trackBackgroundTask(task),
596
+ backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
597
+ }, threadId, runId, checkpointRef, input, interruptContent);
832
598
  }
833
599
  async resolveApprovalRecord(options, thread) {
834
- if (options.approvalId) {
835
- const approval = await this.persistence.getApproval(options.approvalId);
836
- if (!approval) {
837
- throw new Error(`Unknown approval ${options.approvalId}`);
838
- }
839
- return approval;
840
- }
841
- const runId = options.runId ?? thread.latestRunId;
842
- const approvals = await this.persistence.getRunApprovals(options.threadId ?? thread.threadId, runId);
843
- const approval = approvals
844
- .filter((candidate) => candidate.status === "pending")
845
- .sort((left, right) => right.requestedAt.localeCompare(left.requestedAt))[0];
846
- if (!approval) {
847
- throw new Error(`No pending approval for run ${runId}`);
848
- }
849
- return approval;
600
+ return resolveHarnessApprovalRecord(this.persistence, options, thread);
850
601
  }
851
602
  isDecisionRun(options) {
852
603
  return "decision" in options;
@@ -926,7 +677,7 @@ export class AgentHarnessRuntime {
926
677
  released = true;
927
678
  await releaseLease();
928
679
  this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
929
- const next = this.pendingRunSlots.shift();
680
+ const next = shiftNextPendingRunSlot(this.pendingRunSlots);
930
681
  void next?.activate();
931
682
  };
932
683
  }
@@ -1008,81 +759,18 @@ export class AgentHarnessRuntime {
1008
759
  released = true;
1009
760
  await releaseLease();
1010
761
  this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
1011
- const next = this.pendingRunSlots.shift();
762
+ const next = shiftNextPendingRunSlot(this.pendingRunSlots);
1012
763
  void next?.activate();
1013
764
  };
1014
765
  }
1015
766
  dropPendingRunSlot(runId) {
1016
- const index = this.pendingRunSlots.findIndex((entry) => entry.runId === runId);
1017
- if (index < 0) {
1018
- return false;
1019
- }
1020
- const [entry] = this.pendingRunSlots.splice(index, 1);
1021
- entry?.abort();
1022
- return true;
767
+ return dropPendingRunSlot(this.pendingRunSlots, runId);
1023
768
  }
1024
769
  async dispatchRunListeners(stream, listeners) {
1025
- let latestEvent;
1026
- let latestResult;
1027
- let output = "";
1028
- for await (const item of stream) {
1029
- if (item.type === "event") {
1030
- latestEvent = item.event;
1031
- await this.notifyListener(listeners.onEvent, item.event);
1032
- continue;
1033
- }
1034
- if (item.type === "result") {
1035
- latestResult = item.result;
1036
- continue;
1037
- }
1038
- if (item.type === "content") {
1039
- output += item.content;
1040
- await this.notifyListener(listeners.onChunk, item.content);
1041
- continue;
1042
- }
1043
- if (item.type === "content-blocks") {
1044
- await this.notifyListener(listeners.onContentBlocks, item.contentBlocks);
1045
- continue;
1046
- }
1047
- if (item.type === "reasoning") {
1048
- await this.notifyListener(listeners.onReasoning, item.content);
1049
- continue;
1050
- }
1051
- if (item.type === "step") {
1052
- await this.notifyListener(listeners.onStep, item.content);
1053
- continue;
1054
- }
1055
- if (item.type === "tool-result") {
1056
- await this.notifyListener(listeners.onToolResult, {
1057
- toolName: item.toolName,
1058
- output: item.output,
1059
- isError: item.isError,
1060
- });
1061
- }
1062
- }
1063
- if (!latestEvent) {
1064
- throw new Error("run did not emit any events");
1065
- }
1066
- if (latestResult) {
1067
- return {
1068
- ...latestResult,
1069
- output: latestResult.output || output,
1070
- finalMessageText: latestResult.finalMessageText ?? latestResult.output ?? output,
1071
- };
1072
- }
1073
- const thread = await this.getThread(latestEvent.threadId);
1074
- if (!thread) {
1075
- throw new Error(`Unknown thread ${latestEvent.threadId}`);
1076
- }
1077
- return {
1078
- threadId: thread.threadId,
1079
- runId: thread.latestRunId,
1080
- agentId: thread.runs[0]?.agentId ?? thread.entryAgentId,
1081
- state: thread.currentState,
1082
- output,
1083
- approvalId: thread.pendingDecision?.approvalId,
1084
- pendingActionId: thread.pendingDecision?.pendingActionId,
1085
- };
770
+ return dispatchStreamingRunListeners(stream, listeners, {
771
+ notifyListener: (listener, value) => this.notifyListener(listener, value),
772
+ getThread: (threadId) => this.getThread(threadId),
773
+ });
1086
774
  }
1087
775
  async run(options) {
1088
776
  if (this.isDecisionRun(options)) {
@@ -1095,11 +783,19 @@ export class AgentHarnessRuntime {
1095
783
  };
1096
784
  return this.resume(resumeOptions);
1097
785
  }
1098
- if (options.listeners) {
1099
- return this.dispatchRunListeners(this.streamEvents(options), options.listeners);
786
+ const resolvedListeners = resolveRunListeners(options);
787
+ if (resolvedListeners) {
788
+ return this.dispatchRunListeners(this.streamEvents(options), resolvedListeners);
1100
789
  }
1101
- const invocation = this.normalizeInvocationEnvelope(options);
1102
- const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
790
+ const invocation = normalizeInvocationEnvelope(options);
791
+ const selectedAgentId = await resolveSelectedAgentId({
792
+ workspace: this.workspace,
793
+ input: options.input,
794
+ requestedAgentId: options.agentId,
795
+ threadId: options.threadId,
796
+ preferredHostAgentId: AgentHarnessRuntime.DEFAULT_HOST_AGENT_ID,
797
+ getThreadSummary: (threadId) => this.getSession(threadId),
798
+ });
1103
799
  const binding = this.workspace.bindings.get(selectedAgentId);
1104
800
  if (!binding) {
1105
801
  throw new Error(`Unknown agent ${selectedAgentId}`);
@@ -1108,8 +804,8 @@ export class AgentHarnessRuntime {
1108
804
  if (!policyDecision.allowed) {
1109
805
  throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
1110
806
  }
1111
- const priority = this.normalizeRunPriority(options.priority);
1112
- const runRequest = this.buildPersistedRunRequest(options.input, invocation, priority);
807
+ const priority = normalizeRunPriority(options.priority);
808
+ const runRequest = buildPersistedRunRequest(options.input, invocation, priority);
1113
809
  const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, runRequest, options.threadId);
1114
810
  const runCreatedEventPromise = this.emitRunCreated(threadId, runId, {
1115
811
  agentId: binding.agent.id,
@@ -1136,8 +832,15 @@ export class AgentHarnessRuntime {
1136
832
  }
1137
833
  }
1138
834
  async *streamEvents(options) {
1139
- const invocation = this.normalizeInvocationEnvelope(options);
1140
- const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
835
+ const invocation = normalizeInvocationEnvelope(options);
836
+ const selectedAgentId = await resolveSelectedAgentId({
837
+ workspace: this.workspace,
838
+ input: options.input,
839
+ requestedAgentId: options.agentId,
840
+ threadId: options.threadId,
841
+ preferredHostAgentId: AgentHarnessRuntime.DEFAULT_HOST_AGENT_ID,
842
+ getThreadSummary: (threadId) => this.getSession(threadId),
843
+ });
1141
844
  const binding = this.workspace.bindings.get(selectedAgentId);
1142
845
  if (!binding) {
1143
846
  const result = await this.run(options);
@@ -1154,8 +857,8 @@ export class AgentHarnessRuntime {
1154
857
  }
1155
858
  let emitted = false;
1156
859
  let streamActivityObserved = false;
1157
- const priority = this.normalizeRunPriority(options.priority);
1158
- const runRequest = this.buildPersistedRunRequest(options.input, invocation, priority);
860
+ const priority = normalizeRunPriority(options.priority);
861
+ const runRequest = buildPersistedRunRequest(options.input, invocation, priority);
1159
862
  const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, runRequest, options.threadId);
1160
863
  const priorHistoryPromise = Promise.resolve(isNewThread ? [] : undefined).then((historyHint) => historyHint ?? this.loadPriorHistory(threadId, runId));
1161
864
  const runCreatedEventPromise = this.emitRunCreated(threadId, runId, {
@@ -1191,6 +894,18 @@ export class AgentHarnessRuntime {
1191
894
  ? { kind: "interrupt", content: chunk.slice(AGENT_INTERRUPT_SENTINEL_PREFIX.length) }
1192
895
  : { kind: "content", content: chunk }
1193
896
  : chunk;
897
+ if (normalizedChunk.kind === "upstream-event") {
898
+ yield {
899
+ type: "upstream-event",
900
+ threadId,
901
+ runId,
902
+ agentId: selectedAgentId,
903
+ event: normalizedChunk.event.format === "langgraph-v2"
904
+ ? normalizedChunk.event
905
+ : normalizeUpstreamRuntimeEvent(normalizedChunk.event.raw ?? normalizedChunk.event),
906
+ };
907
+ continue;
908
+ }
1194
909
  if (normalizedChunk.kind === "interrupt") {
1195
910
  const checkpointRef = `checkpoints/${threadId}/${runId}/cp-1`;
1196
911
  const waitingEvent = await this.setRunStateAndEmit(threadId, runId, 6, "waiting_for_approval", {
@@ -1490,41 +1205,7 @@ export class AgentHarnessRuntime {
1490
1205
  }
1491
1206
  }
1492
1207
  buildResumePayload(binding, approval, options) {
1493
- if (!isDeepAgentBinding(binding)) {
1494
- return options.decision === "edit" && options.editedInput
1495
- ? { decision: "edit", editedInput: options.editedInput }
1496
- : (options.decision ?? "approve");
1497
- }
1498
- const decisionType = options.decision ?? "approve";
1499
- if (decisionType === "edit" && options.editedInput) {
1500
- return {
1501
- decisions: [
1502
- {
1503
- type: "edit",
1504
- editedAction: {
1505
- name: approval.toolName,
1506
- args: options.editedInput,
1507
- },
1508
- },
1509
- ],
1510
- };
1511
- }
1512
- if (decisionType === "reject") {
1513
- return {
1514
- decisions: [
1515
- {
1516
- type: "reject",
1517
- },
1518
- ],
1519
- };
1520
- }
1521
- return {
1522
- decisions: [
1523
- {
1524
- type: "approve",
1525
- },
1526
- ],
1527
- };
1208
+ return buildHarnessResumePayload(binding, approval, options);
1528
1209
  }
1529
1210
  async restartConversation(options) {
1530
1211
  const thread = await this.getSession(options.threadId);
@@ -1567,7 +1248,7 @@ export class AgentHarnessRuntime {
1567
1248
  if (!run) {
1568
1249
  throw new Error(`Unknown run ${options.runId}`);
1569
1250
  }
1570
- if (this.isTerminalRunState(run.state)) {
1251
+ if (isTerminalRunState(run.state)) {
1571
1252
  return {
1572
1253
  threadId: run.threadId,
1573
1254
  runId: run.runId,
@@ -1616,7 +1297,7 @@ export class AgentHarnessRuntime {
1616
1297
  });
1617
1298
  continue;
1618
1299
  }
1619
- const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running", this.normalizeRunPriority(request.priority));
1300
+ const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running", normalizeRunPriority(request.priority));
1620
1301
  try {
1621
1302
  await this.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, runMeta.agentId, {
1622
1303
  context: request.invocation?.context,
@@ -1659,7 +1340,7 @@ export class AgentHarnessRuntime {
1659
1340
  await this.persistence.releaseRunClaim(thread.latestRunId);
1660
1341
  continue;
1661
1342
  }
1662
- const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running", this.normalizeRunPriority(request.priority));
1343
+ const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running", normalizeRunPriority(request.priority));
1663
1344
  try {
1664
1345
  await this.emit(thread.threadId, thread.latestRunId, 100, "run.resumed", {
1665
1346
  resumeKind: "startup-running-recovery",