@botbotgo/agent-harness 0.0.96 → 0.0.98

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