@botbotgo/agent-harness 0.0.41 → 0.0.42
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/README.md +86 -86
- package/dist/contracts/types.d.ts +8 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/agent-runtime-adapter.d.ts +0 -1
- package/dist/runtime/agent-runtime-adapter.js +9 -41
- package/dist/runtime/checkpoint-maintenance.js +1 -1
- package/dist/runtime/declared-middleware.d.ts +8 -1
- package/dist/runtime/declared-middleware.js +18 -1
- package/dist/runtime/harness.d.ts +4 -0
- package/dist/runtime/harness.js +303 -255
- package/dist/runtime/support/runtime-factories.d.ts +1 -1
- package/dist/runtime/support/runtime-factories.js +3 -0
- package/dist/workspace/agent-binding-compiler.js +11 -8
- package/dist/workspace/object-loader.js +7 -2
- package/dist/workspace/support/workspace-ref-utils.d.ts +4 -0
- package/dist/workspace/support/workspace-ref-utils.js +15 -1
- package/dist/workspace/validate.js +5 -0
- package/package.json +2 -2
package/dist/runtime/harness.js
CHANGED
|
@@ -5,7 +5,7 @@ import { AGENT_INTERRUPT_SENTINEL_PREFIX, AgentRuntimeAdapter, RuntimeOperationT
|
|
|
5
5
|
import { createResourceBackendResolver, createResourceToolResolver } from "../resource/resource.js";
|
|
6
6
|
import { EventBus } from "./event-bus.js";
|
|
7
7
|
import { PolicyEngine } from "./policy-engine.js";
|
|
8
|
-
import { getRecoveryConfig, getRoutingDefaultAgentId, getRoutingRules, getRoutingSystemPrompt, isModelRoutingEnabled, matchRoutingRule, } from "../workspace/support/workspace-ref-utils.js";
|
|
8
|
+
import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getRoutingRules, getRoutingSystemPrompt, isModelRoutingEnabled, matchRoutingRule, } from "../workspace/support/workspace-ref-utils.js";
|
|
9
9
|
import { createHarnessEvent, createPendingApproval, heuristicRoute, inferRoutingBindings, renderRuntimeFailure, renderToolFailure, } from "./support/harness-support.js";
|
|
10
10
|
import { createCheckpointerForConfig, createStoreForConfig } from "./support/runtime-factories.js";
|
|
11
11
|
import { resolveCompiledEmbeddingModel, resolveCompiledEmbeddingModelRef } from "./support/embedding-models.js";
|
|
@@ -36,6 +36,9 @@ export class AgentHarnessRuntime {
|
|
|
36
36
|
resolvedRuntimeAdapterOptions;
|
|
37
37
|
checkpointMaintenance;
|
|
38
38
|
recoveryConfig;
|
|
39
|
+
concurrencyConfig;
|
|
40
|
+
activeRunSlots = 0;
|
|
41
|
+
pendingRunSlots = [];
|
|
39
42
|
listHostBindings() {
|
|
40
43
|
return inferRoutingBindings(this.workspace).hostBindings;
|
|
41
44
|
}
|
|
@@ -138,6 +141,10 @@ export class AgentHarnessRuntime {
|
|
|
138
141
|
return existing;
|
|
139
142
|
}
|
|
140
143
|
const resolvedConfig = binding.harnessRuntime.checkpointer ?? { kind: "FileCheckpointer", path: "checkpoints.json" };
|
|
144
|
+
if (typeof resolvedConfig === "boolean") {
|
|
145
|
+
this.checkpointers.set(key, resolvedConfig);
|
|
146
|
+
return resolvedConfig;
|
|
147
|
+
}
|
|
141
148
|
const saver = createCheckpointerForConfig(resolvedConfig, binding.harnessRuntime.runRoot);
|
|
142
149
|
this.checkpointers.set(key, saver);
|
|
143
150
|
return saver;
|
|
@@ -159,6 +166,7 @@ export class AgentHarnessRuntime {
|
|
|
159
166
|
? new CheckpointMaintenanceLoop(discoverCheckpointMaintenanceTargets(workspace), checkpointMaintenanceConfig)
|
|
160
167
|
: null;
|
|
161
168
|
this.recoveryConfig = getRecoveryConfig(workspace.refs);
|
|
169
|
+
this.concurrencyConfig = getConcurrencyConfig(workspace.refs);
|
|
162
170
|
}
|
|
163
171
|
async initialize() {
|
|
164
172
|
await this.persistence.initialize();
|
|
@@ -468,6 +476,28 @@ export class AgentHarnessRuntime {
|
|
|
468
476
|
}
|
|
469
477
|
await listener(value);
|
|
470
478
|
}
|
|
479
|
+
async acquireRunSlot() {
|
|
480
|
+
const maxConcurrentRuns = this.concurrencyConfig.maxConcurrentRuns;
|
|
481
|
+
if (!maxConcurrentRuns) {
|
|
482
|
+
return () => undefined;
|
|
483
|
+
}
|
|
484
|
+
if (this.activeRunSlots >= maxConcurrentRuns) {
|
|
485
|
+
await new Promise((resolve) => {
|
|
486
|
+
this.pendingRunSlots.push(resolve);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
this.activeRunSlots += 1;
|
|
490
|
+
let released = false;
|
|
491
|
+
return () => {
|
|
492
|
+
if (released) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
released = true;
|
|
496
|
+
this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
|
|
497
|
+
const next = this.pendingRunSlots.shift();
|
|
498
|
+
next?.();
|
|
499
|
+
};
|
|
500
|
+
}
|
|
471
501
|
async dispatchRunListeners(stream, listeners) {
|
|
472
502
|
let latestEvent;
|
|
473
503
|
let output = "";
|
|
@@ -529,285 +559,303 @@ export class AgentHarnessRuntime {
|
|
|
529
559
|
if (options.listeners) {
|
|
530
560
|
return this.dispatchRunListeners(this.streamEvents(options), options.listeners);
|
|
531
561
|
}
|
|
532
|
-
const
|
|
533
|
-
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
534
|
-
if (!binding) {
|
|
535
|
-
throw new Error(`Unknown agent ${selectedAgentId}`);
|
|
536
|
-
}
|
|
537
|
-
const policyDecision = this.policyEngine.evaluate(binding);
|
|
538
|
-
if (!policyDecision.allowed) {
|
|
539
|
-
throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
|
|
540
|
-
}
|
|
541
|
-
const { threadId, runId } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
|
|
542
|
-
await this.emitRunCreated(threadId, runId, {
|
|
543
|
-
agentId: binding.agent.id,
|
|
544
|
-
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
545
|
-
selectedAgentId,
|
|
546
|
-
executionMode: binding.agent.executionMode,
|
|
547
|
-
});
|
|
562
|
+
const releaseRunSlot = await this.acquireRunSlot();
|
|
548
563
|
try {
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
});
|
|
554
|
-
const finalized = await this.finalizeContinuedRun(threadId, runId, options.input, actual, {
|
|
555
|
-
previousState: null,
|
|
556
|
-
stateSequence: 3,
|
|
557
|
-
approvalSequence: 4,
|
|
558
|
-
});
|
|
559
|
-
return {
|
|
560
|
-
...finalized,
|
|
561
|
-
agentId: selectedAgentId,
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
catch (error) {
|
|
565
|
-
await this.emitSyntheticFallback(threadId, runId, selectedAgentId, error);
|
|
566
|
-
await this.setRunStateAndEmit(threadId, runId, 4, "failed", {
|
|
567
|
-
previousState: null,
|
|
568
|
-
error: error instanceof Error ? error.message : String(error),
|
|
569
|
-
});
|
|
570
|
-
return {
|
|
571
|
-
threadId,
|
|
572
|
-
runId,
|
|
573
|
-
agentId: selectedAgentId,
|
|
574
|
-
state: "failed",
|
|
575
|
-
output: renderRuntimeFailure(error),
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
async *streamEvents(options) {
|
|
580
|
-
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
581
|
-
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
582
|
-
if (!binding) {
|
|
583
|
-
const result = await this.run(options);
|
|
584
|
-
for (const line of result.output.split("\n")) {
|
|
585
|
-
yield {
|
|
586
|
-
type: "content",
|
|
587
|
-
threadId: result.threadId,
|
|
588
|
-
runId: result.runId,
|
|
589
|
-
agentId: result.agentId ?? selectedAgentId,
|
|
590
|
-
content: `${line}\n`,
|
|
591
|
-
};
|
|
564
|
+
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
565
|
+
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
566
|
+
if (!binding) {
|
|
567
|
+
throw new Error(`Unknown agent ${selectedAgentId}`);
|
|
592
568
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
569
|
+
const policyDecision = this.policyEngine.evaluate(binding);
|
|
570
|
+
if (!policyDecision.allowed) {
|
|
571
|
+
throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
|
|
572
|
+
}
|
|
573
|
+
const { threadId, runId } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
|
|
574
|
+
await this.emitRunCreated(threadId, runId, {
|
|
575
|
+
agentId: binding.agent.id,
|
|
599
576
|
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
600
577
|
selectedAgentId,
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
state: options.state,
|
|
611
|
-
files: options.files,
|
|
612
|
-
})) {
|
|
613
|
-
if (chunk) {
|
|
614
|
-
const normalizedChunk = typeof chunk === "string"
|
|
615
|
-
? chunk.startsWith(AGENT_INTERRUPT_SENTINEL_PREFIX)
|
|
616
|
-
? { kind: "interrupt", content: chunk.slice(AGENT_INTERRUPT_SENTINEL_PREFIX.length) }
|
|
617
|
-
: { kind: "content", content: chunk }
|
|
618
|
-
: chunk;
|
|
619
|
-
if (normalizedChunk.kind === "interrupt") {
|
|
620
|
-
const checkpointRef = `checkpoints/${threadId}/${runId}/cp-1`;
|
|
621
|
-
const waitingEvent = await this.setRunStateAndEmit(threadId, runId, 4, "waiting_for_approval", {
|
|
622
|
-
previousState: null,
|
|
623
|
-
checkpointRef,
|
|
624
|
-
});
|
|
625
|
-
const approvalRequest = await this.requestApprovalAndEmit(threadId, runId, options.input, normalizedChunk.content, checkpointRef, 5);
|
|
626
|
-
yield {
|
|
627
|
-
type: "event",
|
|
628
|
-
event: waitingEvent,
|
|
629
|
-
};
|
|
630
|
-
yield {
|
|
631
|
-
type: "event",
|
|
632
|
-
event: approvalRequest.event,
|
|
633
|
-
};
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
if (normalizedChunk.kind === "reasoning") {
|
|
637
|
-
await this.emit(threadId, runId, 3, "reasoning.delta", {
|
|
638
|
-
content: normalizedChunk.content,
|
|
639
|
-
});
|
|
640
|
-
yield {
|
|
641
|
-
type: "reasoning",
|
|
642
|
-
threadId,
|
|
643
|
-
runId,
|
|
644
|
-
agentId: selectedAgentId,
|
|
645
|
-
content: normalizedChunk.content,
|
|
646
|
-
};
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
if (normalizedChunk.kind === "step") {
|
|
650
|
-
yield {
|
|
651
|
-
type: "step",
|
|
652
|
-
threadId,
|
|
653
|
-
runId,
|
|
654
|
-
agentId: selectedAgentId,
|
|
655
|
-
content: normalizedChunk.content,
|
|
656
|
-
};
|
|
657
|
-
continue;
|
|
658
|
-
}
|
|
659
|
-
if (normalizedChunk.kind === "tool-result") {
|
|
660
|
-
if (normalizedChunk.isError) {
|
|
661
|
-
toolErrors.push(renderToolFailure(normalizedChunk.toolName, normalizedChunk.output));
|
|
662
|
-
}
|
|
663
|
-
yield {
|
|
664
|
-
type: "tool-result",
|
|
665
|
-
threadId,
|
|
666
|
-
runId,
|
|
667
|
-
agentId: selectedAgentId,
|
|
668
|
-
toolName: normalizedChunk.toolName,
|
|
669
|
-
output: normalizedChunk.output,
|
|
670
|
-
isError: normalizedChunk.isError,
|
|
671
|
-
};
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
emitted = true;
|
|
675
|
-
assistantOutput += normalizedChunk.content;
|
|
676
|
-
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, normalizedChunk.content);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
if (!assistantOutput && toolErrors.length > 0) {
|
|
680
|
-
assistantOutput = toolErrors.join("\n\n");
|
|
681
|
-
emitted = true;
|
|
682
|
-
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, assistantOutput);
|
|
683
|
-
}
|
|
684
|
-
if (!assistantOutput) {
|
|
685
|
-
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId);
|
|
686
|
-
if (actual.output) {
|
|
687
|
-
assistantOutput = actual.output;
|
|
688
|
-
emitted = true;
|
|
689
|
-
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, actual.output);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
await this.appendAssistantMessage(threadId, runId, assistantOutput);
|
|
693
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, "completed", {
|
|
578
|
+
executionMode: binding.agent.executionMode,
|
|
579
|
+
});
|
|
580
|
+
try {
|
|
581
|
+
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId, undefined, {
|
|
582
|
+
context: options.context,
|
|
583
|
+
state: options.state,
|
|
584
|
+
files: options.files,
|
|
585
|
+
});
|
|
586
|
+
const finalized = await this.finalizeContinuedRun(threadId, runId, options.input, actual, {
|
|
694
587
|
previousState: null,
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
error: error instanceof Error ? error.message : String(error),
|
|
703
|
-
}) };
|
|
704
|
-
return;
|
|
588
|
+
stateSequence: 3,
|
|
589
|
+
approvalSequence: 4,
|
|
590
|
+
});
|
|
591
|
+
return {
|
|
592
|
+
...finalized,
|
|
593
|
+
agentId: selectedAgentId,
|
|
594
|
+
};
|
|
705
595
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
596
|
+
catch (error) {
|
|
597
|
+
await this.emitSyntheticFallback(threadId, runId, selectedAgentId, error);
|
|
598
|
+
await this.setRunStateAndEmit(threadId, runId, 4, "failed", {
|
|
599
|
+
previousState: null,
|
|
600
|
+
error: error instanceof Error ? error.message : String(error),
|
|
601
|
+
});
|
|
602
|
+
return {
|
|
713
603
|
threadId,
|
|
714
604
|
runId,
|
|
715
605
|
agentId: selectedAgentId,
|
|
716
|
-
|
|
606
|
+
state: "failed",
|
|
607
|
+
output: renderRuntimeFailure(error),
|
|
717
608
|
};
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
finally {
|
|
612
|
+
releaseRunSlot();
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
async *streamEvents(options) {
|
|
616
|
+
const releaseRunSlot = await this.acquireRunSlot();
|
|
617
|
+
try {
|
|
618
|
+
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
619
|
+
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
620
|
+
if (!binding) {
|
|
621
|
+
const result = await this.run(options);
|
|
622
|
+
for (const line of result.output.split("\n")) {
|
|
623
|
+
yield {
|
|
624
|
+
type: "content",
|
|
625
|
+
threadId: result.threadId,
|
|
626
|
+
runId: result.runId,
|
|
627
|
+
agentId: result.agentId ?? selectedAgentId,
|
|
628
|
+
content: `${line}\n`,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
718
631
|
return;
|
|
719
632
|
}
|
|
633
|
+
let emitted = false;
|
|
634
|
+
const { threadId, runId } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
|
|
635
|
+
yield { type: "event", event: await this.emitRunCreated(threadId, runId, {
|
|
636
|
+
agentId: selectedAgentId,
|
|
637
|
+
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
638
|
+
selectedAgentId,
|
|
639
|
+
input: options.input,
|
|
640
|
+
state: "running",
|
|
641
|
+
}) };
|
|
720
642
|
try {
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
643
|
+
const priorHistory = await this.loadPriorHistory(threadId, runId);
|
|
644
|
+
let assistantOutput = "";
|
|
645
|
+
const toolErrors = [];
|
|
646
|
+
for await (const chunk of this.runtimeAdapter.stream(binding, options.input, threadId, priorHistory, {
|
|
647
|
+
context: options.context,
|
|
648
|
+
state: options.state,
|
|
649
|
+
files: options.files,
|
|
650
|
+
})) {
|
|
651
|
+
if (chunk) {
|
|
652
|
+
const normalizedChunk = typeof chunk === "string"
|
|
653
|
+
? chunk.startsWith(AGENT_INTERRUPT_SENTINEL_PREFIX)
|
|
654
|
+
? { kind: "interrupt", content: chunk.slice(AGENT_INTERRUPT_SENTINEL_PREFIX.length) }
|
|
655
|
+
: { kind: "content", content: chunk }
|
|
656
|
+
: chunk;
|
|
657
|
+
if (normalizedChunk.kind === "interrupt") {
|
|
658
|
+
const checkpointRef = `checkpoints/${threadId}/${runId}/cp-1`;
|
|
659
|
+
const waitingEvent = await this.setRunStateAndEmit(threadId, runId, 4, "waiting_for_approval", {
|
|
660
|
+
previousState: null,
|
|
661
|
+
checkpointRef,
|
|
662
|
+
});
|
|
663
|
+
const approvalRequest = await this.requestApprovalAndEmit(threadId, runId, options.input, normalizedChunk.content, checkpointRef, 5);
|
|
664
|
+
yield {
|
|
665
|
+
type: "event",
|
|
666
|
+
event: waitingEvent,
|
|
667
|
+
};
|
|
668
|
+
yield {
|
|
669
|
+
type: "event",
|
|
670
|
+
event: approvalRequest.event,
|
|
671
|
+
};
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (normalizedChunk.kind === "reasoning") {
|
|
675
|
+
await this.emit(threadId, runId, 3, "reasoning.delta", {
|
|
676
|
+
content: normalizedChunk.content,
|
|
677
|
+
});
|
|
678
|
+
yield {
|
|
679
|
+
type: "reasoning",
|
|
680
|
+
threadId,
|
|
681
|
+
runId,
|
|
682
|
+
agentId: selectedAgentId,
|
|
683
|
+
content: normalizedChunk.content,
|
|
684
|
+
};
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (normalizedChunk.kind === "step") {
|
|
688
|
+
yield {
|
|
689
|
+
type: "step",
|
|
690
|
+
threadId,
|
|
691
|
+
runId,
|
|
692
|
+
agentId: selectedAgentId,
|
|
693
|
+
content: normalizedChunk.content,
|
|
694
|
+
};
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
if (normalizedChunk.kind === "tool-result") {
|
|
698
|
+
if (normalizedChunk.isError) {
|
|
699
|
+
toolErrors.push(renderToolFailure(normalizedChunk.toolName, normalizedChunk.output));
|
|
700
|
+
}
|
|
701
|
+
yield {
|
|
702
|
+
type: "tool-result",
|
|
703
|
+
threadId,
|
|
704
|
+
runId,
|
|
705
|
+
agentId: selectedAgentId,
|
|
706
|
+
toolName: normalizedChunk.toolName,
|
|
707
|
+
output: normalizedChunk.output,
|
|
708
|
+
isError: normalizedChunk.isError,
|
|
709
|
+
};
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
emitted = true;
|
|
713
|
+
assistantOutput += normalizedChunk.content;
|
|
714
|
+
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, normalizedChunk.content);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (!assistantOutput && toolErrors.length > 0) {
|
|
718
|
+
assistantOutput = toolErrors.join("\n\n");
|
|
719
|
+
emitted = true;
|
|
720
|
+
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, assistantOutput);
|
|
725
721
|
}
|
|
726
|
-
|
|
722
|
+
if (!assistantOutput) {
|
|
723
|
+
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId);
|
|
724
|
+
if (actual.output) {
|
|
725
|
+
assistantOutput = actual.output;
|
|
726
|
+
emitted = true;
|
|
727
|
+
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, actual.output);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
await this.appendAssistantMessage(threadId, runId, assistantOutput);
|
|
731
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, "completed", {
|
|
727
732
|
previousState: null,
|
|
728
733
|
}) };
|
|
729
734
|
return;
|
|
730
735
|
}
|
|
731
|
-
catch (
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
runId,
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
736
|
+
catch (error) {
|
|
737
|
+
if (emitted) {
|
|
738
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, "failed", {
|
|
739
|
+
previousState: null,
|
|
740
|
+
error: error instanceof Error ? error.message : String(error),
|
|
741
|
+
}) };
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (error instanceof RuntimeOperationTimeoutError && error.stage === "invoke") {
|
|
745
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, "failed", {
|
|
746
|
+
previousState: null,
|
|
747
|
+
error: error.message,
|
|
748
|
+
}) };
|
|
749
|
+
yield {
|
|
750
|
+
type: "content",
|
|
751
|
+
threadId,
|
|
752
|
+
runId,
|
|
753
|
+
agentId: selectedAgentId,
|
|
754
|
+
content: renderRuntimeFailure(error),
|
|
755
|
+
};
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId);
|
|
760
|
+
await this.appendAssistantMessage(threadId, runId, actual.output);
|
|
761
|
+
if (actual.output) {
|
|
762
|
+
yield await this.emitOutputDeltaAndCreateItem(threadId, runId, selectedAgentId, actual.output);
|
|
763
|
+
}
|
|
764
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, actual.state, {
|
|
765
|
+
previousState: null,
|
|
766
|
+
}) };
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
catch (invokeError) {
|
|
770
|
+
await this.emitSyntheticFallback(threadId, runId, selectedAgentId, invokeError);
|
|
771
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, "failed", {
|
|
772
|
+
previousState: null,
|
|
773
|
+
error: invokeError instanceof Error ? invokeError.message : String(invokeError),
|
|
774
|
+
}) };
|
|
775
|
+
yield {
|
|
776
|
+
type: "content",
|
|
777
|
+
threadId,
|
|
778
|
+
runId,
|
|
779
|
+
agentId: selectedAgentId,
|
|
780
|
+
content: renderRuntimeFailure(invokeError),
|
|
781
|
+
};
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
745
784
|
}
|
|
746
785
|
}
|
|
786
|
+
finally {
|
|
787
|
+
releaseRunSlot();
|
|
788
|
+
}
|
|
747
789
|
}
|
|
748
790
|
async resume(options) {
|
|
749
|
-
const
|
|
750
|
-
const thread = options.threadId
|
|
751
|
-
? await this.getSession(options.threadId)
|
|
752
|
-
: approvalById
|
|
753
|
-
? await this.getSession(approvalById.threadId)
|
|
754
|
-
: null;
|
|
755
|
-
if (!thread) {
|
|
756
|
-
throw new Error("resume requires either threadId or approvalId");
|
|
757
|
-
}
|
|
758
|
-
const approval = approvalById ?? await this.resolveApprovalRecord(options, thread);
|
|
759
|
-
const threadId = approval.threadId;
|
|
760
|
-
const runId = approval.runId;
|
|
761
|
-
const binding = this.workspace.bindings.get(thread.agentId);
|
|
762
|
-
if (!binding) {
|
|
763
|
-
throw new Error(`Unknown agent ${thread.agentId}`);
|
|
764
|
-
}
|
|
765
|
-
await this.persistence.setRunState(threadId, runId, "resuming", `checkpoints/${threadId}/${runId}/cp-1`);
|
|
766
|
-
await this.persistence.saveRecoveryIntent(threadId, runId, {
|
|
767
|
-
kind: "approval-decision",
|
|
768
|
-
savedAt: new Date().toISOString(),
|
|
769
|
-
checkpointRef: `checkpoints/${threadId}/${runId}/cp-1`,
|
|
770
|
-
resumePayload: options.decision === "edit" && options.editedInput
|
|
771
|
-
? { decision: "edit", editedInput: options.editedInput }
|
|
772
|
-
: (options.decision ?? "approve"),
|
|
773
|
-
attempts: 0,
|
|
774
|
-
});
|
|
775
|
-
await this.emit(threadId, runId, 5, "run.resumed", {
|
|
776
|
-
resumeKind: "cross-restart",
|
|
777
|
-
checkpointRef: `checkpoints/${threadId}/${runId}/cp-1`,
|
|
778
|
-
state: "resuming",
|
|
779
|
-
approvalId: approval.approvalId,
|
|
780
|
-
pendingActionId: approval.pendingActionId,
|
|
781
|
-
});
|
|
782
|
-
await this.persistence.resolveApproval(threadId, runId, approval.approvalId, options.decision === "reject" ? "rejected" : options.decision === "edit" ? "edited" : "approved");
|
|
783
|
-
await this.emit(threadId, runId, 6, "approval.resolved", {
|
|
784
|
-
approvalId: approval.approvalId,
|
|
785
|
-
pendingActionId: approval.pendingActionId,
|
|
786
|
-
decision: options.decision ?? "approve",
|
|
787
|
-
toolName: approval.toolName,
|
|
788
|
-
});
|
|
789
|
-
const history = await this.persistence.listThreadMessages(threadId);
|
|
790
|
-
const priorHistory = history.filter((message) => message.runId !== runId);
|
|
791
|
-
const runInput = await this.loadRunInput(threadId, runId);
|
|
792
|
-
const resumeDecision = options.decision === "edit" && options.editedInput
|
|
793
|
-
? { decision: "edit", editedInput: options.editedInput }
|
|
794
|
-
: (options.decision ?? "approve");
|
|
791
|
+
const releaseRunSlot = await this.acquireRunSlot();
|
|
795
792
|
try {
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
793
|
+
const approvalById = options.approvalId ? await this.persistence.getApproval(options.approvalId) : null;
|
|
794
|
+
const thread = options.threadId
|
|
795
|
+
? await this.getSession(options.threadId)
|
|
796
|
+
: approvalById
|
|
797
|
+
? await this.getSession(approvalById.threadId)
|
|
798
|
+
: null;
|
|
799
|
+
if (!thread) {
|
|
800
|
+
throw new Error("resume requires either threadId or approvalId");
|
|
801
|
+
}
|
|
802
|
+
const approval = approvalById ?? await this.resolveApprovalRecord(options, thread);
|
|
803
|
+
const threadId = approval.threadId;
|
|
804
|
+
const runId = approval.runId;
|
|
805
|
+
const binding = this.workspace.bindings.get(thread.agentId);
|
|
806
|
+
if (!binding) {
|
|
807
|
+
throw new Error(`Unknown agent ${thread.agentId}`);
|
|
808
|
+
}
|
|
809
|
+
await this.persistence.setRunState(threadId, runId, "resuming", `checkpoints/${threadId}/${runId}/cp-1`);
|
|
810
|
+
await this.persistence.saveRecoveryIntent(threadId, runId, {
|
|
811
|
+
kind: "approval-decision",
|
|
812
|
+
savedAt: new Date().toISOString(),
|
|
813
|
+
checkpointRef: `checkpoints/${threadId}/${runId}/cp-1`,
|
|
814
|
+
resumePayload: options.decision === "edit" && options.editedInput
|
|
815
|
+
? { decision: "edit", editedInput: options.editedInput }
|
|
816
|
+
: (options.decision ?? "approve"),
|
|
817
|
+
attempts: 0,
|
|
802
818
|
});
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
819
|
+
await this.emit(threadId, runId, 5, "run.resumed", {
|
|
820
|
+
resumeKind: "cross-restart",
|
|
821
|
+
checkpointRef: `checkpoints/${threadId}/${runId}/cp-1`,
|
|
822
|
+
state: "resuming",
|
|
823
|
+
approvalId: approval.approvalId,
|
|
824
|
+
pendingActionId: approval.pendingActionId,
|
|
825
|
+
});
|
|
826
|
+
await this.persistence.resolveApproval(threadId, runId, approval.approvalId, options.decision === "reject" ? "rejected" : options.decision === "edit" ? "edited" : "approved");
|
|
827
|
+
await this.emit(threadId, runId, 6, "approval.resolved", {
|
|
828
|
+
approvalId: approval.approvalId,
|
|
829
|
+
pendingActionId: approval.pendingActionId,
|
|
830
|
+
decision: options.decision ?? "approve",
|
|
831
|
+
toolName: approval.toolName,
|
|
832
|
+
});
|
|
833
|
+
const history = await this.persistence.listThreadMessages(threadId);
|
|
834
|
+
const priorHistory = history.filter((message) => message.runId !== runId);
|
|
835
|
+
const runInput = await this.loadRunInput(threadId, runId);
|
|
836
|
+
const resumeDecision = options.decision === "edit" && options.editedInput
|
|
837
|
+
? { decision: "edit", editedInput: options.editedInput }
|
|
838
|
+
: (options.decision ?? "approve");
|
|
839
|
+
try {
|
|
840
|
+
const actual = await this.runtimeAdapter.invoke(binding, "", threadId, runId, resumeDecision, priorHistory);
|
|
841
|
+
await this.persistence.clearRecoveryIntent(threadId, runId);
|
|
842
|
+
const finalized = await this.finalizeContinuedRun(threadId, runId, runInput, actual, {
|
|
843
|
+
previousState: "resuming",
|
|
844
|
+
stateSequence: 7,
|
|
845
|
+
approvalSequence: 8,
|
|
846
|
+
});
|
|
847
|
+
return {
|
|
848
|
+
...finalized,
|
|
849
|
+
approvalId: finalized.approvalId ?? approval.approvalId,
|
|
850
|
+
pendingActionId: finalized.pendingActionId ?? approval.pendingActionId,
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
catch (error) {
|
|
854
|
+
throw error;
|
|
855
|
+
}
|
|
808
856
|
}
|
|
809
|
-
|
|
810
|
-
|
|
857
|
+
finally {
|
|
858
|
+
releaseRunSlot();
|
|
811
859
|
}
|
|
812
860
|
}
|
|
813
861
|
async restartConversation(options) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type StoreLike } from "../store.js";
|
|
2
2
|
export declare function createStoreForConfig(storeConfig: Record<string, unknown>, runRoot: string): StoreLike;
|
|
3
|
-
export declare function createCheckpointerForConfig(checkpointerConfig: Record<string, unknown
|
|
3
|
+
export declare function createCheckpointerForConfig(checkpointerConfig: Record<string, unknown> | boolean, runRoot: string): unknown;
|
|
@@ -22,6 +22,9 @@ export function createStoreForConfig(storeConfig, runRoot) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
export function createCheckpointerForConfig(checkpointerConfig, runRoot) {
|
|
25
|
+
if (typeof checkpointerConfig === "boolean") {
|
|
26
|
+
return checkpointerConfig;
|
|
27
|
+
}
|
|
25
28
|
const kind = typeof checkpointerConfig.kind === "string" ? checkpointerConfig.kind : "FileCheckpointer";
|
|
26
29
|
switch (kind) {
|
|
27
30
|
case "MemorySaver":
|