@botbotgo/agent-harness 0.0.100 → 0.0.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resource/resource-impl.js +78 -76
- package/dist/runtime/agent-runtime-adapter.d.ts +1 -12
- package/dist/runtime/agent-runtime-adapter.js +33 -148
- package/dist/runtime/harness/run/recovery.d.ts +42 -0
- package/dist/runtime/harness/run/recovery.js +139 -0
- package/dist/runtime/harness.d.ts +2 -17
- package/dist/runtime/harness.js +306 -473
- package/dist/runtime/support/runtime-factories.js +2 -2
- package/dist/workspace/object-loader.js +40 -78
- package/package.json +1 -1
- package/dist/runtime/checkpoint-maintenance.d.ts +0 -1
- package/dist/runtime/checkpoint-maintenance.js +0 -1
- package/dist/runtime/file-checkpoint-saver.d.ts +0 -1
- package/dist/runtime/file-checkpoint-saver.js +0 -1
- package/dist/runtime/sqlite-maintained-checkpoint-saver.d.ts +0 -1
- package/dist/runtime/sqlite-maintained-checkpoint-saver.js +0 -1
package/dist/runtime/harness.js
CHANGED
|
@@ -10,14 +10,14 @@ import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getR
|
|
|
10
10
|
import { createHarnessEvent, inferRoutingBindings, renderRuntimeFailure, renderToolFailure, } from "./support/harness-support.js";
|
|
11
11
|
import { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
|
|
12
12
|
import { FileBackedStore } from "./harness/system/store.js";
|
|
13
|
-
import { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
|
|
13
|
+
import { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, readCheckpointMaintenanceConfig, } from "./maintenance/checkpoint-maintenance.js";
|
|
14
14
|
import { RuntimeRecordMaintenanceLoop, discoverRuntimeRecordMaintenanceTargets, readRuntimeRecordMaintenanceConfig, } from "./maintenance/runtime-record-maintenance.js";
|
|
15
15
|
import { HealthMonitor } from "./harness/system/health-monitor.js";
|
|
16
16
|
import { readHealthMonitorConfig } from "./harness/system/health-monitor.js";
|
|
17
17
|
import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
|
|
18
18
|
import { buildPersistedRunRequest, isTerminalRunState, normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, toPublicApprovalRecord, } from "./harness/run/helpers.js";
|
|
19
|
-
import { emitHarnessEvent, emitRunCreatedEvent, emitSyntheticFallbackEvent,
|
|
20
|
-
import { appendAssistantMessage as appendLifecycleAssistantMessage,
|
|
19
|
+
import { emitHarnessEvent, emitRunCreatedEvent, emitSyntheticFallbackEvent, requestApprovalAndEmitEvent, setRunStateAndEmitEvent, } from "./harness/events/events.js";
|
|
20
|
+
import { appendAssistantMessage as appendLifecycleAssistantMessage, finalizeCancelledRun as finalizeLifecycleCancelledRun, finalizeContinuedRun as finalizeLifecycleContinuedRun, } from "./harness/run/run-lifecycle.js";
|
|
21
21
|
import { createContentBlocksItem as createStreamingContentBlocksItem, createToolResultKey as createStreamingToolResultKey, dispatchRunListeners as dispatchStreamingRunListeners, emitOutputDeltaAndCreateItem as emitStreamingOutputDeltaAndCreateItem, } from "./harness/events/streaming.js";
|
|
22
22
|
import { buildResumePayload as buildHarnessResumePayload, resolveApprovalRecord as resolveHarnessApprovalRecord, } from "./harness/run/resume.js";
|
|
23
23
|
import { dropPendingRunSlot, enqueuePendingRunSlot, shiftNextPendingRunSlot } from "./harness/run/run-queue.js";
|
|
@@ -28,6 +28,7 @@ import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig }
|
|
|
28
28
|
import { isRuntimeEntryBinding } from "./support/runtime-entry.js";
|
|
29
29
|
import { describeWorkspaceInventory, listAgentSkills as listWorkspaceAgentSkills, } from "./harness/system/inventory.js";
|
|
30
30
|
import { createDefaultHealthSnapshot, isInventoryEnabled, isThreadMemorySyncEnabled, } from "./harness/runtime-defaults.js";
|
|
31
|
+
import { recoverQueuedStartupRun, recoverResumingStartupRun, recoverRunningStartupRun, } from "./harness/run/recovery.js";
|
|
31
32
|
export class AgentHarnessRuntime {
|
|
32
33
|
workspace;
|
|
33
34
|
runtimeAdapterOptions;
|
|
@@ -85,42 +86,30 @@ export class AgentHarnessRuntime {
|
|
|
85
86
|
getThreadSummary: (currentThreadId) => this.getSession(currentThreadId),
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
|
-
resolveStore(binding) {
|
|
89
|
-
return resolveStore(this.stores, this.defaultStore, this.defaultRunRoot(), (currentBinding) => currentBinding ? getBindingStoreConfig(currentBinding) : undefined, binding);
|
|
90
|
-
}
|
|
91
|
-
resolveStoreFromConfig(storeConfig, runRoot) {
|
|
92
|
-
return resolveStoreFromConfig(this.stores, storeConfig, runRoot);
|
|
93
|
-
}
|
|
94
|
-
async resolveEmbeddingModel(embeddingModelRef) {
|
|
95
|
-
return resolveEmbeddingModel(this.workspace, this.embeddingModels, embeddingModelRef, this.runtimeAdapterOptions);
|
|
96
|
-
}
|
|
97
|
-
async resolveVectorStore(vectorStoreRef) {
|
|
98
|
-
return resolveVectorStore(this.workspace, this.vectorStores, vectorStoreRef, this.runtimeAdapterOptions);
|
|
99
|
-
}
|
|
100
89
|
constructor(workspace, runtimeAdapterOptions = {}) {
|
|
101
90
|
this.workspace = workspace;
|
|
102
91
|
this.runtimeAdapterOptions = runtimeAdapterOptions;
|
|
103
92
|
const runRoot = this.defaultRunRoot();
|
|
104
93
|
this.persistence = new SqlitePersistence(runRoot);
|
|
105
94
|
const defaultStoreConfig = this.listHostBindings()[0]?.harnessRuntime.store;
|
|
106
|
-
this.defaultStore = this.
|
|
95
|
+
this.defaultStore = resolveStoreFromConfig(this.stores, defaultStoreConfig, runRoot) ?? new FileBackedStore(`${runRoot}/store.json`);
|
|
107
96
|
const runtimeMemoryStoreConfig = typeof this.listHostBindings()[0]?.harnessRuntime.runtimeMemory?.store === "object" &&
|
|
108
97
|
this.listHostBindings()[0]?.harnessRuntime.runtimeMemory?.store
|
|
109
98
|
? this.listHostBindings()[0]?.harnessRuntime.runtimeMemory?.store
|
|
110
99
|
: undefined;
|
|
111
|
-
this.runtimeMemoryStore = this.
|
|
100
|
+
this.runtimeMemoryStore = resolveStoreFromConfig(this.stores, runtimeMemoryStoreConfig, runRoot) ?? this.defaultStore;
|
|
112
101
|
this.resolvedRuntimeAdapterOptions = {
|
|
113
102
|
...runtimeAdapterOptions,
|
|
114
103
|
toolResolver: runtimeAdapterOptions.toolResolver ??
|
|
115
104
|
createResourceToolResolver(workspace, {
|
|
116
|
-
getStore: (binding) => this.
|
|
117
|
-
getEmbeddingModel: (embeddingModelRef) => this.
|
|
118
|
-
getVectorStore: (vectorStoreRef) => this.
|
|
105
|
+
getStore: (binding) => resolveStore(this.stores, this.defaultStore, this.defaultRunRoot(), (currentBinding) => currentBinding ? getBindingStoreConfig(currentBinding) : undefined, binding),
|
|
106
|
+
getEmbeddingModel: (embeddingModelRef) => resolveEmbeddingModel(this.workspace, this.embeddingModels, embeddingModelRef, this.runtimeAdapterOptions),
|
|
107
|
+
getVectorStore: (vectorStoreRef) => resolveVectorStore(this.workspace, this.vectorStores, vectorStoreRef, this.runtimeAdapterOptions),
|
|
119
108
|
}),
|
|
120
109
|
checkpointerResolver: runtimeAdapterOptions.checkpointerResolver ??
|
|
121
110
|
((binding) => resolveCheckpointer(this.checkpointers, binding)),
|
|
122
111
|
storeResolver: runtimeAdapterOptions.storeResolver ??
|
|
123
|
-
((binding) => this.
|
|
112
|
+
((binding) => resolveStore(this.stores, this.defaultStore, this.defaultRunRoot(), (currentBinding) => currentBinding ? getBindingStoreConfig(currentBinding) : undefined, binding)),
|
|
124
113
|
backendResolver: runtimeAdapterOptions.backendResolver ??
|
|
125
114
|
((binding) => createResourceBackendResolver(workspace)(binding)),
|
|
126
115
|
};
|
|
@@ -183,22 +172,12 @@ export class AgentHarnessRuntime {
|
|
|
183
172
|
}
|
|
184
173
|
return createDefaultHealthSnapshot(this.activeRunSlots, this.pendingRunSlots.length);
|
|
185
174
|
}
|
|
186
|
-
getBinding(agentId) {
|
|
187
|
-
return this.workspace.bindings.get(agentId);
|
|
188
|
-
}
|
|
189
|
-
listAgentTools(agentId) {
|
|
190
|
-
const binding = this.getBinding(agentId);
|
|
191
|
-
if (!binding) {
|
|
192
|
-
throw new Error(`Unknown agent ${agentId}`);
|
|
193
|
-
}
|
|
194
|
-
return getBindingPrimaryTools(binding);
|
|
195
|
-
}
|
|
196
175
|
resolveAgentTools(agentId) {
|
|
197
|
-
const binding = this.
|
|
176
|
+
const binding = this.workspace.bindings.get(agentId);
|
|
198
177
|
if (!binding) {
|
|
199
178
|
throw new Error(`Unknown agent ${agentId}`);
|
|
200
179
|
}
|
|
201
|
-
const compiledTools =
|
|
180
|
+
const compiledTools = getBindingPrimaryTools(binding);
|
|
202
181
|
const resolver = this.resolvedRuntimeAdapterOptions.toolResolver;
|
|
203
182
|
const resolvedTools = resolver ? resolver(compiledTools.map((tool) => tool.id), binding) : [];
|
|
204
183
|
return compiledTools.map((compiledTool, index) => ({
|
|
@@ -416,9 +395,6 @@ export class AgentHarnessRuntime {
|
|
|
416
395
|
const userTurn = history.find((message) => message.runId === runId && message.role === "user");
|
|
417
396
|
return userTurn?.content ?? "";
|
|
418
397
|
}
|
|
419
|
-
async appendAssistantMessage(threadId, runId, content) {
|
|
420
|
-
return appendLifecycleAssistantMessage(this.persistence, threadId, runId, content);
|
|
421
|
-
}
|
|
422
398
|
async getRunCancellation(runId) {
|
|
423
399
|
const control = await this.persistence.getRunControl(runId);
|
|
424
400
|
return {
|
|
@@ -426,12 +402,6 @@ export class AgentHarnessRuntime {
|
|
|
426
402
|
...(control?.cancelReason ? { reason: control.cancelReason } : {}),
|
|
427
403
|
};
|
|
428
404
|
}
|
|
429
|
-
async expirePendingApprovals(threadId, runId) {
|
|
430
|
-
return expireLifecyclePendingApprovals({
|
|
431
|
-
persistence: this.persistence,
|
|
432
|
-
emit: (currentThreadId, currentRunId, sequence, eventType, payload, source) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload, source),
|
|
433
|
-
}, threadId, runId);
|
|
434
|
-
}
|
|
435
405
|
async finalizeCancelledRun(threadId, runId, previousState, reason) {
|
|
436
406
|
return finalizeLifecycleCancelledRun({
|
|
437
407
|
persistence: this.persistence,
|
|
@@ -507,7 +477,12 @@ export class AgentHarnessRuntime {
|
|
|
507
477
|
};
|
|
508
478
|
}
|
|
509
479
|
catch (error) {
|
|
510
|
-
await
|
|
480
|
+
await emitSyntheticFallbackEvent({
|
|
481
|
+
persistence: this.persistence,
|
|
482
|
+
publishEvent: (event) => this.eventBus.publish(event),
|
|
483
|
+
trackBackgroundTask: (task) => this.trackBackgroundTask(task),
|
|
484
|
+
backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
|
|
485
|
+
}, threadId, runId, agentId, error, 103);
|
|
511
486
|
await this.setRunStateAndEmit(threadId, runId, 104, "failed", {
|
|
512
487
|
previousState: previousState === "queued" ? "running" : previousState,
|
|
513
488
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -524,9 +499,6 @@ export class AgentHarnessRuntime {
|
|
|
524
499
|
await this.persistence.clearRunRequest(threadId, runId);
|
|
525
500
|
}
|
|
526
501
|
}
|
|
527
|
-
checkpointRefForState(threadId, runId, state) {
|
|
528
|
-
return getCheckpointRefForRunState(threadId, runId, state);
|
|
529
|
-
}
|
|
530
502
|
async finalizeContinuedRun(binding, threadId, runId, input, actual, options) {
|
|
531
503
|
return finalizeLifecycleContinuedRun({
|
|
532
504
|
persistence: this.persistence,
|
|
@@ -535,23 +507,6 @@ export class AgentHarnessRuntime {
|
|
|
535
507
|
requestApprovalAndEmit: (currentThreadId, currentRunId, lifecycleInput, interruptContent, checkpointRef, sequence) => this.requestApprovalAndEmit(currentThreadId, currentRunId, lifecycleInput, interruptContent, checkpointRef, sequence),
|
|
536
508
|
}, binding, threadId, runId, input, actual, options);
|
|
537
509
|
}
|
|
538
|
-
async emitOutputDeltaAndCreateItem(threadId, runId, agentId, content) {
|
|
539
|
-
return emitStreamingOutputDeltaAndCreateItem((currentThreadId, currentRunId, sequence, eventType, payload) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload), threadId, runId, agentId, content);
|
|
540
|
-
}
|
|
541
|
-
createContentBlocksItem(threadId, runId, agentId, contentBlocks) {
|
|
542
|
-
return createStreamingContentBlocksItem(threadId, runId, agentId, contentBlocks);
|
|
543
|
-
}
|
|
544
|
-
createToolResultKey(toolName, output, isError) {
|
|
545
|
-
return createStreamingToolResultKey(toolName, output, isError);
|
|
546
|
-
}
|
|
547
|
-
async emitRunCreated(threadId, runId, payload) {
|
|
548
|
-
return emitRunCreatedEvent({
|
|
549
|
-
persistence: this.persistence,
|
|
550
|
-
publishEvent: (event) => this.eventBus.publish(event),
|
|
551
|
-
trackBackgroundTask: (task) => this.trackBackgroundTask(task),
|
|
552
|
-
backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
|
|
553
|
-
}, threadId, runId, payload);
|
|
554
|
-
}
|
|
555
510
|
async setRunStateAndEmit(threadId, runId, sequence, state, options) {
|
|
556
511
|
return setRunStateAndEmitEvent({
|
|
557
512
|
persistence: this.persistence,
|
|
@@ -568,25 +523,6 @@ export class AgentHarnessRuntime {
|
|
|
568
523
|
backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
|
|
569
524
|
}, threadId, runId, input, interruptContent, checkpointRef, sequence);
|
|
570
525
|
}
|
|
571
|
-
async emitSyntheticFallback(threadId, runId, selectedAgentId, error, sequence = 3) {
|
|
572
|
-
await emitSyntheticFallbackEvent({
|
|
573
|
-
persistence: this.persistence,
|
|
574
|
-
publishEvent: (event) => this.eventBus.publish(event),
|
|
575
|
-
trackBackgroundTask: (task) => this.trackBackgroundTask(task),
|
|
576
|
-
backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
|
|
577
|
-
}, threadId, runId, selectedAgentId, error, sequence);
|
|
578
|
-
}
|
|
579
|
-
async persistApproval(threadId, runId, checkpointRef, input, interruptContent) {
|
|
580
|
-
return persistApproval({
|
|
581
|
-
persistence: this.persistence,
|
|
582
|
-
publishEvent: (event) => this.eventBus.publish(event),
|
|
583
|
-
trackBackgroundTask: (task) => this.trackBackgroundTask(task),
|
|
584
|
-
backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
|
|
585
|
-
}, threadId, runId, checkpointRef, input, interruptContent);
|
|
586
|
-
}
|
|
587
|
-
async resolveApprovalRecord(options, thread) {
|
|
588
|
-
return resolveHarnessApprovalRecord(this.persistence, options, thread);
|
|
589
|
-
}
|
|
590
526
|
isDecisionRun(options) {
|
|
591
527
|
return "decision" in options;
|
|
592
528
|
}
|
|
@@ -596,6 +532,55 @@ export class AgentHarnessRuntime {
|
|
|
596
532
|
}
|
|
597
533
|
await listener(value);
|
|
598
534
|
}
|
|
535
|
+
async prepareRunStart(options, invocation, runCreatedPayload) {
|
|
536
|
+
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
537
|
+
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
538
|
+
if (!binding) {
|
|
539
|
+
throw new Error(`Unknown agent ${selectedAgentId}`);
|
|
540
|
+
}
|
|
541
|
+
const policyDecision = this.policyEngine.evaluate(binding);
|
|
542
|
+
if (!policyDecision.allowed) {
|
|
543
|
+
throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
|
|
544
|
+
}
|
|
545
|
+
const priority = normalizeRunPriority(options.priority);
|
|
546
|
+
const runRequest = buildPersistedRunRequest(options.input, invocation, priority);
|
|
547
|
+
const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, runRequest, options.threadId);
|
|
548
|
+
return {
|
|
549
|
+
binding,
|
|
550
|
+
selectedAgentId,
|
|
551
|
+
priority,
|
|
552
|
+
threadId,
|
|
553
|
+
runId,
|
|
554
|
+
isNewThread,
|
|
555
|
+
runCreatedEventPromise: emitRunCreatedEvent({
|
|
556
|
+
persistence: this.persistence,
|
|
557
|
+
publishEvent: (event) => this.eventBus.publish(event),
|
|
558
|
+
trackBackgroundTask: (task) => this.trackBackgroundTask(task),
|
|
559
|
+
backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
|
|
560
|
+
}, threadId, runId, runCreatedPayload(binding, selectedAgentId)),
|
|
561
|
+
releaseRunSlotPromise: this.acquireRunSlot(threadId, runId, "running", priority),
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
createStartupRecoveryContext() {
|
|
565
|
+
return {
|
|
566
|
+
persistence: this.persistence,
|
|
567
|
+
workspace: this.workspace,
|
|
568
|
+
runtimeAdapter: this.runtimeAdapter,
|
|
569
|
+
recoveryConfig: this.recoveryConfig,
|
|
570
|
+
concurrencyConfig: this.concurrencyConfig,
|
|
571
|
+
getBinding: (agentId) => this.workspace.bindings.get(agentId),
|
|
572
|
+
acquireRunSlot: (threadId, runId, activeState = "running", priority = 0) => this.acquireRunSlot(threadId, runId, activeState, priority),
|
|
573
|
+
executeQueuedRun: (binding, input, threadId, runId, agentId, options = {}) => this.executeQueuedRun(binding, input, threadId, runId, agentId, options),
|
|
574
|
+
setRunStateAndEmit: (threadId, runId, sequence, state, options) => this.setRunStateAndEmit(threadId, runId, sequence, state, options),
|
|
575
|
+
emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
|
|
576
|
+
loadRunInput: (threadId, runId) => this.loadRunInput(threadId, runId),
|
|
577
|
+
finalizeContinuedRun: (binding, threadId, runId, input, actual, options) => this.finalizeContinuedRun(binding, threadId, runId, input, actual, options),
|
|
578
|
+
supportsRunningReplay: (binding) => this.supportsRunningReplay(binding),
|
|
579
|
+
isStaleRunningRun: (thread, nowMs = Date.now()) => this.isStaleRunningRun(thread, nowMs),
|
|
580
|
+
recordLlmSuccess: (startedAt) => this.recordLlmSuccess(startedAt),
|
|
581
|
+
recordLlmFailure: (startedAt) => this.recordLlmFailure(startedAt),
|
|
582
|
+
};
|
|
583
|
+
}
|
|
599
584
|
async acquireRunSlot(threadId, runId, activeState = "running", priority = 0) {
|
|
600
585
|
let stopHeartbeat = () => undefined;
|
|
601
586
|
const beginLease = async (mode) => {
|
|
@@ -776,32 +761,12 @@ export class AgentHarnessRuntime {
|
|
|
776
761
|
return this.dispatchRunListeners(this.streamEvents(options), resolvedListeners);
|
|
777
762
|
}
|
|
778
763
|
const invocation = normalizeInvocationEnvelope(options);
|
|
779
|
-
const selectedAgentId = await
|
|
780
|
-
|
|
781
|
-
input: options.input,
|
|
782
|
-
requestedAgentId: options.agentId,
|
|
783
|
-
threadId: options.threadId,
|
|
784
|
-
preferredHostAgentId: this.routingDefaultAgentId ?? AgentHarnessRuntime.DEFAULT_HOST_AGENT_ID,
|
|
785
|
-
getThreadSummary: (threadId) => this.getSession(threadId),
|
|
786
|
-
});
|
|
787
|
-
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
788
|
-
if (!binding) {
|
|
789
|
-
throw new Error(`Unknown agent ${selectedAgentId}`);
|
|
790
|
-
}
|
|
791
|
-
const policyDecision = this.policyEngine.evaluate(binding);
|
|
792
|
-
if (!policyDecision.allowed) {
|
|
793
|
-
throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
|
|
794
|
-
}
|
|
795
|
-
const priority = normalizeRunPriority(options.priority);
|
|
796
|
-
const runRequest = buildPersistedRunRequest(options.input, invocation, priority);
|
|
797
|
-
const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, runRequest, options.threadId);
|
|
798
|
-
const runCreatedEventPromise = this.emitRunCreated(threadId, runId, {
|
|
799
|
-
agentId: binding.agent.id,
|
|
764
|
+
const { binding, selectedAgentId, threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await this.prepareRunStart(options, invocation, (activeBinding, activeSelectedAgentId) => ({
|
|
765
|
+
agentId: activeBinding.agent.id,
|
|
800
766
|
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
801
|
-
selectedAgentId,
|
|
802
|
-
executionMode: getBindingAdapterKind(
|
|
803
|
-
});
|
|
804
|
-
const releaseRunSlotPromise = this.acquireRunSlot(threadId, runId, "running", priority);
|
|
767
|
+
selectedAgentId: activeSelectedAgentId,
|
|
768
|
+
executionMode: getBindingAdapterKind(activeBinding),
|
|
769
|
+
}));
|
|
805
770
|
await runCreatedEventPromise;
|
|
806
771
|
const releaseRunSlot = await releaseRunSlotPromise;
|
|
807
772
|
try {
|
|
@@ -821,14 +786,7 @@ export class AgentHarnessRuntime {
|
|
|
821
786
|
}
|
|
822
787
|
async *streamEvents(options) {
|
|
823
788
|
const invocation = normalizeInvocationEnvelope(options);
|
|
824
|
-
const selectedAgentId = await resolveSelectedAgentId(
|
|
825
|
-
workspace: this.workspace,
|
|
826
|
-
input: options.input,
|
|
827
|
-
requestedAgentId: options.agentId,
|
|
828
|
-
threadId: options.threadId,
|
|
829
|
-
preferredHostAgentId: this.routingDefaultAgentId ?? AgentHarnessRuntime.DEFAULT_HOST_AGENT_ID,
|
|
830
|
-
getThreadSummary: (threadId) => this.getSession(threadId),
|
|
831
|
-
});
|
|
789
|
+
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
832
790
|
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
833
791
|
if (!binding) {
|
|
834
792
|
const result = await this.run(options);
|
|
@@ -845,268 +803,268 @@ export class AgentHarnessRuntime {
|
|
|
845
803
|
}
|
|
846
804
|
let emitted = false;
|
|
847
805
|
let streamActivityObserved = false;
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, runRequest, options.threadId);
|
|
851
|
-
const priorHistoryPromise = Promise.resolve(isNewThread ? [] : undefined).then((historyHint) => historyHint ?? this.loadPriorHistory(threadId, runId));
|
|
852
|
-
const runCreatedEventPromise = this.emitRunCreated(threadId, runId, {
|
|
853
|
-
agentId: selectedAgentId,
|
|
806
|
+
const { threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await this.prepareRunStart(options, invocation, (_binding, activeSelectedAgentId) => ({
|
|
807
|
+
agentId: activeSelectedAgentId,
|
|
854
808
|
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
855
|
-
selectedAgentId,
|
|
809
|
+
selectedAgentId: activeSelectedAgentId,
|
|
856
810
|
input: options.input,
|
|
857
811
|
state: "running",
|
|
858
|
-
});
|
|
812
|
+
}));
|
|
813
|
+
const priorHistoryPromise = Promise.resolve(isNewThread ? [] : undefined).then((historyHint) => historyHint ?? this.loadPriorHistory(threadId, runId));
|
|
859
814
|
yield { type: "event", event: await runCreatedEventPromise };
|
|
860
|
-
const releaseRunSlotPromise = this.acquireRunSlot(threadId, runId, "running", priority);
|
|
861
815
|
let releaseRunSlot = async () => undefined;
|
|
816
|
+
const emitOutputDelta = (content) => emitStreamingOutputDeltaAndCreateItem((currentThreadId, currentRunId, sequence, eventType, payload) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload), threadId, runId, selectedAgentId, content);
|
|
862
817
|
try {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
? chunk.
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
result: {
|
|
915
|
-
threadId,
|
|
916
|
-
runId,
|
|
917
|
-
agentId: selectedAgentId,
|
|
918
|
-
state: "waiting_for_approval",
|
|
919
|
-
output: assistantOutput,
|
|
920
|
-
finalMessageText: assistantOutput,
|
|
921
|
-
interruptContent: normalizedChunk.content,
|
|
922
|
-
approvalId: approvalRequest.approval.approvalId,
|
|
923
|
-
pendingActionId: approvalRequest.approval.pendingActionId,
|
|
924
|
-
},
|
|
925
|
-
};
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
if (normalizedChunk.kind === "reasoning") {
|
|
929
|
-
await this.emit(threadId, runId, 3, "reasoning.delta", {
|
|
930
|
-
content: normalizedChunk.content,
|
|
931
|
-
});
|
|
932
|
-
yield {
|
|
933
|
-
type: "reasoning",
|
|
934
|
-
threadId,
|
|
935
|
-
runId,
|
|
936
|
-
agentId: selectedAgentId,
|
|
937
|
-
content: normalizedChunk.content,
|
|
938
|
-
};
|
|
939
|
-
continue;
|
|
940
|
-
}
|
|
941
|
-
if (normalizedChunk.kind === "step") {
|
|
942
|
-
yield {
|
|
943
|
-
type: "step",
|
|
818
|
+
const [priorHistory, acquiredReleaseRunSlot] = await Promise.all([
|
|
819
|
+
priorHistoryPromise,
|
|
820
|
+
releaseRunSlotPromise,
|
|
821
|
+
]).then(([loadedPriorHistory, resolvedReleaseRunSlot]) => [loadedPriorHistory, resolvedReleaseRunSlot]);
|
|
822
|
+
releaseRunSlot = acquiredReleaseRunSlot;
|
|
823
|
+
let assistantOutput = "";
|
|
824
|
+
const toolErrors = [];
|
|
825
|
+
let lastToolResultKey = null;
|
|
826
|
+
for await (const chunk of this.runtimeAdapter.stream(binding, options.input, threadId, priorHistory, {
|
|
827
|
+
context: invocation.context,
|
|
828
|
+
state: invocation.state,
|
|
829
|
+
files: invocation.files,
|
|
830
|
+
runId,
|
|
831
|
+
})) {
|
|
832
|
+
if (chunk) {
|
|
833
|
+
streamActivityObserved = true;
|
|
834
|
+
const normalizedChunk = typeof chunk === "string"
|
|
835
|
+
? chunk.startsWith(AGENT_INTERRUPT_SENTINEL_PREFIX)
|
|
836
|
+
? { kind: "interrupt", content: chunk.slice(AGENT_INTERRUPT_SENTINEL_PREFIX.length) }
|
|
837
|
+
: { kind: "content", content: chunk }
|
|
838
|
+
: chunk;
|
|
839
|
+
if (normalizedChunk.kind === "upstream-event") {
|
|
840
|
+
yield {
|
|
841
|
+
type: "upstream-event",
|
|
842
|
+
threadId,
|
|
843
|
+
runId,
|
|
844
|
+
agentId: selectedAgentId,
|
|
845
|
+
event: normalizedChunk.event.format === "langgraph-v2"
|
|
846
|
+
? normalizedChunk.event
|
|
847
|
+
: normalizeUpstreamRuntimeEvent(normalizedChunk.event.raw ?? normalizedChunk.event),
|
|
848
|
+
};
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
if (normalizedChunk.kind === "interrupt") {
|
|
852
|
+
const checkpointRef = `checkpoints/${threadId}/${runId}/cp-1`;
|
|
853
|
+
const waitingEvent = await this.setRunStateAndEmit(threadId, runId, 6, "waiting_for_approval", {
|
|
854
|
+
previousState: "running",
|
|
855
|
+
checkpointRef,
|
|
856
|
+
});
|
|
857
|
+
const approvalRequest = await this.requestApprovalAndEmit(threadId, runId, options.input, normalizedChunk.content, checkpointRef, 7);
|
|
858
|
+
yield {
|
|
859
|
+
type: "event",
|
|
860
|
+
event: waitingEvent,
|
|
861
|
+
};
|
|
862
|
+
yield {
|
|
863
|
+
type: "event",
|
|
864
|
+
event: approvalRequest.event,
|
|
865
|
+
};
|
|
866
|
+
yield {
|
|
867
|
+
type: "result",
|
|
868
|
+
result: {
|
|
944
869
|
threadId,
|
|
945
870
|
runId,
|
|
946
871
|
agentId: selectedAgentId,
|
|
947
|
-
|
|
948
|
-
|
|
872
|
+
state: "waiting_for_approval",
|
|
873
|
+
output: assistantOutput,
|
|
874
|
+
finalMessageText: assistantOutput,
|
|
875
|
+
interruptContent: normalizedChunk.content,
|
|
876
|
+
approvalId: approvalRequest.approval.approvalId,
|
|
877
|
+
pendingActionId: approvalRequest.approval.pendingActionId,
|
|
878
|
+
},
|
|
879
|
+
};
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
if (normalizedChunk.kind === "reasoning") {
|
|
883
|
+
await this.emit(threadId, runId, 3, "reasoning.delta", {
|
|
884
|
+
content: normalizedChunk.content,
|
|
885
|
+
});
|
|
886
|
+
yield {
|
|
887
|
+
type: "reasoning",
|
|
888
|
+
threadId,
|
|
889
|
+
runId,
|
|
890
|
+
agentId: selectedAgentId,
|
|
891
|
+
content: normalizedChunk.content,
|
|
892
|
+
};
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (normalizedChunk.kind === "step") {
|
|
896
|
+
yield {
|
|
897
|
+
type: "step",
|
|
898
|
+
threadId,
|
|
899
|
+
runId,
|
|
900
|
+
agentId: selectedAgentId,
|
|
901
|
+
content: normalizedChunk.content,
|
|
902
|
+
};
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
if (normalizedChunk.kind === "tool-result") {
|
|
906
|
+
const toolResultKey = createStreamingToolResultKey(normalizedChunk.toolName, normalizedChunk.output, normalizedChunk.isError);
|
|
907
|
+
if (toolResultKey === lastToolResultKey) {
|
|
949
908
|
continue;
|
|
950
909
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
continue;
|
|
955
|
-
}
|
|
956
|
-
lastToolResultKey = toolResultKey;
|
|
957
|
-
if (normalizedChunk.isError) {
|
|
958
|
-
toolErrors.push(renderToolFailure(normalizedChunk.toolName, normalizedChunk.output));
|
|
959
|
-
}
|
|
960
|
-
yield {
|
|
961
|
-
type: "tool-result",
|
|
962
|
-
threadId,
|
|
963
|
-
runId,
|
|
964
|
-
agentId: selectedAgentId,
|
|
965
|
-
toolName: normalizedChunk.toolName,
|
|
966
|
-
output: normalizedChunk.output,
|
|
967
|
-
isError: normalizedChunk.isError,
|
|
968
|
-
};
|
|
969
|
-
continue;
|
|
910
|
+
lastToolResultKey = toolResultKey;
|
|
911
|
+
if (normalizedChunk.isError) {
|
|
912
|
+
toolErrors.push(renderToolFailure(normalizedChunk.toolName, normalizedChunk.output));
|
|
970
913
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
914
|
+
yield {
|
|
915
|
+
type: "tool-result",
|
|
916
|
+
threadId,
|
|
917
|
+
runId,
|
|
918
|
+
agentId: selectedAgentId,
|
|
919
|
+
toolName: normalizedChunk.toolName,
|
|
920
|
+
output: normalizedChunk.output,
|
|
921
|
+
isError: normalizedChunk.isError,
|
|
922
|
+
};
|
|
923
|
+
continue;
|
|
974
924
|
}
|
|
975
|
-
}
|
|
976
|
-
if (!assistantOutput && toolErrors.length > 0) {
|
|
977
|
-
assistantOutput = toolErrors.join("\n\n");
|
|
978
925
|
emitted = true;
|
|
979
|
-
|
|
926
|
+
assistantOutput += normalizedChunk.content;
|
|
927
|
+
yield await emitOutputDelta(normalizedChunk.content);
|
|
980
928
|
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
929
|
+
}
|
|
930
|
+
if (!assistantOutput && toolErrors.length > 0) {
|
|
931
|
+
assistantOutput = toolErrors.join("\n\n");
|
|
932
|
+
emitted = true;
|
|
933
|
+
yield await emitOutputDelta(assistantOutput);
|
|
934
|
+
}
|
|
935
|
+
if (!assistantOutput) {
|
|
936
|
+
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId);
|
|
937
|
+
if (Array.isArray(actual.contentBlocks) && actual.contentBlocks.length > 0) {
|
|
938
|
+
yield createStreamingContentBlocksItem(threadId, runId, selectedAgentId, actual.contentBlocks);
|
|
991
939
|
}
|
|
992
|
-
|
|
940
|
+
if (actual.output) {
|
|
941
|
+
assistantOutput = actual.output;
|
|
942
|
+
emitted = true;
|
|
943
|
+
yield await emitOutputDelta(actual.output);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
await appendLifecycleAssistantMessage(this.persistence, threadId, runId, assistantOutput);
|
|
947
|
+
yield {
|
|
948
|
+
type: "result",
|
|
949
|
+
result: {
|
|
950
|
+
threadId,
|
|
951
|
+
runId,
|
|
952
|
+
agentId: selectedAgentId,
|
|
953
|
+
state: "completed",
|
|
954
|
+
output: assistantOutput,
|
|
955
|
+
finalMessageText: assistantOutput,
|
|
956
|
+
},
|
|
957
|
+
};
|
|
958
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "completed", {
|
|
959
|
+
previousState: "running",
|
|
960
|
+
}) };
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
catch (error) {
|
|
964
|
+
if (emitted || streamActivityObserved) {
|
|
965
|
+
const runtimeFailure = renderRuntimeFailure(error);
|
|
966
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "failed", {
|
|
967
|
+
previousState: "running",
|
|
968
|
+
error: error instanceof Error ? error.message : String(error),
|
|
969
|
+
}) };
|
|
970
|
+
yield {
|
|
971
|
+
type: "content",
|
|
972
|
+
threadId,
|
|
973
|
+
runId,
|
|
974
|
+
agentId: selectedAgentId,
|
|
975
|
+
content: runtimeFailure,
|
|
976
|
+
};
|
|
993
977
|
yield {
|
|
994
978
|
type: "result",
|
|
995
979
|
result: {
|
|
996
980
|
threadId,
|
|
997
981
|
runId,
|
|
998
982
|
agentId: selectedAgentId,
|
|
999
|
-
state: "
|
|
1000
|
-
output:
|
|
1001
|
-
finalMessageText:
|
|
983
|
+
state: "failed",
|
|
984
|
+
output: runtimeFailure,
|
|
985
|
+
finalMessageText: runtimeFailure,
|
|
1002
986
|
},
|
|
1003
987
|
};
|
|
1004
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "completed", {
|
|
1005
|
-
previousState: "running",
|
|
1006
|
-
}) };
|
|
1007
988
|
return;
|
|
1008
989
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
990
|
+
if (error instanceof RuntimeOperationTimeoutError && error.stage === "invoke") {
|
|
991
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "failed", {
|
|
992
|
+
previousState: "running",
|
|
993
|
+
error: error.message,
|
|
994
|
+
}) };
|
|
995
|
+
yield {
|
|
996
|
+
type: "content",
|
|
997
|
+
threadId,
|
|
998
|
+
runId,
|
|
999
|
+
agentId: selectedAgentId,
|
|
1000
|
+
content: renderRuntimeFailure(error),
|
|
1001
|
+
};
|
|
1002
|
+
yield {
|
|
1003
|
+
type: "result",
|
|
1004
|
+
result: {
|
|
1018
1005
|
threadId,
|
|
1019
1006
|
runId,
|
|
1020
1007
|
agentId: selectedAgentId,
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
};
|
|
1034
|
-
return;
|
|
1008
|
+
state: "failed",
|
|
1009
|
+
output: renderRuntimeFailure(error),
|
|
1010
|
+
finalMessageText: renderRuntimeFailure(error),
|
|
1011
|
+
},
|
|
1012
|
+
};
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
try {
|
|
1016
|
+
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId);
|
|
1017
|
+
await appendLifecycleAssistantMessage(this.persistence, threadId, runId, actual.output);
|
|
1018
|
+
if (Array.isArray(actual.contentBlocks) && actual.contentBlocks.length > 0) {
|
|
1019
|
+
yield createStreamingContentBlocksItem(threadId, runId, selectedAgentId, actual.contentBlocks);
|
|
1035
1020
|
}
|
|
1036
|
-
if (
|
|
1037
|
-
yield
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1021
|
+
if (actual.output) {
|
|
1022
|
+
yield await emitOutputDelta(actual.output);
|
|
1023
|
+
}
|
|
1024
|
+
yield {
|
|
1025
|
+
type: "result",
|
|
1026
|
+
result: {
|
|
1027
|
+
...actual,
|
|
1043
1028
|
threadId,
|
|
1044
1029
|
runId,
|
|
1045
1030
|
agentId: selectedAgentId,
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
threadId,
|
|
1075
|
-
runId,
|
|
1076
|
-
agentId: selectedAgentId,
|
|
1077
|
-
},
|
|
1078
|
-
};
|
|
1079
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, actual.state, {
|
|
1080
|
-
previousState: "running",
|
|
1081
|
-
}) };
|
|
1082
|
-
return;
|
|
1083
|
-
}
|
|
1084
|
-
catch (invokeError) {
|
|
1085
|
-
await this.emitSyntheticFallback(threadId, runId, selectedAgentId, invokeError);
|
|
1086
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "failed", {
|
|
1087
|
-
previousState: "running",
|
|
1088
|
-
error: invokeError instanceof Error ? invokeError.message : String(invokeError),
|
|
1089
|
-
}) };
|
|
1090
|
-
yield {
|
|
1091
|
-
type: "content",
|
|
1031
|
+
},
|
|
1032
|
+
};
|
|
1033
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, actual.state, {
|
|
1034
|
+
previousState: "running",
|
|
1035
|
+
}) };
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
catch (invokeError) {
|
|
1039
|
+
await emitSyntheticFallbackEvent({
|
|
1040
|
+
persistence: this.persistence,
|
|
1041
|
+
publishEvent: (event) => this.eventBus.publish(event),
|
|
1042
|
+
trackBackgroundTask: (task) => this.trackBackgroundTask(task),
|
|
1043
|
+
backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
|
|
1044
|
+
}, threadId, runId, selectedAgentId, invokeError);
|
|
1045
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "failed", {
|
|
1046
|
+
previousState: "running",
|
|
1047
|
+
error: invokeError instanceof Error ? invokeError.message : String(invokeError),
|
|
1048
|
+
}) };
|
|
1049
|
+
yield {
|
|
1050
|
+
type: "content",
|
|
1051
|
+
threadId,
|
|
1052
|
+
runId,
|
|
1053
|
+
agentId: selectedAgentId,
|
|
1054
|
+
content: renderRuntimeFailure(invokeError),
|
|
1055
|
+
};
|
|
1056
|
+
yield {
|
|
1057
|
+
type: "result",
|
|
1058
|
+
result: {
|
|
1092
1059
|
threadId,
|
|
1093
1060
|
runId,
|
|
1094
1061
|
agentId: selectedAgentId,
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
runId,
|
|
1102
|
-
agentId: selectedAgentId,
|
|
1103
|
-
state: "failed",
|
|
1104
|
-
output: renderRuntimeFailure(invokeError),
|
|
1105
|
-
finalMessageText: renderRuntimeFailure(invokeError),
|
|
1106
|
-
},
|
|
1107
|
-
};
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1062
|
+
state: "failed",
|
|
1063
|
+
output: renderRuntimeFailure(invokeError),
|
|
1064
|
+
finalMessageText: renderRuntimeFailure(invokeError),
|
|
1065
|
+
},
|
|
1066
|
+
};
|
|
1067
|
+
return;
|
|
1110
1068
|
}
|
|
1111
1069
|
}
|
|
1112
1070
|
finally {
|
|
@@ -1124,14 +1082,14 @@ export class AgentHarnessRuntime {
|
|
|
1124
1082
|
if (!thread) {
|
|
1125
1083
|
throw new Error("resume requires either threadId or approvalId");
|
|
1126
1084
|
}
|
|
1127
|
-
const approval = approvalById ?? await this.
|
|
1085
|
+
const approval = approvalById ?? await resolveHarnessApprovalRecord(this.persistence, options, thread);
|
|
1128
1086
|
const threadId = approval.threadId;
|
|
1129
1087
|
const runId = approval.runId;
|
|
1130
1088
|
const binding = this.workspace.bindings.get(thread.agentId);
|
|
1131
1089
|
if (!binding) {
|
|
1132
1090
|
throw new Error(`Unknown agent ${thread.agentId}`);
|
|
1133
1091
|
}
|
|
1134
|
-
const resumePayload =
|
|
1092
|
+
const resumePayload = buildHarnessResumePayload(binding, approval, options);
|
|
1135
1093
|
const cancellation = await this.getRunCancellation(runId);
|
|
1136
1094
|
if (cancellation.requested) {
|
|
1137
1095
|
return this.finalizeCancelledRun(threadId, runId, thread.status, cancellation.reason);
|
|
@@ -1192,9 +1150,6 @@ export class AgentHarnessRuntime {
|
|
|
1192
1150
|
await releaseRunSlot();
|
|
1193
1151
|
}
|
|
1194
1152
|
}
|
|
1195
|
-
buildResumePayload(binding, approval, options) {
|
|
1196
|
-
return buildHarnessResumePayload(binding, approval, options);
|
|
1197
|
-
}
|
|
1198
1153
|
async restartConversation(options) {
|
|
1199
1154
|
const thread = await this.getSession(options.threadId);
|
|
1200
1155
|
if (!thread) {
|
|
@@ -1270,136 +1225,14 @@ export class AgentHarnessRuntime {
|
|
|
1270
1225
|
}
|
|
1271
1226
|
await this.reclaimExpiredClaimedRuns();
|
|
1272
1227
|
const threads = await this.persistence.listSessions();
|
|
1228
|
+
const recoveryContext = this.createStartupRecoveryContext();
|
|
1273
1229
|
for (const thread of threads) {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
continue;
|
|
1279
|
-
}
|
|
1280
|
-
const request = await this.persistence.getRunRequest(thread.threadId, thread.latestRunId);
|
|
1281
|
-
if (!request) {
|
|
1282
|
-
await this.setRunStateAndEmit(thread.threadId, thread.latestRunId, 100, "failed", {
|
|
1283
|
-
previousState: "queued",
|
|
1284
|
-
error: "missing persisted run request for queued run recovery",
|
|
1285
|
-
});
|
|
1286
|
-
continue;
|
|
1287
|
-
}
|
|
1288
|
-
const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running", normalizeRunPriority(request.priority));
|
|
1289
|
-
try {
|
|
1290
|
-
await this.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, runMeta.agentId, {
|
|
1291
|
-
context: request.invocation?.context,
|
|
1292
|
-
state: request.invocation?.inputs,
|
|
1293
|
-
files: request.invocation?.attachments,
|
|
1294
|
-
previousState: "queued",
|
|
1295
|
-
stateSequence: 103,
|
|
1296
|
-
approvalSequence: 104,
|
|
1297
|
-
});
|
|
1298
|
-
}
|
|
1299
|
-
finally {
|
|
1300
|
-
await releaseRunSlot();
|
|
1301
|
-
}
|
|
1302
|
-
continue;
|
|
1303
|
-
}
|
|
1304
|
-
if (thread.status === "running") {
|
|
1305
|
-
const isStale = await this.isStaleRunningRun(thread);
|
|
1306
|
-
if (!isStale) {
|
|
1307
|
-
continue;
|
|
1308
|
-
}
|
|
1309
|
-
const runMeta = await this.persistence.getRunMeta(thread.threadId, thread.latestRunId);
|
|
1310
|
-
const binding = this.workspace.bindings.get(runMeta.agentId);
|
|
1311
|
-
if (!binding) {
|
|
1312
|
-
continue;
|
|
1313
|
-
}
|
|
1314
|
-
if (!this.supportsRunningReplay(binding)) {
|
|
1315
|
-
await this.setRunStateAndEmit(thread.threadId, thread.latestRunId, 100, "failed", {
|
|
1316
|
-
previousState: "running",
|
|
1317
|
-
error: "stale running run cannot be replayed safely",
|
|
1318
|
-
});
|
|
1319
|
-
await this.persistence.releaseRunClaim(thread.latestRunId);
|
|
1320
|
-
continue;
|
|
1321
|
-
}
|
|
1322
|
-
const request = await this.persistence.getRunRequest(thread.threadId, thread.latestRunId);
|
|
1323
|
-
if (!request) {
|
|
1324
|
-
await this.setRunStateAndEmit(thread.threadId, thread.latestRunId, 100, "failed", {
|
|
1325
|
-
previousState: "running",
|
|
1326
|
-
error: "missing persisted run request for stale running run recovery",
|
|
1327
|
-
});
|
|
1328
|
-
await this.persistence.releaseRunClaim(thread.latestRunId);
|
|
1329
|
-
continue;
|
|
1330
|
-
}
|
|
1331
|
-
const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running", normalizeRunPriority(request.priority));
|
|
1332
|
-
try {
|
|
1333
|
-
await this.emit(thread.threadId, thread.latestRunId, 100, "run.resumed", {
|
|
1334
|
-
resumeKind: "startup-running-recovery",
|
|
1335
|
-
state: "running",
|
|
1336
|
-
});
|
|
1337
|
-
await this.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, runMeta.agentId, {
|
|
1338
|
-
context: request.invocation?.context,
|
|
1339
|
-
state: request.invocation?.inputs,
|
|
1340
|
-
files: request.invocation?.attachments,
|
|
1341
|
-
previousState: "running",
|
|
1342
|
-
stateSequence: 103,
|
|
1343
|
-
approvalSequence: 104,
|
|
1344
|
-
});
|
|
1345
|
-
}
|
|
1346
|
-
finally {
|
|
1347
|
-
await releaseRunSlot();
|
|
1348
|
-
}
|
|
1349
|
-
continue;
|
|
1350
|
-
}
|
|
1351
|
-
if (thread.status !== "resuming" || !this.recoveryConfig.resumeResumingRunsOnStartup) {
|
|
1352
|
-
continue;
|
|
1353
|
-
}
|
|
1354
|
-
const binding = this.workspace.bindings.get(thread.agentId);
|
|
1355
|
-
if (!binding) {
|
|
1356
|
-
continue;
|
|
1357
|
-
}
|
|
1358
|
-
const recoveryIntent = await this.persistence.getRecoveryIntent(thread.threadId, thread.latestRunId);
|
|
1359
|
-
if (!recoveryIntent || recoveryIntent.kind !== "approval-decision") {
|
|
1360
|
-
continue;
|
|
1361
|
-
}
|
|
1362
|
-
if (recoveryIntent.attempts >= this.recoveryConfig.maxRecoveryAttempts) {
|
|
1363
|
-
await this.persistence.setRunState(thread.threadId, thread.latestRunId, "failed", recoveryIntent.checkpointRef);
|
|
1364
|
-
await this.persistence.clearRecoveryIntent(thread.threadId, thread.latestRunId);
|
|
1230
|
+
const handled = await recoverQueuedStartupRun(recoveryContext, thread) ||
|
|
1231
|
+
await recoverRunningStartupRun(recoveryContext, thread) ||
|
|
1232
|
+
await recoverResumingStartupRun(recoveryContext, thread);
|
|
1233
|
+
if (handled) {
|
|
1365
1234
|
continue;
|
|
1366
1235
|
}
|
|
1367
|
-
await this.persistence.saveRecoveryIntent(thread.threadId, thread.latestRunId, {
|
|
1368
|
-
...recoveryIntent,
|
|
1369
|
-
attempts: recoveryIntent.attempts + 1,
|
|
1370
|
-
});
|
|
1371
|
-
await this.emit(thread.threadId, thread.latestRunId, 100, "run.resumed", {
|
|
1372
|
-
resumeKind: "startup-recovery",
|
|
1373
|
-
checkpointRef: recoveryIntent.checkpointRef,
|
|
1374
|
-
state: "resuming",
|
|
1375
|
-
});
|
|
1376
|
-
const history = await this.persistence.listThreadMessages(thread.threadId);
|
|
1377
|
-
const priorHistory = history.filter((message) => message.runId !== thread.latestRunId);
|
|
1378
|
-
const runInput = await this.loadRunInput(thread.threadId, thread.latestRunId);
|
|
1379
|
-
const startedAt = Date.now();
|
|
1380
|
-
try {
|
|
1381
|
-
const actual = await this.runtimeAdapter.invoke(binding, "", thread.threadId, thread.latestRunId, recoveryIntent.resumePayload, priorHistory);
|
|
1382
|
-
this.recordLlmSuccess(startedAt);
|
|
1383
|
-
await this.persistence.clearRecoveryIntent(thread.threadId, thread.latestRunId);
|
|
1384
|
-
await this.finalizeContinuedRun(binding, thread.threadId, thread.latestRunId, runInput, actual, {
|
|
1385
|
-
previousState: "resuming",
|
|
1386
|
-
stateSequence: 101,
|
|
1387
|
-
approvalSequence: 102,
|
|
1388
|
-
});
|
|
1389
|
-
}
|
|
1390
|
-
catch (error) {
|
|
1391
|
-
this.recordLlmFailure(startedAt);
|
|
1392
|
-
if (recoveryIntent.attempts + 1 >= this.recoveryConfig.maxRecoveryAttempts) {
|
|
1393
|
-
await this.persistence.setRunState(thread.threadId, thread.latestRunId, "failed", recoveryIntent.checkpointRef);
|
|
1394
|
-
await this.persistence.clearRecoveryIntent(thread.threadId, thread.latestRunId);
|
|
1395
|
-
await this.emit(thread.threadId, thread.latestRunId, 101, "run.state.changed", {
|
|
1396
|
-
previousState: "resuming",
|
|
1397
|
-
state: "failed",
|
|
1398
|
-
checkpointRef: recoveryIntent.checkpointRef,
|
|
1399
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1400
|
-
});
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
1236
|
}
|
|
1404
1237
|
}
|
|
1405
1238
|
async reclaimExpiredClaimedRuns(nowIso = new Date().toISOString()) {
|