@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.
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/sqlite-run-context-store.d.ts +22 -0
- package/dist/persistence/sqlite-run-context-store.js +64 -0
- package/dist/persistence/sqlite-run-queue-store.d.ts +41 -0
- package/dist/persistence/sqlite-run-queue-store.js +120 -0
- package/dist/persistence/sqlite-store.d.ts +2 -2
- package/dist/persistence/sqlite-store.js +31 -117
- package/dist/resource/mcp-tool-support.d.ts +21 -0
- package/dist/resource/mcp-tool-support.js +173 -0
- package/dist/resource/resource-impl.d.ts +1 -18
- package/dist/resource/resource-impl.js +3 -166
- package/dist/runtime/adapter/invoke-runtime.d.ts +22 -0
- package/dist/runtime/adapter/invoke-runtime.js +18 -0
- package/dist/runtime/adapter/stream-runtime.d.ts +46 -0
- package/dist/runtime/adapter/stream-runtime.js +93 -0
- package/dist/runtime/agent-runtime-adapter.js +93 -168
- package/dist/runtime/harness/run/run-operations.d.ts +50 -0
- package/dist/runtime/harness/run/run-operations.js +113 -0
- package/dist/runtime/harness/run/run-slot-acquisition.d.ts +64 -0
- package/dist/runtime/harness/run/run-slot-acquisition.js +157 -0
- package/dist/runtime/harness/run/stream-run.d.ts +53 -0
- package/dist/runtime/harness/run/stream-run.js +304 -0
- package/dist/runtime/harness.js +79 -528
- package/dist/workspace/object-loader.d.ts +1 -8
- package/dist/workspace/object-loader.js +3 -197
- package/dist/workspace/yaml-object-reader.d.ts +15 -0
- package/dist/workspace/yaml-object-reader.js +202 -0
- package/package.json +1 -1
|
@@ -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,
|
|
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 {
|
|
15
|
-
import {
|
|
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
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const
|
|
539
|
-
const
|
|
540
|
-
if (
|
|
541
|
-
|
|
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
|
-
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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 {};
|