@botbotgo/agent-harness 0.0.44 → 0.0.46
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 +369 -29
- package/dist/config/embedding-models.yaml +28 -0
- package/dist/config/mcp.yaml +3 -18
- package/dist/config/models.yaml +19 -19
- package/dist/config/stores.yaml +10 -12
- package/dist/config/tools.yaml +3 -11
- package/dist/config/vector-stores.yaml +25 -0
- package/dist/config/workspace.yaml +20 -18
- package/dist/contracts/types.d.ts +4 -2
- package/dist/extensions.js +3 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.d.ts +9 -1
- package/dist/persistence/file-store.js +16 -0
- package/dist/resource/resource-impl.d.ts +1 -0
- package/dist/runtime/harness.d.ts +3 -0
- package/dist/runtime/harness.js +262 -112
- package/dist/runtime/thread-memory-sync.js +2 -0
- package/dist/tool-modules.d.ts +1 -0
- package/dist/tool-modules.js +11 -0
- package/dist/tools.d.ts +2 -0
- package/dist/workspace/object-loader.js +23 -12
- package/dist/workspace/resource-compilers.js +1 -0
- package/dist/workspace/support/workspace-ref-utils.d.ts +1 -1
- package/dist/workspace/support/workspace-ref-utils.js +1 -1
- package/dist/workspace/tool-hydration.js +1 -0
- package/package.json +1 -1
- package/dist/config/embedding-model.yaml +0 -29
- package/dist/config/vector-store.yaml +0 -26
package/dist/runtime/harness.js
CHANGED
|
@@ -213,6 +213,10 @@ export class AgentHarnessRuntime {
|
|
|
213
213
|
resolvedTool: resolvedTools[index],
|
|
214
214
|
}));
|
|
215
215
|
}
|
|
216
|
+
supportsRunningReplay(binding) {
|
|
217
|
+
const tools = getBindingPrimaryTools(binding);
|
|
218
|
+
return tools.every((tool) => tool.retryable === true);
|
|
219
|
+
}
|
|
216
220
|
async listThreads(filter) {
|
|
217
221
|
const threadSummaries = await this.persistence.listSessions();
|
|
218
222
|
if (!filter?.agentId) {
|
|
@@ -385,6 +389,72 @@ export class AgentHarnessRuntime {
|
|
|
385
389
|
const priorHistory = await this.loadPriorHistory(threadId, runId);
|
|
386
390
|
return this.runtimeAdapter.invoke(binding, input, threadId, runId, resumePayload, priorHistory, options);
|
|
387
391
|
}
|
|
392
|
+
buildPersistedRunRequest(input, invocation) {
|
|
393
|
+
const envelope = invocation.invocation ?? {
|
|
394
|
+
...(invocation.context ? { context: invocation.context } : {}),
|
|
395
|
+
...(invocation.state ? { inputs: invocation.state } : {}),
|
|
396
|
+
...(invocation.files ? { attachments: invocation.files } : {}),
|
|
397
|
+
};
|
|
398
|
+
return {
|
|
399
|
+
input: normalizeMessageContent(input),
|
|
400
|
+
invocation: envelope && Object.keys(envelope).length > 0
|
|
401
|
+
? {
|
|
402
|
+
...(envelope.context ? { context: envelope.context } : {}),
|
|
403
|
+
...(envelope.inputs ? { inputs: envelope.inputs } : {}),
|
|
404
|
+
...(envelope.attachments ? { attachments: envelope.attachments } : {}),
|
|
405
|
+
...(envelope.capabilities ? { capabilities: envelope.capabilities } : {}),
|
|
406
|
+
}
|
|
407
|
+
: undefined,
|
|
408
|
+
savedAt: new Date().toISOString(),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
async executeQueuedRun(binding, input, threadId, runId, agentId, options = {}) {
|
|
412
|
+
const previousState = options.previousState ?? "running";
|
|
413
|
+
if (previousState === "queued") {
|
|
414
|
+
await this.emit(threadId, runId, 101, "run.dequeued", {
|
|
415
|
+
queuePosition: 0,
|
|
416
|
+
activeRunCount: this.activeRunSlots,
|
|
417
|
+
maxConcurrentRuns: this.concurrencyConfig.maxConcurrentRuns,
|
|
418
|
+
recoveredOnStartup: true,
|
|
419
|
+
});
|
|
420
|
+
await this.setRunStateAndEmit(threadId, runId, 102, "running", {
|
|
421
|
+
previousState: "queued",
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
const actual = await this.invokeWithHistory(binding, input, threadId, runId, undefined, {
|
|
426
|
+
context: options.context,
|
|
427
|
+
state: options.state,
|
|
428
|
+
files: options.files,
|
|
429
|
+
});
|
|
430
|
+
const finalized = await this.finalizeContinuedRun(threadId, runId, input, actual, {
|
|
431
|
+
previousState: previousState === "queued" ? "running" : previousState,
|
|
432
|
+
stateSequence: options.stateSequence ?? 103,
|
|
433
|
+
approvalSequence: options.approvalSequence ?? 104,
|
|
434
|
+
});
|
|
435
|
+
return {
|
|
436
|
+
...finalized,
|
|
437
|
+
agentId,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
await this.emitSyntheticFallback(threadId, runId, agentId, error, 103);
|
|
442
|
+
await this.setRunStateAndEmit(threadId, runId, 104, "failed", {
|
|
443
|
+
previousState: previousState === "queued" ? "running" : previousState,
|
|
444
|
+
error: error instanceof Error ? error.message : String(error),
|
|
445
|
+
});
|
|
446
|
+
return {
|
|
447
|
+
threadId,
|
|
448
|
+
runId,
|
|
449
|
+
agentId,
|
|
450
|
+
state: "failed",
|
|
451
|
+
output: renderRuntimeFailure(error),
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
finally {
|
|
455
|
+
await this.persistence.clearRunRequest(threadId, runId);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
388
458
|
checkpointRefForState(threadId, runId, state) {
|
|
389
459
|
return state === "waiting_for_approval" ? `checkpoints/${threadId}/${runId}/cp-1` : null;
|
|
390
460
|
}
|
|
@@ -492,17 +562,56 @@ export class AgentHarnessRuntime {
|
|
|
492
562
|
}
|
|
493
563
|
await listener(value);
|
|
494
564
|
}
|
|
495
|
-
async acquireRunSlot() {
|
|
565
|
+
async acquireRunSlot(threadId, runId, activeState = "running") {
|
|
496
566
|
const maxConcurrentRuns = this.concurrencyConfig.maxConcurrentRuns;
|
|
497
567
|
if (!maxConcurrentRuns) {
|
|
498
568
|
return () => undefined;
|
|
499
569
|
}
|
|
500
|
-
if (this.activeRunSlots
|
|
501
|
-
|
|
502
|
-
|
|
570
|
+
if (this.activeRunSlots < maxConcurrentRuns) {
|
|
571
|
+
this.activeRunSlots += 1;
|
|
572
|
+
let released = false;
|
|
573
|
+
return () => {
|
|
574
|
+
if (released) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
released = true;
|
|
578
|
+
this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
|
|
579
|
+
const next = this.pendingRunSlots.shift();
|
|
580
|
+
void next?.();
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
if (threadId && runId) {
|
|
584
|
+
const queuePosition = this.pendingRunSlots.length + 1;
|
|
585
|
+
await this.setRunStateAndEmit(threadId, runId, 2, "queued", {
|
|
586
|
+
previousState: activeState,
|
|
587
|
+
});
|
|
588
|
+
await this.emit(threadId, runId, 3, "run.queued", {
|
|
589
|
+
queuePosition,
|
|
590
|
+
activeRunCount: this.activeRunSlots,
|
|
591
|
+
maxConcurrentRuns,
|
|
503
592
|
});
|
|
504
593
|
}
|
|
505
|
-
|
|
594
|
+
await new Promise((resolve, reject) => {
|
|
595
|
+
this.pendingRunSlots.push(async () => {
|
|
596
|
+
try {
|
|
597
|
+
this.activeRunSlots += 1;
|
|
598
|
+
if (threadId && runId) {
|
|
599
|
+
await this.emit(threadId, runId, 4, "run.dequeued", {
|
|
600
|
+
queuePosition: 0,
|
|
601
|
+
activeRunCount: this.activeRunSlots,
|
|
602
|
+
maxConcurrentRuns,
|
|
603
|
+
});
|
|
604
|
+
await this.setRunStateAndEmit(threadId, runId, 5, activeState, {
|
|
605
|
+
previousState: "queued",
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
resolve();
|
|
609
|
+
}
|
|
610
|
+
catch (error) {
|
|
611
|
+
reject(error);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
});
|
|
506
615
|
let released = false;
|
|
507
616
|
return () => {
|
|
508
617
|
if (released) {
|
|
@@ -511,7 +620,7 @@ export class AgentHarnessRuntime {
|
|
|
511
620
|
released = true;
|
|
512
621
|
this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
|
|
513
622
|
const next = this.pendingRunSlots.shift();
|
|
514
|
-
next?.();
|
|
623
|
+
void next?.();
|
|
515
624
|
};
|
|
516
625
|
}
|
|
517
626
|
async dispatchRunListeners(stream, listeners) {
|
|
@@ -587,88 +696,68 @@ export class AgentHarnessRuntime {
|
|
|
587
696
|
if (options.listeners) {
|
|
588
697
|
return this.dispatchRunListeners(this.streamEvents(options), options.listeners);
|
|
589
698
|
}
|
|
590
|
-
const
|
|
699
|
+
const invocation = this.normalizeInvocationEnvelope(options);
|
|
700
|
+
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
701
|
+
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
702
|
+
if (!binding) {
|
|
703
|
+
throw new Error(`Unknown agent ${selectedAgentId}`);
|
|
704
|
+
}
|
|
705
|
+
const policyDecision = this.policyEngine.evaluate(binding);
|
|
706
|
+
if (!policyDecision.allowed) {
|
|
707
|
+
throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
|
|
708
|
+
}
|
|
709
|
+
const { threadId, runId } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
|
|
710
|
+
await this.persistence.saveRunRequest(threadId, runId, this.buildPersistedRunRequest(options.input, invocation));
|
|
711
|
+
await this.emitRunCreated(threadId, runId, {
|
|
712
|
+
agentId: binding.agent.id,
|
|
713
|
+
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
714
|
+
selectedAgentId,
|
|
715
|
+
executionMode: getBindingAdapterKind(binding),
|
|
716
|
+
});
|
|
717
|
+
const releaseRunSlot = await this.acquireRunSlot(threadId, runId);
|
|
591
718
|
try {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
if (!policyDecision.allowed) {
|
|
600
|
-
throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
|
|
601
|
-
}
|
|
602
|
-
const { threadId, runId } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
|
|
603
|
-
await this.emitRunCreated(threadId, runId, {
|
|
604
|
-
agentId: binding.agent.id,
|
|
605
|
-
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
606
|
-
selectedAgentId,
|
|
607
|
-
executionMode: getBindingAdapterKind(binding),
|
|
719
|
+
return await this.executeQueuedRun(binding, options.input, threadId, runId, selectedAgentId, {
|
|
720
|
+
context: invocation.context,
|
|
721
|
+
state: invocation.state,
|
|
722
|
+
files: invocation.files,
|
|
723
|
+
previousState: "running",
|
|
724
|
+
stateSequence: 6,
|
|
725
|
+
approvalSequence: 7,
|
|
608
726
|
});
|
|
609
|
-
try {
|
|
610
|
-
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId, undefined, {
|
|
611
|
-
context: invocation.context,
|
|
612
|
-
state: invocation.state,
|
|
613
|
-
files: invocation.files,
|
|
614
|
-
});
|
|
615
|
-
const finalized = await this.finalizeContinuedRun(threadId, runId, options.input, actual, {
|
|
616
|
-
previousState: null,
|
|
617
|
-
stateSequence: 3,
|
|
618
|
-
approvalSequence: 4,
|
|
619
|
-
});
|
|
620
|
-
return {
|
|
621
|
-
...finalized,
|
|
622
|
-
agentId: selectedAgentId,
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
catch (error) {
|
|
626
|
-
await this.emitSyntheticFallback(threadId, runId, selectedAgentId, error);
|
|
627
|
-
await this.setRunStateAndEmit(threadId, runId, 4, "failed", {
|
|
628
|
-
previousState: null,
|
|
629
|
-
error: error instanceof Error ? error.message : String(error),
|
|
630
|
-
});
|
|
631
|
-
return {
|
|
632
|
-
threadId,
|
|
633
|
-
runId,
|
|
634
|
-
agentId: selectedAgentId,
|
|
635
|
-
state: "failed",
|
|
636
|
-
output: renderRuntimeFailure(error),
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
727
|
}
|
|
640
728
|
finally {
|
|
641
729
|
releaseRunSlot();
|
|
642
730
|
}
|
|
643
731
|
}
|
|
644
732
|
async *streamEvents(options) {
|
|
645
|
-
const
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
content: `${line}\n`,
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
return;
|
|
733
|
+
const invocation = this.normalizeInvocationEnvelope(options);
|
|
734
|
+
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
735
|
+
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
736
|
+
if (!binding) {
|
|
737
|
+
const result = await this.run(options);
|
|
738
|
+
for (const line of result.output.split("\n")) {
|
|
739
|
+
yield {
|
|
740
|
+
type: "content",
|
|
741
|
+
threadId: result.threadId,
|
|
742
|
+
runId: result.runId,
|
|
743
|
+
agentId: result.agentId ?? selectedAgentId,
|
|
744
|
+
content: `${line}\n`,
|
|
745
|
+
};
|
|
662
746
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
let emitted = false;
|
|
750
|
+
const { threadId, runId } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
|
|
751
|
+
await this.persistence.saveRunRequest(threadId, runId, this.buildPersistedRunRequest(options.input, invocation));
|
|
752
|
+
yield { type: "event", event: await this.emitRunCreated(threadId, runId, {
|
|
753
|
+
agentId: selectedAgentId,
|
|
754
|
+
requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
|
|
755
|
+
selectedAgentId,
|
|
756
|
+
input: options.input,
|
|
757
|
+
state: "running",
|
|
758
|
+
}) };
|
|
759
|
+
const releaseRunSlot = await this.acquireRunSlot(threadId, runId);
|
|
760
|
+
try {
|
|
672
761
|
try {
|
|
673
762
|
const priorHistory = await this.loadPriorHistory(threadId, runId);
|
|
674
763
|
let assistantOutput = "";
|
|
@@ -686,11 +775,11 @@ export class AgentHarnessRuntime {
|
|
|
686
775
|
: chunk;
|
|
687
776
|
if (normalizedChunk.kind === "interrupt") {
|
|
688
777
|
const checkpointRef = `checkpoints/${threadId}/${runId}/cp-1`;
|
|
689
|
-
const waitingEvent = await this.setRunStateAndEmit(threadId, runId,
|
|
690
|
-
previousState:
|
|
778
|
+
const waitingEvent = await this.setRunStateAndEmit(threadId, runId, 6, "waiting_for_approval", {
|
|
779
|
+
previousState: "running",
|
|
691
780
|
checkpointRef,
|
|
692
781
|
});
|
|
693
|
-
const approvalRequest = await this.requestApprovalAndEmit(threadId, runId, options.input, normalizedChunk.content, checkpointRef,
|
|
782
|
+
const approvalRequest = await this.requestApprovalAndEmit(threadId, runId, options.input, normalizedChunk.content, checkpointRef, 7);
|
|
694
783
|
yield {
|
|
695
784
|
type: "event",
|
|
696
785
|
event: waitingEvent,
|
|
@@ -783,22 +872,22 @@ export class AgentHarnessRuntime {
|
|
|
783
872
|
finalMessageText: assistantOutput,
|
|
784
873
|
},
|
|
785
874
|
};
|
|
786
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId,
|
|
787
|
-
previousState:
|
|
875
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "completed", {
|
|
876
|
+
previousState: "running",
|
|
788
877
|
}) };
|
|
789
878
|
return;
|
|
790
879
|
}
|
|
791
880
|
catch (error) {
|
|
792
881
|
if (emitted) {
|
|
793
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId,
|
|
794
|
-
previousState:
|
|
882
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "failed", {
|
|
883
|
+
previousState: "running",
|
|
795
884
|
error: error instanceof Error ? error.message : String(error),
|
|
796
885
|
}) };
|
|
797
886
|
return;
|
|
798
887
|
}
|
|
799
888
|
if (error instanceof RuntimeOperationTimeoutError && error.stage === "invoke") {
|
|
800
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId,
|
|
801
|
-
previousState:
|
|
889
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "failed", {
|
|
890
|
+
previousState: "running",
|
|
802
891
|
error: error.message,
|
|
803
892
|
}) };
|
|
804
893
|
yield {
|
|
@@ -836,15 +925,15 @@ export class AgentHarnessRuntime {
|
|
|
836
925
|
agentId: selectedAgentId,
|
|
837
926
|
},
|
|
838
927
|
};
|
|
839
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId,
|
|
840
|
-
previousState:
|
|
928
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, actual.state, {
|
|
929
|
+
previousState: "running",
|
|
841
930
|
}) };
|
|
842
931
|
return;
|
|
843
932
|
}
|
|
844
933
|
catch (invokeError) {
|
|
845
934
|
await this.emitSyntheticFallback(threadId, runId, selectedAgentId, invokeError);
|
|
846
|
-
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId,
|
|
847
|
-
previousState:
|
|
935
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 6, "failed", {
|
|
936
|
+
previousState: "running",
|
|
848
937
|
error: invokeError instanceof Error ? invokeError.message : String(invokeError),
|
|
849
938
|
}) };
|
|
850
939
|
yield {
|
|
@@ -870,29 +959,30 @@ export class AgentHarnessRuntime {
|
|
|
870
959
|
}
|
|
871
960
|
}
|
|
872
961
|
finally {
|
|
962
|
+
await this.persistence.clearRunRequest(threadId, runId);
|
|
873
963
|
releaseRunSlot();
|
|
874
964
|
}
|
|
875
965
|
}
|
|
876
966
|
async resume(options) {
|
|
877
|
-
const
|
|
967
|
+
const approvalById = options.approvalId ? await this.persistence.getApproval(options.approvalId) : null;
|
|
968
|
+
const thread = options.threadId
|
|
969
|
+
? await this.getSession(options.threadId)
|
|
970
|
+
: approvalById
|
|
971
|
+
? await this.getSession(approvalById.threadId)
|
|
972
|
+
: null;
|
|
973
|
+
if (!thread) {
|
|
974
|
+
throw new Error("resume requires either threadId or approvalId");
|
|
975
|
+
}
|
|
976
|
+
const approval = approvalById ?? await this.resolveApprovalRecord(options, thread);
|
|
977
|
+
const threadId = approval.threadId;
|
|
978
|
+
const runId = approval.runId;
|
|
979
|
+
const binding = this.workspace.bindings.get(thread.agentId);
|
|
980
|
+
if (!binding) {
|
|
981
|
+
throw new Error(`Unknown agent ${thread.agentId}`);
|
|
982
|
+
}
|
|
983
|
+
await this.persistence.setRunState(threadId, runId, "resuming", `checkpoints/${threadId}/${runId}/cp-1`);
|
|
984
|
+
const releaseRunSlot = await this.acquireRunSlot(threadId, runId, "resuming");
|
|
878
985
|
try {
|
|
879
|
-
const approvalById = options.approvalId ? await this.persistence.getApproval(options.approvalId) : null;
|
|
880
|
-
const thread = options.threadId
|
|
881
|
-
? await this.getSession(options.threadId)
|
|
882
|
-
: approvalById
|
|
883
|
-
? await this.getSession(approvalById.threadId)
|
|
884
|
-
: null;
|
|
885
|
-
if (!thread) {
|
|
886
|
-
throw new Error("resume requires either threadId or approvalId");
|
|
887
|
-
}
|
|
888
|
-
const approval = approvalById ?? await this.resolveApprovalRecord(options, thread);
|
|
889
|
-
const threadId = approval.threadId;
|
|
890
|
-
const runId = approval.runId;
|
|
891
|
-
const binding = this.workspace.bindings.get(thread.agentId);
|
|
892
|
-
if (!binding) {
|
|
893
|
-
throw new Error(`Unknown agent ${thread.agentId}`);
|
|
894
|
-
}
|
|
895
|
-
await this.persistence.setRunState(threadId, runId, "resuming", `checkpoints/${threadId}/${runId}/cp-1`);
|
|
896
986
|
await this.persistence.saveRecoveryIntent(threadId, runId, {
|
|
897
987
|
kind: "approval-decision",
|
|
898
988
|
savedAt: new Date().toISOString(),
|
|
@@ -978,12 +1068,72 @@ export class AgentHarnessRuntime {
|
|
|
978
1068
|
await this.close();
|
|
979
1069
|
}
|
|
980
1070
|
async recoverStartupRuns() {
|
|
981
|
-
if (!this.recoveryConfig.enabled
|
|
1071
|
+
if (!this.recoveryConfig.enabled) {
|
|
982
1072
|
return;
|
|
983
1073
|
}
|
|
984
1074
|
const threads = await this.persistence.listSessions();
|
|
985
1075
|
for (const thread of threads) {
|
|
986
|
-
if (thread.status
|
|
1076
|
+
if (thread.status === "queued") {
|
|
1077
|
+
const runMeta = await this.persistence.getRunMeta(thread.threadId, thread.latestRunId);
|
|
1078
|
+
const binding = this.workspace.bindings.get(runMeta.agentId);
|
|
1079
|
+
if (!binding) {
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
const request = await this.persistence.getRunRequest(thread.threadId, thread.latestRunId);
|
|
1083
|
+
if (!request) {
|
|
1084
|
+
await this.setRunStateAndEmit(thread.threadId, thread.latestRunId, 100, "failed", {
|
|
1085
|
+
previousState: "queued",
|
|
1086
|
+
error: "missing persisted run request for queued run recovery",
|
|
1087
|
+
});
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
const releaseRunSlot = await this.acquireRunSlot();
|
|
1091
|
+
try {
|
|
1092
|
+
await this.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, runMeta.agentId, {
|
|
1093
|
+
context: request.invocation?.context,
|
|
1094
|
+
state: request.invocation?.inputs,
|
|
1095
|
+
files: request.invocation?.attachments,
|
|
1096
|
+
previousState: "queued",
|
|
1097
|
+
stateSequence: 103,
|
|
1098
|
+
approvalSequence: 104,
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
finally {
|
|
1102
|
+
releaseRunSlot();
|
|
1103
|
+
}
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
if (thread.status === "running") {
|
|
1107
|
+
const runMeta = await this.persistence.getRunMeta(thread.threadId, thread.latestRunId);
|
|
1108
|
+
const binding = this.workspace.bindings.get(runMeta.agentId);
|
|
1109
|
+
if (!binding || !this.supportsRunningReplay(binding)) {
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
const request = await this.persistence.getRunRequest(thread.threadId, thread.latestRunId);
|
|
1113
|
+
if (!request) {
|
|
1114
|
+
continue;
|
|
1115
|
+
}
|
|
1116
|
+
const releaseRunSlot = await this.acquireRunSlot();
|
|
1117
|
+
try {
|
|
1118
|
+
await this.emit(thread.threadId, thread.latestRunId, 100, "run.resumed", {
|
|
1119
|
+
resumeKind: "startup-running-recovery",
|
|
1120
|
+
state: "running",
|
|
1121
|
+
});
|
|
1122
|
+
await this.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, runMeta.agentId, {
|
|
1123
|
+
context: request.invocation?.context,
|
|
1124
|
+
state: request.invocation?.inputs,
|
|
1125
|
+
files: request.invocation?.attachments,
|
|
1126
|
+
previousState: "running",
|
|
1127
|
+
stateSequence: 103,
|
|
1128
|
+
approvalSequence: 104,
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
finally {
|
|
1132
|
+
releaseRunSlot();
|
|
1133
|
+
}
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (thread.status !== "resuming" || !this.recoveryConfig.resumeResumingRunsOnStartup) {
|
|
987
1137
|
continue;
|
|
988
1138
|
}
|
|
989
1139
|
const binding = this.workspace.bindings.get(thread.agentId);
|
package/dist/tool-modules.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type LoadedToolModule = {
|
|
|
9
9
|
invoke: (input: unknown, context?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
10
10
|
schema: SchemaLike;
|
|
11
11
|
description: string;
|
|
12
|
+
retryable?: boolean;
|
|
12
13
|
};
|
|
13
14
|
export declare function isSupportedToolModulePath(filePath: string): boolean;
|
|
14
15
|
export declare function discoverAnnotatedFunctionNames(sourceText: string): string[];
|
package/dist/tool-modules.js
CHANGED
|
@@ -76,6 +76,7 @@ function loadToolObjectDefinition(imported, exportName) {
|
|
|
76
76
|
invoke: definition.invoke,
|
|
77
77
|
schema: normalizeToolSchema(definition.schema),
|
|
78
78
|
description: definition.description.trim(),
|
|
79
|
+
retryable: definition.retryable === true,
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
export function isSupportedToolModulePath(filePath) {
|
|
@@ -113,6 +114,11 @@ export function discoverToolModuleDefinitions(sourceText, imported) {
|
|
|
113
114
|
invoke: invoke,
|
|
114
115
|
schema,
|
|
115
116
|
description: readToolDescription(imported, implementationName, schema),
|
|
117
|
+
retryable: typeof imported[`${implementationName}Retryable`] === "boolean"
|
|
118
|
+
? imported[`${implementationName}Retryable`] === true
|
|
119
|
+
: typeof imported.retryable === "boolean"
|
|
120
|
+
? imported.retryable === true
|
|
121
|
+
: undefined,
|
|
116
122
|
});
|
|
117
123
|
}
|
|
118
124
|
return discovered;
|
|
@@ -139,5 +145,10 @@ export function loadToolModuleDefinition(imported, implementationName) {
|
|
|
139
145
|
invoke: invoke,
|
|
140
146
|
schema,
|
|
141
147
|
description: readToolDescription(imported, implementationName, schema),
|
|
148
|
+
retryable: typeof imported[`${implementationName}Retryable`] === "boolean"
|
|
149
|
+
? imported[`${implementationName}Retryable`] === true
|
|
150
|
+
: typeof imported.retryable === "boolean"
|
|
151
|
+
? imported.retryable === true
|
|
152
|
+
: undefined,
|
|
142
153
|
};
|
|
143
154
|
}
|
package/dist/tools.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type ToolDefinitionObject = {
|
|
|
5
5
|
name?: string;
|
|
6
6
|
description: string;
|
|
7
7
|
schema: SchemaInput;
|
|
8
|
+
retryable?: boolean;
|
|
8
9
|
invoke: (input: unknown, context?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
9
10
|
[TOOL_DEFINITION_MARKER]: true;
|
|
10
11
|
};
|
|
@@ -15,6 +16,7 @@ export declare function tool(definition: {
|
|
|
15
16
|
name?: string;
|
|
16
17
|
description: string;
|
|
17
18
|
schema: SchemaInput;
|
|
19
|
+
retryable?: boolean;
|
|
18
20
|
invoke: (input: unknown, context?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
19
21
|
}): ToolDefinitionObject;
|
|
20
22
|
export {};
|
|
@@ -61,7 +61,7 @@ function toArray(value) {
|
|
|
61
61
|
function asObject(value) {
|
|
62
62
|
return typeof value === "object" && value ? value : undefined;
|
|
63
63
|
}
|
|
64
|
-
function
|
|
64
|
+
function normalizeCatalogSpec(document, options = {}) {
|
|
65
65
|
const typed = asObject(document);
|
|
66
66
|
const spec = typed?.spec;
|
|
67
67
|
if (!Array.isArray(spec)) {
|
|
@@ -75,12 +75,15 @@ function normalizeNamedResourceSpec(document, kind) {
|
|
|
75
75
|
: typeof item.name === "string" && item.name.trim()
|
|
76
76
|
? item.name
|
|
77
77
|
: undefined;
|
|
78
|
-
|
|
78
|
+
const itemKind = typeof item.kind === "string" && item.kind.trim()
|
|
79
|
+
? item.kind
|
|
80
|
+
: options.defaultKind;
|
|
81
|
+
if (!id || !itemKind) {
|
|
79
82
|
return null;
|
|
80
83
|
}
|
|
81
84
|
return {
|
|
82
85
|
...item,
|
|
83
|
-
kind,
|
|
86
|
+
kind: itemKind,
|
|
84
87
|
id,
|
|
85
88
|
};
|
|
86
89
|
});
|
|
@@ -305,6 +308,21 @@ async function objectItemsFromDocument(document, sourcePath) {
|
|
|
305
308
|
if (typeof document !== "object" || !document) {
|
|
306
309
|
return [];
|
|
307
310
|
}
|
|
311
|
+
const catalogKind = typeof document.kind === "string"
|
|
312
|
+
? String(document.kind)
|
|
313
|
+
: undefined;
|
|
314
|
+
const catalogItems = catalogKind === "Models"
|
|
315
|
+
? normalizeCatalogSpec(document, { defaultKind: "Model" })
|
|
316
|
+
: catalogKind === "Stores"
|
|
317
|
+
? normalizeCatalogSpec(document)
|
|
318
|
+
: catalogKind === "Tools"
|
|
319
|
+
? normalizeCatalogSpec(document, { defaultKind: "Tool" })
|
|
320
|
+
: catalogKind === "McpServers"
|
|
321
|
+
? normalizeCatalogSpec(document)
|
|
322
|
+
: [];
|
|
323
|
+
if (catalogItems.length > 0) {
|
|
324
|
+
return catalogItems;
|
|
325
|
+
}
|
|
308
326
|
const items = document.items;
|
|
309
327
|
if (Array.isArray(items)) {
|
|
310
328
|
const records = [];
|
|
@@ -497,15 +515,7 @@ async function readNamedModelItems(root) {
|
|
|
497
515
|
}
|
|
498
516
|
const parsedDocuments = parseAllDocuments(await readYamlOrJson(filePath));
|
|
499
517
|
for (const parsedDocument of parsedDocuments) {
|
|
500
|
-
const
|
|
501
|
-
const catalogItems = normalizeNamedResourceSpec(document, "model");
|
|
502
|
-
if (catalogItems.length > 0) {
|
|
503
|
-
for (const item of catalogItems) {
|
|
504
|
-
records.push({ item: normalizeYamlItem(item), sourcePath: filePath });
|
|
505
|
-
}
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
for (const item of await objectItemsFromDocument(document, filePath)) {
|
|
518
|
+
for (const item of await objectItemsFromDocument(parsedDocument.toJSON(), filePath)) {
|
|
509
519
|
const normalized = normalizeYamlItem(item);
|
|
510
520
|
if (normalized.kind === "model" && typeof normalized.id === "string" && normalized.id.trim()) {
|
|
511
521
|
records.push({ item: normalized, sourcePath: filePath });
|
|
@@ -556,6 +566,7 @@ export async function readToolModuleItems(root) {
|
|
|
556
566
|
name: definition.implementationName,
|
|
557
567
|
description: definition.description,
|
|
558
568
|
implementationName: definition.implementationName,
|
|
569
|
+
...(definition.retryable !== undefined ? { retryable: definition.retryable } : {}),
|
|
559
570
|
},
|
|
560
571
|
sourcePath: filePath,
|
|
561
572
|
});
|
|
@@ -22,7 +22,7 @@ export type RecoveryConfig = {
|
|
|
22
22
|
maxRecoveryAttempts: number;
|
|
23
23
|
};
|
|
24
24
|
export type ConcurrencyConfig = {
|
|
25
|
-
maxConcurrentRuns
|
|
25
|
+
maxConcurrentRuns: number;
|
|
26
26
|
};
|
|
27
27
|
export declare function getWorkspaceObject(refs: Map<string, WorkspaceObject | ParsedAgentObject>, ref: string | undefined): WorkspaceObject | undefined;
|
|
28
28
|
export declare function getRuntimeDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
|
|
@@ -62,7 +62,7 @@ export function getConcurrencyConfig(refs) {
|
|
|
62
62
|
Number.isFinite(concurrency.maxConcurrentRuns) &&
|
|
63
63
|
concurrency.maxConcurrentRuns > 0
|
|
64
64
|
? Math.floor(concurrency.maxConcurrentRuns)
|
|
65
|
-
:
|
|
65
|
+
: 3;
|
|
66
66
|
return { maxConcurrentRuns };
|
|
67
67
|
}
|
|
68
68
|
export function getRoutingSystemPrompt(refs) {
|
|
@@ -105,6 +105,7 @@ export async function hydrateResourceAndExternalTools(tools, toolSourceRefs, wor
|
|
|
105
105
|
backendOperation: existing?.backendOperation ?? resourceTool.backendOperation,
|
|
106
106
|
bundleRefs: existing?.bundleRefs ?? [],
|
|
107
107
|
hitl: existing?.hitl ?? resourceTool.hitl,
|
|
108
|
+
retryable: existing?.retryable ?? resourceTool.retryable,
|
|
108
109
|
sourcePath: existing?.sourcePath ?? resourceTool.toolPath,
|
|
109
110
|
});
|
|
110
111
|
}
|