@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
@@ -3,7 +3,7 @@ import { Command, MemorySaver } from "@langchain/langgraph";
3
3
  import { HumanMessage } from "@langchain/core/messages";
4
4
  import { DEFAULT_SUBAGENT_PROMPT, createDeepAgent, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, } from "deepagents";
5
5
  import { createAgent, humanInTheLoopMiddleware } from "langchain";
6
- import { extractToolFallbackContext, extractVisibleOutput, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, wrapResolvedModel, } from "./parsing/output-parsing.js";
6
+ import { extractToolFallbackContext, extractVisibleOutput, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, wrapResolvedModel, } from "./parsing/output-parsing.js";
7
7
  import { readStreamDelta, } from "./parsing/stream-event-parsing.js";
8
8
  import { wrapToolForExecution } from "./adapter/tool/tool-hitl.js";
9
9
  import { resolveDeclaredMiddleware } from "./adapter/tool/declared-middleware.js";
@@ -11,16 +11,14 @@ import { applyDeepAgentDelegationPromptCompatibility, materializeDeepAgentSkillS
11
11
  import { buildToolNameMapping, } from "./adapter/tool/tool-name-mapping.js";
12
12
  import { createBuiltinMiddlewareTools } from "./adapter/tool/builtin-middleware-tools.js";
13
13
  import { finalizeInvocationResult } from "./adapter/invocation-result.js";
14
- import { runLocalToolInvocationLoop } from "./adapter/local-tool-invocation.js";
15
- import { createStreamEventProjectionState, projectRuntimeStreamEvent } from "./adapter/stream-event-projection.js";
16
- import { projectTextStreamChunks } from "./adapter/stream-text-consumption.js";
14
+ import { invokeRuntimeWithLocalTools } from "./adapter/invoke-runtime.js";
15
+ import { streamRuntimeExecution } from "./adapter/stream-runtime.js";
17
16
  import { buildDeepAgentRunnableConfig } from "./adapter/deepagent-runnable-config.js";
18
17
  import { buildLangChainRunnableConfig } from "./adapter/langchain-runnable-config.js";
19
18
  import { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, } from "./adapter/resilience.js";
20
19
  import { createResolvedModel } from "./adapter/model/model-providers.js";
21
20
  import { buildInvocationRequest, } from "./adapter/model/invocation-request.js";
22
21
  import { compileInterruptOn } from "./adapter/tool/interrupt-policy.js";
23
- import { buildRawModelMessages } from "./adapter/model/message-assembly.js";
24
22
  import { asStructuredExecutableTool, hasCallableToolHandler, normalizeResolvedToolSchema, wrapResolvedToolWithModelFacingName, } from "./adapter/tool/resolved-tool.js";
25
23
  import { instantiateProviderTool } from "./adapter/tool/provider-tool.js";
26
24
  import { countConfiguredTools, hasConfiguredMiddlewareKind, hasConfiguredSubagentSupport, isObject, isRecord, sleep, } from "./adapter/runtime-adapter-support.js";
@@ -512,7 +510,6 @@ export class AgentRuntimeAdapter {
512
510
  const request = resumePayload === undefined
513
511
  ? buildInvocationRequest(binding, history, input, options)
514
512
  : new Command({ resume: resumePayload });
515
- let result;
516
513
  const callRuntime = async (activeBinding, activeRequest) => {
517
514
  return this.invokeWithProviderRetry(activeBinding, async () => {
518
515
  const runnable = await this.create(activeBinding);
@@ -530,64 +527,54 @@ export class AgentRuntimeAdapter {
530
527
  return callRuntime(this.applyStrictToolJsonInstruction(binding), activeRequest);
531
528
  }
532
529
  };
533
- const executedToolResults = [];
534
- if (resumePayload !== undefined) {
535
- result = await callRuntimeWithToolParseRecovery(request);
536
- }
537
- else {
538
- const primaryTools = getBindingPrimaryTools(binding);
539
- const defersToUpstreamHitlExecution = primaryTools.some((tool) => tool.hitl?.enabled === true);
540
- if (defersToUpstreamHitlExecution) {
541
- result = await callRuntimeWithToolParseRecovery(request);
530
+ const primaryTools = getBindingPrimaryTools(binding);
531
+ const resolvedTools = this.resolveTools(primaryTools, binding);
532
+ const toolNameMapping = buildToolNameMapping(primaryTools);
533
+ const executableTools = new Map();
534
+ for (let index = 0; index < primaryTools.length; index += 1) {
535
+ const compiledTool = primaryTools[index];
536
+ const resolvedTool = resolvedTools[index];
537
+ if (!compiledTool || !resolvedTool || !hasCallableToolHandler(resolvedTool)) {
538
+ continue;
542
539
  }
543
- else {
544
- const resolvedTools = this.resolveTools(primaryTools, binding);
545
- const toolNameMapping = buildToolNameMapping(primaryTools);
546
- const executableTools = new Map();
547
- const builtinExecutableTools = await this.resolveBuiltinMiddlewareTools(binding, options);
548
- for (let index = 0; index < primaryTools.length; index += 1) {
549
- const compiledTool = primaryTools[index];
550
- const resolvedTool = resolvedTools[index];
551
- if (!compiledTool || !resolvedTool || !hasCallableToolHandler(resolvedTool)) {
552
- continue;
553
- }
554
- const handler = async (toolInput) => {
555
- const callable = typeof resolvedTool.invoke === "function"
556
- ? resolvedTool.invoke
557
- : typeof resolvedTool.call === "function"
558
- ? resolvedTool.call
559
- : resolvedTool.func;
560
- if (!callable) {
561
- throw new Error(`Tool ${compiledTool.name} has no callable handler.`);
562
- }
563
- return Promise.resolve(callable.call(resolvedTool, toolInput, options.context ? { context: options.context } : undefined));
564
- };
565
- const modelFacingName = toolNameMapping.originalToModelFacing.get(compiledTool.name) ?? compiledTool.name;
566
- const normalizedSchema = normalizeResolvedToolSchema(resolvedTool);
567
- executableTools.set(modelFacingName, {
568
- name: compiledTool.name,
569
- schema: normalizedSchema,
570
- invoke: handler,
571
- });
572
- executableTools.set(compiledTool.name, {
573
- name: compiledTool.name,
574
- schema: normalizedSchema,
575
- invoke: handler,
576
- });
540
+ const handler = async (toolInput) => {
541
+ const callable = typeof resolvedTool.invoke === "function"
542
+ ? resolvedTool.invoke
543
+ : typeof resolvedTool.call === "function"
544
+ ? resolvedTool.call
545
+ : resolvedTool.func;
546
+ if (!callable) {
547
+ throw new Error(`Tool ${compiledTool.name} has no callable handler.`);
577
548
  }
578
- const localInvocation = await runLocalToolInvocationLoop({
579
- binding,
580
- request,
581
- primaryTools,
582
- toolNameMapping,
583
- executableTools: executableTools,
584
- builtinExecutableTools: builtinExecutableTools,
585
- callRuntimeWithToolParseRecovery,
586
- });
587
- result = localInvocation.result;
588
- executedToolResults.push(...localInvocation.executedToolResults);
589
- }
549
+ return Promise.resolve(callable.call(resolvedTool, toolInput, options.context ? { context: options.context } : undefined));
550
+ };
551
+ const modelFacingName = toolNameMapping.originalToModelFacing.get(compiledTool.name) ?? compiledTool.name;
552
+ const normalizedSchema = normalizeResolvedToolSchema(resolvedTool);
553
+ executableTools.set(modelFacingName, {
554
+ name: compiledTool.name,
555
+ schema: normalizedSchema,
556
+ invoke: handler,
557
+ });
558
+ executableTools.set(compiledTool.name, {
559
+ name: compiledTool.name,
560
+ schema: normalizedSchema,
561
+ invoke: handler,
562
+ });
590
563
  }
564
+ const builtinExecutableTools = await this.resolveBuiltinMiddlewareTools(binding, options);
565
+ const localOrUpstreamInvocation = await invokeRuntimeWithLocalTools({
566
+ binding,
567
+ request,
568
+ resumePayload,
569
+ primaryTools,
570
+ defersToUpstreamHitlExecution: primaryTools.some((tool) => tool.hitl?.enabled === true),
571
+ toolNameMapping,
572
+ executableTools,
573
+ builtinExecutableTools: builtinExecutableTools,
574
+ callRuntimeWithToolParseRecovery,
575
+ });
576
+ const result = localOrUpstreamInvocation.result;
577
+ const executedToolResults = [...localOrUpstreamInvocation.executedToolResults];
591
578
  if (!result) {
592
579
  throw new Error("Agent invocation returned no result");
593
580
  }
@@ -600,113 +587,51 @@ export class AgentRuntimeAdapter {
600
587
  });
601
588
  }
602
589
  async *stream(binding, input, threadId, history = [], options = {}) {
603
- try {
604
- const invokeTimeoutMs = resolveBindingTimeout(binding);
605
- const streamIdleTimeoutMs = resolveStreamIdleTimeout(binding);
606
- const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
607
- const primaryTools = getBindingPrimaryTools(binding);
608
- const toolNameMapping = buildToolNameMapping(primaryTools);
609
- const primaryModel = getBindingPrimaryModel(binding);
610
- const forceInvokeFallback = isLangChainBinding(binding) &&
611
- primaryTools.length > 0 &&
612
- primaryModel?.provider === "openai-compatible";
613
- if (isLangChainBinding(binding)) {
614
- const langchainParams = getBindingLangChainParams(binding);
615
- const resolvedModel = (await this.resolveModel(langchainParams.model));
616
- const tools = this.resolveTools(langchainParams.tools, binding);
617
- const canUseDirectModelStream = tools.length === 0 || typeof resolvedModel.bindTools !== "function";
618
- const model = canUseDirectModelStream
619
- ? resolvedModel
620
- : typeof resolvedModel.bindTools === "function" && tools.length > 0
621
- ? resolvedModel.bindTools(tools)
622
- : resolvedModel;
623
- // For tool-using langchain agents, a raw model.stream pass cannot execute the
624
- // agent loop and only adds an extra model round-trip before the runnable path.
625
- if (canUseDirectModelStream && typeof model.stream === "function") {
626
- const stream = await this.withTimeout(() => model.stream(buildRawModelMessages(binding, getBindingSystemPrompt(binding), history, input)), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "model stream start", "stream");
627
- let emitted = false;
628
- const projected = projectTextStreamChunks(this.iterateWithTimeout(stream, streamIdleTimeoutMs, "model stream", streamDeadlineAt, invokeTimeoutMs));
629
- let nextChunk = await projected.next();
630
- while (!nextChunk.done) {
631
- if (nextChunk.value.kind === "content") {
632
- emitted = true;
633
- }
634
- yield nextChunk.value;
635
- nextChunk = await projected.next();
636
- }
637
- if (nextChunk.value.emittedContent || emitted) {
638
- return;
639
- }
640
- }
641
- }
642
- const runnable = await this.create(binding);
643
- const request = buildInvocationRequest(binding, history, input, options);
644
- if (!forceInvokeFallback && typeof runnable.streamEvents === "function") {
645
- const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId, run_id: options.runId }, version: "v2", ...(options.context ? { context: options.context } : {}) }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent streamEvents start", "stream");
646
- const projectionState = createStreamEventProjectionState();
647
- for await (const event of this.iterateWithTimeout(events, streamIdleTimeoutMs, "agent streamEvents", streamDeadlineAt, invokeTimeoutMs)) {
648
- const projectedChunks = projectRuntimeStreamEvent({
649
- event,
650
- allowVisibleStreamDeltas: isLangChainBinding(binding),
651
- includeStateStreamOutput: isDeepAgentBinding(binding),
652
- toolNameMapping,
653
- primaryTools,
654
- state: projectionState,
655
- });
656
- for (const chunk of projectedChunks) {
657
- yield chunk;
658
- }
659
- }
660
- if (projectionState.emittedOutput || projectionState.emittedToolResult || projectionState.emittedToolError) {
661
- return;
662
- }
663
- }
664
- if (!forceInvokeFallback && isLangChainBinding(binding) && typeof runnable.stream === "function") {
665
- const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId, run_id: options.runId } }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent stream start", "stream");
666
- let emitted = false;
667
- const projected = projectTextStreamChunks(this.iterateWithTimeout(stream, streamIdleTimeoutMs, "agent stream", streamDeadlineAt, invokeTimeoutMs));
668
- let nextChunk = await projected.next();
669
- while (!nextChunk.done) {
670
- if (nextChunk.value.kind === "content") {
671
- emitted = true;
672
- }
673
- yield nextChunk.value;
674
- nextChunk = await projected.next();
675
- }
676
- if (nextChunk.value.emittedContent || emitted) {
677
- return;
678
- }
679
- }
680
- const result = await this.invoke(binding, input, threadId, options.runId ?? threadId, undefined, history, options);
681
- const executedToolResults = Array.isArray(result.metadata?.executedToolResults)
682
- ? result.metadata.executedToolResults
683
- : [];
684
- for (const toolResult of executedToolResults) {
685
- yield {
686
- kind: "tool-result",
687
- toolName: toolResult.toolName,
688
- output: toolResult.output,
689
- isError: toolResult.isError,
690
- };
691
- }
692
- if (result.output) {
693
- yield { kind: "content", content: sanitizeVisibleText(result.output) };
694
- }
695
- }
696
- catch (error) {
697
- if (countConfiguredTools(binding) > 0 &&
698
- error instanceof Error &&
699
- error.message.includes("does not support tool binding")) {
700
- throw error;
701
- }
702
- if (!isToolCallParseFailure(error)) {
703
- throw error;
704
- }
705
- const retried = await this.invoke(this.applyStrictToolJsonInstruction(binding), input, threadId, options.runId ?? threadId, undefined, history, options);
706
- if (retried.output) {
707
- yield { kind: "content", content: sanitizeVisibleText(retried.output) };
708
- }
709
- }
590
+ const invokeTimeoutMs = resolveBindingTimeout(binding);
591
+ const streamIdleTimeoutMs = resolveStreamIdleTimeout(binding);
592
+ const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
593
+ const primaryTools = getBindingPrimaryTools(binding);
594
+ const toolNameMapping = buildToolNameMapping(primaryTools);
595
+ const primaryModel = getBindingPrimaryModel(binding);
596
+ const forceInvokeFallback = isLangChainBinding(binding) &&
597
+ primaryTools.length > 0 &&
598
+ primaryModel?.provider === "openai-compatible";
599
+ const langchainParams = isLangChainBinding(binding) ? getBindingLangChainParams(binding) : undefined;
600
+ const resolvedLangChainModel = langchainParams
601
+ ? (await this.resolveModel(langchainParams.model))
602
+ : undefined;
603
+ const resolvedLangChainTools = langchainParams ? this.resolveTools(langchainParams.tools, binding) : [];
604
+ const canUseDirectModelStream = !!resolvedLangChainModel &&
605
+ (resolvedLangChainTools.length === 0 || typeof resolvedLangChainModel.bindTools !== "function");
606
+ const langChainStreamModel = resolvedLangChainModel && canUseDirectModelStream
607
+ ? resolvedLangChainModel
608
+ : resolvedLangChainModel && typeof resolvedLangChainModel.bindTools === "function" && resolvedLangChainTools.length > 0
609
+ ? resolvedLangChainModel.bindTools(resolvedLangChainTools)
610
+ : undefined;
611
+ yield* streamRuntimeExecution({
612
+ binding,
613
+ input,
614
+ threadId,
615
+ history,
616
+ runtimeOptions: options,
617
+ primaryTools,
618
+ toolNameMapping,
619
+ forceInvokeFallback,
620
+ canUseDirectModelStream,
621
+ langChainStreamModel,
622
+ createRunnable: () => this.create(binding),
623
+ withTimeout: (producer, timeoutMs, operation, stage) => this.withTimeout(producer, timeoutMs, operation, stage),
624
+ iterateWithTimeout: (iterable, timeoutMs, operation, deadlineAt, deadlineTimeoutMs) => this.iterateWithTimeout(iterable, timeoutMs, operation, deadlineAt, deadlineTimeoutMs),
625
+ invokeTimeoutMs: computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs),
626
+ streamIdleTimeoutMs,
627
+ streamDeadlineAt,
628
+ invoke: (activeBinding, activeInput, activeThreadId, runId, resumePayload, activeHistory, invokeOptions) => this.invoke(activeBinding, activeInput, activeThreadId, runId, resumePayload, activeHistory, invokeOptions),
629
+ applyStrictToolJsonInstruction: (activeBinding) => this.applyStrictToolJsonInstruction(activeBinding),
630
+ getSystemPrompt: (activeBinding) => getBindingSystemPrompt(activeBinding),
631
+ isLangChainBinding,
632
+ isDeepAgentBinding,
633
+ countConfiguredTools,
634
+ });
710
635
  }
711
636
  }
712
637
  export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError, };
@@ -0,0 +1,50 @@
1
+ import type { CancelOptions, CompiledAgentBinding, InternalApprovalRecord, MessageContent, ResumeOptions, RunResult, ThreadSummary, TranscriptMessage } from "../../../contracts/types.js";
2
+ import type { RecoveryIntent } from "../../../persistence/types.js";
3
+ type ResumeOperationRuntime = {
4
+ getApprovalById: (approvalId: string) => Promise<InternalApprovalRecord | null>;
5
+ getSession: (threadId: string) => Promise<ThreadSummary | null>;
6
+ resolveApprovalRecord: (options: ResumeOptions, thread: ThreadSummary) => Promise<InternalApprovalRecord>;
7
+ getBinding: (agentId: string) => CompiledAgentBinding | undefined;
8
+ buildResumePayload: (binding: CompiledAgentBinding, approval: InternalApprovalRecord, options: ResumeOptions) => unknown;
9
+ getRunCancellation: (runId: string) => Promise<{
10
+ requested: boolean;
11
+ reason?: string;
12
+ }>;
13
+ finalizeCancelledRun: (threadId: string, runId: string, previousState: RunResult["state"] | null, reason?: string) => Promise<RunResult>;
14
+ setRunState: (threadId: string, runId: string, state: RunResult["state"], checkpointRef: string | null) => Promise<void>;
15
+ acquireRunSlot: (threadId?: string, runId?: string, activeState?: RunResult["state"], priority?: number) => Promise<() => Promise<void>>;
16
+ resolvePersistedRunPriority: (threadId: string, runId: string) => Promise<number>;
17
+ saveRecoveryIntent: (threadId: string, runId: string, payload: RecoveryIntent) => Promise<unknown>;
18
+ emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<unknown>;
19
+ resolveApproval: (threadId: string, runId: string, approvalId: string, resolution: "approved" | "edited" | "rejected" | "expired") => Promise<unknown>;
20
+ listThreadMessages: (threadId: string) => Promise<TranscriptMessage[]>;
21
+ loadRunInput: (threadId: string, runId: string) => Promise<MessageContent>;
22
+ invoke: (binding: CompiledAgentBinding, input: MessageContent, threadId: string, runId: string, resumePayload: unknown, priorHistory: TranscriptMessage[]) => Promise<RunResult>;
23
+ recordLlmSuccess: (startedAt: number) => void;
24
+ recordLlmFailure: (startedAt: number) => void;
25
+ clearRecoveryIntent: (threadId: string, runId: string) => Promise<void>;
26
+ finalizeContinuedRun: (binding: CompiledAgentBinding, threadId: string, runId: string, input: MessageContent, actual: RunResult, options: {
27
+ previousState: RunResult["state"] | null;
28
+ stateSequence: number;
29
+ approvalSequence?: number;
30
+ }) => Promise<RunResult>;
31
+ };
32
+ type CancelOperationRuntime = {
33
+ getRun: (runId: string) => Promise<{
34
+ threadId: string;
35
+ runId: string;
36
+ agentId: string;
37
+ state: RunResult["state"];
38
+ } | null>;
39
+ requestRunCancel: (runId: string, reason?: string) => Promise<unknown>;
40
+ dropPendingRunSlot: (runId: string) => boolean;
41
+ finalizeCancelledRun: (threadId: string, runId: string, previousState: RunResult["state"] | null, reason?: string) => Promise<RunResult>;
42
+ setRunStateAndEmit: (threadId: string, runId: string, sequence: number, state: RunResult["state"], options: {
43
+ previousState: string | null;
44
+ checkpointRef?: string | null;
45
+ error?: string;
46
+ }) => Promise<unknown>;
47
+ };
48
+ export declare function resumeRun(runtime: ResumeOperationRuntime, options: ResumeOptions): Promise<RunResult>;
49
+ export declare function cancelRunOperation(runtime: CancelOperationRuntime, options: CancelOptions): Promise<RunResult>;
50
+ export {};
@@ -0,0 +1,113 @@
1
+ import { isTerminalRunState } from "./helpers.js";
2
+ export async function resumeRun(runtime, options) {
3
+ const approvalById = options.approvalId ? await runtime.getApprovalById(options.approvalId) : null;
4
+ const thread = options.threadId
5
+ ? await runtime.getSession(options.threadId)
6
+ : approvalById
7
+ ? await runtime.getSession(approvalById.threadId)
8
+ : null;
9
+ if (!thread) {
10
+ throw new Error("resume requires either threadId or approvalId");
11
+ }
12
+ const approval = approvalById ?? await runtime.resolveApprovalRecord(options, thread);
13
+ const threadId = approval.threadId;
14
+ const runId = approval.runId;
15
+ const binding = runtime.getBinding(thread.agentId);
16
+ if (!binding) {
17
+ throw new Error(`Unknown agent ${thread.agentId}`);
18
+ }
19
+ const resumePayload = runtime.buildResumePayload(binding, approval, options);
20
+ const cancellation = await runtime.getRunCancellation(runId);
21
+ if (cancellation.requested) {
22
+ return runtime.finalizeCancelledRun(threadId, runId, thread.status, cancellation.reason);
23
+ }
24
+ const checkpointRef = `checkpoints/${threadId}/${runId}/cp-1`;
25
+ await runtime.setRunState(threadId, runId, "resuming", checkpointRef);
26
+ const releaseRunSlot = await runtime.acquireRunSlot(threadId, runId, "resuming", await runtime.resolvePersistedRunPriority(threadId, runId));
27
+ try {
28
+ await runtime.saveRecoveryIntent(threadId, runId, {
29
+ kind: "approval-decision",
30
+ savedAt: new Date().toISOString(),
31
+ checkpointRef,
32
+ resumePayload,
33
+ attempts: 0,
34
+ });
35
+ await runtime.emit(threadId, runId, 5, "run.resumed", {
36
+ resumeKind: "cross-restart",
37
+ checkpointRef,
38
+ state: "resuming",
39
+ approvalId: approval.approvalId,
40
+ pendingActionId: approval.pendingActionId,
41
+ });
42
+ await runtime.resolveApproval(threadId, runId, approval.approvalId, options.decision === "reject" ? "rejected" : options.decision === "edit" ? "edited" : "approved");
43
+ await runtime.emit(threadId, runId, 6, "approval.resolved", {
44
+ approvalId: approval.approvalId,
45
+ pendingActionId: approval.pendingActionId,
46
+ decision: options.decision ?? "approve",
47
+ toolName: approval.toolName,
48
+ });
49
+ const history = await runtime.listThreadMessages(threadId);
50
+ const priorHistory = history.filter((message) => message.runId !== runId);
51
+ const runInput = await runtime.loadRunInput(threadId, runId);
52
+ const startedAt = Date.now();
53
+ try {
54
+ const actual = await runtime.invoke(binding, "", threadId, runId, resumePayload, priorHistory);
55
+ runtime.recordLlmSuccess(startedAt);
56
+ const cancelledAfterInvoke = await runtime.getRunCancellation(runId);
57
+ if (cancelledAfterInvoke.requested) {
58
+ return runtime.finalizeCancelledRun(threadId, runId, "resuming", cancelledAfterInvoke.reason);
59
+ }
60
+ await runtime.clearRecoveryIntent(threadId, runId);
61
+ const finalized = await runtime.finalizeContinuedRun(binding, threadId, runId, runInput, actual, {
62
+ previousState: "resuming",
63
+ stateSequence: 7,
64
+ approvalSequence: 8,
65
+ });
66
+ return {
67
+ ...finalized,
68
+ approvalId: finalized.approvalId ?? approval.approvalId,
69
+ pendingActionId: finalized.pendingActionId ?? approval.pendingActionId,
70
+ };
71
+ }
72
+ catch (error) {
73
+ runtime.recordLlmFailure(startedAt);
74
+ throw error;
75
+ }
76
+ }
77
+ finally {
78
+ await releaseRunSlot();
79
+ }
80
+ }
81
+ export async function cancelRunOperation(runtime, options) {
82
+ const run = await runtime.getRun(options.runId);
83
+ if (!run) {
84
+ throw new Error(`Unknown run ${options.runId}`);
85
+ }
86
+ if (isTerminalRunState(run.state)) {
87
+ return {
88
+ threadId: run.threadId,
89
+ runId: run.runId,
90
+ agentId: run.agentId,
91
+ state: run.state,
92
+ output: run.state,
93
+ };
94
+ }
95
+ await runtime.requestRunCancel(run.runId, options.reason);
96
+ if (run.state === "queued" || run.state === "waiting_for_approval" || run.state === "claimed") {
97
+ if (run.state === "queued") {
98
+ runtime.dropPendingRunSlot(run.runId);
99
+ }
100
+ return runtime.finalizeCancelledRun(run.threadId, run.runId, run.state, options.reason);
101
+ }
102
+ await runtime.setRunStateAndEmit(run.threadId, run.runId, 103, "cancelling", {
103
+ previousState: run.state,
104
+ ...(options.reason ? { error: options.reason } : {}),
105
+ });
106
+ return {
107
+ threadId: run.threadId,
108
+ runId: run.runId,
109
+ agentId: run.agentId,
110
+ state: "cancelling",
111
+ output: options.reason ? `cancelling: ${options.reason}` : "cancelling",
112
+ };
113
+ }
@@ -0,0 +1,64 @@
1
+ import type { RunResult } from "../../../contracts/types.js";
2
+ import { type PendingRunSlot } from "./run-queue.js";
3
+ export type RunSlotConcurrencyConfig = {
4
+ maxConcurrentRuns?: number;
5
+ leaseMs: number;
6
+ heartbeatIntervalMs: number;
7
+ };
8
+ type RunClaimPersistence = {
9
+ claimQueuedRun(payload: {
10
+ threadId: string;
11
+ runId: string;
12
+ workerId: string;
13
+ claimedAt: string;
14
+ leaseExpiresAt: string;
15
+ }): Promise<unknown>;
16
+ renewRunLease(payload: {
17
+ runId: string;
18
+ workerId: string;
19
+ heartbeatAt: string;
20
+ leaseExpiresAt: string;
21
+ }): Promise<unknown>;
22
+ releaseRunClaim(runId: string): Promise<unknown>;
23
+ enqueueRun(payload: {
24
+ threadId: string;
25
+ runId: string;
26
+ priority: number;
27
+ }): Promise<unknown>;
28
+ getRun(runId: string): Promise<{
29
+ state: RunResult["state"];
30
+ } | null>;
31
+ };
32
+ type RunSlotRuntime = {
33
+ persistence: RunClaimPersistence;
34
+ workerId: string;
35
+ concurrencyConfig: RunSlotConcurrencyConfig;
36
+ pendingRunSlots: PendingRunSlot[];
37
+ getActiveRunSlots: () => number;
38
+ setActiveRunSlots: (count: number) => void;
39
+ enqueuePendingRunSlot: (entry: {
40
+ threadId?: string;
41
+ runId?: string;
42
+ priority: number;
43
+ activate: () => void | Promise<void>;
44
+ abort: () => void;
45
+ }) => Array<{
46
+ threadId: string;
47
+ runId: string;
48
+ priority: number;
49
+ queuePosition: number;
50
+ }>;
51
+ emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<unknown>;
52
+ setRunStateAndEmit: (threadId: string, runId: string, sequence: number, state: RunResult["state"], options: {
53
+ previousState: string | null;
54
+ checkpointRef?: string | null;
55
+ error?: string;
56
+ }) => Promise<unknown>;
57
+ };
58
+ export declare function acquireRunSlot(runtime: RunSlotRuntime, options?: {
59
+ threadId?: string;
60
+ runId?: string;
61
+ activeState?: RunResult["state"];
62
+ priority?: number;
63
+ }): Promise<() => Promise<void>>;
64
+ export {};