@botbotgo/agent-harness 0.0.101 → 0.0.102

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 (29) hide show
  1. package/dist/package-version.d.ts +1 -1
  2. package/dist/package-version.js +1 -1
  3. package/dist/persistence/sqlite-run-context-store.d.ts +22 -0
  4. package/dist/persistence/sqlite-run-context-store.js +64 -0
  5. package/dist/persistence/sqlite-run-queue-store.d.ts +41 -0
  6. package/dist/persistence/sqlite-run-queue-store.js +120 -0
  7. package/dist/persistence/sqlite-store.d.ts +2 -2
  8. package/dist/persistence/sqlite-store.js +31 -117
  9. package/dist/resource/mcp-tool-support.d.ts +21 -0
  10. package/dist/resource/mcp-tool-support.js +173 -0
  11. package/dist/resource/resource-impl.d.ts +1 -18
  12. package/dist/resource/resource-impl.js +3 -166
  13. package/dist/runtime/adapter/invoke-runtime.d.ts +22 -0
  14. package/dist/runtime/adapter/invoke-runtime.js +18 -0
  15. package/dist/runtime/adapter/stream-runtime.d.ts +46 -0
  16. package/dist/runtime/adapter/stream-runtime.js +93 -0
  17. package/dist/runtime/agent-runtime-adapter.js +93 -168
  18. package/dist/runtime/harness/run/run-operations.d.ts +50 -0
  19. package/dist/runtime/harness/run/run-operations.js +113 -0
  20. package/dist/runtime/harness/run/run-slot-acquisition.d.ts +64 -0
  21. package/dist/runtime/harness/run/run-slot-acquisition.js +157 -0
  22. package/dist/runtime/harness/run/stream-run.d.ts +53 -0
  23. package/dist/runtime/harness/run/stream-run.js +304 -0
  24. package/dist/runtime/harness.js +79 -528
  25. package/dist/workspace/object-loader.d.ts +1 -8
  26. package/dist/workspace/object-loader.js +3 -197
  27. package/dist/workspace/yaml-object-reader.d.ts +15 -0
  28. package/dist/workspace/yaml-object-reader.js +202 -0
  29. package/package.json +1 -1
@@ -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 { AGENT_INTERRUPT_SENTINEL_PREFIX, AgentRuntimeAdapter, RuntimeOperationTimeoutError } from "./agent-runtime-adapter.js";
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, renderToolFailure, } from "./support/harness-support.js";
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";
@@ -18,9 +17,11 @@ import { extractMessageText, normalizeMessageContent } from "../utils/message-co
18
17
  import { buildPersistedRunRequest, isTerminalRunState, normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, toPublicApprovalRecord, } 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 { createContentBlocksItem as createStreamingContentBlocksItem, createToolResultKey as createStreamingToolResultKey, dispatchRunListeners as dispatchStreamingRunListeners, emitOutputDeltaAndCreateItem as emitStreamingOutputDeltaAndCreateItem, } from "./harness/events/streaming.js";
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 { dropPendingRunSlot, enqueuePendingRunSlot, shiftNextPendingRunSlot } from "./harness/run/run-queue.js";
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";
@@ -29,6 +30,7 @@ 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
32
  import { recoverQueuedStartupRun, recoverResumingStartupRun, recoverRunningStartupRun, } from "./harness/run/recovery.js";
33
+ import { streamHarnessRun } from "./harness/run/stream-run.js";
32
34
  export class AgentHarnessRuntime {
33
35
  workspace;
34
36
  runtimeAdapterOptions;
@@ -582,159 +584,24 @@ export class AgentHarnessRuntime {
582
584
  };
583
585
  }
584
586
  async acquireRunSlot(threadId, runId, activeState = "running", priority = 0) {
585
- let stopHeartbeat = () => undefined;
586
- const beginLease = async (mode) => {
587
- if (!threadId || !runId) {
588
- return;
589
- }
590
- const claimedAt = new Date().toISOString();
591
- if (mode === "queue-claim") {
592
- await this.persistence.claimQueuedRun({
593
- threadId,
594
- runId,
595
- workerId: this.workerId,
596
- claimedAt,
597
- leaseExpiresAt: new Date(Date.now() + this.concurrencyConfig.leaseMs).toISOString(),
598
- });
599
- }
600
- else {
601
- await this.persistence.renewRunLease({
602
- runId,
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
- };
587
+ return acquireHarnessRunSlot({
588
+ persistence: this.persistence,
589
+ workerId: this.workerId,
590
+ concurrencyConfig: this.concurrencyConfig,
591
+ pendingRunSlots: this.pendingRunSlots,
592
+ getActiveRunSlots: () => this.activeRunSlots,
593
+ setActiveRunSlots: (count) => {
594
+ this.activeRunSlots = count;
595
+ },
596
+ enqueuePendingRunSlot: (entry) => this.enqueuePendingRunSlot(entry),
597
+ emit: (currentThreadId, currentRunId, sequence, eventType, payload) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload),
598
+ setRunStateAndEmit: (currentThreadId, currentRunId, sequence, state, stateOptions) => this.setRunStateAndEmit(currentThreadId, currentRunId, sequence, state, stateOptions),
599
+ }, {
600
+ threadId,
601
+ runId,
602
+ activeState,
603
+ priority,
604
+ });
738
605
  }
739
606
  dropPendingRunSlot(runId) {
740
607
  return dropPendingRunSlot(this.pendingRunSlots, runId);
@@ -801,8 +668,6 @@ export class AgentHarnessRuntime {
801
668
  }
802
669
  return;
803
670
  }
804
- let emitted = false;
805
- let streamActivityObserved = false;
806
671
  const { threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await this.prepareRunStart(options, invocation, (_binding, activeSelectedAgentId) => ({
807
672
  agentId: activeSelectedAgentId,
808
673
  requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
@@ -810,345 +675,55 @@ export class AgentHarnessRuntime {
810
675
  input: options.input,
811
676
  state: "running",
812
677
  }));
813
- const priorHistoryPromise = Promise.resolve(isNewThread ? [] : undefined).then((historyHint) => historyHint ?? this.loadPriorHistory(threadId, runId));
814
- yield { type: "event", event: await runCreatedEventPromise };
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);
817
- try {
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: {
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
- }
678
+ yield* streamHarnessRun({
679
+ binding,
680
+ input: options.input,
681
+ invocation,
682
+ threadId,
683
+ runId,
684
+ selectedAgentId,
685
+ isNewThread,
686
+ runCreatedEventPromise,
687
+ releaseRunSlotPromise,
688
+ loadPriorHistory: (currentThreadId, currentRunId) => this.loadPriorHistory(currentThreadId, currentRunId),
689
+ stream: (activeBinding, input, currentThreadId, priorHistory, streamOptions) => this.runtimeAdapter.stream(activeBinding, input, currentThreadId, priorHistory, streamOptions),
690
+ invokeWithHistory: (activeBinding, input, currentThreadId, currentRunId) => this.invokeWithHistory(activeBinding, input, currentThreadId, currentRunId),
691
+ emit: (currentThreadId, currentRunId, sequence, eventType, payload) => this.emit(currentThreadId, currentRunId, sequence, eventType, payload),
692
+ setRunStateAndEmit: (currentThreadId, currentRunId, sequence, state, stateOptions) => this.setRunStateAndEmit(currentThreadId, currentRunId, sequence, state, stateOptions),
693
+ requestApprovalAndEmit: (currentThreadId, currentRunId, input, interruptContent, checkpointRef, sequence) => this.requestApprovalAndEmit(currentThreadId, currentRunId, input, interruptContent, checkpointRef, sequence),
694
+ appendAssistantMessage: (currentThreadId, currentRunId, content) => appendLifecycleAssistantMessage(this.persistence, currentThreadId, currentRunId, content),
695
+ clearRunRequest: (currentThreadId, currentRunId) => this.persistence.clearRunRequest(currentThreadId, currentRunId),
696
+ emitSyntheticFallback: (currentThreadId, currentRunId, currentSelectedAgentId, error) => emitSyntheticFallbackEvent({
697
+ persistence: this.persistence,
698
+ publishEvent: (event) => this.eventBus.publish(event),
699
+ trackBackgroundTask: (task) => this.trackBackgroundTask(task),
700
+ backgroundEventTypes: AgentHarnessRuntime.BACKGROUND_EVENT_TYPES,
701
+ }, currentThreadId, currentRunId, currentSelectedAgentId, error),
702
+ });
1074
703
  }
1075
704
  async resume(options) {
1076
- const approvalById = options.approvalId ? await this.persistence.getApproval(options.approvalId) : null;
1077
- const thread = options.threadId
1078
- ? await this.getSession(options.threadId)
1079
- : approvalById
1080
- ? await this.getSession(approvalById.threadId)
1081
- : null;
1082
- if (!thread) {
1083
- throw new Error("resume requires either threadId or approvalId");
1084
- }
1085
- const approval = approvalById ?? await resolveHarnessApprovalRecord(this.persistence, options, thread);
1086
- const threadId = approval.threadId;
1087
- const runId = approval.runId;
1088
- const binding = this.workspace.bindings.get(thread.agentId);
1089
- if (!binding) {
1090
- throw new Error(`Unknown agent ${thread.agentId}`);
1091
- }
1092
- const resumePayload = buildHarnessResumePayload(binding, approval, options);
1093
- const cancellation = await this.getRunCancellation(runId);
1094
- if (cancellation.requested) {
1095
- return this.finalizeCancelledRun(threadId, runId, thread.status, cancellation.reason);
1096
- }
1097
- await this.persistence.setRunState(threadId, runId, "resuming", `checkpoints/${threadId}/${runId}/cp-1`);
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
- }
705
+ return resumeRun({
706
+ getApprovalById: (approvalId) => this.persistence.getApproval(approvalId),
707
+ getSession: (threadId) => this.getSession(threadId),
708
+ resolveApprovalRecord: (resumeOptions, thread) => resolveHarnessApprovalRecord(this.persistence, resumeOptions, thread),
709
+ getBinding: (agentId) => this.workspace.bindings.get(agentId),
710
+ buildResumePayload: (binding, approval, resumeOptions) => buildHarnessResumePayload(binding, approval, resumeOptions),
711
+ getRunCancellation: (runId) => this.getRunCancellation(runId),
712
+ finalizeCancelledRun: (threadId, runId, previousState, reason) => this.finalizeCancelledRun(threadId, runId, previousState, reason),
713
+ setRunState: (threadId, runId, state, checkpointRef) => this.persistence.setRunState(threadId, runId, state, checkpointRef),
714
+ acquireRunSlot: (threadId, runId, activeState, priority) => this.acquireRunSlot(threadId, runId, activeState, priority),
715
+ resolvePersistedRunPriority: (threadId, runId) => this.resolvePersistedRunPriority(threadId, runId),
716
+ saveRecoveryIntent: (threadId, runId, payload) => this.persistence.saveRecoveryIntent(threadId, runId, payload),
717
+ emit: (threadId, runId, sequence, eventType, payload) => this.emit(threadId, runId, sequence, eventType, payload),
718
+ resolveApproval: (threadId, runId, approvalId, resolution) => this.persistence.resolveApproval(threadId, runId, approvalId, resolution),
719
+ listThreadMessages: (threadId) => this.persistence.listThreadMessages(threadId),
720
+ loadRunInput: (threadId, runId) => this.loadRunInput(threadId, runId),
721
+ invoke: (binding, input, threadId, runId, resumePayload, priorHistory) => this.runtimeAdapter.invoke(binding, input, threadId, runId, resumePayload, priorHistory),
722
+ recordLlmSuccess: (startedAt) => this.recordLlmSuccess(startedAt),
723
+ recordLlmFailure: (startedAt) => this.recordLlmFailure(startedAt),
724
+ clearRecoveryIntent: (threadId, runId) => this.persistence.clearRecoveryIntent(threadId, runId),
725
+ finalizeContinuedRun: (binding, threadId, runId, input, actual, operationOptions) => this.finalizeContinuedRun(binding, threadId, runId, input, actual, operationOptions),
726
+ }, options);
1152
727
  }
1153
728
  async restartConversation(options) {
1154
729
  const thread = await this.getSession(options.threadId);
@@ -1187,37 +762,13 @@ export class AgentHarnessRuntime {
1187
762
  await this.close();
1188
763
  }
1189
764
  async cancelRun(options) {
1190
- const run = await this.persistence.getRun(options.runId);
1191
- if (!run) {
1192
- throw new Error(`Unknown run ${options.runId}`);
1193
- }
1194
- if (isTerminalRunState(run.state)) {
1195
- return {
1196
- threadId: run.threadId,
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
- };
765
+ return cancelRunOperation({
766
+ getRun: (runId) => this.persistence.getRun(runId),
767
+ requestRunCancel: (runId, reason) => this.persistence.requestRunCancel(runId, reason),
768
+ dropPendingRunSlot: (runId) => this.dropPendingRunSlot(runId),
769
+ finalizeCancelledRun: (threadId, runId, previousState, reason) => this.finalizeCancelledRun(threadId, runId, previousState, reason),
770
+ setRunStateAndEmit: (threadId, runId, sequence, state, stateOptions) => this.setRunStateAndEmit(threadId, runId, sequence, state, stateOptions),
771
+ }, options);
1221
772
  }
1222
773
  async recoverStartupRuns() {
1223
774
  if (!this.recoveryConfig.enabled) {