@botbotgo/agent-harness 0.0.101 → 0.0.103
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/persistence/sqlite-run-context-store.d.ts +22 -0
- package/dist/persistence/sqlite-run-context-store.js +64 -0
- package/dist/persistence/sqlite-run-queue-store.d.ts +41 -0
- package/dist/persistence/sqlite-run-queue-store.js +120 -0
- package/dist/persistence/sqlite-store.d.ts +2 -2
- package/dist/persistence/sqlite-store.js +31 -117
- package/dist/resource/mcp-tool-support.d.ts +21 -0
- package/dist/resource/mcp-tool-support.js +173 -0
- package/dist/resource/resource-impl.d.ts +1 -18
- package/dist/resource/resource-impl.js +3 -166
- package/dist/runtime/adapter/invoke-runtime.d.ts +22 -0
- package/dist/runtime/adapter/invoke-runtime.js +18 -0
- package/dist/runtime/adapter/middleware-assembly.d.ts +75 -0
- package/dist/runtime/adapter/middleware-assembly.js +175 -0
- package/dist/runtime/adapter/runtime-shell.d.ts +27 -0
- package/dist/runtime/adapter/runtime-shell.js +168 -0
- package/dist/runtime/adapter/stream-runtime.d.ts +46 -0
- package/dist/runtime/adapter/stream-runtime.js +93 -0
- package/dist/runtime/adapter/tool-resolution.d.ts +14 -0
- package/dist/runtime/adapter/tool-resolution.js +57 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +1 -6
- package/dist/runtime/agent-runtime-adapter.js +140 -495
- package/dist/runtime/harness/run/run-operations.d.ts +50 -0
- package/dist/runtime/harness/run/run-operations.js +113 -0
- package/dist/runtime/harness/run/run-slot-acquisition.d.ts +64 -0
- package/dist/runtime/harness/run/run-slot-acquisition.js +157 -0
- package/dist/runtime/harness/run/startup-runtime.d.ts +37 -0
- package/dist/runtime/harness/run/startup-runtime.js +68 -0
- package/dist/runtime/harness/run/stream-run.d.ts +53 -0
- package/dist/runtime/harness/run/stream-run.js +304 -0
- package/dist/runtime/harness/run/thread-records.d.ts +21 -0
- package/dist/runtime/harness/run/thread-records.js +59 -0
- package/dist/runtime/harness.js +121 -639
- package/dist/workspace/object-loader.d.ts +1 -8
- package/dist/workspace/object-loader.js +3 -197
- package/dist/workspace/yaml-object-reader.d.ts +15 -0
- package/dist/workspace/yaml-object-reader.js +202 -0
- package/package.json +1 -1
package/dist/runtime/harness.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { AUTO_AGENT_ID } from "../contracts/types.js";
|
|
2
2
|
import { SqlitePersistence } from "../persistence/sqlite-store.js";
|
|
3
3
|
import { createPersistentId } from "../utils/id.js";
|
|
4
|
-
import {
|
|
5
|
-
import { normalizeUpstreamRuntimeEvent } from "./parsing/stream-event-parsing.js";
|
|
4
|
+
import { AgentRuntimeAdapter } from "./agent-runtime-adapter.js";
|
|
6
5
|
import { createResourceBackendResolver, createResourceToolResolver } from "../resource/resource.js";
|
|
7
6
|
import { EventBus } from "./harness/events/event-bus.js";
|
|
8
7
|
import { PolicyEngine } from "./harness/system/policy-engine.js";
|
|
9
8
|
import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getRoutingRules, matchRoutingRule, } from "../workspace/support/workspace-ref-utils.js";
|
|
10
|
-
import { createHarnessEvent, inferRoutingBindings, renderRuntimeFailure,
|
|
9
|
+
import { createHarnessEvent, inferRoutingBindings, renderRuntimeFailure, } from "./support/harness-support.js";
|
|
11
10
|
import { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
|
|
12
11
|
import { FileBackedStore } from "./harness/system/store.js";
|
|
13
12
|
import { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, readCheckpointMaintenanceConfig, } from "./maintenance/checkpoint-maintenance.js";
|
|
@@ -15,12 +14,14 @@ import { RuntimeRecordMaintenanceLoop, discoverRuntimeRecordMaintenanceTargets,
|
|
|
15
14
|
import { HealthMonitor } from "./harness/system/health-monitor.js";
|
|
16
15
|
import { readHealthMonitorConfig } from "./harness/system/health-monitor.js";
|
|
17
16
|
import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
|
|
18
|
-
import { buildPersistedRunRequest,
|
|
17
|
+
import { buildPersistedRunRequest, normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, } from "./harness/run/helpers.js";
|
|
19
18
|
import { emitHarnessEvent, emitRunCreatedEvent, emitSyntheticFallbackEvent, requestApprovalAndEmitEvent, setRunStateAndEmitEvent, } from "./harness/events/events.js";
|
|
20
19
|
import { appendAssistantMessage as appendLifecycleAssistantMessage, finalizeCancelledRun as finalizeLifecycleCancelledRun, finalizeContinuedRun as finalizeLifecycleContinuedRun, } from "./harness/run/run-lifecycle.js";
|
|
21
|
-
import {
|
|
20
|
+
import { dispatchRunListeners as dispatchStreamingRunListeners, } from "./harness/events/streaming.js";
|
|
22
21
|
import { buildResumePayload as buildHarnessResumePayload, resolveApprovalRecord as resolveHarnessApprovalRecord, } from "./harness/run/resume.js";
|
|
23
|
-
import {
|
|
22
|
+
import { cancelRunOperation, resumeRun } from "./harness/run/run-operations.js";
|
|
23
|
+
import { acquireRunSlot as acquireHarnessRunSlot } from "./harness/run/run-slot-acquisition.js";
|
|
24
|
+
import { dropPendingRunSlot, enqueuePendingRunSlot } from "./harness/run/run-queue.js";
|
|
24
25
|
import { getDefaultHostAgentId, resolveSelectedAgentId } from "./harness/run/routing.js";
|
|
25
26
|
import { resolveCheckpointer, resolveEmbeddingModel, resolveStore, resolveStoreFromConfig, resolveVectorStore, } from "./harness/run/resources.js";
|
|
26
27
|
import { createToolMcpServerFromTools, serveToolsOverStdioFromHarness } from "../mcp.js";
|
|
@@ -28,7 +29,9 @@ import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig }
|
|
|
28
29
|
import { isRuntimeEntryBinding } from "./support/runtime-entry.js";
|
|
29
30
|
import { describeWorkspaceInventory, listAgentSkills as listWorkspaceAgentSkills, } from "./harness/system/inventory.js";
|
|
30
31
|
import { createDefaultHealthSnapshot, isInventoryEnabled, isThreadMemorySyncEnabled, } from "./harness/runtime-defaults.js";
|
|
31
|
-
import {
|
|
32
|
+
import { initializeHarnessRuntime, isStaleRunningRun as isHarnessStaleRunningRun, reclaimExpiredClaimedRuns as reclaimHarnessExpiredClaimedRuns, recoverStartupRuns as recoverHarnessStartupRuns, } from "./harness/run/startup-runtime.js";
|
|
33
|
+
import { streamHarnessRun } from "./harness/run/stream-run.js";
|
|
34
|
+
import { deleteThreadRecord, getPublicApproval, getThreadRecord, listPublicApprovals, } from "./harness/run/thread-records.js";
|
|
32
35
|
export class AgentHarnessRuntime {
|
|
33
36
|
workspace;
|
|
34
37
|
runtimeAdapterOptions;
|
|
@@ -157,11 +160,13 @@ export class AgentHarnessRuntime {
|
|
|
157
160
|
this.healthMonitor?.recordLlmFailure(Date.now() - startedAt);
|
|
158
161
|
}
|
|
159
162
|
async initialize() {
|
|
160
|
-
await
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
await initializeHarnessRuntime({
|
|
164
|
+
persistence: this.persistence,
|
|
165
|
+
checkpointMaintenance: this.checkpointMaintenance,
|
|
166
|
+
runtimeRecordMaintenance: this.runtimeRecordMaintenance,
|
|
167
|
+
healthMonitor: this.healthMonitor,
|
|
168
|
+
recoverStartupRuns: () => this.recoverStartupRuns(),
|
|
169
|
+
});
|
|
165
170
|
}
|
|
166
171
|
subscribe(listener) {
|
|
167
172
|
return this.eventBus.subscribe(listener);
|
|
@@ -202,47 +207,20 @@ export class AgentHarnessRuntime {
|
|
|
202
207
|
return this.persistence.getSession(threadId);
|
|
203
208
|
}
|
|
204
209
|
async getThread(threadId) {
|
|
205
|
-
|
|
206
|
-
this.
|
|
207
|
-
this.
|
|
208
|
-
|
|
209
|
-
this.persistence.listThreadRuns(threadId),
|
|
210
|
-
]);
|
|
211
|
-
if (!threadSummary || !meta) {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
const latestRunId = threadSummary.latestRunId;
|
|
215
|
-
const latestApprovals = await this.persistence.getRunApprovals(threadId, latestRunId);
|
|
216
|
-
const pendingApproval = latestApprovals
|
|
217
|
-
.filter((approval) => approval.status === "pending")
|
|
218
|
-
.sort((left, right) => right.requestedAt.localeCompare(left.requestedAt))[0];
|
|
219
|
-
return {
|
|
220
|
-
threadId,
|
|
221
|
-
entryAgentId: meta.entryAgentId,
|
|
222
|
-
currentState: threadSummary.status,
|
|
223
|
-
latestRunId,
|
|
224
|
-
createdAt: meta.createdAt,
|
|
225
|
-
updatedAt: threadSummary.updatedAt,
|
|
226
|
-
messages,
|
|
227
|
-
runs,
|
|
228
|
-
pendingDecision: pendingApproval
|
|
229
|
-
? {
|
|
230
|
-
approvalId: pendingApproval.approvalId,
|
|
231
|
-
pendingActionId: pendingApproval.pendingActionId,
|
|
232
|
-
toolName: pendingApproval.toolName,
|
|
233
|
-
allowedDecisions: pendingApproval.allowedDecisions,
|
|
234
|
-
requestedAt: pendingApproval.requestedAt,
|
|
235
|
-
}
|
|
236
|
-
: undefined,
|
|
237
|
-
};
|
|
210
|
+
return getThreadRecord({
|
|
211
|
+
persistence: this.persistence,
|
|
212
|
+
getSession: (currentThreadId) => this.getSession(currentThreadId),
|
|
213
|
+
}, threadId);
|
|
238
214
|
}
|
|
239
215
|
async listApprovals(filter) {
|
|
240
|
-
|
|
241
|
-
|
|
216
|
+
return listPublicApprovals({
|
|
217
|
+
persistence: this.persistence,
|
|
218
|
+
}, filter);
|
|
242
219
|
}
|
|
243
220
|
async getApproval(approvalId) {
|
|
244
|
-
|
|
245
|
-
|
|
221
|
+
return getPublicApproval({
|
|
222
|
+
persistence: this.persistence,
|
|
223
|
+
}, approvalId);
|
|
246
224
|
}
|
|
247
225
|
listAgentSkills(agentId, options = {}) {
|
|
248
226
|
return listWorkspaceAgentSkills(this.workspace, agentId, {
|
|
@@ -275,19 +253,11 @@ export class AgentHarnessRuntime {
|
|
|
275
253
|
}
|
|
276
254
|
}
|
|
277
255
|
async deleteThread(threadId) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (activeRun) {
|
|
284
|
-
throw new Error(`Cannot delete thread ${threadId} while run ${activeRun.runId} is ${activeRun.state}`);
|
|
285
|
-
}
|
|
286
|
-
const deleted = await this.persistence.deleteThread(threadId);
|
|
287
|
-
if (deleted) {
|
|
288
|
-
await this.deleteThreadCheckpoints(threadId);
|
|
289
|
-
}
|
|
290
|
-
return deleted;
|
|
256
|
+
return deleteThreadRecord({
|
|
257
|
+
getThread: (currentThreadId) => this.getThread(currentThreadId),
|
|
258
|
+
deleteThread: (currentThreadId) => this.persistence.deleteThread(currentThreadId),
|
|
259
|
+
deleteThreadCheckpoints: (currentThreadId) => this.deleteThreadCheckpoints(currentThreadId),
|
|
260
|
+
}, threadId);
|
|
291
261
|
}
|
|
292
262
|
async createToolMcpServer(options) {
|
|
293
263
|
const tools = this.resolveAgentTools(options.agentId).map(({ compiledTool, resolvedTool }) => ({
|
|
@@ -582,159 +552,24 @@ export class AgentHarnessRuntime {
|
|
|
582
552
|
};
|
|
583
553
|
}
|
|
584
554
|
async acquireRunSlot(threadId, runId, activeState = "running", priority = 0) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
workerId: this.workerId,
|
|
604
|
-
heartbeatAt: claimedAt,
|
|
605
|
-
leaseExpiresAt: new Date(Date.now() + this.concurrencyConfig.leaseMs).toISOString(),
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
if (this.concurrencyConfig.heartbeatIntervalMs <= 0) {
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
const timer = setInterval(() => {
|
|
612
|
-
void this.persistence.renewRunLease({
|
|
613
|
-
runId,
|
|
614
|
-
workerId: this.workerId,
|
|
615
|
-
heartbeatAt: new Date().toISOString(),
|
|
616
|
-
leaseExpiresAt: new Date(Date.now() + this.concurrencyConfig.leaseMs).toISOString(),
|
|
617
|
-
});
|
|
618
|
-
}, this.concurrencyConfig.heartbeatIntervalMs);
|
|
619
|
-
timer.unref?.();
|
|
620
|
-
stopHeartbeat = () => {
|
|
621
|
-
clearInterval(timer);
|
|
622
|
-
};
|
|
623
|
-
};
|
|
624
|
-
const releaseLease = async () => {
|
|
625
|
-
stopHeartbeat();
|
|
626
|
-
if (runId) {
|
|
627
|
-
await this.persistence.releaseRunClaim(runId);
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
const maxConcurrentRuns = this.concurrencyConfig.maxConcurrentRuns;
|
|
631
|
-
if (!maxConcurrentRuns) {
|
|
632
|
-
await beginLease("direct-heartbeat");
|
|
633
|
-
return async () => {
|
|
634
|
-
await releaseLease();
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
const canActivateImmediately = this.activeRunSlots < maxConcurrentRuns;
|
|
638
|
-
const useDirectHeartbeatFastPath = canActivateImmediately && maxConcurrentRuns > 1;
|
|
639
|
-
if (canActivateImmediately) {
|
|
640
|
-
this.activeRunSlots += 1;
|
|
641
|
-
if (threadId && runId && !useDirectHeartbeatFastPath) {
|
|
642
|
-
await this.persistence.enqueueRun({ threadId, runId, priority });
|
|
643
|
-
}
|
|
644
|
-
await beginLease(useDirectHeartbeatFastPath ? "direct-heartbeat" : "queue-claim");
|
|
645
|
-
let released = false;
|
|
646
|
-
return async () => {
|
|
647
|
-
if (released) {
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
released = true;
|
|
651
|
-
await releaseLease();
|
|
652
|
-
this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
|
|
653
|
-
const next = shiftNextPendingRunSlot(this.pendingRunSlots);
|
|
654
|
-
void next?.activate();
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
const activateQueuedRun = async () => {
|
|
658
|
-
const currentRun = runId ? await this.persistence.getRun(runId) : null;
|
|
659
|
-
if (currentRun?.state === "cancelled") {
|
|
660
|
-
return "abort";
|
|
661
|
-
}
|
|
662
|
-
this.activeRunSlots += 1;
|
|
663
|
-
if (threadId && runId) {
|
|
664
|
-
await this.emit(threadId, runId, 4, "run.dequeued", {
|
|
665
|
-
queuePosition: 0,
|
|
666
|
-
activeRunCount: this.activeRunSlots,
|
|
667
|
-
maxConcurrentRuns,
|
|
668
|
-
priority,
|
|
669
|
-
});
|
|
670
|
-
await this.setRunStateAndEmit(threadId, runId, 5, activeState, {
|
|
671
|
-
previousState: "queued",
|
|
672
|
-
});
|
|
673
|
-
await beginLease("queue-claim");
|
|
674
|
-
}
|
|
675
|
-
return "activate";
|
|
676
|
-
};
|
|
677
|
-
if (threadId && runId) {
|
|
678
|
-
await this.persistence.enqueueRun({ threadId, runId, priority });
|
|
679
|
-
const slotAcquisition = new Promise((resolve, reject) => {
|
|
680
|
-
const displacedEntries = this.enqueuePendingRunSlot({
|
|
681
|
-
threadId,
|
|
682
|
-
runId,
|
|
683
|
-
priority,
|
|
684
|
-
activate: async () => {
|
|
685
|
-
try {
|
|
686
|
-
resolve(await activateQueuedRun());
|
|
687
|
-
}
|
|
688
|
-
catch (error) {
|
|
689
|
-
reject(error);
|
|
690
|
-
}
|
|
691
|
-
},
|
|
692
|
-
abort: () => resolve("abort"),
|
|
693
|
-
});
|
|
694
|
-
void Promise.all(displacedEntries.map((candidate) => this.emit(candidate.threadId, candidate.runId, 3, "run.queued", {
|
|
695
|
-
queuePosition: candidate.queuePosition,
|
|
696
|
-
activeRunCount: this.activeRunSlots,
|
|
697
|
-
maxConcurrentRuns,
|
|
698
|
-
priority: candidate.priority,
|
|
699
|
-
})));
|
|
700
|
-
});
|
|
701
|
-
const queuePosition = this.pendingRunSlots.findIndex((entry) => entry.runId === runId) + 1;
|
|
702
|
-
await this.setRunStateAndEmit(threadId, runId, 2, "queued", {
|
|
703
|
-
previousState: activeState,
|
|
704
|
-
});
|
|
705
|
-
await this.emit(threadId, runId, 3, "run.queued", {
|
|
706
|
-
queuePosition,
|
|
707
|
-
activeRunCount: this.activeRunSlots,
|
|
708
|
-
maxConcurrentRuns,
|
|
709
|
-
priority,
|
|
710
|
-
});
|
|
711
|
-
const slotAcquisitionResult = await slotAcquisition;
|
|
712
|
-
if (slotAcquisitionResult === "abort") {
|
|
713
|
-
return async () => undefined;
|
|
714
|
-
}
|
|
715
|
-
let released = false;
|
|
716
|
-
return async () => {
|
|
717
|
-
if (released) {
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
released = true;
|
|
721
|
-
await releaseLease();
|
|
722
|
-
this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
|
|
723
|
-
const next = this.pendingRunSlots.shift();
|
|
724
|
-
void next?.activate();
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
let released = false;
|
|
728
|
-
return async () => {
|
|
729
|
-
if (released) {
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
released = true;
|
|
733
|
-
await releaseLease();
|
|
734
|
-
this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
|
|
735
|
-
const next = shiftNextPendingRunSlot(this.pendingRunSlots);
|
|
736
|
-
void next?.activate();
|
|
737
|
-
};
|
|
555
|
+
return acquireHarnessRunSlot({
|
|
556
|
+
persistence: this.persistence,
|
|
557
|
+
workerId: this.workerId,
|
|
558
|
+
concurrencyConfig: this.concurrencyConfig,
|
|
559
|
+
pendingRunSlots: this.pendingRunSlots,
|
|
560
|
+
getActiveRunSlots: () => this.activeRunSlots,
|
|
561
|
+
setActiveRunSlots: (count) => {
|
|
562
|
+
this.activeRunSlots = count;
|
|
563
|
+
},
|
|
564
|
+
enqueuePendingRunSlot: (entry) => this.enqueuePendingRunSlot(entry),
|
|
565
|
+
emit: (currentThreadId, currentRunId, sequence, eventType, payload) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload),
|
|
566
|
+
setRunStateAndEmit: (currentThreadId, currentRunId, sequence, state, stateOptions) => this.setRunStateAndEmit(currentThreadId, currentRunId, sequence, state, stateOptions),
|
|
567
|
+
}, {
|
|
568
|
+
threadId,
|
|
569
|
+
runId,
|
|
570
|
+
activeState,
|
|
571
|
+
priority,
|
|
572
|
+
});
|
|
738
573
|
}
|
|
739
574
|
dropPendingRunSlot(runId) {
|
|
740
575
|
return dropPendingRunSlot(this.pendingRunSlots, runId);
|
|
@@ -801,8 +636,6 @@ export class AgentHarnessRuntime {
|
|
|
801
636
|
}
|
|
802
637
|
return;
|
|
803
638
|
}
|
|
804
|
-
let emitted = false;
|
|
805
|
-
let streamActivityObserved = false;
|
|
806
639
|
const { threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await this.prepareRunStart(options, invocation, (_binding, activeSelectedAgentId) => ({
|
|
807
640
|
agentId: activeSelectedAgentId,
|
|
808
641
|
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
@@ -810,345 +643,55 @@ export class AgentHarnessRuntime {
|
|
|
810
643
|
input: options.input,
|
|
811
644
|
state: "running",
|
|
812
645
|
}));
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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: {
|
|
869
|
-
threadId,
|
|
870
|
-
runId,
|
|
871
|
-
agentId: selectedAgentId,
|
|
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) {
|
|
908
|
-
continue;
|
|
909
|
-
}
|
|
910
|
-
lastToolResultKey = toolResultKey;
|
|
911
|
-
if (normalizedChunk.isError) {
|
|
912
|
-
toolErrors.push(renderToolFailure(normalizedChunk.toolName, normalizedChunk.output));
|
|
913
|
-
}
|
|
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;
|
|
924
|
-
}
|
|
925
|
-
emitted = true;
|
|
926
|
-
assistantOutput += normalizedChunk.content;
|
|
927
|
-
yield await emitOutputDelta(normalizedChunk.content);
|
|
928
|
-
}
|
|
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);
|
|
939
|
-
}
|
|
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
|
-
};
|
|
977
|
-
yield {
|
|
978
|
-
type: "result",
|
|
979
|
-
result: {
|
|
980
|
-
threadId,
|
|
981
|
-
runId,
|
|
982
|
-
agentId: selectedAgentId,
|
|
983
|
-
state: "failed",
|
|
984
|
-
output: runtimeFailure,
|
|
985
|
-
finalMessageText: runtimeFailure,
|
|
986
|
-
},
|
|
987
|
-
};
|
|
988
|
-
return;
|
|
989
|
-
}
|
|
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: {
|
|
1005
|
-
threadId,
|
|
1006
|
-
runId,
|
|
1007
|
-
agentId: selectedAgentId,
|
|
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);
|
|
1020
|
-
}
|
|
1021
|
-
if (actual.output) {
|
|
1022
|
-
yield await emitOutputDelta(actual.output);
|
|
1023
|
-
}
|
|
1024
|
-
yield {
|
|
1025
|
-
type: "result",
|
|
1026
|
-
result: {
|
|
1027
|
-
...actual,
|
|
1028
|
-
threadId,
|
|
1029
|
-
runId,
|
|
1030
|
-
agentId: selectedAgentId,
|
|
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: {
|
|
1059
|
-
threadId,
|
|
1060
|
-
runId,
|
|
1061
|
-
agentId: selectedAgentId,
|
|
1062
|
-
state: "failed",
|
|
1063
|
-
output: renderRuntimeFailure(invokeError),
|
|
1064
|
-
finalMessageText: renderRuntimeFailure(invokeError),
|
|
1065
|
-
},
|
|
1066
|
-
};
|
|
1067
|
-
return;
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
finally {
|
|
1071
|
-
await this.persistence.clearRunRequest(threadId, runId);
|
|
1072
|
-
await releaseRunSlot();
|
|
1073
|
-
}
|
|
646
|
+
yield* streamHarnessRun({
|
|
647
|
+
binding,
|
|
648
|
+
input: options.input,
|
|
649
|
+
invocation,
|
|
650
|
+
threadId,
|
|
651
|
+
runId,
|
|
652
|
+
selectedAgentId,
|
|
653
|
+
isNewThread,
|
|
654
|
+
runCreatedEventPromise,
|
|
655
|
+
releaseRunSlotPromise,
|
|
656
|
+
loadPriorHistory: (currentThreadId, currentRunId) => this.loadPriorHistory(currentThreadId, currentRunId),
|
|
657
|
+
stream: (activeBinding, input, currentThreadId, priorHistory, streamOptions) => this.runtimeAdapter.stream(activeBinding, input, currentThreadId, priorHistory, streamOptions),
|
|
658
|
+
invokeWithHistory: (activeBinding, input, currentThreadId, currentRunId) => this.invokeWithHistory(activeBinding, input, currentThreadId, currentRunId),
|
|
659
|
+
emit: (currentThreadId, currentRunId, sequence, eventType, payload) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload),
|
|
660
|
+
setRunStateAndEmit: (currentThreadId, currentRunId, sequence, state, stateOptions) => this.setRunStateAndEmit(currentThreadId, currentRunId, sequence, state, stateOptions),
|
|
661
|
+
requestApprovalAndEmit: (currentThreadId, currentRunId, input, interruptContent, checkpointRef, sequence) => this.requestApprovalAndEmit(currentThreadId, currentRunId, input, interruptContent, checkpointRef, sequence),
|
|
662
|
+
appendAssistantMessage: (currentThreadId, currentRunId, content) => appendLifecycleAssistantMessage(this.persistence, currentThreadId, currentRunId, content),
|
|
663
|
+
clearRunRequest: (currentThreadId, currentRunId) => this.persistence.clearRunRequest(currentThreadId, currentRunId),
|
|
664
|
+
emitSyntheticFallback: (currentThreadId, currentRunId, currentSelectedAgentId, error) => emitSyntheticFallbackEvent({
|
|
665
|
+
persistence: this.persistence,
|
|
666
|
+
publishEvent: (event) => this.eventBus.publish(event),
|
|
667
|
+
trackBackgroundTask: (task) => this.trackBackgroundTask(task),
|
|
668
|
+
backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
|
|
669
|
+
}, currentThreadId, currentRunId, currentSelectedAgentId, error),
|
|
670
|
+
});
|
|
1074
671
|
}
|
|
1075
672
|
async resume(options) {
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
:
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const releaseRunSlot = await this.acquireRunSlot(threadId, runId, "resuming", await this.resolvePersistedRunPriority(threadId, runId));
|
|
1099
|
-
try {
|
|
1100
|
-
await this.persistence.saveRecoveryIntent(threadId, runId, {
|
|
1101
|
-
kind: "approval-decision",
|
|
1102
|
-
savedAt: new Date().toISOString(),
|
|
1103
|
-
checkpointRef: `checkpoints/${threadId}/${runId}/cp-1`,
|
|
1104
|
-
resumePayload,
|
|
1105
|
-
attempts: 0,
|
|
1106
|
-
});
|
|
1107
|
-
await this.emit(threadId, runId, 5, "run.resumed", {
|
|
1108
|
-
resumeKind: "cross-restart",
|
|
1109
|
-
checkpointRef: `checkpoints/${threadId}/${runId}/cp-1`,
|
|
1110
|
-
state: "resuming",
|
|
1111
|
-
approvalId: approval.approvalId,
|
|
1112
|
-
pendingActionId: approval.pendingActionId,
|
|
1113
|
-
});
|
|
1114
|
-
await this.persistence.resolveApproval(threadId, runId, approval.approvalId, options.decision === "reject" ? "rejected" : options.decision === "edit" ? "edited" : "approved");
|
|
1115
|
-
await this.emit(threadId, runId, 6, "approval.resolved", {
|
|
1116
|
-
approvalId: approval.approvalId,
|
|
1117
|
-
pendingActionId: approval.pendingActionId,
|
|
1118
|
-
decision: options.decision ?? "approve",
|
|
1119
|
-
toolName: approval.toolName,
|
|
1120
|
-
});
|
|
1121
|
-
const history = await this.persistence.listThreadMessages(threadId);
|
|
1122
|
-
const priorHistory = history.filter((message) => message.runId !== runId);
|
|
1123
|
-
const runInput = await this.loadRunInput(threadId, runId);
|
|
1124
|
-
const startedAt = Date.now();
|
|
1125
|
-
try {
|
|
1126
|
-
const actual = await this.runtimeAdapter.invoke(binding, "", threadId, runId, resumePayload, priorHistory);
|
|
1127
|
-
this.recordLlmSuccess(startedAt);
|
|
1128
|
-
const cancelledAfterInvoke = await this.getRunCancellation(runId);
|
|
1129
|
-
if (cancelledAfterInvoke.requested) {
|
|
1130
|
-
return this.finalizeCancelledRun(threadId, runId, "resuming", cancelledAfterInvoke.reason);
|
|
1131
|
-
}
|
|
1132
|
-
await this.persistence.clearRecoveryIntent(threadId, runId);
|
|
1133
|
-
const finalized = await this.finalizeContinuedRun(binding, threadId, runId, runInput, actual, {
|
|
1134
|
-
previousState: "resuming",
|
|
1135
|
-
stateSequence: 7,
|
|
1136
|
-
approvalSequence: 8,
|
|
1137
|
-
});
|
|
1138
|
-
return {
|
|
1139
|
-
...finalized,
|
|
1140
|
-
approvalId: finalized.approvalId ?? approval.approvalId,
|
|
1141
|
-
pendingActionId: finalized.pendingActionId ?? approval.pendingActionId,
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
catch (error) {
|
|
1145
|
-
this.recordLlmFailure(startedAt);
|
|
1146
|
-
throw error;
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
finally {
|
|
1150
|
-
await releaseRunSlot();
|
|
1151
|
-
}
|
|
673
|
+
return resumeRun({
|
|
674
|
+
getApprovalById: (approvalId) => this.persistence.getApproval(approvalId),
|
|
675
|
+
getSession: (threadId) => this.getSession(threadId),
|
|
676
|
+
resolveApprovalRecord: (resumeOptions, thread) => resolveHarnessApprovalRecord(this.persistence, resumeOptions, thread),
|
|
677
|
+
getBinding: (agentId) => this.workspace.bindings.get(agentId),
|
|
678
|
+
buildResumePayload: (binding, approval, resumeOptions) => buildHarnessResumePayload(binding, approval, resumeOptions),
|
|
679
|
+
getRunCancellation: (runId) => this.getRunCancellation(runId),
|
|
680
|
+
finalizeCancelledRun: (threadId, runId, previousState, reason) => this.finalizeCancelledRun(threadId, runId, previousState, reason),
|
|
681
|
+
setRunState: (threadId, runId, state, checkpointRef) => this.persistence.setRunState(threadId, runId, state, checkpointRef),
|
|
682
|
+
acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
|
|
683
|
+
resolvePersistedRunPriority: (threadId, runId) => this.resolvePersistedRunPriority(threadId, runId),
|
|
684
|
+
saveRecoveryIntent: (threadId, runId, payload) => this.persistence.saveRecoveryIntent(threadId, runId, payload),
|
|
685
|
+
emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
|
|
686
|
+
resolveApproval: (threadId, runId, approvalId, resolution) => this.persistence.resolveApproval(threadId, runId, approvalId, resolution),
|
|
687
|
+
listThreadMessages: (threadId) => this.persistence.listThreadMessages(threadId),
|
|
688
|
+
loadRunInput: (threadId, runId) => this.loadRunInput(threadId, runId),
|
|
689
|
+
invoke: (binding, input, threadId, runId, resumePayload, priorHistory) => this.runtimeAdapter.invoke(binding, input, threadId, runId, resumePayload, priorHistory),
|
|
690
|
+
recordLlmSuccess: (startedAt) => this.recordLlmSuccess(startedAt),
|
|
691
|
+
recordLlmFailure: (startedAt) => this.recordLlmFailure(startedAt),
|
|
692
|
+
clearRecoveryIntent: (threadId, runId) => this.persistence.clearRecoveryIntent(threadId, runId),
|
|
693
|
+
finalizeContinuedRun: (binding, threadId, runId, input, actual, operationOptions) => this.finalizeContinuedRun(binding, threadId, runId, input, actual, operationOptions),
|
|
694
|
+
}, options);
|
|
1152
695
|
}
|
|
1153
696
|
async restartConversation(options) {
|
|
1154
697
|
const thread = await this.getSession(options.threadId);
|
|
@@ -1187,96 +730,35 @@ export class AgentHarnessRuntime {
|
|
|
1187
730
|
await this.close();
|
|
1188
731
|
}
|
|
1189
732
|
async cancelRun(options) {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
runId: run.runId,
|
|
1198
|
-
agentId: run.agentId,
|
|
1199
|
-
state: run.state,
|
|
1200
|
-
output: run.state,
|
|
1201
|
-
};
|
|
1202
|
-
}
|
|
1203
|
-
await this.persistence.requestRunCancel(run.runId, options.reason);
|
|
1204
|
-
if (run.state === "queued" || run.state === "waiting_for_approval" || run.state === "claimed") {
|
|
1205
|
-
if (run.state === "queued") {
|
|
1206
|
-
this.dropPendingRunSlot(run.runId);
|
|
1207
|
-
}
|
|
1208
|
-
return this.finalizeCancelledRun(run.threadId, run.runId, run.state, options.reason);
|
|
1209
|
-
}
|
|
1210
|
-
await this.setRunStateAndEmit(run.threadId, run.runId, 103, "cancelling", {
|
|
1211
|
-
previousState: run.state,
|
|
1212
|
-
...(options.reason ? { error: options.reason } : {}),
|
|
1213
|
-
});
|
|
1214
|
-
return {
|
|
1215
|
-
threadId: run.threadId,
|
|
1216
|
-
runId: run.runId,
|
|
1217
|
-
agentId: run.agentId,
|
|
1218
|
-
state: "cancelling",
|
|
1219
|
-
output: options.reason ? `cancelling: ${options.reason}` : "cancelling",
|
|
1220
|
-
};
|
|
733
|
+
return cancelRunOperation({
|
|
734
|
+
getRun: (runId) => this.persistence.getRun(runId),
|
|
735
|
+
requestRunCancel: (runId, reason) => this.persistence.requestRunCancel(runId, reason),
|
|
736
|
+
dropPendingRunSlot: (runId) => this.dropPendingRunSlot(runId),
|
|
737
|
+
finalizeCancelledRun: (threadId, runId, previousState, reason) => this.finalizeCancelledRun(threadId, runId, previousState, reason),
|
|
738
|
+
setRunStateAndEmit: (threadId, runId, sequence, state, stateOptions) => this.setRunStateAndEmit(threadId, runId, sequence, state, stateOptions),
|
|
739
|
+
}, options);
|
|
1221
740
|
}
|
|
1222
741
|
async recoverStartupRuns() {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
for (const thread of threads) {
|
|
1230
|
-
const handled = await recoverQueuedStartupRun(recoveryContext, thread) ||
|
|
1231
|
-
await recoverRunningStartupRun(recoveryContext, thread) ||
|
|
1232
|
-
await recoverResumingStartupRun(recoveryContext, thread);
|
|
1233
|
-
if (handled) {
|
|
1234
|
-
continue;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
742
|
+
await recoverHarnessStartupRuns({
|
|
743
|
+
recoveryConfig: this.recoveryConfig,
|
|
744
|
+
persistence: this.persistence,
|
|
745
|
+
createStartupRecoveryContext: () => this.createStartupRecoveryContext(),
|
|
746
|
+
reclaimExpiredClaimedRuns: (nowIso) => this.reclaimExpiredClaimedRuns(nowIso),
|
|
747
|
+
});
|
|
1237
748
|
}
|
|
1238
749
|
async reclaimExpiredClaimedRuns(nowIso = new Date().toISOString()) {
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
const lifecycle = await this.persistence.getRunLifecycle(claim.threadId, claim.runId);
|
|
1247
|
-
if (lifecycle.state === "claimed") {
|
|
1248
|
-
await this.persistence.enqueueRun({
|
|
1249
|
-
threadId: claim.threadId,
|
|
1250
|
-
runId: claim.runId,
|
|
1251
|
-
priority: claim.priority,
|
|
1252
|
-
queueKey: claim.queueKey,
|
|
1253
|
-
availableAt: nowIso,
|
|
1254
|
-
});
|
|
1255
|
-
await this.setRunStateAndEmit(claim.threadId, claim.runId, 99, "queued", {
|
|
1256
|
-
previousState: "claimed",
|
|
1257
|
-
});
|
|
1258
|
-
await this.emit(claim.threadId, claim.runId, 100, "run.queued", {
|
|
1259
|
-
queuePosition: 0,
|
|
1260
|
-
activeRunCount: this.activeRunSlots,
|
|
1261
|
-
maxConcurrentRuns: this.concurrencyConfig.maxConcurrentRuns,
|
|
1262
|
-
recoveredOnStartup: true,
|
|
1263
|
-
reclaimReason: "expired-lease",
|
|
1264
|
-
});
|
|
1265
|
-
continue;
|
|
1266
|
-
}
|
|
1267
|
-
await this.persistence.releaseRunClaim(claim.runId);
|
|
1268
|
-
}
|
|
750
|
+
await reclaimHarnessExpiredClaimedRuns({
|
|
751
|
+
persistence: this.persistence,
|
|
752
|
+
setRunStateAndEmit: (threadId, runId, sequence, state, options) => this.setRunStateAndEmit(threadId, runId, sequence, state, options),
|
|
753
|
+
emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
|
|
754
|
+
concurrencyConfig: this.concurrencyConfig,
|
|
755
|
+
getActiveRunSlots: () => this.activeRunSlots,
|
|
756
|
+
}, nowIso);
|
|
1269
757
|
}
|
|
1270
758
|
async isStaleRunningRun(thread, nowMs = Date.now()) {
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
}
|
|
1276
|
-
const heartbeatAtMs = Date.parse(heartbeatAt);
|
|
1277
|
-
if (!Number.isFinite(heartbeatAtMs)) {
|
|
1278
|
-
return true;
|
|
1279
|
-
}
|
|
1280
|
-
return nowMs - heartbeatAtMs >= this.concurrencyConfig.heartbeatTimeoutMs;
|
|
759
|
+
return isHarnessStaleRunningRun({
|
|
760
|
+
persistence: this.persistence,
|
|
761
|
+
concurrencyConfig: this.concurrencyConfig,
|
|
762
|
+
}, thread, nowMs);
|
|
1281
763
|
}
|
|
1282
764
|
}
|