@botbotgo/agent-harness 0.0.107 → 0.0.109

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 (42) hide show
  1. package/dist/package-version.d.ts +1 -1
  2. package/dist/package-version.js +1 -1
  3. package/dist/runtime/adapter/execution-context.d.ts +34 -0
  4. package/dist/runtime/adapter/execution-context.js +52 -0
  5. package/dist/runtime/adapter/middleware-assembly.js +29 -14
  6. package/dist/runtime/adapter/runnable-config.d.ts +57 -0
  7. package/dist/runtime/adapter/runnable-config.js +64 -0
  8. package/dist/runtime/adapter/runtime-adapter-support.d.ts +0 -1
  9. package/dist/runtime/adapter/runtime-adapter-support.js +5 -8
  10. package/dist/runtime/agent-runtime-adapter.js +28 -34
  11. package/dist/runtime/harness/background-runtime.d.ts +13 -0
  12. package/dist/runtime/harness/background-runtime.js +8 -0
  13. package/dist/runtime/harness/bindings.d.ts +14 -0
  14. package/dist/runtime/harness/bindings.js +23 -0
  15. package/dist/runtime/harness/events/listener-runtime.d.ts +18 -0
  16. package/dist/runtime/harness/events/listener-runtime.js +9 -0
  17. package/dist/runtime/harness/events/runtime-event-operations.d.ts +17 -0
  18. package/dist/runtime/harness/events/runtime-event-operations.js +9 -0
  19. package/dist/runtime/harness/run/recovery.d.ts +1 -1
  20. package/dist/runtime/harness/run/resume-runtime.d.ts +55 -0
  21. package/dist/runtime/harness/run/resume-runtime.js +26 -0
  22. package/dist/runtime/harness/run/routing.d.ts +8 -0
  23. package/dist/runtime/harness/run/routing.js +21 -0
  24. package/dist/runtime/harness/run/run-operations.d.ts +47 -0
  25. package/dist/runtime/harness/run/run-operations.js +67 -0
  26. package/dist/runtime/harness/run/start-run.d.ts +82 -0
  27. package/dist/runtime/harness/run/start-run.js +88 -0
  28. package/dist/runtime/harness/run/startup-runtime.d.ts +2 -1
  29. package/dist/runtime/harness/run/startup-runtime.js +38 -3
  30. package/dist/runtime/harness/run/stream-runtime.d.ts +48 -0
  31. package/dist/runtime/harness/run/stream-runtime.js +14 -0
  32. package/dist/runtime/harness.d.ts +5 -6
  33. package/dist/runtime/harness.js +186 -299
  34. package/dist/runtime/support/compiled-binding.d.ts +14 -0
  35. package/dist/runtime/support/compiled-binding.js +14 -10
  36. package/dist/runtime/support/runtime-adapter-options.d.ts +17 -0
  37. package/dist/runtime/support/runtime-adapter-options.js +29 -0
  38. package/package.json +1 -1
  39. package/dist/runtime/adapter/deepagent-runnable-config.d.ts +0 -30
  40. package/dist/runtime/adapter/deepagent-runnable-config.js +0 -22
  41. package/dist/runtime/adapter/langchain-runnable-config.d.ts +0 -25
  42. package/dist/runtime/adapter/langchain-runnable-config.js +0 -19
@@ -1,32 +1,35 @@
1
- import { AUTO_AGENT_ID } from "../contracts/types.js";
2
1
  import { SqlitePersistence } from "../persistence/sqlite-store.js";
3
2
  import { createPersistentId } from "../utils/id.js";
4
3
  import { AgentRuntimeAdapter } from "./agent-runtime-adapter.js";
5
- import { createResourceBackendResolver, createResourceToolResolver } from "../resource/resource.js";
6
4
  import { EventBus } from "./harness/events/event-bus.js";
5
+ import { createBackgroundEventRuntime } from "./harness/background-runtime.js";
7
6
  import { PolicyEngine } from "./harness/system/policy-engine.js";
8
- import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getRoutingRules, matchRoutingRule, } from "../workspace/support/workspace-ref-utils.js";
7
+ import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getRoutingRules, } from "../workspace/support/workspace-ref-utils.js";
9
8
  import { createHarnessEvent, inferRoutingBindings, renderRuntimeFailure, } from "./support/harness-support.js";
10
9
  import { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
11
10
  import { FileBackedStore } from "./harness/system/store.js";
12
11
  import { HealthMonitor, readHealthMonitorConfig, } from "./harness/system/health-monitor.js";
13
- import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
14
- import { buildPersistedRunRequest, normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, } from "./harness/run/helpers.js";
15
- import { emitHarnessEvent, emitRunCreatedEvent, emitSyntheticFallbackEvent, requestApprovalAndEmitEvent, setRunStateAndEmitEvent, } from "./harness/events/events.js";
16
- import { appendAssistantMessage as appendLifecycleAssistantMessage, finalizeCancelledRun as finalizeLifecycleCancelledRun, finalizeContinuedRun as finalizeLifecycleContinuedRun, } from "./harness/run/run-lifecycle.js";
17
- import { dispatchRunListeners as dispatchStreamingRunListeners, } from "./harness/events/streaming.js";
18
- import { buildResumePayload as buildHarnessResumePayload, resolveApprovalRecord as resolveHarnessApprovalRecord, } from "./harness/run/resume.js";
19
- import { cancelRunOperation, resumeRun } from "./harness/run/run-operations.js";
12
+ import { normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, } from "./harness/run/helpers.js";
13
+ import { emitHarnessEvent, } from "./harness/events/events.js";
14
+ import { createRuntimeEventOperations } from "./harness/events/runtime-event-operations.js";
15
+ import { finalizeCancelledRun as finalizeLifecycleCancelledRun, finalizeContinuedRun as finalizeLifecycleContinuedRun, } from "./harness/run/run-lifecycle.js";
16
+ import { createListenerDispatchRuntime } from "./harness/events/listener-runtime.js";
17
+ import { cancelRunOperation, executeQueuedRunOperation, resumeRun } from "./harness/run/run-operations.js";
18
+ import { createResumeRunRuntime } from "./harness/run/resume-runtime.js";
20
19
  import { acquireRunSlot as acquireHarnessRunSlot } from "./harness/run/run-slot-acquisition.js";
21
20
  import { dropPendingRunSlot, enqueuePendingRunSlot } from "./harness/run/run-queue.js";
22
- import { getDefaultHostAgentId, resolveSelectedAgentId } from "./harness/run/routing.js";
23
- import { resolveCheckpointer, resolveEmbeddingModel, resolveStore, resolveStoreFromConfig, resolveVectorStore, } from "./harness/run/resources.js";
21
+ import { getDefaultHostAgentId, resolveSelectedAgentId, routeAgentId } from "./harness/run/routing.js";
22
+ import { resolveStoreFromConfig, } from "./harness/run/resources.js";
24
23
  import { createToolMcpServerFromTools, serveToolsOverStdioFromHarness } from "../mcp.js";
25
- import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig } from "./support/compiled-binding.js";
24
+ import { getBindingAdapterKind } from "./support/compiled-binding.js";
25
+ import { bindingSupportsRunningReplay, getWorkspaceBinding, resolveWorkspaceAgentTools, } from "./harness/bindings.js";
26
26
  import { describeWorkspaceInventory, listAgentSkills as listWorkspaceAgentSkills, } from "./harness/system/inventory.js";
27
27
  import { createDefaultHealthSnapshot, isInventoryEnabled, isThreadMemorySyncEnabled, } from "./harness/runtime-defaults.js";
28
- import { initializeHarnessRuntime, isStaleRunningRun as isHarnessStaleRunningRun, } from "./harness/run/startup-runtime.js";
28
+ import { resolveRuntimeAdapterOptions } from "./support/runtime-adapter-options.js";
29
+ import { initializeHarnessRuntime, reclaimExpiredClaimedRuns as reclaimHarnessExpiredClaimedRuns, recoverStartupRuns as recoverHarnessStartupRuns, isStaleRunningRun as isHarnessStaleRunningRun, } from "./harness/run/startup-runtime.js";
29
30
  import { streamHarnessRun } from "./harness/run/stream-run.js";
31
+ import { createStreamRunRuntime } from "./harness/run/stream-runtime.js";
32
+ import { defaultRequestedAgentId, prepareRunStart } from "./harness/run/start-run.js";
30
33
  import { deleteThreadRecord, getPublicApproval, getThreadRecord, listPublicApprovals, } from "./harness/run/thread-records.js";
31
34
  export class AgentHarnessRuntime {
32
35
  workspace;
@@ -65,6 +68,10 @@ export class AgentHarnessRuntime {
65
68
  pendingRunInsertionOrder = 0;
66
69
  pendingRunSlots = [];
67
70
  runtimeEventSequence = 0;
71
+ initialized = false;
72
+ closed = false;
73
+ backgroundEventRuntime;
74
+ runtimeEventOperations;
68
75
  defaultRunRoot() {
69
76
  return this.defaultRunRootValue;
70
77
  }
@@ -96,22 +103,24 @@ export class AgentHarnessRuntime {
96
103
  ? this.defaultHostBinding.harnessRuntime.runtimeMemory.store
97
104
  : undefined;
98
105
  this.runtimeMemoryStore = resolveStoreFromConfig(this.stores, runtimeMemoryStoreConfig, runRoot) ?? this.defaultStore;
99
- this.resolvedRuntimeAdapterOptions = {
100
- ...runtimeAdapterOptions,
101
- toolResolver: runtimeAdapterOptions.toolResolver ??
102
- createResourceToolResolver(workspace, {
103
- getStore: (binding) => resolveStore(this.stores, this.defaultStore, this.defaultRunRoot(), (currentBinding) => currentBinding ? getBindingStoreConfig(currentBinding) : undefined, binding),
104
- getEmbeddingModel: (embeddingModelRef) => resolveEmbeddingModel(this.workspace, this.embeddingModels, embeddingModelRef, this.runtimeAdapterOptions),
105
- getVectorStore: (vectorStoreRef) => resolveVectorStore(this.workspace, this.vectorStores, vectorStoreRef, this.runtimeAdapterOptions),
106
- }),
107
- checkpointerResolver: runtimeAdapterOptions.checkpointerResolver ??
108
- ((binding) => resolveCheckpointer(this.checkpointers, binding)),
109
- storeResolver: runtimeAdapterOptions.storeResolver ??
110
- ((binding) => resolveStore(this.stores, this.defaultStore, this.defaultRunRoot(), (currentBinding) => currentBinding ? getBindingStoreConfig(currentBinding) : undefined, binding)),
111
- backendResolver: runtimeAdapterOptions.backendResolver ??
112
- ((binding) => createResourceBackendResolver(workspace)(binding)),
113
- };
106
+ this.resolvedRuntimeAdapterOptions = resolveRuntimeAdapterOptions({
107
+ workspace,
108
+ runtimeAdapterOptions,
109
+ checkpointers: this.checkpointers,
110
+ stores: this.stores,
111
+ defaultStore: this.defaultStore,
112
+ embeddingModels: this.embeddingModels,
113
+ vectorStores: this.vectorStores,
114
+ getDefaultRunRoot: () => this.defaultRunRoot(),
115
+ });
114
116
  this.runtimeAdapter = new AgentRuntimeAdapter(this.resolvedRuntimeAdapterOptions);
117
+ this.backgroundEventRuntime = createBackgroundEventRuntime({
118
+ persistence: this.persistence,
119
+ publishEvent: (event) => this.eventBus.publish(event),
120
+ trackBackgroundTask: (task) => this.trackBackgroundTask(task),
121
+ backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
122
+ });
123
+ this.runtimeEventOperations = createRuntimeEventOperations(this.backgroundEventRuntime);
115
124
  this.routingRules = getRoutingRules(workspace.refs);
116
125
  this.routingDefaultAgentId = getRoutingDefaultAgentId(workspace.refs);
117
126
  if (isThreadMemorySyncEnabled(workspace)) {
@@ -147,10 +156,18 @@ export class AgentHarnessRuntime {
147
156
  this.healthMonitor?.recordLlmFailure(Date.now() - startedAt);
148
157
  }
149
158
  async initialize() {
159
+ if (this.closed) {
160
+ throw new Error("AgentHarnessRuntime has been closed and cannot be reinitialized");
161
+ }
162
+ if (this.initialized) {
163
+ return;
164
+ }
150
165
  await initializeHarnessRuntime({
151
166
  persistence: this.persistence,
152
167
  healthMonitor: this.healthMonitor,
153
168
  });
169
+ await this.recoverStartupRuns();
170
+ this.initialized = true;
154
171
  }
155
172
  subscribe(listener) {
156
173
  return this.eventBus.subscribe(listener);
@@ -161,23 +178,6 @@ export class AgentHarnessRuntime {
161
178
  }
162
179
  return createDefaultHealthSnapshot(this.activeRunSlots, this.pendingRunSlots.length);
163
180
  }
164
- resolveAgentTools(agentId) {
165
- const binding = this.workspace.bindings.get(agentId);
166
- if (!binding) {
167
- throw new Error(`Unknown agent ${agentId}`);
168
- }
169
- const compiledTools = getBindingPrimaryTools(binding);
170
- const resolver = this.resolvedRuntimeAdapterOptions.toolResolver;
171
- const resolvedTools = resolver ? resolver(compiledTools.map((tool) => tool.id), binding) : [];
172
- return compiledTools.map((compiledTool, index) => ({
173
- compiledTool,
174
- resolvedTool: resolvedTools[index],
175
- }));
176
- }
177
- supportsRunningReplay(binding) {
178
- const tools = getBindingPrimaryTools(binding);
179
- return tools.every((tool) => tool.retryable === true);
180
- }
181
181
  async listThreads(filter) {
182
182
  return this.persistence.listSessions(filter);
183
183
  }
@@ -244,37 +244,21 @@ export class AgentHarnessRuntime {
244
244
  }, threadId);
245
245
  }
246
246
  async createToolMcpServer(options) {
247
- const tools = this.resolveAgentTools(options.agentId).map(({ compiledTool, resolvedTool }) => ({
248
- compiledTool,
249
- resolvedTool,
250
- sourceTool: this.workspace.tools.get(compiledTool.id),
251
- }));
247
+ const tools = this.resolveToolMcpServerTools(options.agentId);
252
248
  return createToolMcpServerFromTools(tools, options);
253
249
  }
254
250
  async serveToolsOverStdio(options) {
255
- const tools = this.resolveAgentTools(options.agentId).map(({ compiledTool, resolvedTool }) => ({
256
- compiledTool,
257
- resolvedTool,
258
- sourceTool: this.workspace.tools.get(compiledTool.id),
259
- }));
251
+ const tools = this.resolveToolMcpServerTools(options.agentId);
260
252
  return serveToolsOverStdioFromHarness(tools, options);
261
253
  }
262
254
  async routeAgent(input, options = {}) {
263
- const rawInput = extractMessageText(input);
264
- const configuredRule = this.routingRules.find((rule) => matchRoutingRule(rawInput, rule, options));
265
- if (configuredRule) {
266
- const configuredBinding = this.workspace.bindings.get(configuredRule.agentId);
267
- if (configuredBinding) {
268
- return configuredBinding.agent.id;
269
- }
270
- }
271
- const defaultBinding = this.routingDefaultAgentId
272
- ? this.workspace.bindings.get(this.routingDefaultAgentId)
273
- : undefined;
274
- if (defaultBinding) {
275
- return defaultBinding.agent.id;
276
- }
277
- return this.getDefaultHostAgentId();
255
+ return routeAgentId({
256
+ workspace: this.workspace,
257
+ input,
258
+ routingRules: this.routingRules,
259
+ routingDefaultAgentId: this.routingDefaultAgentId,
260
+ threadId: options.threadId,
261
+ });
278
262
  }
279
263
  async emit(threadId, runId, sequence, eventType, payload, source = "runtime") {
280
264
  return emitHarnessEvent({
@@ -290,55 +274,16 @@ export class AgentHarnessRuntime {
290
274
  this.backgroundTasks.delete(task);
291
275
  });
292
276
  }
293
- async ensureThreadStarted(selectedAgentId, binding, input, runRequest, existingThreadId) {
294
- const threadId = existingThreadId ?? createPersistentId();
295
- const runId = createPersistentId();
296
- const createdAt = new Date().toISOString();
297
- const isNewThread = !existingThreadId;
298
- const userMessage = {
299
- role: "user",
300
- content: normalizeMessageContent(input),
301
- runId,
302
- createdAt,
303
- };
304
- if (typeof this.persistence.bootstrapRun === "function") {
305
- await this.persistence.bootstrapRun({
306
- threadId,
307
- agentId: binding.agent.id,
308
- runId,
309
- status: "running",
310
- createdAt,
311
- executionMode: getBindingAdapterKind(binding),
312
- adapterKind: getBindingAdapterKind(binding),
313
- userMessage,
314
- runRequest,
315
- createThread: isNewThread,
316
- });
317
- }
318
- else {
319
- if (isNewThread) {
320
- await this.persistence.createThread({
321
- threadId,
322
- agentId: selectedAgentId,
323
- runId,
324
- status: "running",
325
- createdAt,
326
- });
327
- }
328
- await Promise.all([
329
- this.persistence.appendThreadMessage(threadId, userMessage),
330
- this.persistence.createRun({
331
- threadId,
332
- runId,
333
- agentId: binding.agent.id,
334
- executionMode: getBindingAdapterKind(binding),
335
- adapterKind: getBindingAdapterKind(binding),
336
- createdAt,
337
- }),
338
- this.persistence.saveRunRequest(threadId, runId, runRequest),
339
- ]);
340
- }
341
- return { threadId, runId, createdAt, isNewThread };
277
+ resolveToolMcpServerTools(agentId) {
278
+ return resolveWorkspaceAgentTools({
279
+ workspace: this.workspace,
280
+ agentId,
281
+ toolResolver: this.resolvedRuntimeAdapterOptions.toolResolver,
282
+ }).map(({ compiledTool, resolvedTool }) => ({
283
+ compiledTool,
284
+ resolvedTool,
285
+ sourceTool: this.workspace.tools.get(compiledTool.id),
286
+ }));
342
287
  }
343
288
  async loadPriorHistory(threadId, runId) {
344
289
  const history = await this.persistence.listThreadMessages(threadId);
@@ -384,74 +329,33 @@ export class AgentHarnessRuntime {
384
329
  return enqueuePendingRunSlot(this.pendingRunSlots, entry, this.pendingRunInsertionOrder++);
385
330
  }
386
331
  async executeQueuedRun(binding, input, threadId, runId, agentId, options = {}) {
387
- const previousState = options.previousState ?? "running";
388
- const currentRun = await this.persistence.getRun(runId);
389
- if (currentRun?.state === "cancelled") {
390
- return {
391
- threadId,
392
- runId,
393
- agentId,
394
- state: "cancelled",
395
- output: "cancelled",
396
- };
397
- }
398
- const cancellation = await this.getRunCancellation(runId);
399
- if (cancellation.requested) {
400
- return this.finalizeCancelledRun(threadId, runId, previousState, cancellation.reason);
401
- }
402
- if (previousState === "queued") {
403
- await this.emit(threadId, runId, 101, "run.dequeued", {
404
- queuePosition: 0,
405
- activeRunCount: this.activeRunSlots,
406
- maxConcurrentRuns: this.concurrencyConfig.maxConcurrentRuns,
407
- recoveredOnStartup: true,
408
- });
409
- await this.setRunStateAndEmit(threadId, runId, 102, "running", {
410
- previousState: "queued",
411
- });
412
- }
413
- try {
414
- const actual = await this.invokeWithHistory(binding, input, threadId, runId, undefined, options.priorHistory, {
415
- context: options.context,
416
- state: options.state,
417
- files: options.files,
418
- });
419
- const cancelledAfterInvoke = await this.getRunCancellation(runId);
420
- if (cancelledAfterInvoke.requested) {
421
- return this.finalizeCancelledRun(threadId, runId, previousState === "queued" ? "running" : previousState, cancelledAfterInvoke.reason);
422
- }
423
- const finalized = await this.finalizeContinuedRun(binding, threadId, runId, input, actual, {
424
- previousState: previousState === "queued" ? "running" : previousState,
425
- stateSequence: options.stateSequence ?? 103,
426
- approvalSequence: options.approvalSequence ?? 104,
427
- });
428
- return {
429
- ...finalized,
430
- agentId,
431
- };
432
- }
433
- catch (error) {
434
- await emitSyntheticFallbackEvent({
435
- persistence: this.persistence,
436
- publishEvent: (event) => this.eventBus.publish(event),
437
- trackBackgroundTask: (task) => this.trackBackgroundTask(task),
438
- backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
439
- }, threadId, runId, agentId, error, 103);
440
- await this.setRunStateAndEmit(threadId, runId, 104, "failed", {
441
- previousState: previousState === "queued" ? "running" : previousState,
442
- error: error instanceof Error ? error.message : String(error),
443
- });
444
- return {
445
- threadId,
446
- runId,
447
- agentId,
448
- state: "failed",
449
- output: renderRuntimeFailure(error),
450
- };
451
- }
452
- finally {
453
- await this.persistence.clearRunRequest(threadId, runId);
454
- }
332
+ return executeQueuedRunOperation({
333
+ persistence: this.persistence,
334
+ getRunCancellation: (currentRunId) => this.getRunCancellation(currentRunId),
335
+ finalizeCancelledRun: (currentThreadId, currentRunId, previousRunState, reason) => this.finalizeCancelledRun(currentThreadId, currentRunId, previousRunState, reason),
336
+ emit: (currentThreadId, currentRunId, sequence, eventType, payload) => {
337
+ if (eventType === "run.dequeued") {
338
+ return this.emit(currentThreadId, currentRunId, sequence, eventType, {
339
+ ...payload,
340
+ activeRunCount: this.activeRunSlots,
341
+ maxConcurrentRuns: this.concurrencyConfig.maxConcurrentRuns,
342
+ });
343
+ }
344
+ return this.emit(currentThreadId, currentRunId, sequence, eventType, payload);
345
+ },
346
+ setRunStateAndEmit: (currentThreadId, currentRunId, sequence, state, stateOptions) => this.setRunStateAndEmit(currentThreadId, currentRunId, sequence, state, stateOptions),
347
+ invokeWithHistory: (activeBinding, activeInput, currentThreadId, currentRunId, resumePayload, priorHistory, invokeOptions) => this.invokeWithHistory(activeBinding, activeInput, currentThreadId, currentRunId, resumePayload, priorHistory, invokeOptions),
348
+ finalizeContinuedRun: (activeBinding, currentThreadId, currentRunId, currentInput, actual, finalizeOptions) => this.finalizeContinuedRun(activeBinding, currentThreadId, currentRunId, currentInput, actual, finalizeOptions),
349
+ emitSyntheticFallback: (currentThreadId, currentRunId, currentAgentId, error) => this.runtimeEventOperations.emitSyntheticFallback(currentThreadId, currentRunId, currentAgentId, error, 103),
350
+ renderRuntimeFailure,
351
+ }, {
352
+ binding,
353
+ message: input,
354
+ threadId,
355
+ runId,
356
+ agentId,
357
+ options,
358
+ });
455
359
  }
456
360
  async finalizeContinuedRun(binding, threadId, runId, input, actual, options) {
457
361
  return finalizeLifecycleContinuedRun({
@@ -462,20 +366,10 @@ export class AgentHarnessRuntime {
462
366
  }, binding, threadId, runId, input, actual, options);
463
367
  }
464
368
  async setRunStateAndEmit(threadId, runId, sequence, state, options) {
465
- return setRunStateAndEmitEvent({
466
- persistence: this.persistence,
467
- publishEvent: (event) => this.eventBus.publish(event),
468
- trackBackgroundTask: (task) => this.trackBackgroundTask(task),
469
- backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
470
- }, threadId, runId, sequence, state, options);
369
+ return this.runtimeEventOperations.setRunStateAndEmit(threadId, runId, sequence, state, options);
471
370
  }
472
371
  async requestApprovalAndEmit(threadId, runId, input, interruptContent, checkpointRef, sequence) {
473
- return requestApprovalAndEmitEvent({
474
- persistence: this.persistence,
475
- publishEvent: (event) => this.eventBus.publish(event),
476
- trackBackgroundTask: (task) => this.trackBackgroundTask(task),
477
- backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
478
- }, threadId, runId, input, interruptContent, checkpointRef, sequence);
372
+ return this.runtimeEventOperations.requestApprovalAndEmit(threadId, runId, input, interruptContent, checkpointRef, sequence);
479
373
  }
480
374
  isDecisionRun(options) {
481
375
  return "decision" in options;
@@ -486,55 +380,6 @@ export class AgentHarnessRuntime {
486
380
  }
487
381
  await listener(value);
488
382
  }
489
- async prepareRunStart(options, invocation, runCreatedPayload) {
490
- const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
491
- const binding = this.workspace.bindings.get(selectedAgentId);
492
- if (!binding) {
493
- throw new Error(`Unknown agent ${selectedAgentId}`);
494
- }
495
- const policyDecision = this.policyEngine.evaluate(binding);
496
- if (!policyDecision.allowed) {
497
- throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
498
- }
499
- const priority = normalizeRunPriority(options.priority);
500
- const runRequest = buildPersistedRunRequest(options.input, invocation, priority);
501
- const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, runRequest, options.threadId);
502
- return {
503
- binding,
504
- selectedAgentId,
505
- priority,
506
- threadId,
507
- runId,
508
- isNewThread,
509
- runCreatedEventPromise: emitRunCreatedEvent({
510
- persistence: this.persistence,
511
- publishEvent: (event) => this.eventBus.publish(event),
512
- trackBackgroundTask: (task) => this.trackBackgroundTask(task),
513
- backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
514
- }, threadId, runId, runCreatedPayload(binding, selectedAgentId)),
515
- releaseRunSlotPromise: this.acquireRunSlot(threadId, runId, "running", priority),
516
- };
517
- }
518
- createStartupRecoveryContext() {
519
- return {
520
- persistence: this.persistence,
521
- workspace: this.workspace,
522
- runtimeAdapter: this.runtimeAdapter,
523
- recoveryConfig: this.recoveryConfig,
524
- concurrencyConfig: this.concurrencyConfig,
525
- getBinding: (agentId) => this.workspace.bindings.get(agentId),
526
- acquireRunSlot: (threadId, runId, activeState = "running", priority = 0) => this.acquireRunSlot(threadId, runId, activeState, priority),
527
- executeQueuedRun: (binding, input, threadId, runId, agentId, options = {}) => this.executeQueuedRun(binding, input, threadId, runId, agentId, options),
528
- setRunStateAndEmit: (threadId, runId, sequence, state, options) => this.setRunStateAndEmit(threadId, runId, sequence, state, options),
529
- emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
530
- loadRunInput: (threadId, runId) => this.loadRunInput(threadId, runId),
531
- finalizeContinuedRun: (binding, threadId, runId, input, actual, options) => this.finalizeContinuedRun(binding, threadId, runId, input, actual, options),
532
- supportsRunningReplay: (binding) => this.supportsRunningReplay(binding),
533
- isStaleRunningRun: (thread, nowMs = Date.now()) => this.isStaleRunningRun(thread, nowMs),
534
- recordLlmSuccess: (startedAt) => this.recordLlmSuccess(startedAt),
535
- recordLlmFailure: (startedAt) => this.recordLlmFailure(startedAt),
536
- };
537
- }
538
383
  async acquireRunSlot(threadId, runId, activeState = "running", priority = 0) {
539
384
  return acquireHarnessRunSlot({
540
385
  persistence: this.persistence,
@@ -558,12 +403,6 @@ export class AgentHarnessRuntime {
558
403
  dropPendingRunSlot(runId) {
559
404
  return dropPendingRunSlot(this.pendingRunSlots, runId);
560
405
  }
561
- async dispatchRunListeners(stream, listeners) {
562
- return dispatchStreamingRunListeners(stream, listeners, {
563
- notifyListener: (listener, value) => this.notifyListener(listener, value),
564
- getThread: (threadId) => this.getThread(threadId),
565
- });
566
- }
567
406
  async run(options) {
568
407
  if (this.isDecisionRun(options)) {
569
408
  const resumeOptions = {
@@ -577,15 +416,29 @@ export class AgentHarnessRuntime {
577
416
  }
578
417
  const resolvedListeners = resolveRunListeners(options);
579
418
  if (resolvedListeners) {
580
- return this.dispatchRunListeners(this.streamEvents(options), resolvedListeners);
419
+ return createListenerDispatchRuntime({
420
+ notifyListener: (listener, value) => this.notifyListener(listener, value),
421
+ getThread: (threadId) => this.getThread(threadId),
422
+ }).dispatchRunListeners(this.streamEvents(options), resolvedListeners);
581
423
  }
582
424
  const invocation = normalizeInvocationEnvelope(options);
583
- const { binding, selectedAgentId, threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await this.prepareRunStart(options, invocation, (activeBinding, activeSelectedAgentId) => ({
584
- agentId: activeBinding.agent.id,
585
- requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
586
- selectedAgentId: activeSelectedAgentId,
587
- executionMode: getBindingAdapterKind(activeBinding),
588
- }));
425
+ const { binding, selectedAgentId, threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await prepareRunStart({
426
+ workspace: this.workspace,
427
+ policyEngine: this.policyEngine,
428
+ persistence: this.persistence,
429
+ resolveSelectedAgentId: (input, requestedAgentId, threadId) => this.resolveSelectedAgentId(input, requestedAgentId, threadId),
430
+ emitRunCreated: (threadId, runId, payload) => this.runtimeEventOperations.emitRunCreated(threadId, runId, payload),
431
+ acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
432
+ }, {
433
+ options,
434
+ invocation,
435
+ runCreatedPayload: (activeBinding, activeSelectedAgentId) => ({
436
+ agentId: activeBinding.agent.id,
437
+ requestedAgentId: defaultRequestedAgentId(options.agentId),
438
+ selectedAgentId: activeSelectedAgentId,
439
+ executionMode: getBindingAdapterKind(activeBinding),
440
+ }),
441
+ });
589
442
  await runCreatedEventPromise;
590
443
  const releaseRunSlot = await releaseRunSlotPromise;
591
444
  try {
@@ -606,7 +459,7 @@ export class AgentHarnessRuntime {
606
459
  async *streamEvents(options) {
607
460
  const invocation = normalizeInvocationEnvelope(options);
608
461
  const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
609
- const binding = this.workspace.bindings.get(selectedAgentId);
462
+ const binding = getWorkspaceBinding(this.workspace, selectedAgentId);
610
463
  if (!binding) {
611
464
  const result = await this.run(options);
612
465
  for (const line of result.output.split("\n")) {
@@ -620,13 +473,34 @@ export class AgentHarnessRuntime {
620
473
  }
621
474
  return;
622
475
  }
623
- const { threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await this.prepareRunStart(options, invocation, (_binding, activeSelectedAgentId) => ({
624
- agentId: activeSelectedAgentId,
625
- requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
626
- selectedAgentId: activeSelectedAgentId,
627
- input: options.input,
628
- state: "running",
629
- }));
476
+ const { threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await prepareRunStart({
477
+ workspace: this.workspace,
478
+ policyEngine: this.policyEngine,
479
+ persistence: this.persistence,
480
+ resolveSelectedAgentId: (input, requestedAgentId, threadId) => this.resolveSelectedAgentId(input, requestedAgentId, threadId),
481
+ emitRunCreated: (threadId, runId, payload) => this.runtimeEventOperations.emitRunCreated(threadId, runId, payload),
482
+ acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
483
+ }, {
484
+ options,
485
+ invocation,
486
+ runCreatedPayload: (_binding, activeSelectedAgentId) => ({
487
+ agentId: activeSelectedAgentId,
488
+ requestedAgentId: defaultRequestedAgentId(options.agentId),
489
+ selectedAgentId: activeSelectedAgentId,
490
+ input: options.input,
491
+ state: "running",
492
+ }),
493
+ });
494
+ const streamRuntime = createStreamRunRuntime({
495
+ persistence: this.persistence,
496
+ runtimeAdapter: this.runtimeAdapter,
497
+ emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
498
+ setRunStateAndEmit: (threadId, runId, sequence, state, stateOptions) => this.setRunStateAndEmit(threadId, runId, sequence, state, stateOptions),
499
+ requestApprovalAndEmit: (threadId, runId, input, interruptContent, checkpointRef, sequence) => this.requestApprovalAndEmit(threadId, runId, input, interruptContent, checkpointRef, sequence),
500
+ loadPriorHistory: (threadId, runId) => this.loadPriorHistory(threadId, runId),
501
+ invokeWithHistory: (binding, input, threadId, runId) => this.invokeWithHistory(binding, input, threadId, runId),
502
+ emitSyntheticFallback: (threadId, runId, selectedAgentId, error) => this.runtimeEventOperations.emitSyntheticFallback(threadId, runId, selectedAgentId, error),
503
+ });
630
504
  yield* streamHarnessRun({
631
505
  binding,
632
506
  input: options.input,
@@ -637,45 +511,25 @@ export class AgentHarnessRuntime {
637
511
  isNewThread,
638
512
  runCreatedEventPromise,
639
513
  releaseRunSlotPromise,
640
- loadPriorHistory: (currentThreadId, currentRunId) => this.loadPriorHistory(currentThreadId, currentRunId),
641
- stream: (activeBinding, input, currentThreadId, priorHistory, streamOptions) => this.runtimeAdapter.stream(activeBinding, input, currentThreadId, priorHistory, streamOptions),
642
- invokeWithHistory: (activeBinding, input, currentThreadId, currentRunId) => this.invokeWithHistory(activeBinding, input, currentThreadId, currentRunId),
643
- emit: (currentThreadId, currentRunId, sequence, eventType, payload) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload),
644
- setRunStateAndEmit: (currentThreadId, currentRunId, sequence, state, stateOptions) => this.setRunStateAndEmit(currentThreadId, currentRunId, sequence, state, stateOptions),
645
- requestApprovalAndEmit: (currentThreadId, currentRunId, input, interruptContent, checkpointRef, sequence) => this.requestApprovalAndEmit(currentThreadId, currentRunId, input, interruptContent, checkpointRef, sequence),
646
- appendAssistantMessage: (currentThreadId, currentRunId, content) => appendLifecycleAssistantMessage(this.persistence, currentThreadId, currentRunId, content),
647
- clearRunRequest: (currentThreadId, currentRunId) => this.persistence.clearRunRequest(currentThreadId, currentRunId),
648
- emitSyntheticFallback: (currentThreadId, currentRunId, currentSelectedAgentId, error) => emitSyntheticFallbackEvent({
649
- persistence: this.persistence,
650
- publishEvent: (event) => this.eventBus.publish(event),
651
- trackBackgroundTask: (task) => this.trackBackgroundTask(task),
652
- backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
653
- }, currentThreadId, currentRunId, currentSelectedAgentId, error),
514
+ ...streamRuntime,
654
515
  });
655
516
  }
656
517
  async resume(options) {
657
- return resumeRun({
658
- getApprovalById: (approvalId) => this.persistence.getApproval(approvalId),
518
+ return resumeRun(createResumeRunRuntime({
519
+ persistence: this.persistence,
520
+ workspace: this.workspace,
521
+ runtimeAdapter: this.runtimeAdapter,
659
522
  getSession: (threadId) => this.getSession(threadId),
660
- resolveApprovalRecord: (resumeOptions, thread) => resolveHarnessApprovalRecord(this.persistence, resumeOptions, thread),
661
- getBinding: (agentId) => this.workspace.bindings.get(agentId),
662
- buildResumePayload: (binding, approval, resumeOptions) => buildHarnessResumePayload(binding, approval, resumeOptions),
663
523
  getRunCancellation: (runId) => this.getRunCancellation(runId),
664
524
  finalizeCancelledRun: (threadId, runId, previousState, reason) => this.finalizeCancelledRun(threadId, runId, previousState, reason),
665
- setRunState: (threadId, runId, state, checkpointRef) => this.persistence.setRunState(threadId, runId, state, checkpointRef),
666
525
  acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
667
526
  resolvePersistedRunPriority: (threadId, runId) => this.resolvePersistedRunPriority(threadId, runId),
668
- saveRecoveryIntent: (threadId, runId, payload) => this.persistence.saveRecoveryIntent(threadId, runId, payload),
669
527
  emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
670
- resolveApproval: (threadId, runId, approvalId, resolution) => this.persistence.resolveApproval(threadId, runId, approvalId, resolution),
671
- listThreadMessages: (threadId) => this.persistence.listThreadMessages(threadId),
672
528
  loadRunInput: (threadId, runId) => this.loadRunInput(threadId, runId),
673
- invoke: (binding, input, threadId, runId, resumePayload, priorHistory) => this.runtimeAdapter.invoke(binding, input, threadId, runId, resumePayload, priorHistory),
674
529
  recordLlmSuccess: (startedAt) => this.recordLlmSuccess(startedAt),
675
530
  recordLlmFailure: (startedAt) => this.recordLlmFailure(startedAt),
676
- clearRecoveryIntent: (threadId, runId) => this.persistence.clearRecoveryIntent(threadId, runId),
677
531
  finalizeContinuedRun: (binding, threadId, runId, input, actual, operationOptions) => this.finalizeContinuedRun(binding, threadId, runId, input, actual, operationOptions),
678
- }, options);
532
+ }), options);
679
533
  }
680
534
  async restartConversation(options) {
681
535
  const thread = await this.getSession(options.threadId);
@@ -703,10 +557,15 @@ export class AgentHarnessRuntime {
703
557
  };
704
558
  }
705
559
  async close() {
560
+ if (this.closed) {
561
+ return;
562
+ }
563
+ this.closed = true;
706
564
  await this.healthMonitor?.stop();
707
565
  this.unregisterThreadMemorySync();
708
566
  await Promise.allSettled(Array.from(this.backgroundTasks));
709
567
  await this.threadMemorySync?.close();
568
+ this.initialized = false;
710
569
  }
711
570
  async stop() {
712
571
  await this.close();
@@ -721,10 +580,38 @@ export class AgentHarnessRuntime {
721
580
  }, options);
722
581
  }
723
582
  async recoverStartupRuns() {
724
- return;
583
+ await recoverHarnessStartupRuns({
584
+ recoveryConfig: this.recoveryConfig,
585
+ persistence: this.persistence,
586
+ createStartupRecoveryContext: () => ({
587
+ persistence: this.persistence,
588
+ workspace: this.workspace,
589
+ runtimeAdapter: this.runtimeAdapter,
590
+ recoveryConfig: this.recoveryConfig,
591
+ concurrencyConfig: this.concurrencyConfig,
592
+ getBinding: (agentId) => getWorkspaceBinding(this.workspace, agentId),
593
+ acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
594
+ executeQueuedRun: (binding, input, threadId, runId, agentId, options) => this.executeQueuedRun(binding, input, threadId, runId, agentId, options),
595
+ setRunStateAndEmit: (threadId, runId, sequence, state, options) => this.setRunStateAndEmit(threadId, runId, sequence, state, options),
596
+ emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
597
+ loadRunInput: (threadId, runId) => this.loadRunInput(threadId, runId),
598
+ finalizeContinuedRun: (binding, threadId, runId, input, actual, options) => this.finalizeContinuedRun(binding, threadId, runId, input, actual, options),
599
+ supportsRunningReplay: (binding) => bindingSupportsRunningReplay(binding),
600
+ isStaleRunningRun: (thread, nowMs) => this.isStaleRunningRun(thread, nowMs),
601
+ recordLlmSuccess: (startedAt) => this.recordLlmSuccess(startedAt),
602
+ recordLlmFailure: (startedAt) => this.recordLlmFailure(startedAt),
603
+ }),
604
+ reclaimExpiredClaimedRuns: (nowIso) => this.reclaimExpiredClaimedRuns(nowIso),
605
+ });
725
606
  }
726
- async reclaimExpiredClaimedRuns(_nowIso = new Date().toISOString()) {
727
- return;
607
+ async reclaimExpiredClaimedRuns(nowIso = new Date().toISOString()) {
608
+ await reclaimHarnessExpiredClaimedRuns({
609
+ persistence: this.persistence,
610
+ setRunStateAndEmit: (threadId, runId, sequence, state, options) => this.setRunStateAndEmit(threadId, runId, sequence, state, options),
611
+ emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
612
+ concurrencyConfig: this.concurrencyConfig,
613
+ getActiveRunSlots: () => this.activeRunSlots,
614
+ }, nowIso);
728
615
  }
729
616
  async isStaleRunningRun(thread, nowMs = Date.now()) {
730
617
  return isHarnessStaleRunningRun({