@rowan-agent/agent 0.7.5 → 0.8.0
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 +26 -7
- package/dist/index.d.ts +83 -3
- package/dist/index.js +416 -70
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @rowan-agent/agent
|
|
2
2
|
|
|
3
|
-
Durable Agent Runtime. The public
|
|
4
|
-
Stores, Config Providers, Run handles, and
|
|
5
|
-
|
|
3
|
+
Durable Agent Runtime. The public interface consists of `AgentRuntime`, Durable
|
|
4
|
+
Stores, Config Providers, Run handles, and Run Events. An Agent is a persistent
|
|
5
|
+
identity, not a process-local Session object.
|
|
6
6
|
|
|
7
7
|
## Quick start
|
|
8
8
|
|
|
@@ -31,7 +31,13 @@ const agentId = await runtime.createAgent({
|
|
|
31
31
|
const run = await runtime.start(agentId, "Summarize the workspace.", {
|
|
32
32
|
idempotencyKey: "run-example", // One Agent can have multiple independent Runs
|
|
33
33
|
});
|
|
34
|
+
const observing = (async () => {
|
|
35
|
+
for await (const event of run.observe()) {
|
|
36
|
+
if (event.kind === "message_delta") process.stdout.write(event.text);
|
|
37
|
+
}
|
|
38
|
+
})();
|
|
34
39
|
const boundary = await run.wait();
|
|
40
|
+
await observing;
|
|
35
41
|
await runtime.close();
|
|
36
42
|
```
|
|
37
43
|
|
|
@@ -40,7 +46,7 @@ await runtime.close();
|
|
|
40
46
|
1. `AgentRuntime.init({ store })` opens a Runtime Owner with an in-memory Config Provider by default. Pass `configs` when configuration must survive process boundaries.
|
|
41
47
|
2. `createAgent()` creates a persistent Agent identity and binds a configuration snapshot.
|
|
42
48
|
3. `start()` creates a queued Run; `run(runId)` returns a stateless Run handle.
|
|
43
|
-
4. `observe()`
|
|
49
|
+
4. `observe()` follows display-oriented `RunEvent` values; `wait()` waits for an authoritative boundary.
|
|
44
50
|
5. `respond()` continues an `input_required` Run; `cancel()` terminates an unfinished Run.
|
|
45
51
|
6. `close()` seals the Owner and releases the Store.
|
|
46
52
|
|
|
@@ -69,16 +75,29 @@ Tools are supplied through `AgentConfig.context.tools` and persist through:
|
|
|
69
75
|
When an external side effect cannot be confirmed, the Tool must become
|
|
70
76
|
`indeterminate`; the Run then fails and is never automatically retried.
|
|
71
77
|
|
|
78
|
+
While running, a Tool may call `context.reportProgress(progress)` with a
|
|
79
|
+
JSON-safe value. Progress is live-only and may be dropped.
|
|
80
|
+
|
|
72
81
|
## Events
|
|
73
82
|
|
|
74
|
-
`run.observe()`
|
|
83
|
+
`run.observe()` delivers `RunEvent` values for live presentation:
|
|
84
|
+
|
|
85
|
+
- transient `message_delta` and `tool_progress` events are live-only and
|
|
86
|
+
best-effort;
|
|
87
|
+
- durable `message_committed`, `run_transitioned`, and `tool_state_changed`
|
|
88
|
+
events are replayable;
|
|
89
|
+
- a durable `message_committed` event is the authoritative full content if a
|
|
90
|
+
transient delta was coalesced or dropped.
|
|
91
|
+
|
|
92
|
+
`runtime.consume()` delivers only `DurableRunEvent` values:
|
|
75
93
|
|
|
76
94
|
- `message_committed`
|
|
77
95
|
- `run_transitioned`
|
|
78
96
|
- `tool_state_changed`
|
|
79
97
|
|
|
80
|
-
|
|
81
|
-
transaction.
|
|
98
|
+
Durable events and their corresponding Run aggregate changes commit in one
|
|
99
|
+
Store transaction. Reliable consumers persist progress through cursors and
|
|
100
|
+
checkpoints; transient events never enter the Durable Store.
|
|
82
101
|
|
|
83
102
|
## Resources
|
|
84
103
|
|
package/dist/index.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ type PhaseExecution = {
|
|
|
71
71
|
restore(snapshot: PhaseContextSnapshot): void;
|
|
72
72
|
invokeModel(context: PhaseContext): Promise<ModelInvokeOutput>;
|
|
73
73
|
executeTool(context: AgentContext, toolCall: ToolCall): Promise<ToolResult>;
|
|
74
|
+
executeTools(context: AgentContext, toolCalls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
/**
|
|
@@ -461,6 +462,12 @@ type PhaseInvocation = {
|
|
|
461
462
|
count: number;
|
|
462
463
|
sourcePhaseId: string;
|
|
463
464
|
};
|
|
465
|
+
/** Durable identity of the Run execution currently invoking a Phase. */
|
|
466
|
+
type PhaseExecutionIdentity = Readonly<{
|
|
467
|
+
agentId: string;
|
|
468
|
+
runId: string;
|
|
469
|
+
executionId: string;
|
|
470
|
+
}>;
|
|
464
471
|
/** Everything a phase needs to execute */
|
|
465
472
|
interface PhaseContext {
|
|
466
473
|
systemPrompt: string;
|
|
@@ -473,6 +480,8 @@ interface PhaseContext {
|
|
|
473
480
|
state: PhaseState;
|
|
474
481
|
/** Identity and dispatch metadata for this phase execution */
|
|
475
482
|
readonly invocation: PhaseInvocation;
|
|
483
|
+
/** Durable Run identity for cross-store integrations and idempotent commands. */
|
|
484
|
+
readonly execution: PhaseExecutionIdentity;
|
|
476
485
|
/** Additional guideline bullets appended to the system prompt */
|
|
477
486
|
promptGuidelines?: string[];
|
|
478
487
|
/** Text to append after the system prompt */
|
|
@@ -597,6 +606,8 @@ type ThinkingContent = Readonly<{
|
|
|
597
606
|
type ToolUseContent = Readonly<{
|
|
598
607
|
type: "tool_use";
|
|
599
608
|
toolCallId: ToolCallId;
|
|
609
|
+
/** Provider/model correlation, retained only to reconstruct the next request. */
|
|
610
|
+
providerToolCallId?: string;
|
|
600
611
|
name: string;
|
|
601
612
|
input: JsonValue;
|
|
602
613
|
}>;
|
|
@@ -615,6 +626,8 @@ type DurableToolResult = Readonly<{
|
|
|
615
626
|
type ToolResultContent = Readonly<{
|
|
616
627
|
type: "tool_result";
|
|
617
628
|
toolCallId: ToolCallId;
|
|
629
|
+
/** Provider/model correlation, retained only to reconstruct the next request. */
|
|
630
|
+
providerToolCallId?: string;
|
|
618
631
|
result: ToolExecutionResult;
|
|
619
632
|
}>;
|
|
620
633
|
type UserContent = string | readonly (TextContent | ImageContent)[];
|
|
@@ -685,6 +698,7 @@ type RunningRunFailure = Extract<RunFailure, {
|
|
|
685
698
|
type ToolCallState = "pending" | "running" | "completed" | "failed" | "indeterminate";
|
|
686
699
|
type ToolCallSnapshotBase = Readonly<{
|
|
687
700
|
id: ToolCallId;
|
|
701
|
+
providerToolCallId: string;
|
|
688
702
|
agentId: AgentId;
|
|
689
703
|
runId: RunId;
|
|
690
704
|
executionId: ExecutionId;
|
|
@@ -831,6 +845,24 @@ type ToolStateChanged = DurableEventBase & (Readonly<{
|
|
|
831
845
|
}>;
|
|
832
846
|
}>);
|
|
833
847
|
type DurableRunEvent = MessageCommitted | RunTransitioned | ToolStateChanged;
|
|
848
|
+
type MessageDelta = Readonly<{
|
|
849
|
+
kind: "message_delta";
|
|
850
|
+
durability: "transient";
|
|
851
|
+
runId: RunId;
|
|
852
|
+
executionId: ExecutionId;
|
|
853
|
+
messageId: MessageId;
|
|
854
|
+
offset: number;
|
|
855
|
+
text: string;
|
|
856
|
+
}>;
|
|
857
|
+
type ToolProgress = Readonly<{
|
|
858
|
+
kind: "tool_progress";
|
|
859
|
+
durability: "transient";
|
|
860
|
+
runId: RunId;
|
|
861
|
+
executionId: ExecutionId;
|
|
862
|
+
toolCallId: ToolCallId;
|
|
863
|
+
progress: JsonValue;
|
|
864
|
+
}>;
|
|
865
|
+
type RunEvent = DurableRunEvent | MessageDelta | ToolProgress;
|
|
834
866
|
|
|
835
867
|
type UserInput = string | Readonly<{
|
|
836
868
|
content: UserContent;
|
|
@@ -840,6 +872,7 @@ type ToolInvocationContext = Readonly<{
|
|
|
840
872
|
agentId: AgentId;
|
|
841
873
|
runId: RunId;
|
|
842
874
|
toolCallId: ToolCallId;
|
|
875
|
+
reportProgress(progress: JsonValue): void;
|
|
843
876
|
}>;
|
|
844
877
|
type Tool = Readonly<{
|
|
845
878
|
name: string;
|
|
@@ -930,10 +963,20 @@ type InputRequiredCommit = Readonly<{
|
|
|
930
963
|
prompt: AssistantMessage;
|
|
931
964
|
request: InputRequest;
|
|
932
965
|
}>;
|
|
966
|
+
type ToolCallReservation = Readonly<{
|
|
967
|
+
providerToolCallId: string;
|
|
968
|
+
name: string;
|
|
969
|
+
args: JsonValue;
|
|
970
|
+
toolCallId?: ToolCallId;
|
|
971
|
+
}>;
|
|
933
972
|
type ToolCommit = Readonly<{
|
|
934
973
|
run: RunRecord;
|
|
935
974
|
toolCall: ToolCallSnapshot;
|
|
936
975
|
}>;
|
|
976
|
+
type ToolBatchCommit = Readonly<{
|
|
977
|
+
run: RunRecord;
|
|
978
|
+
toolCalls: readonly ToolCallSnapshot[];
|
|
979
|
+
}>;
|
|
937
980
|
type RunRecord = Readonly<{
|
|
938
981
|
id: RunId;
|
|
939
982
|
agentId: AgentId;
|
|
@@ -1115,7 +1158,15 @@ interface OwnedStore {
|
|
|
1115
1158
|
name: string;
|
|
1116
1159
|
args: JsonValue;
|
|
1117
1160
|
toolCallId?: ToolCallId;
|
|
1161
|
+
providerToolCallId?: string;
|
|
1118
1162
|
}): Promise<ToolCommit>;
|
|
1163
|
+
reserveToolCalls(input: {
|
|
1164
|
+
runId: RunId;
|
|
1165
|
+
execution: ExecutionToken;
|
|
1166
|
+
expectedRevision: number;
|
|
1167
|
+
requestMessageId: MessageId;
|
|
1168
|
+
calls: readonly ToolCallReservation[];
|
|
1169
|
+
}): Promise<ToolBatchCommit>;
|
|
1119
1170
|
startToolCall(input: {
|
|
1120
1171
|
runId: RunId;
|
|
1121
1172
|
execution: ExecutionToken;
|
|
@@ -1175,7 +1226,7 @@ interface AgentRun {
|
|
|
1175
1226
|
observe(options?: {
|
|
1176
1227
|
after?: EventCursor;
|
|
1177
1228
|
signal?: AbortSignal;
|
|
1178
|
-
}): AsyncIterable<
|
|
1229
|
+
}): AsyncIterable<RunEvent>;
|
|
1179
1230
|
wait(options?: {
|
|
1180
1231
|
signal?: AbortSignal;
|
|
1181
1232
|
}): Promise<RunBoundary>;
|
|
@@ -1230,6 +1281,7 @@ declare class AgentRuntime implements AgentRuntime$1 {
|
|
|
1230
1281
|
private readonly activeAgents;
|
|
1231
1282
|
private readonly executions;
|
|
1232
1283
|
private readonly consumers;
|
|
1284
|
+
private readonly transientEvents;
|
|
1233
1285
|
private heartbeat?;
|
|
1234
1286
|
private pumping;
|
|
1235
1287
|
private closed;
|
|
@@ -1275,12 +1327,14 @@ declare class AgentRuntime implements AgentRuntime$1 {
|
|
|
1275
1327
|
private execute;
|
|
1276
1328
|
private failQueued;
|
|
1277
1329
|
private executeTool;
|
|
1330
|
+
private executeToolBatch;
|
|
1331
|
+
private executeReservedTool;
|
|
1278
1332
|
private requireAgent;
|
|
1279
1333
|
private runConsumer;
|
|
1280
1334
|
observe(runId: RunId, options?: {
|
|
1281
1335
|
after?: EventCursor;
|
|
1282
1336
|
signal?: AbortSignal;
|
|
1283
|
-
}): AsyncIterable<
|
|
1337
|
+
}): AsyncIterable<RunEvent>;
|
|
1284
1338
|
wait(runId: RunId, options?: {
|
|
1285
1339
|
signal?: AbortSignal;
|
|
1286
1340
|
}): Promise<RunBoundary>;
|
|
@@ -1390,7 +1444,20 @@ declare class InMemoryStore implements DurableStore {
|
|
|
1390
1444
|
name: string;
|
|
1391
1445
|
args: JsonValue;
|
|
1392
1446
|
toolCallId?: ToolCallId;
|
|
1447
|
+
providerToolCallId?: string;
|
|
1393
1448
|
}): ToolCommit;
|
|
1449
|
+
reserveToolCalls(lease: OwnerLease, input: {
|
|
1450
|
+
runId: RunId;
|
|
1451
|
+
execution: ExecutionToken;
|
|
1452
|
+
expectedRevision: number;
|
|
1453
|
+
requestMessageId: MessageId;
|
|
1454
|
+
calls: readonly Readonly<{
|
|
1455
|
+
providerToolCallId: string;
|
|
1456
|
+
name: string;
|
|
1457
|
+
args: JsonValue;
|
|
1458
|
+
toolCallId?: ToolCallId;
|
|
1459
|
+
}>[];
|
|
1460
|
+
}): ToolBatchCommit;
|
|
1394
1461
|
startToolCall(lease: OwnerLease, input: {
|
|
1395
1462
|
runId: RunId;
|
|
1396
1463
|
execution: ExecutionToken;
|
|
@@ -1531,7 +1598,20 @@ declare class SqliteStore implements DurableStore {
|
|
|
1531
1598
|
name: string;
|
|
1532
1599
|
args: JsonValue;
|
|
1533
1600
|
toolCallId?: ToolCallId;
|
|
1601
|
+
providerToolCallId?: string;
|
|
1534
1602
|
}): Promise<ToolCommit>;
|
|
1603
|
+
reserveToolCalls(lease: OwnerLease, input: {
|
|
1604
|
+
runId: RunId;
|
|
1605
|
+
execution: ExecutionToken;
|
|
1606
|
+
expectedRevision: number;
|
|
1607
|
+
requestMessageId: MessageId;
|
|
1608
|
+
calls: readonly Readonly<{
|
|
1609
|
+
providerToolCallId: string;
|
|
1610
|
+
name: string;
|
|
1611
|
+
args: JsonValue;
|
|
1612
|
+
toolCallId?: ToolCallId;
|
|
1613
|
+
}>[];
|
|
1614
|
+
}): Promise<ToolBatchCommit>;
|
|
1535
1615
|
startToolCall(lease: OwnerLease, input: {
|
|
1536
1616
|
runId: RunId;
|
|
1537
1617
|
execution: ExecutionToken;
|
|
@@ -1720,4 +1800,4 @@ declare function loadExtensionsFromPath(path: string): Promise<{
|
|
|
1720
1800
|
|
|
1721
1801
|
declare function createCoreTools(input?: CoreToolContext): Tool[];
|
|
1722
1802
|
|
|
1723
|
-
export { type AfterToolCall, type AgentConfig, type AgentDefinitionContext, type AgentId, type AgentListCursor, type AgentRecord, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type CoreToolContext, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionFactory, type HookEvent, type HookEventType, type HookHandler, InMemoryConfigProvider, InMemoryStore, type InputRequest, type InputRequestId, type InputRequiredCommit, type JsonObject, type JsonPrimitive, type JsonValue, type LoadExtensionsResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageId, type Metadata, type OpaqueId, type Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseInvocation, type PhaseOutput, type PhaseRegistry, type PhaseState, type RunBoundary, type RunClaim, type RunFailure, type RunId, type RunListCursor, type RunRecord, type RunSnapshot, type RunState, type RunSummary, type RunTransitioned, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, type Skill, SqliteStore, type TextContent, type ThinkingContent, type Tool, type ToolCallId, type ToolCallSnapshot, type ToolCallState, type ToolDefinition, type ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, brandConfigToken, createCoreTools, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhases, loadSkills };
|
|
1803
|
+
export { type AfterToolCall, type AgentConfig, type AgentDefinitionContext, type AgentId, type AgentListCursor, type AgentRecord, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type CoreToolContext, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionFactory, type HookEvent, type HookEventType, type HookHandler, InMemoryConfigProvider, InMemoryStore, type InputRequest, type InputRequestId, type InputRequiredCommit, type JsonObject, type JsonPrimitive, type JsonValue, type LoadExtensionsResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type Metadata, type OpaqueId, type Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseExecutionIdentity, type PhaseInvocation, type PhaseOutput, type PhaseRegistry, type PhaseState, type RunBoundary, type RunClaim, type RunEvent, type RunFailure, type RunId, type RunListCursor, type RunRecord, type RunSnapshot, type RunState, type RunSummary, type RunTransitioned, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, type Skill, SqliteStore, type TextContent, type ThinkingContent, type Tool, type ToolCallId, type ToolCallSnapshot, type ToolCallState, type ToolDefinition, type ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolProgress, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, brandConfigToken, createCoreTools, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhases, loadSkills };
|
package/dist/index.js
CHANGED
|
@@ -832,12 +832,11 @@ async function collectStreamResult(input) {
|
|
|
832
832
|
if (event.type === "text_delta") {
|
|
833
833
|
lastPartial = event.partial;
|
|
834
834
|
if (!activeMessageId) {
|
|
835
|
-
activeMessageId = input.message.
|
|
835
|
+
activeMessageId = input.message.reserve("assistant", {
|
|
836
836
|
phase: input.metadataPhase
|
|
837
837
|
});
|
|
838
|
-
} else {
|
|
839
|
-
await input.message.update(activeMessageId, event.text);
|
|
840
838
|
}
|
|
839
|
+
await input.message.update(activeMessageId, event.text);
|
|
841
840
|
}
|
|
842
841
|
if (event.type === "tool_call_start" || event.type === "tool_call_delta" || event.type === "tool_call_end") {
|
|
843
842
|
activeMessageId ??= input.message.reserve("assistant", {
|
|
@@ -1541,7 +1540,7 @@ function createRunResult(config, state, outcome) {
|
|
|
1541
1540
|
}
|
|
1542
1541
|
async function completeRun(config, state, outcome) {
|
|
1543
1542
|
if (outcome.display) {
|
|
1544
|
-
const messages = createMessageManager(config.context, config.onMessage);
|
|
1543
|
+
const messages = createMessageManager(config.context, config.onMessage, config.onMessageDelta);
|
|
1545
1544
|
const messageId = messages.start("assistant", outcome.message, { outcomeId: outcome.id });
|
|
1546
1545
|
await messages.end(messageId);
|
|
1547
1546
|
}
|
|
@@ -1560,7 +1559,7 @@ function buildToolsWithRouting(config, availablePhases) {
|
|
|
1560
1559
|
}
|
|
1561
1560
|
return tools;
|
|
1562
1561
|
}
|
|
1563
|
-
function createMessageManager(context, onMessage) {
|
|
1562
|
+
function createMessageManager(context, onMessage, onMessageDelta) {
|
|
1564
1563
|
const activeMessages = /* @__PURE__ */ new Map();
|
|
1565
1564
|
const emitStart = (active) => {
|
|
1566
1565
|
if (active.emitted) return;
|
|
@@ -1584,7 +1583,12 @@ function createMessageManager(context, onMessage) {
|
|
|
1584
1583
|
const active = activeMessages.get(messageId);
|
|
1585
1584
|
if (!active) return;
|
|
1586
1585
|
emitStart(active);
|
|
1586
|
+
const offset = typeof active.message.content === "string" ? active.message.content.length : active.message.content.reduce(
|
|
1587
|
+
(length, part) => length + (part.type === "text" ? part.text.length : 0),
|
|
1588
|
+
0
|
|
1589
|
+
);
|
|
1587
1590
|
active.message.content = typeof active.message.content === "string" ? active.message.content + delta : [...active.message.content, { type: "text", text: delta }];
|
|
1591
|
+
onMessageDelta?.({ messageId, offset, text: delta });
|
|
1588
1592
|
},
|
|
1589
1593
|
replaceContent(messageId, content) {
|
|
1590
1594
|
const active = activeMessages.get(messageId);
|
|
@@ -1670,8 +1674,8 @@ async function executePhaseWithModel(ctx) {
|
|
|
1670
1674
|
if (executableToolCalls.length === 0) {
|
|
1671
1675
|
return output;
|
|
1672
1676
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1677
|
+
const results = await ctx.execution.executeTools(roundContext, executableToolCalls);
|
|
1678
|
+
for (const result of results) {
|
|
1675
1679
|
const messageId = ctx.messageManager.start("tool", createToolResultContent(result), {
|
|
1676
1680
|
phase: ctx.phase.name
|
|
1677
1681
|
});
|
|
@@ -1731,7 +1735,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1731
1735
|
}
|
|
1732
1736
|
state.currentPhase = currentPhaseId;
|
|
1733
1737
|
const allTools = buildToolsWithRouting(config, availablePhases);
|
|
1734
|
-
const messageManager = createMessageManager(config.context, config.onMessage);
|
|
1738
|
+
const messageManager = createMessageManager(config.context, config.onMessage, config.onMessageDelta);
|
|
1735
1739
|
const toolExecutionManager = createToolExecutionManager();
|
|
1736
1740
|
const execution = createPhaseExecution(config, state, phase, messageManager, toolExecutionManager, registry);
|
|
1737
1741
|
const phaseTools = phase.tools ? allTools.filter((t) => t.name === PhaseRouteTool || phase.tools.includes(t.name)) : allTools;
|
|
@@ -1745,6 +1749,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1745
1749
|
mode: "serial",
|
|
1746
1750
|
instanceId: currentPhaseId
|
|
1747
1751
|
},
|
|
1752
|
+
execution: config.execution,
|
|
1748
1753
|
state: {
|
|
1749
1754
|
current: currentPhaseId,
|
|
1750
1755
|
available: Array.from(registry.phases.keys()),
|
|
@@ -2098,6 +2103,25 @@ async function executeToolCall(input) {
|
|
|
2098
2103
|
toolName: input.toolCall.name
|
|
2099
2104
|
};
|
|
2100
2105
|
}
|
|
2106
|
+
async function executeToolCalls(input) {
|
|
2107
|
+
if (input.config.runtime?.toolsBatch) {
|
|
2108
|
+
const results2 = await input.config.runtime.toolsBatch({
|
|
2109
|
+
config: input.config,
|
|
2110
|
+
toolCalls: input.toolCalls
|
|
2111
|
+
});
|
|
2112
|
+
if (results2.length !== input.toolCalls.length) throw new Error("Runtime returned an invalid Tool batch result");
|
|
2113
|
+
return results2.map((result, index) => ({
|
|
2114
|
+
...result,
|
|
2115
|
+
toolCallId: input.toolCalls[index].id,
|
|
2116
|
+
toolName: input.toolCalls[index].name
|
|
2117
|
+
}));
|
|
2118
|
+
}
|
|
2119
|
+
const results = [];
|
|
2120
|
+
for (const toolCall of input.toolCalls) {
|
|
2121
|
+
results.push(await executeToolCall({ ...input, toolCall }));
|
|
2122
|
+
}
|
|
2123
|
+
return results;
|
|
2124
|
+
}
|
|
2101
2125
|
function createPhaseExecution(config, state, phase, messageManager, toolExecutionManager, registry) {
|
|
2102
2126
|
return {
|
|
2103
2127
|
snapshot() {
|
|
@@ -2175,6 +2199,30 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
|
|
|
2175
2199
|
await toolExecutionManager.end(result.toolCallId, result.toolName, result, !result.ok);
|
|
2176
2200
|
return result;
|
|
2177
2201
|
});
|
|
2202
|
+
},
|
|
2203
|
+
async executeTools(phaseContext, toolCalls) {
|
|
2204
|
+
return runTurn(async () => {
|
|
2205
|
+
const tools = phaseContext.tools.filter((tool) => tool.name !== PhaseRouteTool);
|
|
2206
|
+
for (const toolCall of toolCalls) {
|
|
2207
|
+
await toolExecutionManager.start(toolCall.id, toolCall.name, toolCall.args);
|
|
2208
|
+
}
|
|
2209
|
+
const results = await executeToolCalls({
|
|
2210
|
+
config: {
|
|
2211
|
+
...config,
|
|
2212
|
+
context: {
|
|
2213
|
+
...config.context,
|
|
2214
|
+
tools,
|
|
2215
|
+
skills: phaseContext.skills
|
|
2216
|
+
}
|
|
2217
|
+
},
|
|
2218
|
+
tools,
|
|
2219
|
+
toolCalls
|
|
2220
|
+
});
|
|
2221
|
+
for (const result of results) {
|
|
2222
|
+
await toolExecutionManager.end(result.toolCallId, result.toolName, result, !result.ok);
|
|
2223
|
+
}
|
|
2224
|
+
return results;
|
|
2225
|
+
});
|
|
2178
2226
|
}
|
|
2179
2227
|
};
|
|
2180
2228
|
}
|
|
@@ -2197,6 +2245,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
|
|
|
2197
2245
|
count,
|
|
2198
2246
|
sourcePhaseId
|
|
2199
2247
|
},
|
|
2248
|
+
execution: config.execution,
|
|
2200
2249
|
state: {
|
|
2201
2250
|
current: phase.name,
|
|
2202
2251
|
available: Array.from(registry.phases.keys()),
|
|
@@ -2204,7 +2253,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
|
|
|
2204
2253
|
payload
|
|
2205
2254
|
}
|
|
2206
2255
|
};
|
|
2207
|
-
const messageManager = createMessageManager({ messages }, config.onMessage);
|
|
2256
|
+
const messageManager = createMessageManager({ messages }, config.onMessage, config.onMessageDelta);
|
|
2208
2257
|
const phaseMsgId = injectPhaseContent(
|
|
2209
2258
|
phase,
|
|
2210
2259
|
{ results: payload !== void 0 ? [{ name: sourcePhaseId, output: payload }] : [] },
|
|
@@ -2395,6 +2444,7 @@ async function executeOnce(input) {
|
|
|
2395
2444
|
};
|
|
2396
2445
|
const config = {
|
|
2397
2446
|
context,
|
|
2447
|
+
execution: input.execution,
|
|
2398
2448
|
model: input.model,
|
|
2399
2449
|
stream: input.stream,
|
|
2400
2450
|
maxAttempts: input.maxAttempts,
|
|
@@ -2405,6 +2455,7 @@ async function executeOnce(input) {
|
|
|
2405
2455
|
afterPhase: input.afterPhase,
|
|
2406
2456
|
beforePrompt: input.beforePrompt,
|
|
2407
2457
|
onMessage: input.onMessage,
|
|
2458
|
+
onMessageDelta: input.onMessageDelta,
|
|
2408
2459
|
onOutcome: input.onOutcome,
|
|
2409
2460
|
onModelTranscript: input.onModelTranscript,
|
|
2410
2461
|
runtime: input.runtime,
|
|
@@ -2911,13 +2962,13 @@ function projectAssistantContent(content) {
|
|
|
2911
2962
|
return content.map((part) => {
|
|
2912
2963
|
if (part.type === "text") return { ...part };
|
|
2913
2964
|
if (part.type === "thinking") return { ...part };
|
|
2914
|
-
return { type: "tool_use", id: part.toolCallId, name: part.name, input: part.input };
|
|
2965
|
+
return { type: "tool_use", id: part.providerToolCallId ?? part.toolCallId, name: part.name, input: part.input };
|
|
2915
2966
|
});
|
|
2916
2967
|
}
|
|
2917
2968
|
function projectToolContent(content) {
|
|
2918
2969
|
return content.map((part) => ({
|
|
2919
2970
|
type: "tool_result",
|
|
2920
|
-
toolUseId: part.toolCallId,
|
|
2971
|
+
toolUseId: part.providerToolCallId ?? part.toolCallId,
|
|
2921
2972
|
content: jsonText(part.result.content),
|
|
2922
2973
|
...part.result.ok ? {} : { isError: true }
|
|
2923
2974
|
}));
|
|
@@ -2931,7 +2982,8 @@ function projectTool(tool, agentId, runId) {
|
|
|
2931
2982
|
const result = await tool.execute(args, {
|
|
2932
2983
|
agentId,
|
|
2933
2984
|
runId,
|
|
2934
|
-
toolCallId: context.toolCallId
|
|
2985
|
+
toolCallId: context.toolCallId,
|
|
2986
|
+
reportProgress: () => void 0
|
|
2935
2987
|
}, signal ?? new AbortController().signal);
|
|
2936
2988
|
return { toolCallId: context.toolCallId, toolName: tool.name, ...result };
|
|
2937
2989
|
}
|
|
@@ -3623,6 +3675,144 @@ function fromLoopResult(result) {
|
|
|
3623
3675
|
return result.ok ? { ok: true, content: result.content } : { ok: false, content: result.content, error: result.error ?? "Extension hook rejected the Tool result." };
|
|
3624
3676
|
}
|
|
3625
3677
|
|
|
3678
|
+
// src/runtime/transient-run-events.ts
|
|
3679
|
+
var MAX_BUFFERED_EVENTS = 128;
|
|
3680
|
+
var MAX_BUFFERED_TEXT = 64 * 1024;
|
|
3681
|
+
function deferred() {
|
|
3682
|
+
let resolve6;
|
|
3683
|
+
const promise = new Promise((done) => {
|
|
3684
|
+
resolve6 = done;
|
|
3685
|
+
});
|
|
3686
|
+
return { promise, resolve: resolve6 };
|
|
3687
|
+
}
|
|
3688
|
+
var TransientRunEventSubscription = class {
|
|
3689
|
+
queue = [];
|
|
3690
|
+
wake = deferred();
|
|
3691
|
+
version = 0;
|
|
3692
|
+
stopped = false;
|
|
3693
|
+
push(event) {
|
|
3694
|
+
if (this.stopped) return;
|
|
3695
|
+
const previous = this.queue.at(-1);
|
|
3696
|
+
if (previous?.kind === "message_delta" && event.kind === "message_delta" && previous.executionId === event.executionId && previous.messageId === event.messageId && previous.offset + previous.text.length === event.offset) {
|
|
3697
|
+
this.queue[this.queue.length - 1] = {
|
|
3698
|
+
...previous,
|
|
3699
|
+
text: previous.text + event.text
|
|
3700
|
+
};
|
|
3701
|
+
} else {
|
|
3702
|
+
if (this.queue.length >= MAX_BUFFERED_EVENTS) this.queue.shift();
|
|
3703
|
+
this.queue.push(event);
|
|
3704
|
+
}
|
|
3705
|
+
this.trimText();
|
|
3706
|
+
this.notify();
|
|
3707
|
+
}
|
|
3708
|
+
shift() {
|
|
3709
|
+
return this.queue.shift();
|
|
3710
|
+
}
|
|
3711
|
+
clear(executionId) {
|
|
3712
|
+
if (executionId === void 0) this.queue.length = 0;
|
|
3713
|
+
else {
|
|
3714
|
+
for (let index = this.queue.length - 1; index >= 0; index -= 1) {
|
|
3715
|
+
if (this.queue[index]?.executionId === executionId) this.queue.splice(index, 1);
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
this.notify();
|
|
3719
|
+
}
|
|
3720
|
+
clearMessage(messageId) {
|
|
3721
|
+
for (let index = this.queue.length - 1; index >= 0; index -= 1) {
|
|
3722
|
+
const event = this.queue[index];
|
|
3723
|
+
if (event?.kind === "message_delta" && event.messageId === messageId) this.queue.splice(index, 1);
|
|
3724
|
+
}
|
|
3725
|
+
this.notify();
|
|
3726
|
+
}
|
|
3727
|
+
clearTool(toolCallId) {
|
|
3728
|
+
for (let index = this.queue.length - 1; index >= 0; index -= 1) {
|
|
3729
|
+
const event = this.queue[index];
|
|
3730
|
+
if (event?.kind === "tool_progress" && event.toolCallId === toolCallId) this.queue.splice(index, 1);
|
|
3731
|
+
}
|
|
3732
|
+
this.notify();
|
|
3733
|
+
}
|
|
3734
|
+
checkpoint() {
|
|
3735
|
+
return this.version;
|
|
3736
|
+
}
|
|
3737
|
+
changed(since) {
|
|
3738
|
+
if (this.version !== since || this.queue.length > 0 || this.stopped) return Promise.resolve();
|
|
3739
|
+
return this.wake.promise;
|
|
3740
|
+
}
|
|
3741
|
+
stop() {
|
|
3742
|
+
if (this.stopped) return;
|
|
3743
|
+
this.stopped = true;
|
|
3744
|
+
this.queue.length = 0;
|
|
3745
|
+
this.notify();
|
|
3746
|
+
}
|
|
3747
|
+
notify() {
|
|
3748
|
+
const wake = this.wake;
|
|
3749
|
+
this.wake = deferred();
|
|
3750
|
+
this.version += 1;
|
|
3751
|
+
wake.resolve();
|
|
3752
|
+
}
|
|
3753
|
+
trimText() {
|
|
3754
|
+
let buffered = this.queue.reduce(
|
|
3755
|
+
(length, event) => length + (event.kind === "message_delta" ? event.text.length : 0),
|
|
3756
|
+
0
|
|
3757
|
+
);
|
|
3758
|
+
while (buffered > MAX_BUFFERED_TEXT) {
|
|
3759
|
+
const index = this.queue.findIndex((event2) => event2.kind === "message_delta");
|
|
3760
|
+
if (index < 0) return;
|
|
3761
|
+
const event = this.queue[index];
|
|
3762
|
+
const overflow = buffered - MAX_BUFFERED_TEXT;
|
|
3763
|
+
if (event.text.length <= overflow) {
|
|
3764
|
+
this.queue.splice(index, 1);
|
|
3765
|
+
buffered -= event.text.length;
|
|
3766
|
+
} else {
|
|
3767
|
+
this.queue[index] = {
|
|
3768
|
+
...event,
|
|
3769
|
+
offset: event.offset + overflow,
|
|
3770
|
+
text: event.text.slice(overflow)
|
|
3771
|
+
};
|
|
3772
|
+
buffered -= overflow;
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
};
|
|
3777
|
+
var TransientRunEventHub = class {
|
|
3778
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
3779
|
+
subscribe(runId) {
|
|
3780
|
+
const subscription = new TransientRunEventSubscription();
|
|
3781
|
+
const current = this.subscriptions.get(runId) ?? /* @__PURE__ */ new Set();
|
|
3782
|
+
current.add(subscription);
|
|
3783
|
+
this.subscriptions.set(runId, current);
|
|
3784
|
+
return subscription;
|
|
3785
|
+
}
|
|
3786
|
+
unsubscribe(runId, subscription) {
|
|
3787
|
+
subscription.stop();
|
|
3788
|
+
const current = this.subscriptions.get(runId);
|
|
3789
|
+
current?.delete(subscription);
|
|
3790
|
+
if (current?.size === 0) this.subscriptions.delete(runId);
|
|
3791
|
+
}
|
|
3792
|
+
publish(event) {
|
|
3793
|
+
for (const subscription of this.subscriptions.get(event.runId) ?? []) {
|
|
3794
|
+
subscription.push(event);
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
clear(runId, executionId) {
|
|
3798
|
+
for (const subscription of this.subscriptions.get(runId) ?? []) {
|
|
3799
|
+
subscription.clear(executionId);
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
clearMessage(runId, messageId) {
|
|
3803
|
+
for (const subscription of this.subscriptions.get(runId) ?? []) subscription.clearMessage(messageId);
|
|
3804
|
+
}
|
|
3805
|
+
clearTool(runId, toolCallId) {
|
|
3806
|
+
for (const subscription of this.subscriptions.get(runId) ?? []) subscription.clearTool(toolCallId);
|
|
3807
|
+
}
|
|
3808
|
+
close() {
|
|
3809
|
+
for (const subscriptions of this.subscriptions.values()) {
|
|
3810
|
+
for (const subscription of subscriptions) subscription.stop();
|
|
3811
|
+
}
|
|
3812
|
+
this.subscriptions.clear();
|
|
3813
|
+
}
|
|
3814
|
+
};
|
|
3815
|
+
|
|
3626
3816
|
// src/runtime/durable-runtime.ts
|
|
3627
3817
|
var DEFAULT_CONCURRENCY = 10;
|
|
3628
3818
|
var DEFAULT_POLL_MS = 25;
|
|
@@ -3636,6 +3826,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3636
3826
|
activeAgents = /* @__PURE__ */ new Set();
|
|
3637
3827
|
executions = /* @__PURE__ */ new Map();
|
|
3638
3828
|
consumers = /* @__PURE__ */ new Map();
|
|
3829
|
+
transientEvents = new TransientRunEventHub();
|
|
3639
3830
|
heartbeat;
|
|
3640
3831
|
pumping = false;
|
|
3641
3832
|
closed = false;
|
|
@@ -3693,7 +3884,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3693
3884
|
if (input.signal.aborted) throw abortError();
|
|
3694
3885
|
if (this.consumers.has(input.consumerId)) throw new RuntimeError("consumer_already_active", { consumerId: input.consumerId });
|
|
3695
3886
|
const controller = new AbortController();
|
|
3696
|
-
const subscription = { consumerId: input.consumerId, controller, input, caughtUp:
|
|
3887
|
+
const subscription = { consumerId: input.consumerId, controller, input, caughtUp: deferred2(), done: deferred2() };
|
|
3697
3888
|
this.consumers.set(input.consumerId, subscription);
|
|
3698
3889
|
const onAbort = () => controller.abort();
|
|
3699
3890
|
input.signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -3710,8 +3901,9 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3710
3901
|
if (this.closed) return;
|
|
3711
3902
|
this.closed = true;
|
|
3712
3903
|
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
3713
|
-
for (const
|
|
3904
|
+
for (const execution of this.executions.values()) execution.controller.abort();
|
|
3714
3905
|
this.executions.clear();
|
|
3906
|
+
this.transientEvents.close();
|
|
3715
3907
|
for (const subscription of this.consumers.values()) subscription.controller.abort();
|
|
3716
3908
|
await this.owned.sealAndReleaseOwner();
|
|
3717
3909
|
}
|
|
@@ -3732,7 +3924,9 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3732
3924
|
this.assertOpen();
|
|
3733
3925
|
const snapshot = await this.owned.snapshotRun(runId);
|
|
3734
3926
|
await this.owned.cancelRun({ runId, expectedRevision: snapshot.revision, ...reason === void 0 ? {} : { reason } });
|
|
3735
|
-
this.executions.get(runId)
|
|
3927
|
+
const active = this.executions.get(runId);
|
|
3928
|
+
if (active) this.transientEvents.clear(runId, active.executionId);
|
|
3929
|
+
active?.controller.abort();
|
|
3736
3930
|
return boundaryFromSnapshot(await this.owned.snapshotRun(runId));
|
|
3737
3931
|
}
|
|
3738
3932
|
async pump() {
|
|
@@ -3745,6 +3939,8 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3745
3939
|
if (!next) return;
|
|
3746
3940
|
this.activeAgents.add(next.agentId);
|
|
3747
3941
|
const task = this.execute(next).finally(() => {
|
|
3942
|
+
const execution = this.executions.get(next.id);
|
|
3943
|
+
if (execution) this.transientEvents.clear(next.id, execution.executionId);
|
|
3748
3944
|
this.activeAgents.delete(next.agentId);
|
|
3749
3945
|
this.executions.delete(next.id);
|
|
3750
3946
|
void this.pump();
|
|
@@ -3783,7 +3979,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3783
3979
|
const executionId = createId("exec");
|
|
3784
3980
|
claim = await this.owned.claimRun({ runId: run.id, expectedRevision: run.revision, executionId, configToken: token });
|
|
3785
3981
|
const controller = new AbortController();
|
|
3786
|
-
this.executions.set(run.id, controller);
|
|
3982
|
+
this.executions.set(run.id, { controller, executionId });
|
|
3787
3983
|
const config = resolution.config;
|
|
3788
3984
|
const assembly = await assembleExtensions(config);
|
|
3789
3985
|
const executionConfig = {
|
|
@@ -3811,6 +4007,11 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3811
4007
|
const result = await executeOnce({
|
|
3812
4008
|
canonicalMessages: executionContext.messages,
|
|
3813
4009
|
context: executionContext,
|
|
4010
|
+
execution: {
|
|
4011
|
+
agentId: run.agentId,
|
|
4012
|
+
runId: run.id,
|
|
4013
|
+
executionId: claim.execution.executionId
|
|
4014
|
+
},
|
|
3814
4015
|
model,
|
|
3815
4016
|
stream,
|
|
3816
4017
|
maxAttempts: executionConfig.maxAttempts,
|
|
@@ -3819,20 +4020,49 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3819
4020
|
beforePhase: assembly.beforePhase,
|
|
3820
4021
|
afterPhase: assembly.afterPhase,
|
|
3821
4022
|
beforePrompt: assembly.beforePrompt,
|
|
4023
|
+
onMessageDelta: (event) => {
|
|
4024
|
+
const active = this.executions.get(run.id);
|
|
4025
|
+
if (this.closed || controller.signal.aborted || active?.executionId !== claim.execution.executionId) return;
|
|
4026
|
+
this.transientEvents.publish({
|
|
4027
|
+
kind: "message_delta",
|
|
4028
|
+
durability: "transient",
|
|
4029
|
+
runId: run.id,
|
|
4030
|
+
executionId: claim.execution.executionId,
|
|
4031
|
+
messageId: event.messageId,
|
|
4032
|
+
offset: event.offset,
|
|
4033
|
+
text: event.text
|
|
4034
|
+
});
|
|
4035
|
+
},
|
|
3822
4036
|
onContext: assembly.setContext,
|
|
3823
4037
|
runtime: {
|
|
3824
4038
|
tools: ({ toolCall }) => {
|
|
3825
4039
|
const task = toolQueue.then(async () => {
|
|
3826
|
-
const execution = await this.
|
|
4040
|
+
const execution = await this.executeToolBatch({
|
|
4041
|
+
run,
|
|
4042
|
+
execution: claim.execution,
|
|
4043
|
+
expectedRevision: executionRevision,
|
|
4044
|
+
config: executionConfig,
|
|
4045
|
+
toolCalls: [toolCall],
|
|
4046
|
+
signal: controller.signal
|
|
4047
|
+
});
|
|
4048
|
+
executionRevision = execution.revision;
|
|
4049
|
+
return execution.results[0];
|
|
4050
|
+
});
|
|
4051
|
+
toolQueue = task.then(() => void 0, () => void 0);
|
|
4052
|
+
return task;
|
|
4053
|
+
},
|
|
4054
|
+
toolsBatch: ({ toolCalls }) => {
|
|
4055
|
+
const task = toolQueue.then(async () => {
|
|
4056
|
+
const execution = await this.executeToolBatch({
|
|
3827
4057
|
run,
|
|
3828
4058
|
execution: claim.execution,
|
|
3829
4059
|
expectedRevision: executionRevision,
|
|
3830
4060
|
config: executionConfig,
|
|
3831
|
-
|
|
4061
|
+
toolCalls,
|
|
3832
4062
|
signal: controller.signal
|
|
3833
4063
|
});
|
|
3834
4064
|
executionRevision = execution.revision;
|
|
3835
|
-
return execution.
|
|
4065
|
+
return execution.results;
|
|
3836
4066
|
});
|
|
3837
4067
|
toolQueue = task.then(() => void 0, () => void 0);
|
|
3838
4068
|
return task;
|
|
@@ -3842,15 +4072,18 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3842
4072
|
if (result.type === "input_required") {
|
|
3843
4073
|
const prompt = promptMessage(run, result.request.prompt, result.messages.length);
|
|
3844
4074
|
await this.owned.commitInputRequired({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, requestId: createId("input"), prompt, checkpoint: result.checkpoint });
|
|
4075
|
+
this.transientEvents.clear(run.id, claim.execution.executionId);
|
|
3845
4076
|
return;
|
|
3846
4077
|
}
|
|
3847
4078
|
if (result.type === "completed") {
|
|
3848
4079
|
const output = latestAssistant(run, result.messages, claim.history.length);
|
|
3849
4080
|
await this.owned.commitOutcome({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, outcome: durableOutcome(result.outcome), ...output ? { output } : {} });
|
|
4081
|
+
this.transientEvents.clear(run.id, claim.execution.executionId);
|
|
3850
4082
|
return;
|
|
3851
4083
|
}
|
|
3852
4084
|
const failure = { code: "execution_failed", message: result.error instanceof Error ? result.error.message : "Execution failed." };
|
|
3853
4085
|
await this.owned.commitOutcome({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, failure });
|
|
4086
|
+
this.transientEvents.clear(run.id, claim.execution.executionId);
|
|
3854
4087
|
} catch (error) {
|
|
3855
4088
|
if (!claim && error instanceof RuntimeError && ["run_state_conflict", "runtime_ownership_lost", "run_not_found"].includes(error.code)) return;
|
|
3856
4089
|
if (claim) {
|
|
@@ -3871,29 +4104,75 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3871
4104
|
});
|
|
3872
4105
|
}
|
|
3873
4106
|
async executeTool(input) {
|
|
3874
|
-
const
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
const reserved = await this.owned.
|
|
4107
|
+
const batch = await this.executeToolBatch({ ...input, toolCalls: [input.toolCall] });
|
|
4108
|
+
return { result: batch.results[0], revision: batch.revision };
|
|
4109
|
+
}
|
|
4110
|
+
async executeToolBatch(input) {
|
|
4111
|
+
const reserved = await this.owned.reserveToolCalls({
|
|
3879
4112
|
runId: input.run.id,
|
|
3880
4113
|
execution: input.execution,
|
|
3881
4114
|
expectedRevision: input.expectedRevision,
|
|
3882
|
-
requestMessageId,
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
4115
|
+
requestMessageId: createId("msg"),
|
|
4116
|
+
calls: input.toolCalls.map((toolCall) => ({
|
|
4117
|
+
providerToolCallId: toolCall.id,
|
|
4118
|
+
name: toolCall.name,
|
|
4119
|
+
args: toJsonValue(toolCall.args)
|
|
4120
|
+
}))
|
|
3886
4121
|
});
|
|
3887
4122
|
let revision = reserved.run.revision;
|
|
4123
|
+
const results = [];
|
|
4124
|
+
for (let index = 0; index < input.toolCalls.length; index += 1) {
|
|
4125
|
+
const toolCall = input.toolCalls[index];
|
|
4126
|
+
const tool = reserved.toolCalls[index];
|
|
4127
|
+
const execution = await this.executeReservedTool({
|
|
4128
|
+
...input,
|
|
4129
|
+
toolCall,
|
|
4130
|
+
tool,
|
|
4131
|
+
expectedRevision: revision
|
|
4132
|
+
});
|
|
4133
|
+
revision = execution.revision;
|
|
4134
|
+
results.push(execution.result);
|
|
4135
|
+
}
|
|
4136
|
+
return { results, revision };
|
|
4137
|
+
}
|
|
4138
|
+
async executeReservedTool(input) {
|
|
4139
|
+
const durableTool = input.config.context.tools.find((candidate) => candidate.name === input.toolCall.name);
|
|
4140
|
+
const providerToolCallId = input.toolCall.id;
|
|
4141
|
+
const toolCallId = input.tool.id;
|
|
4142
|
+
let revision = input.expectedRevision;
|
|
3888
4143
|
const protocolFailure = (error) => ({ toolCallId: providerToolCallId, toolName: input.toolCall.name, ok: false, content: null, error });
|
|
3889
|
-
if (!
|
|
4144
|
+
if (!durableTool) {
|
|
3890
4145
|
const failed = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: "failed", result: { ok: false, content: null, error: `Tool ${input.toolCall.name} is not available.` } });
|
|
3891
4146
|
return { result: protocolFailure(`Tool ${input.toolCall.name} is not available.`), revision: failed.run.revision };
|
|
3892
4147
|
}
|
|
3893
|
-
|
|
4148
|
+
let progressActive = false;
|
|
4149
|
+
const context = {
|
|
4150
|
+
agentId: input.run.agentId,
|
|
4151
|
+
runId: input.run.id,
|
|
4152
|
+
toolCallId,
|
|
4153
|
+
reportProgress: (progress) => {
|
|
4154
|
+
const active = this.executions.get(input.run.id);
|
|
4155
|
+
if (!progressActive || this.closed || input.signal.aborted || active?.executionId !== input.execution.executionId || !isJsonValue(progress)) return;
|
|
4156
|
+
let copy;
|
|
4157
|
+
try {
|
|
4158
|
+
if (JSON.stringify(progress).length > 64 * 1024) return;
|
|
4159
|
+
copy = structuredClone(progress);
|
|
4160
|
+
} catch {
|
|
4161
|
+
return;
|
|
4162
|
+
}
|
|
4163
|
+
this.transientEvents.publish({
|
|
4164
|
+
kind: "tool_progress",
|
|
4165
|
+
durability: "transient",
|
|
4166
|
+
runId: input.run.id,
|
|
4167
|
+
executionId: input.execution.executionId,
|
|
4168
|
+
toolCallId,
|
|
4169
|
+
progress: copy
|
|
4170
|
+
});
|
|
4171
|
+
}
|
|
4172
|
+
};
|
|
3894
4173
|
try {
|
|
3895
4174
|
if (input.config.beforeToolCall) {
|
|
3896
|
-
const decision = await input.config.beforeToolCall({ tool, args: toJsonValue(input.toolCall.args), context, signal: input.signal });
|
|
4175
|
+
const decision = await input.config.beforeToolCall({ tool: durableTool, args: toJsonValue(input.toolCall.args), context, signal: input.signal });
|
|
3897
4176
|
if (!decision.allow) {
|
|
3898
4177
|
const failed = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: "failed", result: { ok: false, content: null, error: decision.reason } });
|
|
3899
4178
|
return { result: protocolFailure(decision.reason), revision: failed.run.revision };
|
|
@@ -3906,10 +4185,11 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3906
4185
|
}
|
|
3907
4186
|
const started = await this.owned.startToolCall({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId });
|
|
3908
4187
|
revision = started.run.revision;
|
|
4188
|
+
progressActive = true;
|
|
3909
4189
|
try {
|
|
3910
|
-
let result = await
|
|
4190
|
+
let result = await durableTool.execute(toJsonValue(input.toolCall.args), context, input.signal);
|
|
3911
4191
|
assertToolExecutionResult(result);
|
|
3912
|
-
if (input.config.afterToolCall) result = await input.config.afterToolCall({ tool, result, context, signal: input.signal });
|
|
4192
|
+
if (input.config.afterToolCall) result = await input.config.afterToolCall({ tool: durableTool, result, context, signal: input.signal });
|
|
3913
4193
|
assertToolExecutionResult(result);
|
|
3914
4194
|
const committed = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: result.ok ? "completed" : "failed", result });
|
|
3915
4195
|
return {
|
|
@@ -3920,6 +4200,9 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3920
4200
|
const reason = error instanceof Error ? error.message : "Tool execution outcome is indeterminate.";
|
|
3921
4201
|
const indeterminate = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: "indeterminate", reason, result: { ok: false, content: null, error: reason } });
|
|
3922
4202
|
return { result: protocolFailure(reason), revision: indeterminate.run.revision };
|
|
4203
|
+
} finally {
|
|
4204
|
+
progressActive = false;
|
|
4205
|
+
this.transientEvents.clearTool(input.run.id, toolCallId);
|
|
3923
4206
|
}
|
|
3924
4207
|
}
|
|
3925
4208
|
async requireAgent(agentId) {
|
|
@@ -3963,23 +4246,37 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3963
4246
|
}
|
|
3964
4247
|
async *observe(runId, options = {}) {
|
|
3965
4248
|
let cursor = options.after;
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
const
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
4249
|
+
const subscription = this.transientEvents.subscribe(runId);
|
|
4250
|
+
try {
|
|
4251
|
+
while (true) {
|
|
4252
|
+
if (options.signal?.aborted) throw abortError();
|
|
4253
|
+
const observationVersion = subscription.checkpoint();
|
|
4254
|
+
const snapshot = await this.owned.snapshotRun(runId);
|
|
4255
|
+
if (["completed", "failed", "cancelled"].includes(snapshot.state)) {
|
|
4256
|
+
const pending = await this.owned.listEvents(cursor ? { after: cursor } : {});
|
|
4257
|
+
if (!pending.some((event) => event.runId === runId)) return;
|
|
4258
|
+
}
|
|
4259
|
+
const events = await this.owned.listEvents(cursor ? { after: cursor } : {});
|
|
4260
|
+
for (const event of events) {
|
|
4261
|
+
cursor = event.cursor;
|
|
4262
|
+
if (event.runId !== runId) continue;
|
|
4263
|
+
if (event.kind === "message_committed") {
|
|
4264
|
+
subscription.clearMessage(event.message.id);
|
|
4265
|
+
} else if (event.kind === "tool_state_changed" && ["completed", "failed", "indeterminate"].includes(event.transition.to)) {
|
|
4266
|
+
subscription.clearTool(event.toolCall.id);
|
|
4267
|
+
}
|
|
4268
|
+
yield event;
|
|
4269
|
+
if (event.kind === "run_transitioned" && ["completed", "failed", "cancelled"].includes(event.to)) return;
|
|
4270
|
+
}
|
|
4271
|
+
const transient = subscription.shift();
|
|
4272
|
+
if (transient) {
|
|
4273
|
+
yield transient;
|
|
4274
|
+
continue;
|
|
4275
|
+
}
|
|
4276
|
+
await Promise.race([subscription.changed(observationVersion), delay(DEFAULT_POLL_MS)]);
|
|
3980
4277
|
}
|
|
3981
|
-
|
|
3982
|
-
|
|
4278
|
+
} finally {
|
|
4279
|
+
this.transientEvents.unsubscribe(runId, subscription);
|
|
3983
4280
|
}
|
|
3984
4281
|
}
|
|
3985
4282
|
async wait(runId, options = {}) {
|
|
@@ -3996,7 +4293,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
3996
4293
|
startHeartbeat() {
|
|
3997
4294
|
this.heartbeat = setInterval(() => {
|
|
3998
4295
|
void this.owned.renewOwner(OWNER_LEASE_MS).catch(() => {
|
|
3999
|
-
for (const
|
|
4296
|
+
for (const execution of this.executions.values()) execution.controller.abort();
|
|
4000
4297
|
});
|
|
4001
4298
|
}, OWNER_RENEWAL_MS);
|
|
4002
4299
|
this.heartbeat.unref?.();
|
|
@@ -4070,7 +4367,7 @@ function toJsonValue(value) {
|
|
|
4070
4367
|
assertJsonValue(value, "tool arguments");
|
|
4071
4368
|
return value;
|
|
4072
4369
|
}
|
|
4073
|
-
function
|
|
4370
|
+
function deferred2() {
|
|
4074
4371
|
let resolve6;
|
|
4075
4372
|
let reject;
|
|
4076
4373
|
const promise = new Promise((res, rej) => {
|
|
@@ -4404,54 +4701,91 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
4404
4701
|
return result;
|
|
4405
4702
|
}
|
|
4406
4703
|
reserveToolCall(lease, input) {
|
|
4704
|
+
const providerToolCallId = input.providerToolCallId ?? (input.toolCallId ? String(input.toolCallId) : createId("provider"));
|
|
4705
|
+
const result = this.reserveToolCalls(lease, {
|
|
4706
|
+
runId: input.runId,
|
|
4707
|
+
execution: input.execution,
|
|
4708
|
+
expectedRevision: input.expectedRevision,
|
|
4709
|
+
requestMessageId: input.requestMessageId,
|
|
4710
|
+
calls: [{
|
|
4711
|
+
providerToolCallId,
|
|
4712
|
+
name: input.name,
|
|
4713
|
+
args: input.args,
|
|
4714
|
+
...input.toolCallId === void 0 ? {} : { toolCallId: input.toolCallId }
|
|
4715
|
+
}]
|
|
4716
|
+
});
|
|
4717
|
+
return { run: result.run, toolCall: result.toolCalls[0] };
|
|
4718
|
+
}
|
|
4719
|
+
reserveToolCalls(lease, input) {
|
|
4407
4720
|
this.assertOwner(lease);
|
|
4408
|
-
|
|
4409
|
-
const
|
|
4410
|
-
const
|
|
4721
|
+
if (input.calls.length === 0) throw new TypeError("calls must be non-empty");
|
|
4722
|
+
const providerIds = /* @__PURE__ */ new Set();
|
|
4723
|
+
const durableIds = /* @__PURE__ */ new Set();
|
|
4724
|
+
for (const call of input.calls) {
|
|
4725
|
+
if (call.providerToolCallId.trim().length === 0) throw new TypeError("providerToolCallId must be non-empty");
|
|
4726
|
+
if (providerIds.has(call.providerToolCallId)) throw new TypeError("Tool provider IDs must be unique within one response");
|
|
4727
|
+
providerIds.add(call.providerToolCallId);
|
|
4728
|
+
assertToolValue(call.args, "tool.args");
|
|
4729
|
+
if (call.toolCallId !== void 0) {
|
|
4730
|
+
if (durableIds.has(call.toolCallId)) throw new TypeError("Tool Call IDs must be unique within one response");
|
|
4731
|
+
durableIds.add(call.toolCallId);
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
const operationKey = `tool_reserve_batch:${input.requestMessageId}`;
|
|
4411
4735
|
const operationPayload = canonicalJson([
|
|
4412
4736
|
input.runId,
|
|
4413
4737
|
input.expectedRevision,
|
|
4414
4738
|
input.execution.executionId,
|
|
4415
4739
|
input.requestMessageId,
|
|
4416
|
-
input.
|
|
4417
|
-
input.args
|
|
4740
|
+
input.calls
|
|
4418
4741
|
]);
|
|
4419
4742
|
const replay = this.replayOperation(operationKey, operationPayload);
|
|
4420
4743
|
if (replay) return clone(replay);
|
|
4421
4744
|
const run = this.requireRun(input.runId);
|
|
4422
4745
|
this.assertExecution(run, input.execution, input.expectedRevision);
|
|
4423
|
-
|
|
4424
|
-
if (existing) throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
|
|
4746
|
+
if (this.messages.has(input.requestMessageId)) throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
|
|
4425
4747
|
const timestamp = createTimestamp();
|
|
4426
|
-
const
|
|
4427
|
-
id: toolCallId,
|
|
4748
|
+
const toolCalls = input.calls.map((call) => ({
|
|
4749
|
+
id: call.toolCallId ?? createId("tool"),
|
|
4750
|
+
providerToolCallId: call.providerToolCallId,
|
|
4428
4751
|
agentId: run.agentId,
|
|
4429
4752
|
runId: run.id,
|
|
4430
4753
|
executionId: input.execution.executionId,
|
|
4431
4754
|
requestMessageId: input.requestMessageId,
|
|
4432
|
-
name:
|
|
4433
|
-
args: clone(
|
|
4755
|
+
name: call.name,
|
|
4756
|
+
args: clone(call.args),
|
|
4434
4757
|
state: "pending",
|
|
4435
4758
|
createdAt: timestamp,
|
|
4436
4759
|
updatedAt: timestamp
|
|
4437
|
-
};
|
|
4760
|
+
}));
|
|
4761
|
+
for (const toolCall of toolCalls) {
|
|
4762
|
+
if (this.toolCalls.has(toolCall.id)) throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
|
|
4763
|
+
}
|
|
4438
4764
|
const requestMessage = {
|
|
4439
4765
|
id: input.requestMessageId,
|
|
4440
4766
|
agentId: run.agentId,
|
|
4441
4767
|
runId: run.id,
|
|
4442
4768
|
role: "assistant",
|
|
4443
|
-
content:
|
|
4769
|
+
content: toolCalls.map((toolCall) => ({
|
|
4770
|
+
type: "tool_use",
|
|
4771
|
+
toolCallId: toolCall.id,
|
|
4772
|
+
providerToolCallId: toolCall.providerToolCallId,
|
|
4773
|
+
name: toolCall.name,
|
|
4774
|
+
input: clone(toolCall.args)
|
|
4775
|
+
})),
|
|
4444
4776
|
sequenceWithinRun: this.nextMessageSequence(run.id),
|
|
4445
4777
|
createdAt: timestamp
|
|
4446
4778
|
};
|
|
4447
|
-
|
|
4448
|
-
this.toolCalls.set(toolCall.id, toolCall);
|
|
4779
|
+
for (const toolCall of toolCalls) this.toolCalls.set(toolCall.id, toolCall);
|
|
4449
4780
|
this.messages.set(requestMessage.id, requestMessage);
|
|
4450
4781
|
run.revision += 1;
|
|
4451
4782
|
run.updatedAt = timestamp;
|
|
4452
4783
|
this.appendMessage(run, requestMessage);
|
|
4453
|
-
this.appendToolTransition(run, { from: null, to: "pending" }, toolCall);
|
|
4454
|
-
const result = {
|
|
4784
|
+
for (const toolCall of toolCalls) this.appendToolTransition(run, { from: null, to: "pending" }, toolCall);
|
|
4785
|
+
const result = {
|
|
4786
|
+
run: clone(run),
|
|
4787
|
+
toolCalls: toolCalls.map((toolCall) => clone(toolCall))
|
|
4788
|
+
};
|
|
4455
4789
|
this.writeOperationReceipt(operationKey, operationPayload, result);
|
|
4456
4790
|
return result;
|
|
4457
4791
|
}
|
|
@@ -4500,7 +4834,7 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
4500
4834
|
agentId: run.agentId,
|
|
4501
4835
|
runId: run.id,
|
|
4502
4836
|
role: "tool",
|
|
4503
|
-
content: [{ type: "tool_result", toolCallId: toolCall.id, result: clone(input.result) }],
|
|
4837
|
+
content: [{ type: "tool_result", toolCallId: toolCall.id, providerToolCallId: toolCall.providerToolCallId, result: clone(input.result) }],
|
|
4504
4838
|
sequenceWithinRun: this.nextMessageSequence(run.id),
|
|
4505
4839
|
createdAt: createTimestamp()
|
|
4506
4840
|
};
|
|
@@ -4660,6 +4994,9 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
4660
4994
|
listEvents(lease, input = {}) {
|
|
4661
4995
|
this.assertOwner(lease);
|
|
4662
4996
|
const after = input.after ? this.parseCursor(input.after) : 0;
|
|
4997
|
+
if (after > this.eventSequence) {
|
|
4998
|
+
throw new RuntimeError("invalid_cursor", { cursorType: "event", reason: "beyond_waterline" });
|
|
4999
|
+
}
|
|
4663
5000
|
return clone(this.events.filter((event) => this.parseCursor(event.cursor) > after));
|
|
4664
5001
|
}
|
|
4665
5002
|
openConsumer(lease, consumerId) {
|
|
@@ -4730,7 +5067,7 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
4730
5067
|
agentId: run.agentId,
|
|
4731
5068
|
runId: run.id,
|
|
4732
5069
|
role: "tool",
|
|
4733
|
-
content: [{ type: "tool_result", toolCallId: toolCall.id, result }],
|
|
5070
|
+
content: [{ type: "tool_result", toolCallId: toolCall.id, providerToolCallId: toolCall.providerToolCallId, result }],
|
|
4734
5071
|
sequenceWithinRun: this.nextMessageSequence(run.id),
|
|
4735
5072
|
createdAt: createTimestamp()
|
|
4736
5073
|
};
|
|
@@ -4865,6 +5202,9 @@ var MemoryOwnedStore = class {
|
|
|
4865
5202
|
async reserveToolCall(input) {
|
|
4866
5203
|
return this.store.reserveToolCall(this.lease, input);
|
|
4867
5204
|
}
|
|
5205
|
+
async reserveToolCalls(input) {
|
|
5206
|
+
return this.store.reserveToolCalls(this.lease, input);
|
|
5207
|
+
}
|
|
4868
5208
|
async startToolCall(input) {
|
|
4869
5209
|
return this.store.startToolCall(this.lease, input);
|
|
4870
5210
|
}
|
|
@@ -5118,6 +5458,9 @@ var SqliteStore = class {
|
|
|
5118
5458
|
async reserveToolCall(lease, input) {
|
|
5119
5459
|
return this.invoke(lease, (store, current) => store.reserveToolCall(current, input));
|
|
5120
5460
|
}
|
|
5461
|
+
async reserveToolCalls(lease, input) {
|
|
5462
|
+
return this.invoke(lease, (store, current) => store.reserveToolCalls(current, input));
|
|
5463
|
+
}
|
|
5121
5464
|
async startToolCall(lease, input) {
|
|
5122
5465
|
return this.invoke(lease, (store, current) => store.startToolCall(current, input));
|
|
5123
5466
|
}
|
|
@@ -5367,6 +5710,9 @@ var SqliteOwnedStore = class {
|
|
|
5367
5710
|
reserveToolCall(input) {
|
|
5368
5711
|
return this.store.reserveToolCall(this.lease, input);
|
|
5369
5712
|
}
|
|
5713
|
+
reserveToolCalls(input) {
|
|
5714
|
+
return this.store.reserveToolCalls(this.lease, input);
|
|
5715
|
+
}
|
|
5370
5716
|
startToolCall(input) {
|
|
5371
5717
|
return this.store.startToolCall(this.lease, input);
|
|
5372
5718
|
}
|