stitchkit 0.68.11 → 0.70.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 +30 -1
- package/dist/agent-runtime/coding-tool-contract.d.ts +156 -0
- package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -0
- package/dist/agent-runtime/coding-tool-files.d.ts +3 -0
- package/dist/agent-runtime/coding-tool-files.d.ts.map +1 -0
- package/dist/agent-runtime/coding-tool-paths.d.ts +20 -0
- package/dist/agent-runtime/coding-tool-paths.d.ts.map +1 -0
- package/dist/agent-runtime/coding-tool-search-patch.d.ts +3 -0
- package/dist/agent-runtime/coding-tool-search-patch.d.ts.map +1 -0
- package/dist/agent-runtime/coding-tool-shell.d.ts +3 -0
- package/dist/agent-runtime/coding-tool-shell.d.ts.map +1 -0
- package/dist/agent-runtime/coding-tools.d.ts +17 -0
- package/dist/agent-runtime/coding-tools.d.ts.map +1 -0
- package/dist/agent-runtime/compaction.d.ts +2 -0
- package/dist/agent-runtime/compaction.d.ts.map +1 -1
- package/dist/agent-runtime/contained-files.d.ts +56 -0
- package/dist/agent-runtime/contained-files.d.ts.map +1 -0
- package/dist/agent-runtime/control-schema.d.ts +1074 -0
- package/dist/agent-runtime/control-schema.d.ts.map +1 -0
- package/dist/agent-runtime/conversations.d.ts +128 -0
- package/dist/agent-runtime/conversations.d.ts.map +1 -0
- package/dist/agent-runtime/event-schema.d.ts +96 -0
- package/dist/agent-runtime/event-schema.d.ts.map +1 -1
- package/dist/agent-runtime/harness-contract.d.ts +169 -0
- package/dist/agent-runtime/harness-contract.d.ts.map +1 -0
- package/dist/agent-runtime/harness-control.d.ts +25 -0
- package/dist/agent-runtime/harness-control.d.ts.map +1 -0
- package/dist/agent-runtime/harness-file-resources.d.ts +33 -0
- package/dist/agent-runtime/harness-file-resources.d.ts.map +1 -0
- package/dist/agent-runtime/harness-resources.d.ts +5 -0
- package/dist/agent-runtime/harness-resources.d.ts.map +1 -0
- package/dist/agent-runtime/harness.d.ts +12 -0
- package/dist/agent-runtime/harness.d.ts.map +1 -0
- package/dist/agent-runtime/history.d.ts +2 -0
- package/dist/agent-runtime/history.d.ts.map +1 -1
- package/dist/agent-runtime/models.d.ts +199 -0
- package/dist/agent-runtime/models.d.ts.map +1 -1
- package/dist/agent-runtime/prompt.d.ts +2 -0
- package/dist/agent-runtime/prompt.d.ts.map +1 -1
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +11 -3
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schemas.d.ts +49 -0
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/sqlite.d.ts +2 -0
- package/dist/agent-runtime/sqlite.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts +61 -212
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +368 -1
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-commit.d.ts +12 -1
- package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-status.d.ts +8 -0
- package/dist/agent-runtime/terminal-status.d.ts.map +1 -1
- package/dist/agent-runtime-browser.d.ts +1 -0
- package/dist/agent-runtime-browser.d.ts.map +1 -1
- package/dist/agent-runtime-browser.js +217 -1
- package/dist/agent-runtime-coding-tools.d.ts +2 -0
- package/dist/agent-runtime-coding-tools.d.ts.map +1 -0
- package/dist/agent-runtime-coding-tools.js +766 -0
- package/dist/agent-runtime-harness.d.ts +2 -0
- package/dist/agent-runtime-harness.d.ts.map +1 -0
- package/dist/agent-runtime-harness.js +704 -0
- package/dist/agent-runtime-openrouter.d.ts +14 -1
- package/dist/agent-runtime-openrouter.d.ts.map +1 -1
- package/dist/agent-runtime-openrouter.js +211 -0
- package/dist/agent-runtime-sqlite-bun.js +4 -3
- package/dist/agent-runtime-sqlite-node.js +4 -3
- package/dist/agent-runtime.d.ts +5 -3
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +108 -2580
- package/dist/application/diagnostic-journal-contract.d.ts +164 -0
- package/dist/application/diagnostic-journal-contract.d.ts.map +1 -0
- package/dist/application/diagnostic-journal-manager.d.ts +12 -0
- package/dist/application/diagnostic-journal-manager.d.ts.map +1 -0
- package/dist/application/diagnostic-journal-storage.d.ts +29 -0
- package/dist/application/diagnostic-journal-storage.d.ts.map +1 -0
- package/dist/application/diagnostic-journal.d.ts +11 -0
- package/dist/application/diagnostic-journal.d.ts.map +1 -0
- package/dist/application.d.ts +2 -0
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +561 -17
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.js +9 -6
- package/dist/{index-t23p2b68.js → index-0yphgata.js} +1 -1
- package/dist/{index-8vpzxg55.js → index-10gbbbaa.js} +103 -2
- package/dist/{index-hcx9yypn.js → index-38hs3a58.js} +13 -11
- package/dist/index-3xnq72rz.js +21 -0
- package/dist/index-7rey2b8s.js +73 -0
- package/dist/{index-22tjt2rk.js → index-aczggrty.js} +1 -1
- package/dist/index-bd17ytae.js +192 -0
- package/dist/{index-xyvxez9r.js → index-da1aqnhb.js} +5 -5
- package/dist/index-es0h4w26.js +63 -0
- package/dist/{index-hqza5nde.js → index-hsabxjz0.js} +78 -77
- package/dist/{index-f24xg2cw.js → index-ktsps64f.js} +4 -2
- package/dist/{index-2t6zt5cg.js → index-mjx0wt4c.js} +11 -7
- package/dist/{index-y2ctppmg.js → index-qzbg46b6.js} +10 -74
- package/dist/{index-bt5179zb.js → index-s8nt8c22.js} +16 -11
- package/dist/index-tg3m2ec5.js +157 -0
- package/dist/{index-hxh98zbm.js → index-vbf2p6me.js} +1 -1
- package/dist/index-vc498pst.js +51 -0
- package/dist/{index-pgsyp3xh.js → index-vj3vvpaa.js} +1 -192
- package/dist/index-vy5bjy07.js +2569 -0
- package/dist/{index-hb0mncmj.js → index-wc80at9n.js} +2 -2
- package/dist/{index-eqqyd9v8.js → index-wwst0td5.js} +2 -46
- package/dist/{index-ysphyxax.js → index-x1th9s8c.js} +23 -2
- package/dist/index-xbtcszs7.js +202 -0
- package/dist/{index-grgvpbch.js → index-y3w12g8d.js} +4 -2
- package/dist/index.js +51 -37
- package/dist/internal/ai-sdk-typed.d.ts +14 -0
- package/dist/internal/ai-sdk-typed.d.ts.map +1 -0
- package/dist/node.js +6 -5
- package/dist/observability/index.js +5 -4
- package/dist/realtime/request.d.ts +5 -0
- package/dist/realtime/request.d.ts.map +1 -1
- package/dist/remote.js +7 -6
- package/dist/server/index.js +13 -11
- package/dist/testing.js +6 -5
- package/dist/tool-invoker.js +7 -5
- package/dist/tools/agent-tool-error.d.ts +14 -0
- package/dist/tools/agent-tool-error.d.ts.map +1 -0
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools.js +24 -17
- package/llms-full.txt +386 -29
- package/package.json +10 -2
package/dist/agent-runtime.js
CHANGED
|
@@ -1,8 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentAdmissionEventSchema,
|
|
3
|
+
AgentCheckpointEventSchema,
|
|
4
|
+
AgentContextOverflowError,
|
|
5
|
+
AgentReasoningDeltaEventSchema,
|
|
6
|
+
AgentReasoningEndEventSchema,
|
|
7
|
+
AgentReasoningStartEventSchema,
|
|
8
|
+
AgentRunStateEventSchema,
|
|
9
|
+
AgentRuntimeConflictError,
|
|
10
|
+
AgentRuntimeEventCursorSchema,
|
|
11
|
+
AgentRuntimeEventSchema,
|
|
12
|
+
AgentTerminalEventSchema,
|
|
13
|
+
AgentTokenCountSchema,
|
|
14
|
+
AgentToolStatusEventSchema,
|
|
15
|
+
AgentTransientDeltaEventSchema,
|
|
16
|
+
advanceAgentRuntimeEventCursor,
|
|
17
|
+
agentDurableEventId,
|
|
18
|
+
composeAgentPrompt,
|
|
19
|
+
createAgentRuntime,
|
|
20
|
+
createAgentRuntimeEventSink,
|
|
21
|
+
createAgentSessionCoordinator,
|
|
22
|
+
createAgentToolFenceLifecycle,
|
|
23
|
+
deferredToolCatalog,
|
|
24
|
+
projectAgentHistory,
|
|
25
|
+
projectAgentHistoryDetailed,
|
|
26
|
+
registerDeferredToolCatalog,
|
|
27
|
+
registerDeferredToolRepair,
|
|
28
|
+
repairedSearchCall,
|
|
29
|
+
selectAgentHistory
|
|
30
|
+
} from "./index-vy5bjy07.js";
|
|
31
|
+
import {
|
|
32
|
+
AgentModelCapabilitySchema,
|
|
33
|
+
AgentModelCatalogEntrySchema,
|
|
34
|
+
AgentModelCatalogSchema,
|
|
35
|
+
AgentModelDescriptorSchema,
|
|
36
|
+
AgentModelMetricSchema,
|
|
37
|
+
AgentModelPopularitySchema,
|
|
38
|
+
AgentModelPriceSchema,
|
|
39
|
+
AgentModelRegistrySnapshotSchema,
|
|
40
|
+
AgentModelSearchInputSchema,
|
|
41
|
+
AgentModelSearchResultSchema,
|
|
42
|
+
AgentModelSelectionSchema,
|
|
43
|
+
createMemoryAgentModelSelectionStore,
|
|
44
|
+
defineModelRegistry,
|
|
45
|
+
searchAgentModelCatalog,
|
|
46
|
+
validateAgentModelSnapshot
|
|
47
|
+
} from "./index-tg3m2ec5.js";
|
|
1
48
|
import {
|
|
2
49
|
ACTIVE_AGENT_RUN_STATES,
|
|
3
50
|
AcceptInputAndAssignRunSchema,
|
|
4
51
|
AcquireAgentRunSchema,
|
|
5
52
|
AgentAdmissionReceiptSchema,
|
|
53
|
+
AgentConversationMessagePageSchema,
|
|
54
|
+
AgentConversationPageSchema,
|
|
55
|
+
AgentConversationSummarySchema,
|
|
6
56
|
AgentHistoryMutationSchema,
|
|
7
57
|
AgentRecoverableDescriptorSchema,
|
|
8
58
|
AgentRecoverablePageSchema,
|
|
@@ -19,26 +69,28 @@ import {
|
|
|
19
69
|
RecoverAgentRunSchema,
|
|
20
70
|
ReplaceCompactedRangeSchema,
|
|
21
71
|
RequestRunInterruptSchema,
|
|
22
|
-
assistantStatus,
|
|
23
72
|
createAgentRuntimeStore,
|
|
24
|
-
createMemoryAgentRuntimeStore
|
|
25
|
-
|
|
26
|
-
|
|
73
|
+
createMemoryAgentRuntimeStore
|
|
74
|
+
} from "./index-hsabxjz0.js";
|
|
75
|
+
import {
|
|
76
|
+
isAssistantHistoryEvidence,
|
|
77
|
+
isCompleteAgentHistoryTurn
|
|
78
|
+
} from "./index-es0h4w26.js";
|
|
27
79
|
import {
|
|
28
80
|
createBoundedSinkManager
|
|
29
81
|
} from "./index-a59da114.js";
|
|
30
82
|
import {
|
|
31
83
|
buildToolManifest,
|
|
32
84
|
mountAgent
|
|
33
|
-
} from "./index-
|
|
85
|
+
} from "./index-s8nt8c22.js";
|
|
86
|
+
import"./index-3xnq72rz.js";
|
|
87
|
+
import"./index-vc498pst.js";
|
|
34
88
|
import {
|
|
35
89
|
defineRuntimeTool
|
|
36
|
-
} from "./index-
|
|
37
|
-
import
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
} from "./index-y2ctppmg.js";
|
|
41
|
-
import"./index-f24xg2cw.js";
|
|
90
|
+
} from "./index-wwst0td5.js";
|
|
91
|
+
import"./index-7rey2b8s.js";
|
|
92
|
+
import"./index-qzbg46b6.js";
|
|
93
|
+
import"./index-ktsps64f.js";
|
|
42
94
|
import {
|
|
43
95
|
AgentAssistantPlaceholderSchema,
|
|
44
96
|
AgentControlPartSchema,
|
|
@@ -64,43 +116,36 @@ import {
|
|
|
64
116
|
AgentTerminalReasonSchema,
|
|
65
117
|
AgentTextPartSchema,
|
|
66
118
|
AgentTimestampSchema,
|
|
119
|
+
AgentToolApprovalRequestPartSchema,
|
|
120
|
+
AgentToolApprovalResponsePartSchema,
|
|
67
121
|
AgentToolCallPartSchema,
|
|
68
122
|
AgentToolResultPartSchema,
|
|
69
123
|
AgentUsageSchema,
|
|
70
124
|
AgentUsageValueSchema,
|
|
71
125
|
runStateForTerminalReason
|
|
72
|
-
} from "./index-
|
|
126
|
+
} from "./index-x1th9s8c.js";
|
|
73
127
|
import"./index-22by16v6.js";
|
|
74
128
|
import"./index-cby4ar3v.js";
|
|
75
129
|
import {
|
|
76
130
|
childSpan,
|
|
77
131
|
createTraceContext
|
|
78
|
-
} from "./index-
|
|
132
|
+
} from "./index-bd17ytae.js";
|
|
133
|
+
import"./index-vj3vvpaa.js";
|
|
134
|
+
import"./index-0w9abg87.js";
|
|
79
135
|
import {
|
|
80
136
|
executableAgentRuntimeTools
|
|
81
137
|
} from "./index-qyrqwr4c.js";
|
|
82
|
-
import"./index-0w9abg87.js";
|
|
83
138
|
import"./index-6k1937bx.js";
|
|
84
139
|
|
|
85
140
|
// src/agent-runtime/compaction.ts
|
|
86
|
-
function
|
|
87
|
-
if (messages[0]?.role !== "user")
|
|
88
|
-
return false;
|
|
89
|
-
const assistant = messages.find((message) => message.role === "assistant");
|
|
90
|
-
if (!assistant || !isSpeakableAssistantStatus(assistant.status))
|
|
91
|
-
return false;
|
|
92
|
-
const calls = new Set(assistant.parts.filter((part) => part.type === "tool-call").map((part) => part.callId));
|
|
93
|
-
const results = new Set(assistant.parts.filter((part) => part.type === "tool-result").map((part) => part.callId));
|
|
94
|
-
return [...calls].every((callId) => results.has(callId)) && [...results].every((callId) => calls.has(callId));
|
|
95
|
-
}
|
|
96
|
-
function groupProviderTurns(messages) {
|
|
141
|
+
function groupProviderTurns(messages, evidencePolicy) {
|
|
97
142
|
const turns = [];
|
|
98
143
|
let current = [];
|
|
99
144
|
for (const message of messages) {
|
|
100
145
|
if (message.role === "user" && current.length > 0) {
|
|
101
146
|
turns.push({
|
|
102
147
|
messages: current,
|
|
103
|
-
complete:
|
|
148
|
+
complete: isCompleteAgentHistoryTurn(current, evidencePolicy)
|
|
104
149
|
});
|
|
105
150
|
current = [];
|
|
106
151
|
}
|
|
@@ -109,14 +154,14 @@ function groupProviderTurns(messages) {
|
|
|
109
154
|
if (current.length > 0) {
|
|
110
155
|
turns.push({
|
|
111
156
|
messages: current,
|
|
112
|
-
complete:
|
|
157
|
+
complete: isCompleteAgentHistoryTurn(current, evidencePolicy)
|
|
113
158
|
});
|
|
114
159
|
}
|
|
115
160
|
return turns;
|
|
116
161
|
}
|
|
117
|
-
function eligibleForCompaction(messages, keepRecentTurns) {
|
|
162
|
+
function eligibleForCompaction(messages, keepRecentTurns, evidencePolicy) {
|
|
118
163
|
const withoutLeadingSummary = messages[0]?.role === "summary" ? messages.slice(1) : messages;
|
|
119
|
-
const turns = groupProviderTurns(withoutLeadingSummary);
|
|
164
|
+
const turns = groupProviderTurns(withoutLeadingSummary, evidencePolicy);
|
|
120
165
|
const firstIncomplete = turns.findIndex((turn) => !turn.complete);
|
|
121
166
|
const completeTurns = firstIncomplete === -1 ? turns : turns.slice(0, firstIncomplete);
|
|
122
167
|
const eligibleCount = Math.max(0, completeTurns.length - keepRecentTurns);
|
|
@@ -144,7 +189,7 @@ function structuredCompaction(config) {
|
|
|
144
189
|
if (!await config.threshold(snapshot)) {
|
|
145
190
|
return { outcome: "not_needed", snapshot, attempts: attempt };
|
|
146
191
|
}
|
|
147
|
-
const eligibleMessages = eligibleForCompaction(snapshot.messages, config.keepRecentTurns);
|
|
192
|
+
const eligibleMessages = eligibleForCompaction(snapshot.messages, config.keepRecentTurns, config.evidencePolicy);
|
|
148
193
|
if (eligibleMessages.length === 0) {
|
|
149
194
|
return { outcome: "nothing_eligible", snapshot, attempts: attempt };
|
|
150
195
|
}
|
|
@@ -184,155 +229,6 @@ function structuredCompaction(config) {
|
|
|
184
229
|
return { outcome: "conflict", snapshot: lastSnapshot, attempts: maxAttempts };
|
|
185
230
|
};
|
|
186
231
|
}
|
|
187
|
-
// src/agent-runtime/context-refusal.ts
|
|
188
|
-
class AgentContextOverflowError extends Error {
|
|
189
|
-
name = "AgentContextOverflowError";
|
|
190
|
-
constructor(message = "Agent context exceeds the configured model budget", options) {
|
|
191
|
-
super(message, options);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
// src/agent-runtime/coordinator.ts
|
|
195
|
-
function assertCloseBudgets(options) {
|
|
196
|
-
for (const [name, value] of Object.entries(options)) {
|
|
197
|
-
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
|
|
198
|
-
throw new TypeError(`${name} must be a non-negative safe integer`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function createAgentSessionCoordinator() {
|
|
203
|
-
const lanes = new Map;
|
|
204
|
-
let closed = false;
|
|
205
|
-
const waitWithin = async (settled, timeoutMs) => {
|
|
206
|
-
if (timeoutMs === 0)
|
|
207
|
-
return false;
|
|
208
|
-
let timer;
|
|
209
|
-
const timeout = new Promise((resolve) => {
|
|
210
|
-
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
211
|
-
});
|
|
212
|
-
const completed = settled.then(() => true);
|
|
213
|
-
const result = await Promise.race([completed, timeout]);
|
|
214
|
-
if (timer !== undefined)
|
|
215
|
-
clearTimeout(timer);
|
|
216
|
-
return result;
|
|
217
|
-
};
|
|
218
|
-
const laneFor = (key) => {
|
|
219
|
-
const existing = lanes.get(key);
|
|
220
|
-
if (existing)
|
|
221
|
-
return existing;
|
|
222
|
-
const created = { queue: [] };
|
|
223
|
-
lanes.set(key, created);
|
|
224
|
-
return created;
|
|
225
|
-
};
|
|
226
|
-
const startNext = (key, lane) => {
|
|
227
|
-
if (lane.active)
|
|
228
|
-
return;
|
|
229
|
-
const next = lane.queue.shift();
|
|
230
|
-
if (!next) {
|
|
231
|
-
lanes.delete(key);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
next.start();
|
|
235
|
-
};
|
|
236
|
-
return {
|
|
237
|
-
submit(input) {
|
|
238
|
-
const accepted = Promise.withResolvers();
|
|
239
|
-
const result = Promise.withResolvers();
|
|
240
|
-
if (closed) {
|
|
241
|
-
const error = new Error("Agent session coordinator is closed");
|
|
242
|
-
accepted.reject(error);
|
|
243
|
-
result.reject(error);
|
|
244
|
-
return { accepted: accepted.promise, result: result.promise };
|
|
245
|
-
}
|
|
246
|
-
const lane = laneFor(input.key);
|
|
247
|
-
const pending = {
|
|
248
|
-
priority: input.policy === "interrupt-next",
|
|
249
|
-
reject(reason) {
|
|
250
|
-
accepted.reject(reason);
|
|
251
|
-
result.reject(reason);
|
|
252
|
-
},
|
|
253
|
-
async start() {
|
|
254
|
-
const controller = new AbortController;
|
|
255
|
-
const settlement = Promise.withResolvers();
|
|
256
|
-
lane.active = { controller, settled: settlement.promise };
|
|
257
|
-
try {
|
|
258
|
-
const coordinated = await input.create(controller.signal);
|
|
259
|
-
accepted.resolve();
|
|
260
|
-
result.resolve(await coordinated.execute());
|
|
261
|
-
} catch (error) {
|
|
262
|
-
accepted.reject(error);
|
|
263
|
-
result.reject(error);
|
|
264
|
-
} finally {
|
|
265
|
-
settlement.resolve();
|
|
266
|
-
lane.active = undefined;
|
|
267
|
-
startNext(input.key, lane);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
if (input.policy === "interrupt" || input.policy === "interrupt-next") {
|
|
272
|
-
lane.active?.controller.abort("user-interrupt");
|
|
273
|
-
}
|
|
274
|
-
if (input.policy === "supersede")
|
|
275
|
-
lane.active?.controller.abort("supersede");
|
|
276
|
-
if (pending.priority) {
|
|
277
|
-
const firstOrdinary = lane.queue.findIndex((queued) => !queued.priority);
|
|
278
|
-
if (firstOrdinary === -1)
|
|
279
|
-
lane.queue.push(pending);
|
|
280
|
-
else
|
|
281
|
-
lane.queue.splice(firstOrdinary, 0, pending);
|
|
282
|
-
} else {
|
|
283
|
-
lane.queue.push(pending);
|
|
284
|
-
}
|
|
285
|
-
startNext(input.key, lane);
|
|
286
|
-
return { accepted: accepted.promise, result: result.promise };
|
|
287
|
-
},
|
|
288
|
-
stop(key, reason = "user-interrupt") {
|
|
289
|
-
const lane = lanes.get(key);
|
|
290
|
-
if (!lane?.active)
|
|
291
|
-
return false;
|
|
292
|
-
lane.active.controller.abort(reason);
|
|
293
|
-
return true;
|
|
294
|
-
},
|
|
295
|
-
async close(options = {}) {
|
|
296
|
-
assertCloseBudgets(options);
|
|
297
|
-
closed = true;
|
|
298
|
-
const error = new Error("Agent session coordinator is closed");
|
|
299
|
-
for (const lane of lanes.values()) {
|
|
300
|
-
for (const pending of lane.queue.splice(0))
|
|
301
|
-
pending.reject(error);
|
|
302
|
-
}
|
|
303
|
-
const active = [...lanes.values()].map((lane) => lane.active).filter((run) => run !== undefined);
|
|
304
|
-
const settlements = active.map((run) => run.settled);
|
|
305
|
-
const outstanding = new Set(settlements);
|
|
306
|
-
for (const settled of settlements) {
|
|
307
|
-
settled.then(() => {
|
|
308
|
-
outstanding.delete(settled);
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
const drain = Promise.all(settlements).then(() => {
|
|
312
|
-
return;
|
|
313
|
-
});
|
|
314
|
-
const finished = () => ({
|
|
315
|
-
settled: outstanding.size === 0,
|
|
316
|
-
timedOut: outstanding.size > 0,
|
|
317
|
-
remaining: outstanding.size
|
|
318
|
-
});
|
|
319
|
-
if (options.gracePeriodMs !== undefined && await waitWithin(drain, options.gracePeriodMs)) {
|
|
320
|
-
return finished();
|
|
321
|
-
}
|
|
322
|
-
for (const run of active)
|
|
323
|
-
run.controller.abort("shutdown");
|
|
324
|
-
if (options.forceTimeoutMs === undefined) {
|
|
325
|
-
await drain;
|
|
326
|
-
return finished();
|
|
327
|
-
}
|
|
328
|
-
await waitWithin(drain, options.forceTimeoutMs);
|
|
329
|
-
return finished();
|
|
330
|
-
},
|
|
331
|
-
isRunning(key) {
|
|
332
|
-
return lanes.get(key)?.active !== undefined;
|
|
333
|
-
}
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
232
|
// src/agent-runtime/deferred-tool-types.ts
|
|
337
233
|
import { z } from "zod";
|
|
338
234
|
var DeferredAgentToolSearchInputSchema = z.object({
|
|
@@ -547,32 +443,6 @@ function createDeferredSearchTool(config, runContext, surface) {
|
|
|
547
443
|
});
|
|
548
444
|
}
|
|
549
445
|
|
|
550
|
-
// src/agent-runtime/deferred-tools-internal.ts
|
|
551
|
-
var repairs = new WeakMap;
|
|
552
|
-
var catalogs = new WeakMap;
|
|
553
|
-
function registerDeferredToolRepair(prepareStep, repair) {
|
|
554
|
-
repairs.set(prepareStep, repair);
|
|
555
|
-
}
|
|
556
|
-
function deferredToolRepair(prepareStep) {
|
|
557
|
-
return prepareStep ? repairs.get(prepareStep) : undefined;
|
|
558
|
-
}
|
|
559
|
-
function registerDeferredToolCatalog(tools, searchName, names) {
|
|
560
|
-
const search = tools[searchName];
|
|
561
|
-
if (search && typeof search === "object")
|
|
562
|
-
catalogs.set(search, names);
|
|
563
|
-
}
|
|
564
|
-
function deferredToolCatalog(tools, searchName) {
|
|
565
|
-
const search = tools[searchName];
|
|
566
|
-
return search && typeof search === "object" ? catalogs.get(search) : undefined;
|
|
567
|
-
}
|
|
568
|
-
function repairedSearchCall(toolCall, searchName) {
|
|
569
|
-
return {
|
|
570
|
-
...toolCall,
|
|
571
|
-
toolName: searchName,
|
|
572
|
-
input: JSON.stringify({ query: toolCall.toolName, reason: "inactive_call" })
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
|
|
576
446
|
// src/agent-runtime/deferred-tools.ts
|
|
577
447
|
function configuredSurfaces(config) {
|
|
578
448
|
return "surfaces" in config ? config.surfaces : { default: config };
|
|
@@ -717,523 +587,39 @@ function createDeferredAgentToolSurface(config) {
|
|
|
717
587
|
}
|
|
718
588
|
};
|
|
719
589
|
}
|
|
720
|
-
// src/agent-runtime/event-schema.ts
|
|
721
|
-
import { z as z2 } from "zod";
|
|
722
|
-
var AgentAdmissionEventSchema = z2.object({
|
|
723
|
-
type: z2.literal("admission"),
|
|
724
|
-
eventId: AgentRecordIdSchema,
|
|
725
|
-
conversationId: AgentRecordIdSchema,
|
|
726
|
-
runId: AgentRecordIdSchema,
|
|
727
|
-
snapshotVersion: AgentRecordVersionSchema,
|
|
728
|
-
input: AgentMessageSchema,
|
|
729
|
-
run: AgentRunSchema,
|
|
730
|
-
assistant: z2.union([AgentAssistantPlaceholderSchema, AgentMessageSchema]),
|
|
731
|
-
emittedAt: AgentTimestampSchema
|
|
732
|
-
});
|
|
733
|
-
var EventIdentitySchema = z2.object({
|
|
734
|
-
conversationId: AgentRecordIdSchema,
|
|
735
|
-
runId: AgentRecordIdSchema,
|
|
736
|
-
emittedAt: AgentTimestampSchema
|
|
737
|
-
});
|
|
738
|
-
var AgentTransientDeltaEventSchema = EventIdentitySchema.extend({
|
|
739
|
-
type: z2.literal("assistant-delta"),
|
|
740
|
-
runtimeEpoch: z2.string().min(1),
|
|
741
|
-
sequence: z2.int().nonnegative(),
|
|
742
|
-
textDelta: z2.string()
|
|
743
|
-
});
|
|
744
|
-
var AgentTransientReasoningIdentitySchema = EventIdentitySchema.extend({
|
|
745
|
-
runtimeEpoch: z2.string().min(1),
|
|
746
|
-
sequence: z2.int().nonnegative(),
|
|
747
|
-
provider: AgentProviderEnvelopeSchema.optional()
|
|
748
|
-
});
|
|
749
|
-
var AgentReasoningStartEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
750
|
-
type: z2.literal("reasoning-start")
|
|
751
|
-
});
|
|
752
|
-
var AgentReasoningDeltaEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
753
|
-
type: z2.literal("reasoning-delta"),
|
|
754
|
-
textDelta: z2.string()
|
|
755
|
-
});
|
|
756
|
-
var AgentReasoningEndEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
757
|
-
type: z2.literal("reasoning-end")
|
|
758
|
-
});
|
|
759
|
-
var AgentCheckpointEventSchema = EventIdentitySchema.extend({
|
|
760
|
-
type: z2.literal("assistant-checkpoint"),
|
|
761
|
-
eventId: AgentRecordIdSchema,
|
|
762
|
-
snapshotVersion: AgentRecordVersionSchema,
|
|
763
|
-
message: AgentMessageSchema,
|
|
764
|
-
metrics: AgentRunMetricsSchema.optional()
|
|
765
|
-
});
|
|
766
|
-
var AgentRunStateEventSchema = EventIdentitySchema.extend({
|
|
767
|
-
type: z2.literal("run-state"),
|
|
768
|
-
eventId: AgentRecordIdSchema,
|
|
769
|
-
snapshotVersion: AgentRecordVersionSchema,
|
|
770
|
-
state: AgentRunStateSchema
|
|
771
|
-
});
|
|
772
|
-
var AgentToolStatusEventSchema = EventIdentitySchema.extend({
|
|
773
|
-
type: z2.literal("tool-status"),
|
|
774
|
-
runtimeEpoch: z2.string().min(1),
|
|
775
|
-
sequence: z2.int().nonnegative(),
|
|
776
|
-
callId: AgentRecordIdSchema,
|
|
777
|
-
toolName: z2.string().min(1),
|
|
778
|
-
status: z2.enum(["started", "completed", "failed", "interrupted"]),
|
|
779
|
-
input: z2.json().optional(),
|
|
780
|
-
output: z2.json().optional()
|
|
781
|
-
});
|
|
782
|
-
var AgentTerminalEventSchema = EventIdentitySchema.extend({
|
|
783
|
-
type: z2.literal("terminal"),
|
|
784
|
-
eventId: AgentRecordIdSchema,
|
|
785
|
-
snapshotVersion: AgentRecordVersionSchema,
|
|
786
|
-
reason: AgentTerminalReasonSchema,
|
|
787
|
-
policyName: z2.string().min(1).optional(),
|
|
788
|
-
message: AgentMessageSchema,
|
|
789
|
-
metrics: AgentRunMetricsSchema.optional()
|
|
790
|
-
});
|
|
791
|
-
var AgentRuntimeEventSchema = z2.discriminatedUnion("type", [
|
|
792
|
-
AgentAdmissionEventSchema,
|
|
793
|
-
AgentTransientDeltaEventSchema,
|
|
794
|
-
AgentReasoningStartEventSchema,
|
|
795
|
-
AgentReasoningDeltaEventSchema,
|
|
796
|
-
AgentReasoningEndEventSchema,
|
|
797
|
-
AgentCheckpointEventSchema,
|
|
798
|
-
AgentRunStateEventSchema,
|
|
799
|
-
AgentToolStatusEventSchema,
|
|
800
|
-
AgentTerminalEventSchema
|
|
801
|
-
]);
|
|
802
|
-
var AgentRuntimeEventCursorSchema = z2.object({
|
|
803
|
-
snapshotVersion: AgentRecordVersionSchema.optional(),
|
|
804
|
-
durableEventIds: z2.array(AgentRecordIdSchema).optional(),
|
|
805
|
-
runtimeEpoch: z2.string().min(1).optional(),
|
|
806
|
-
sequence: z2.int().nonnegative().optional()
|
|
807
|
-
});
|
|
808
|
-
function isDurableEvent(event) {
|
|
809
|
-
return event.type === "admission" || event.type === "assistant-checkpoint" || event.type === "run-state" || event.type === "terminal";
|
|
810
|
-
}
|
|
811
|
-
function advanceAgentRuntimeEventCursor(rawCursor, event) {
|
|
812
|
-
const cursor = AgentRuntimeEventCursorSchema.parse(rawCursor);
|
|
813
|
-
if (isDurableEvent(event)) {
|
|
814
|
-
const previous = cursor.snapshotVersion;
|
|
815
|
-
const durableEventIds = previous === event.snapshotVersion ? cursor.durableEventIds ?? [] : [];
|
|
816
|
-
if (previous !== undefined && event.snapshotVersion < previous || durableEventIds.includes(event.eventId)) {
|
|
817
|
-
return { status: "duplicate", cursor };
|
|
818
|
-
}
|
|
819
|
-
return {
|
|
820
|
-
status: "accepted",
|
|
821
|
-
cursor: {
|
|
822
|
-
...cursor,
|
|
823
|
-
snapshotVersion: event.snapshotVersion,
|
|
824
|
-
durableEventIds: [...durableEventIds, event.eventId]
|
|
825
|
-
}
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
const previousSequence = cursor.runtimeEpoch === event.runtimeEpoch ? cursor.sequence : undefined;
|
|
829
|
-
if (previousSequence !== undefined && event.sequence <= previousSequence) {
|
|
830
|
-
return { status: "duplicate", cursor };
|
|
831
|
-
}
|
|
832
|
-
return {
|
|
833
|
-
status: previousSequence !== undefined && event.sequence > previousSequence + 1 ? "gap" : "accepted",
|
|
834
|
-
cursor: { ...cursor, runtimeEpoch: event.runtimeEpoch, sequence: event.sequence }
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
function agentDurableEventId(type, runId, snapshotVersion) {
|
|
838
|
-
return `${runId}:${type}:${snapshotVersion}`;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// src/agent-runtime/events.ts
|
|
842
|
-
function createAgentRuntimeEventSink(config) {
|
|
843
|
-
const manager = createBoundedSinkManager({
|
|
844
|
-
write: config.write,
|
|
845
|
-
...config.maxPending !== undefined && { maxPending: config.maxPending },
|
|
846
|
-
...config.onSinkError && { onSinkError: config.onSinkError },
|
|
847
|
-
...config.onDrop && { onDrop: config.onDrop }
|
|
848
|
-
});
|
|
849
|
-
return {
|
|
850
|
-
publish(rawEvent) {
|
|
851
|
-
const event = AgentRuntimeEventSchema.parse(rawEvent);
|
|
852
|
-
const projected = config.project?.(event) ?? (config.project ? undefined : event);
|
|
853
|
-
if (projected)
|
|
854
|
-
manager.submit(() => AgentRuntimeEventSchema.parse(projected));
|
|
855
|
-
},
|
|
856
|
-
flush: () => manager.flush(),
|
|
857
|
-
getStatus: () => manager.getStatus(),
|
|
858
|
-
close: () => manager.close()
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
// src/agent-runtime/history.ts
|
|
862
|
-
import { modelMessageSchema } from "ai";
|
|
863
|
-
function providerOptions(envelope) {
|
|
864
|
-
if (!envelope)
|
|
865
|
-
return;
|
|
866
|
-
return envelope.provider === "ai-sdk" ? envelope.data : { [envelope.provider]: envelope.data };
|
|
867
|
-
}
|
|
868
|
-
function textContent(parts) {
|
|
869
|
-
return parts.filter((part) => part.type === "text").map((part) => part.text).join(`
|
|
870
|
-
`);
|
|
871
|
-
}
|
|
872
|
-
function unrepresented(parts, rendered) {
|
|
873
|
-
const missing = new Set;
|
|
874
|
-
for (const part of parts) {
|
|
875
|
-
if (!rendered.has(part.type))
|
|
876
|
-
missing.add(part.type);
|
|
877
|
-
}
|
|
878
|
-
return [...missing];
|
|
879
|
-
}
|
|
880
|
-
function decide(message, action, reason, rendered) {
|
|
881
|
-
if (action === "omitted" || !rendered)
|
|
882
|
-
return { messageId: message.id, action, reason };
|
|
883
|
-
const omittedParts = unrepresented(message.parts, rendered);
|
|
884
|
-
return {
|
|
885
|
-
messageId: message.id,
|
|
886
|
-
action,
|
|
887
|
-
reason,
|
|
888
|
-
...omittedParts.length > 0 && { omittedParts }
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
var INTERRUPTION_NOTE = "[interrupted: this turn was cut off before it finished]";
|
|
892
|
-
async function userMessage(message, options) {
|
|
893
|
-
const rendered = new Set;
|
|
894
|
-
const text = textContent(message.parts);
|
|
895
|
-
const content = [];
|
|
896
|
-
if (text) {
|
|
897
|
-
content.push({ type: "text", text });
|
|
898
|
-
rendered.add("text");
|
|
899
|
-
}
|
|
900
|
-
for (const part of message.parts) {
|
|
901
|
-
if (part.type !== "file")
|
|
902
|
-
continue;
|
|
903
|
-
if (options.resolveFile) {
|
|
904
|
-
content.push({
|
|
905
|
-
type: "file",
|
|
906
|
-
data: await options.resolveFile(part, message),
|
|
907
|
-
mediaType: part.mediaType,
|
|
908
|
-
...part.filename && { filename: part.filename }
|
|
909
|
-
});
|
|
910
|
-
rendered.add("file");
|
|
911
|
-
continue;
|
|
912
|
-
}
|
|
913
|
-
const fallback = options.unresolvedFile ?? "omit";
|
|
914
|
-
if (fallback === "error") {
|
|
915
|
-
throw new Error(`No history file resolver configured for ${part.reference}`);
|
|
916
|
-
}
|
|
917
|
-
if (fallback === "text") {
|
|
918
|
-
const described = part.filename ?? part.mediaType;
|
|
919
|
-
content.push({
|
|
920
|
-
type: "text",
|
|
921
|
-
text: described ? `[attachment: ${described}]` : "[attachment]"
|
|
922
|
-
});
|
|
923
|
-
rendered.add("file");
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
if (content.length === 0)
|
|
927
|
-
return { rendered };
|
|
928
|
-
return { message: modelMessageSchema.parse({ role: "user", content }), rendered };
|
|
929
|
-
}
|
|
930
|
-
function assistantMessages(message, interrupted) {
|
|
931
|
-
const rendered = new Set;
|
|
932
|
-
const messages = [];
|
|
933
|
-
let role;
|
|
934
|
-
let content = [];
|
|
935
|
-
const flush = () => {
|
|
936
|
-
if (!role || content.length === 0)
|
|
937
|
-
return;
|
|
938
|
-
messages.push(modelMessageSchema.parse({ role, content }));
|
|
939
|
-
content = [];
|
|
940
|
-
};
|
|
941
|
-
const append = (nextRole, part) => {
|
|
942
|
-
if (role !== nextRole) {
|
|
943
|
-
flush();
|
|
944
|
-
role = nextRole;
|
|
945
|
-
}
|
|
946
|
-
content.push(part);
|
|
947
|
-
};
|
|
948
|
-
for (const part of message.parts) {
|
|
949
|
-
if (part.type === "text") {
|
|
950
|
-
append("assistant", { type: "text", text: part.text });
|
|
951
|
-
rendered.add("text");
|
|
952
|
-
}
|
|
953
|
-
if (part.type === "reasoning") {
|
|
954
|
-
append("assistant", {
|
|
955
|
-
type: "reasoning",
|
|
956
|
-
text: part.text,
|
|
957
|
-
...part.provider && { providerOptions: providerOptions(part.provider) }
|
|
958
|
-
});
|
|
959
|
-
rendered.add("reasoning");
|
|
960
|
-
}
|
|
961
|
-
if (part.type === "tool-call") {
|
|
962
|
-
append("assistant", {
|
|
963
|
-
type: "tool-call",
|
|
964
|
-
toolCallId: part.callId,
|
|
965
|
-
toolName: part.toolName,
|
|
966
|
-
input: part.input,
|
|
967
|
-
...part.provider && { providerOptions: providerOptions(part.provider) }
|
|
968
|
-
});
|
|
969
|
-
rendered.add("tool-call");
|
|
970
|
-
}
|
|
971
|
-
if (part.type === "tool-result") {
|
|
972
|
-
const output = part.outcome === "success" ? { type: "json", value: part.output ?? null } : { type: "error-json", value: part.output ?? { message: part.outcome } };
|
|
973
|
-
append("tool", {
|
|
974
|
-
type: "tool-result",
|
|
975
|
-
toolCallId: part.callId,
|
|
976
|
-
toolName: part.toolName,
|
|
977
|
-
output
|
|
978
|
-
});
|
|
979
|
-
rendered.add("tool-result");
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
if (interrupted && (messages.length > 0 || content.length > 0)) {
|
|
983
|
-
append("assistant", { type: "text", text: INTERRUPTION_NOTE });
|
|
984
|
-
rendered.add("control");
|
|
985
|
-
}
|
|
986
|
-
flush();
|
|
987
|
-
return { messages, rendered };
|
|
988
|
-
}
|
|
989
|
-
function interruptedSystemNote(message) {
|
|
990
|
-
const rendered = new Set;
|
|
991
|
-
const text = textContent(message.parts);
|
|
992
|
-
if (!text)
|
|
993
|
-
return { rendered };
|
|
994
|
-
rendered.add("text");
|
|
995
|
-
rendered.add("control");
|
|
996
|
-
return { text: `[interrupted] partial response: ${text}`, rendered };
|
|
997
|
-
}
|
|
998
|
-
function completeToolChronology(message) {
|
|
999
|
-
const pending = new Set;
|
|
1000
|
-
const completed = new Set;
|
|
1001
|
-
let resultsStarted = false;
|
|
1002
|
-
for (const part of message.parts) {
|
|
1003
|
-
if (part.type === "tool-call") {
|
|
1004
|
-
if (resultsStarted && pending.size > 0)
|
|
1005
|
-
return false;
|
|
1006
|
-
if (pending.size === 0)
|
|
1007
|
-
resultsStarted = false;
|
|
1008
|
-
if (pending.has(part.callId) || completed.has(part.callId))
|
|
1009
|
-
return false;
|
|
1010
|
-
pending.add(part.callId);
|
|
1011
|
-
}
|
|
1012
|
-
if (part.type === "tool-result") {
|
|
1013
|
-
if (!pending.delete(part.callId))
|
|
1014
|
-
return false;
|
|
1015
|
-
completed.add(part.callId);
|
|
1016
|
-
resultsStarted = true;
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
return pending.size === 0;
|
|
1020
|
-
}
|
|
1021
|
-
async function projectAgentHistoryDetailed(messages, options = {}) {
|
|
1022
|
-
const projected = [];
|
|
1023
|
-
const system = [];
|
|
1024
|
-
const decisions = [];
|
|
1025
|
-
const interruptedRule = options.interruptedAssistant ?? "assistant-marked";
|
|
1026
|
-
let observedUser = false;
|
|
1027
|
-
for (const message of messages) {
|
|
1028
|
-
if (message.role === "assistant" && !isSpeakableAssistantStatus(message.status)) {
|
|
1029
|
-
decisions.push(decide(message, "omitted", message.status === "superseded" ? "superseded" : "draft-or-failed"));
|
|
1030
|
-
continue;
|
|
1031
|
-
}
|
|
1032
|
-
if (message.status === "streaming" || message.status === "failed") {
|
|
1033
|
-
decisions.push(decide(message, "omitted", "draft-or-failed"));
|
|
1034
|
-
continue;
|
|
1035
|
-
}
|
|
1036
|
-
if (message.role === "user") {
|
|
1037
|
-
const user = await userMessage(message, options);
|
|
1038
|
-
observedUser = true;
|
|
1039
|
-
if (user.message) {
|
|
1040
|
-
projected.push(user.message);
|
|
1041
|
-
decisions.push(decide(message, "projected", "projected", user.rendered));
|
|
1042
|
-
} else {
|
|
1043
|
-
decisions.push(decide(message, "omitted", "empty"));
|
|
1044
|
-
}
|
|
1045
|
-
continue;
|
|
1046
|
-
}
|
|
1047
|
-
if (message.role === "system" || message.role === "summary") {
|
|
1048
|
-
const content = textContent(message.parts);
|
|
1049
|
-
if (content) {
|
|
1050
|
-
system.push(content);
|
|
1051
|
-
decisions.push(decide(message, "projected", "projected", new Set(["text"])));
|
|
1052
|
-
} else {
|
|
1053
|
-
decisions.push(decide(message, "omitted", "empty"));
|
|
1054
|
-
}
|
|
1055
|
-
continue;
|
|
1056
|
-
}
|
|
1057
|
-
if (!observedUser && options.leadingAssistant !== "allow") {
|
|
1058
|
-
if (options.leadingAssistant === "error") {
|
|
1059
|
-
throw new Error(`Assistant message ${message.id} precedes the first user message`);
|
|
1060
|
-
}
|
|
1061
|
-
decisions.push(decide(message, "omitted", "leading-assistant"));
|
|
1062
|
-
continue;
|
|
1063
|
-
}
|
|
1064
|
-
const interrupted = message.status === "interrupted";
|
|
1065
|
-
if (interrupted && interruptedRule === "omit") {
|
|
1066
|
-
decisions.push(decide(message, "omitted", "interrupted"));
|
|
1067
|
-
continue;
|
|
1068
|
-
}
|
|
1069
|
-
if (interrupted && interruptedRule === "system-note") {
|
|
1070
|
-
const note = interruptedSystemNote(message);
|
|
1071
|
-
if (note.text) {
|
|
1072
|
-
system.push(note.text);
|
|
1073
|
-
decisions.push(decide(message, "projected", "projected", note.rendered));
|
|
1074
|
-
} else {
|
|
1075
|
-
decisions.push(decide(message, "omitted", "empty"));
|
|
1076
|
-
}
|
|
1077
|
-
continue;
|
|
1078
|
-
}
|
|
1079
|
-
if (!completeToolChronology(message)) {
|
|
1080
|
-
if (options.incompleteToolTurn === "error") {
|
|
1081
|
-
throw new Error(`Assistant message ${message.id} has incomplete tool chronology`);
|
|
1082
|
-
}
|
|
1083
|
-
decisions.push(decide(message, "omitted", "incomplete-tool-turn"));
|
|
1084
|
-
continue;
|
|
1085
|
-
}
|
|
1086
|
-
const assistant = assistantMessages(message, interrupted);
|
|
1087
|
-
projected.push(...assistant.messages);
|
|
1088
|
-
decisions.push(assistant.messages.length > 0 ? decide(message, "projected", "projected", assistant.rendered) : decide(message, "omitted", "empty"));
|
|
1089
|
-
}
|
|
1090
|
-
return { messages: projected, decisions, system };
|
|
1091
|
-
}
|
|
1092
|
-
async function projectAgentHistory(messages, options = {}) {
|
|
1093
|
-
return [...(await projectAgentHistoryDetailed(messages, options)).messages];
|
|
1094
|
-
}
|
|
1095
|
-
// src/agent-runtime/managed-tools.ts
|
|
1096
|
-
async function assertFence(config, input) {
|
|
1097
|
-
const rejected = await config.assertCurrent(input);
|
|
1098
|
-
if (rejected)
|
|
1099
|
-
throw new ToolExecutionControlError(rejected);
|
|
1100
|
-
}
|
|
1101
|
-
function createAgentToolFenceLifecycle(config) {
|
|
1102
|
-
const callContext = (ctx, endpoint) => ({
|
|
1103
|
-
runId: config.runId,
|
|
1104
|
-
...config.context?.(ctx, endpoint)
|
|
1105
|
-
});
|
|
1106
|
-
return {
|
|
1107
|
-
async beforeHandle(ctx, endpoint) {
|
|
1108
|
-
await assertFence(config, callContext(ctx, endpoint));
|
|
1109
|
-
},
|
|
1110
|
-
async afterHandle(ctx, result, endpoint) {
|
|
1111
|
-
const current = callContext(ctx, endpoint);
|
|
1112
|
-
await assertFence(config, current);
|
|
1113
|
-
await config.onSettled?.(current);
|
|
1114
|
-
return result;
|
|
1115
|
-
}
|
|
1116
|
-
};
|
|
1117
|
-
}
|
|
1118
|
-
// src/agent-runtime/models.ts
|
|
1119
|
-
import { z as z3 } from "zod";
|
|
1120
|
-
var AgentModelCapabilitySchema = z3.enum(["tools", "vision", "reasoning", "files"]);
|
|
1121
|
-
var AgentModelDescriptorSchema = z3.object({
|
|
1122
|
-
provider: z3.string().min(1),
|
|
1123
|
-
modelId: z3.string().min(1),
|
|
1124
|
-
contextWindow: z3.int().positive(),
|
|
1125
|
-
capabilities: z3.array(AgentModelCapabilitySchema),
|
|
1126
|
-
observedAt: z3.iso.datetime({ offset: true }).optional(),
|
|
1127
|
-
source: z3.string().min(1).optional(),
|
|
1128
|
-
availability: z3.enum(["available", "unavailable"]).optional()
|
|
1129
|
-
});
|
|
1130
|
-
var AgentModelRegistrySnapshotSchema = z3.object({
|
|
1131
|
-
schemaVersion: z3.literal(1),
|
|
1132
|
-
source: z3.string().min(1),
|
|
1133
|
-
observedAt: z3.iso.datetime({ offset: true }),
|
|
1134
|
-
models: z3.record(z3.string().min(1), AgentModelDescriptorSchema)
|
|
1135
|
-
});
|
|
1136
|
-
function defineModelRegistry(config) {
|
|
1137
|
-
const descriptors = new Map;
|
|
1138
|
-
for (const key in config.models) {
|
|
1139
|
-
const declaration = config.models[key];
|
|
1140
|
-
if (declaration)
|
|
1141
|
-
descriptors.set(key, AgentModelDescriptorSchema.parse(declaration));
|
|
1142
|
-
}
|
|
1143
|
-
const descriptor = (key) => {
|
|
1144
|
-
const found = descriptors.get(key);
|
|
1145
|
-
if (!found)
|
|
1146
|
-
throw new Error(`Unknown agent model: ${key}`);
|
|
1147
|
-
return found;
|
|
1148
|
-
};
|
|
1149
|
-
const supports = (key, capabilities) => {
|
|
1150
|
-
const available = new Set(descriptor(key).capabilities);
|
|
1151
|
-
return capabilities.every((capability) => available.has(capability));
|
|
1152
|
-
};
|
|
1153
|
-
const preflight = (key, required = []) => {
|
|
1154
|
-
const selected = descriptor(key);
|
|
1155
|
-
if (selected.availability === "unavailable") {
|
|
1156
|
-
throw new Error(`Agent model ${key} is unavailable`);
|
|
1157
|
-
}
|
|
1158
|
-
if (!supports(key, required)) {
|
|
1159
|
-
throw new Error(`Agent model ${key} does not satisfy required capabilities`);
|
|
1160
|
-
}
|
|
1161
|
-
if (!config.providers[selected.provider]) {
|
|
1162
|
-
throw new Error(`Unknown agent model provider: ${selected.provider}`);
|
|
1163
|
-
}
|
|
1164
|
-
return selected;
|
|
1165
|
-
};
|
|
1166
|
-
return {
|
|
1167
|
-
keys: () => [...descriptors.keys()],
|
|
1168
|
-
descriptor,
|
|
1169
|
-
supports,
|
|
1170
|
-
preflight,
|
|
1171
|
-
resolve(key, required = []) {
|
|
1172
|
-
const selected = preflight(key, required);
|
|
1173
|
-
const provider = config.providers[selected.provider];
|
|
1174
|
-
if (!provider)
|
|
1175
|
-
throw new Error(`Unknown agent model provider: ${selected.provider}`);
|
|
1176
|
-
return {
|
|
1177
|
-
descriptor: selected,
|
|
1178
|
-
model: provider.create(selected.modelId),
|
|
1179
|
-
...provider.normalizeUsage && { normalizeUsage: provider.normalizeUsage }
|
|
1180
|
-
};
|
|
1181
|
-
},
|
|
1182
|
-
snapshot(input) {
|
|
1183
|
-
return AgentModelRegistrySnapshotSchema.parse({
|
|
1184
|
-
schemaVersion: 1,
|
|
1185
|
-
source: input.source,
|
|
1186
|
-
observedAt: input.observedAt,
|
|
1187
|
-
models: Object.fromEntries(descriptors.entries())
|
|
1188
|
-
});
|
|
1189
|
-
}
|
|
1190
|
-
};
|
|
1191
|
-
}
|
|
1192
|
-
function validateAgentModelSnapshot(input, policy) {
|
|
1193
|
-
if (!Number.isSafeInteger(policy.maxAgeMs) || policy.maxAgeMs < 0) {
|
|
1194
|
-
throw new TypeError("maxAgeMs must be a non-negative safe integer");
|
|
1195
|
-
}
|
|
1196
|
-
const snapshot = AgentModelRegistrySnapshotSchema.parse(input);
|
|
1197
|
-
const now = policy.now?.() ?? new Date;
|
|
1198
|
-
const age = now.getTime() - new Date(snapshot.observedAt).getTime();
|
|
1199
|
-
if (age < 0 || age > policy.maxAgeMs) {
|
|
1200
|
-
throw new Error(`Agent model snapshot from ${snapshot.source} is stale`);
|
|
1201
|
-
}
|
|
1202
|
-
return snapshot;
|
|
1203
|
-
}
|
|
1204
590
|
// src/agent-runtime/observability.ts
|
|
1205
|
-
import { z as
|
|
1206
|
-
var AgentRunEventBaseSchema =
|
|
1207
|
-
schemaVersion:
|
|
591
|
+
import { z as z2 } from "zod";
|
|
592
|
+
var AgentRunEventBaseSchema = z2.object({
|
|
593
|
+
schemaVersion: z2.literal(1),
|
|
1208
594
|
eventId: AgentRecordIdSchema,
|
|
1209
595
|
conversationId: AgentRecordIdSchema,
|
|
1210
596
|
runId: AgentRecordIdSchema,
|
|
1211
|
-
traceId:
|
|
1212
|
-
spanId:
|
|
1213
|
-
parentSpanId:
|
|
597
|
+
traceId: z2.string().min(1),
|
|
598
|
+
spanId: z2.string().min(1),
|
|
599
|
+
parentSpanId: z2.string().min(1).optional(),
|
|
1214
600
|
state: AgentRunStateSchema,
|
|
1215
|
-
modelId:
|
|
1216
|
-
durationMs:
|
|
601
|
+
modelId: z2.string().min(1).optional(),
|
|
602
|
+
durationMs: z2.number().nonnegative().optional(),
|
|
1217
603
|
emittedAt: AgentTimestampSchema
|
|
1218
604
|
});
|
|
1219
605
|
var AgentRunStartedEventSchema = AgentRunEventBaseSchema.extend({
|
|
1220
|
-
type:
|
|
1221
|
-
queueWaitMs:
|
|
606
|
+
type: z2.literal("run-started"),
|
|
607
|
+
queueWaitMs: z2.number().nonnegative().optional()
|
|
1222
608
|
});
|
|
1223
609
|
var AgentStepFinishedEventSchema = AgentRunEventBaseSchema.extend({
|
|
1224
|
-
type:
|
|
1225
|
-
step:
|
|
1226
|
-
ttftMs:
|
|
610
|
+
type: z2.literal("step-finished"),
|
|
611
|
+
step: z2.int().nonnegative(),
|
|
612
|
+
ttftMs: z2.number().nonnegative().optional(),
|
|
1227
613
|
usage: AgentUsageSchema
|
|
1228
614
|
});
|
|
1229
615
|
var AgentRunTerminalEventSchema = AgentRunEventBaseSchema.extend({
|
|
1230
|
-
type:
|
|
616
|
+
type: z2.literal("run-terminal"),
|
|
1231
617
|
terminalReason: AgentTerminalReasonSchema,
|
|
1232
|
-
ttftMs:
|
|
1233
|
-
internalCause:
|
|
618
|
+
ttftMs: z2.number().nonnegative().optional(),
|
|
619
|
+
internalCause: z2.unknown().optional(),
|
|
1234
620
|
usage: AgentUsageSchema
|
|
1235
621
|
});
|
|
1236
|
-
var AgentRunEventSchema =
|
|
622
|
+
var AgentRunEventSchema = z2.discriminatedUnion("type", [
|
|
1237
623
|
AgentRunStartedEventSchema,
|
|
1238
624
|
AgentStepFinishedEventSchema,
|
|
1239
625
|
AgentRunTerminalEventSchema
|
|
@@ -1281,196 +667,6 @@ function createAgentObservability(config) {
|
|
|
1281
667
|
close: () => manager.close()
|
|
1282
668
|
};
|
|
1283
669
|
}
|
|
1284
|
-
// src/agent-runtime/prompt.ts
|
|
1285
|
-
import { z as z5 } from "zod";
|
|
1286
|
-
var AgentTokenCountSchema = z5.object({
|
|
1287
|
-
value: z5.int().nonnegative().optional(),
|
|
1288
|
-
provenance: AgentProvenanceSchema.extract([
|
|
1289
|
-
"measured",
|
|
1290
|
-
"computed",
|
|
1291
|
-
"estimated",
|
|
1292
|
-
"unavailable"
|
|
1293
|
-
])
|
|
1294
|
-
});
|
|
1295
|
-
function completeTurn(messages) {
|
|
1296
|
-
if (messages[0]?.role !== "user")
|
|
1297
|
-
return false;
|
|
1298
|
-
const assistant = messages.find((message) => message.role === "assistant");
|
|
1299
|
-
if (assistant?.status !== "completed")
|
|
1300
|
-
return false;
|
|
1301
|
-
const calls = new Set(assistant.parts.filter((part) => part.type === "tool-call").map((part) => part.callId));
|
|
1302
|
-
const results = new Set(assistant.parts.filter((part) => part.type === "tool-result").map((part) => part.callId));
|
|
1303
|
-
return [...calls].every((callId) => results.has(callId)) && [...results].every((callId) => calls.has(callId));
|
|
1304
|
-
}
|
|
1305
|
-
function budgetTurns(messages) {
|
|
1306
|
-
const turns = [];
|
|
1307
|
-
let current = [];
|
|
1308
|
-
const flush = () => {
|
|
1309
|
-
if (current.length === 0)
|
|
1310
|
-
return;
|
|
1311
|
-
turns.push({ messages: current, complete: completeTurn(current), protectedSystem: false });
|
|
1312
|
-
current = [];
|
|
1313
|
-
};
|
|
1314
|
-
for (const message of messages) {
|
|
1315
|
-
if (message.role === "system" || message.role === "summary") {
|
|
1316
|
-
flush();
|
|
1317
|
-
turns.push({ messages: [message], complete: true, protectedSystem: true });
|
|
1318
|
-
continue;
|
|
1319
|
-
}
|
|
1320
|
-
if (message.role === "user")
|
|
1321
|
-
flush();
|
|
1322
|
-
current.push(message);
|
|
1323
|
-
}
|
|
1324
|
-
flush();
|
|
1325
|
-
return turns;
|
|
1326
|
-
}
|
|
1327
|
-
async function selectAgentHistory(options) {
|
|
1328
|
-
if (!Number.isSafeInteger(options.availableTokens) || options.availableTokens < 0) {
|
|
1329
|
-
throw new TypeError("availableTokens must be a non-negative safe integer");
|
|
1330
|
-
}
|
|
1331
|
-
const keepRecentTurns = options.keepRecentTurns ?? 1;
|
|
1332
|
-
if (!Number.isSafeInteger(keepRecentTurns) || keepRecentTurns < 0) {
|
|
1333
|
-
throw new TypeError("keepRecentTurns must be a non-negative safe integer");
|
|
1334
|
-
}
|
|
1335
|
-
const spoken = options.messages.filter((message) => message.role !== "assistant" || isSpeakableAssistantStatus(message.status));
|
|
1336
|
-
const counts = new Map;
|
|
1337
|
-
let total = 0;
|
|
1338
|
-
let estimated = false;
|
|
1339
|
-
for (const message of spoken) {
|
|
1340
|
-
const count = AgentTokenCountSchema.parse(await options.estimateMessage(message));
|
|
1341
|
-
counts.set(message.id, count);
|
|
1342
|
-
const value = knownValue(count);
|
|
1343
|
-
if (value === undefined) {
|
|
1344
|
-
return {
|
|
1345
|
-
messages: [...spoken],
|
|
1346
|
-
decisions: options.messages.map((candidate) => ({
|
|
1347
|
-
messageId: candidate.id,
|
|
1348
|
-
action: candidate.role === "assistant" && !isSpeakableAssistantStatus(candidate.status) ? "removed" : "kept",
|
|
1349
|
-
reason: candidate.role === "assistant" && !isSpeakableAssistantStatus(candidate.status) ? "unspeakable" : "token-count-unavailable",
|
|
1350
|
-
tokens: counts.get(candidate.id) ?? { provenance: "unavailable" }
|
|
1351
|
-
})),
|
|
1352
|
-
totalTokens: { provenance: "unavailable" },
|
|
1353
|
-
outcome: "unavailable"
|
|
1354
|
-
};
|
|
1355
|
-
}
|
|
1356
|
-
total += value;
|
|
1357
|
-
if (count.provenance === "estimated")
|
|
1358
|
-
estimated = true;
|
|
1359
|
-
}
|
|
1360
|
-
const turns = budgetTurns(spoken);
|
|
1361
|
-
const completeIndexes = turns.map((turn, index) => ({ turn, index })).filter(({ turn }) => turn.complete && !turn.protectedSystem).map(({ index }) => index);
|
|
1362
|
-
const protectedRecent = new Set(completeIndexes.slice(-keepRecentTurns));
|
|
1363
|
-
const removed = new Set;
|
|
1364
|
-
for (let index = 0;index < turns.length && total > options.availableTokens; index += 1) {
|
|
1365
|
-
const turn = turns[index];
|
|
1366
|
-
if (!turn || turn.protectedSystem || !turn.complete || protectedRecent.has(index))
|
|
1367
|
-
continue;
|
|
1368
|
-
for (const message of turn.messages) {
|
|
1369
|
-
removed.add(message.id);
|
|
1370
|
-
total -= knownValue(counts.get(message.id) ?? { provenance: "unavailable" }) ?? 0;
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
const messages = spoken.filter((message) => !removed.has(message.id));
|
|
1374
|
-
const decisions = options.messages.map((message) => {
|
|
1375
|
-
if (message.role === "assistant" && !isSpeakableAssistantStatus(message.status)) {
|
|
1376
|
-
return {
|
|
1377
|
-
messageId: message.id,
|
|
1378
|
-
action: "removed",
|
|
1379
|
-
reason: "unspeakable",
|
|
1380
|
-
tokens: { provenance: "unavailable" }
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
|
-
const turnIndex = turns.findIndex((turn2) => turn2.messages.some((item) => item.id === message.id));
|
|
1384
|
-
const turn = turns[turnIndex];
|
|
1385
|
-
let reason = "within-budget";
|
|
1386
|
-
if (removed.has(message.id))
|
|
1387
|
-
reason = "oldest-eligible-turn";
|
|
1388
|
-
else if (turn?.protectedSystem)
|
|
1389
|
-
reason = "protected-system";
|
|
1390
|
-
else if (turn && !turn.complete)
|
|
1391
|
-
reason = "protected-incomplete-turn";
|
|
1392
|
-
else if (protectedRecent.has(turnIndex))
|
|
1393
|
-
reason = "protected-recent-turn";
|
|
1394
|
-
return {
|
|
1395
|
-
messageId: message.id,
|
|
1396
|
-
action: removed.has(message.id) ? "removed" : "kept",
|
|
1397
|
-
reason,
|
|
1398
|
-
tokens: counts.get(message.id) ?? { provenance: "unavailable" }
|
|
1399
|
-
};
|
|
1400
|
-
});
|
|
1401
|
-
return {
|
|
1402
|
-
messages,
|
|
1403
|
-
decisions,
|
|
1404
|
-
totalTokens: { value: total, provenance: estimated ? "estimated" : "computed" },
|
|
1405
|
-
outcome: total > options.availableTokens ? "oversized" : removed.size > 0 ? "truncated" : "fits"
|
|
1406
|
-
};
|
|
1407
|
-
}
|
|
1408
|
-
function knownValue(value) {
|
|
1409
|
-
return value.provenance === "unavailable" ? undefined : value.value;
|
|
1410
|
-
}
|
|
1411
|
-
function composeAgentPrompt(sections) {
|
|
1412
|
-
return async (options) => {
|
|
1413
|
-
const rendered = [];
|
|
1414
|
-
let total = 0;
|
|
1415
|
-
let unavailable = false;
|
|
1416
|
-
let estimated = false;
|
|
1417
|
-
for (const section of sections) {
|
|
1418
|
-
const text = await section.render({ context: options.context, signal: options.signal });
|
|
1419
|
-
rendered.push({ name: section.name, stability: section.stability, text });
|
|
1420
|
-
let count;
|
|
1421
|
-
if (section.estimateTokens) {
|
|
1422
|
-
count = AgentTokenCountSchema.parse(await section.estimateTokens(text));
|
|
1423
|
-
} else if (options.estimateFallback) {
|
|
1424
|
-
count = AgentTokenCountSchema.parse(await options.estimateFallback(text));
|
|
1425
|
-
} else
|
|
1426
|
-
count = { provenance: "unavailable" };
|
|
1427
|
-
const value = knownValue(count);
|
|
1428
|
-
if (value === undefined)
|
|
1429
|
-
unavailable = true;
|
|
1430
|
-
else
|
|
1431
|
-
total += value;
|
|
1432
|
-
if (count.provenance === "estimated")
|
|
1433
|
-
estimated = true;
|
|
1434
|
-
}
|
|
1435
|
-
const instructionTokens = unavailable ? { provenance: "unavailable" } : { value: total, provenance: estimated ? "estimated" : "computed" };
|
|
1436
|
-
let availableHistoryTokens;
|
|
1437
|
-
let contextDecision = "unavailable";
|
|
1438
|
-
if (options.budget) {
|
|
1439
|
-
if (!Number.isSafeInteger(options.budget.contextWindow) || options.budget.contextWindow < 0 || !Number.isSafeInteger(options.budget.reservedOutput) || options.budget.reservedOutput < 0) {
|
|
1440
|
-
throw new TypeError("contextWindow and reservedOutput must be non-negative safe integers");
|
|
1441
|
-
}
|
|
1442
|
-
const reserveValues = [
|
|
1443
|
-
options.budget.reservedOutput,
|
|
1444
|
-
knownValue(AgentTokenCountSchema.parse(options.budget.toolSchemas)),
|
|
1445
|
-
knownValue(AgentTokenCountSchema.parse(options.budget.attachments)),
|
|
1446
|
-
knownValue(AgentTokenCountSchema.parse(options.budget.providerOverhead)),
|
|
1447
|
-
knownValue(instructionTokens)
|
|
1448
|
-
];
|
|
1449
|
-
if (reserveValues.every((value) => value !== undefined)) {
|
|
1450
|
-
availableHistoryTokens = options.budget.contextWindow - reserveValues.reduce((sum, value) => sum + (value ?? 0), 0);
|
|
1451
|
-
const historyTokens = options.historyTokens ? knownValue(AgentTokenCountSchema.parse(options.historyTokens)) : undefined;
|
|
1452
|
-
if (historyTokens !== undefined) {
|
|
1453
|
-
if (availableHistoryTokens < 0)
|
|
1454
|
-
contextDecision = "oversized";
|
|
1455
|
-
else if (historyTokens <= availableHistoryTokens)
|
|
1456
|
-
contextDecision = "fits";
|
|
1457
|
-
else
|
|
1458
|
-
contextDecision = options.oversizePolicy === "compact" ? "requires-compaction" : "oversized";
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
const instructionText = rendered.map((section) => section.text).join(`
|
|
1463
|
-
|
|
1464
|
-
`);
|
|
1465
|
-
return {
|
|
1466
|
-
instructions: options.adaptInstructions ? await options.adaptInstructions(instructionText) : instructionText,
|
|
1467
|
-
sections: rendered,
|
|
1468
|
-
instructionTokens,
|
|
1469
|
-
contextDecision,
|
|
1470
|
-
...availableHistoryTokens !== undefined && { availableHistoryTokens }
|
|
1471
|
-
};
|
|
1472
|
-
};
|
|
1473
|
-
}
|
|
1474
670
|
// src/agent-runtime/protocol.ts
|
|
1475
671
|
function hasAgentTerminalOutput(input) {
|
|
1476
672
|
for (const part of input.message.parts) {
|
|
@@ -1498,1690 +694,6 @@ function defineAgentProtocol(config) {
|
|
|
1498
694
|
}
|
|
1499
695
|
};
|
|
1500
696
|
}
|
|
1501
|
-
// src/agent-runtime/admission-lanes.ts
|
|
1502
|
-
function createRuntimeAdmissionLanes() {
|
|
1503
|
-
const lanes = new Map;
|
|
1504
|
-
const admit = (runId) => {
|
|
1505
|
-
const admission = { runId, completion: Promise.withResolvers() };
|
|
1506
|
-
admission.completion.promise.catch(() => {
|
|
1507
|
-
return;
|
|
1508
|
-
});
|
|
1509
|
-
return admission;
|
|
1510
|
-
};
|
|
1511
|
-
return {
|
|
1512
|
-
reserve(key, runId) {
|
|
1513
|
-
const existing = lanes.get(key);
|
|
1514
|
-
const lane = existing ?? { acceptanceTail: Promise.resolve() };
|
|
1515
|
-
if (!existing)
|
|
1516
|
-
lanes.set(key, lane);
|
|
1517
|
-
if (!lane.head) {
|
|
1518
|
-
lane.head = admit(runId);
|
|
1519
|
-
return { lane, admission: lane.head, shouldSchedule: true };
|
|
1520
|
-
}
|
|
1521
|
-
if (!lane.pending) {
|
|
1522
|
-
lane.pending = admit(runId);
|
|
1523
|
-
return { lane, admission: lane.pending, shouldSchedule: true };
|
|
1524
|
-
}
|
|
1525
|
-
return { lane, admission: lane.pending, shouldSchedule: false };
|
|
1526
|
-
},
|
|
1527
|
-
settle(key, admission) {
|
|
1528
|
-
const lane = lanes.get(key);
|
|
1529
|
-
if (!lane)
|
|
1530
|
-
return;
|
|
1531
|
-
if (lane.head?.runId === admission.runId) {
|
|
1532
|
-
lane.head = lane.pending;
|
|
1533
|
-
lane.pending = undefined;
|
|
1534
|
-
} else if (lane.pending?.runId === admission.runId) {
|
|
1535
|
-
lane.pending = undefined;
|
|
1536
|
-
}
|
|
1537
|
-
if (!lane.head && !lane.pending)
|
|
1538
|
-
lanes.delete(key);
|
|
1539
|
-
},
|
|
1540
|
-
async waitForAcceptances(lane) {
|
|
1541
|
-
while (true) {
|
|
1542
|
-
const tail = lane.acceptanceTail;
|
|
1543
|
-
await tail.catch(() => {
|
|
1544
|
-
return;
|
|
1545
|
-
});
|
|
1546
|
-
if (tail === lane.acceptanceTail)
|
|
1547
|
-
return;
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
};
|
|
1551
|
-
}
|
|
1552
|
-
|
|
1553
|
-
// src/agent-runtime/injection.ts
|
|
1554
|
-
function createAgentInjectionRegistry() {
|
|
1555
|
-
const offered = new Map;
|
|
1556
|
-
return {
|
|
1557
|
-
offer(key, entry) {
|
|
1558
|
-
const existing = offered.get(key);
|
|
1559
|
-
if (!existing) {
|
|
1560
|
-
offered.set(key, [entry]);
|
|
1561
|
-
return;
|
|
1562
|
-
}
|
|
1563
|
-
if (existing.some((candidate) => candidate.input.id === entry.input.id))
|
|
1564
|
-
return;
|
|
1565
|
-
existing.push(entry);
|
|
1566
|
-
},
|
|
1567
|
-
take(key, excludeRunId) {
|
|
1568
|
-
const existing = offered.get(key);
|
|
1569
|
-
if (!existing)
|
|
1570
|
-
return [];
|
|
1571
|
-
const taken = existing.filter((candidate) => candidate.runId !== excludeRunId);
|
|
1572
|
-
const kept = existing.filter((candidate) => candidate.runId === excludeRunId);
|
|
1573
|
-
if (kept.length === 0)
|
|
1574
|
-
offered.delete(key);
|
|
1575
|
-
else
|
|
1576
|
-
offered.set(key, kept);
|
|
1577
|
-
return taken;
|
|
1578
|
-
},
|
|
1579
|
-
withdraw(key, runId) {
|
|
1580
|
-
const existing = offered.get(key);
|
|
1581
|
-
if (!existing)
|
|
1582
|
-
return;
|
|
1583
|
-
const kept = existing.filter((candidate) => candidate.runId !== runId);
|
|
1584
|
-
if (kept.length === 0)
|
|
1585
|
-
offered.delete(key);
|
|
1586
|
-
else
|
|
1587
|
-
offered.set(key, kept);
|
|
1588
|
-
},
|
|
1589
|
-
clear() {
|
|
1590
|
-
offered.clear();
|
|
1591
|
-
}
|
|
1592
|
-
};
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
// src/agent-runtime/run-execution.ts
|
|
1596
|
-
import {
|
|
1597
|
-
stepCountIs,
|
|
1598
|
-
streamText
|
|
1599
|
-
} from "ai";
|
|
1600
|
-
|
|
1601
|
-
// src/agent-runtime/runtime-internals.ts
|
|
1602
|
-
import { z as z6 } from "zod";
|
|
1603
|
-
|
|
1604
|
-
// src/agent-runtime/terminal-commit.ts
|
|
1605
|
-
class AgentRuntimeConflictError extends Error {
|
|
1606
|
-
constructor(operation) {
|
|
1607
|
-
super(`Agent runtime store conflict during ${operation}`);
|
|
1608
|
-
this.name = "AgentRuntimeConflictError";
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
function appliedSnapshot(result, operation) {
|
|
1612
|
-
if (result.outcome === "applied" || result.outcome === "duplicate")
|
|
1613
|
-
return result.snapshot;
|
|
1614
|
-
throw new AgentRuntimeConflictError(operation);
|
|
1615
|
-
}
|
|
1616
|
-
function viewOfSnapshot(snapshot, runId, retainedAssistant) {
|
|
1617
|
-
const run = snapshot.runs.find((candidate) => candidate.id === runId);
|
|
1618
|
-
const assistant = snapshot.messages.find((message) => message.id === run?.assistantMessageId) ?? retainedAssistant;
|
|
1619
|
-
return {
|
|
1620
|
-
snapshotVersion: snapshot.version,
|
|
1621
|
-
conversationId: snapshot.conversationId,
|
|
1622
|
-
...run && { run },
|
|
1623
|
-
...assistant && { assistant }
|
|
1624
|
-
};
|
|
1625
|
-
}
|
|
1626
|
-
function viewOfRun(view, conversationId) {
|
|
1627
|
-
return {
|
|
1628
|
-
snapshotVersion: view.snapshotVersion,
|
|
1629
|
-
conversationId,
|
|
1630
|
-
run: view.run,
|
|
1631
|
-
...view.assistant && { assistant: view.assistant }
|
|
1632
|
-
};
|
|
1633
|
-
}
|
|
1634
|
-
function canonicalTerminal(view) {
|
|
1635
|
-
const { run, assistant } = view;
|
|
1636
|
-
if (!run?.terminalReason)
|
|
1637
|
-
return;
|
|
1638
|
-
if (view.conversationId !== run.conversationId || !assistant || assistant.id !== run.assistantMessageId || assistant.conversationId !== run.conversationId || assistant.runId !== run.id || assistant.role !== "assistant" || assistant.status !== assistantStatus(run.terminalReason)) {
|
|
1639
|
-
throw new AgentRuntimeConflictError("terminal result projection");
|
|
1640
|
-
}
|
|
1641
|
-
return {
|
|
1642
|
-
snapshotVersion: view.snapshotVersion,
|
|
1643
|
-
run,
|
|
1644
|
-
assistant,
|
|
1645
|
-
reason: run.terminalReason,
|
|
1646
|
-
committedByCaller: false,
|
|
1647
|
-
...run.terminalPolicyName && { policyName: run.terminalPolicyName },
|
|
1648
|
-
...run.usage && { usage: run.usage }
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
function interruptedCandidate(candidate, run, now) {
|
|
1652
|
-
const reason = candidate.reason === "superseded" ? "superseded" : "interrupted";
|
|
1653
|
-
const hasInterruptControl = candidate.assistant.parts.some((part) => part.type === "control" && part.reason === "run-interrupted");
|
|
1654
|
-
const parts = hasInterruptControl ? candidate.assistant.parts : [
|
|
1655
|
-
...candidate.assistant.parts,
|
|
1656
|
-
AgentMessagePartSchema.parse({ type: "control", reason: "run-interrupted" })
|
|
1657
|
-
];
|
|
1658
|
-
return {
|
|
1659
|
-
...candidate.usage && { usage: candidate.usage },
|
|
1660
|
-
run,
|
|
1661
|
-
assistant: AgentMessageSchema.parse({
|
|
1662
|
-
...candidate.assistant,
|
|
1663
|
-
status: assistantStatus(reason),
|
|
1664
|
-
parts,
|
|
1665
|
-
updatedAt: now().toISOString()
|
|
1666
|
-
}),
|
|
1667
|
-
reason
|
|
1668
|
-
};
|
|
1669
|
-
}
|
|
1670
|
-
function canRetryTerminal(current, previous, runtimeEpoch) {
|
|
1671
|
-
return (current.state === "running" || current.state === "interrupt_requested") && current.ownerId === runtimeEpoch && current.fencingToken === previous.fencingToken;
|
|
1672
|
-
}
|
|
1673
|
-
var TERMINAL_COMMIT_ATTEMPTS = 32;
|
|
1674
|
-
async function commitAgentRunTerminal(input) {
|
|
1675
|
-
let candidate = input.candidate;
|
|
1676
|
-
for (let attempt = 0;attempt < TERMINAL_COMMIT_ATTEMPTS; attempt += 1) {
|
|
1677
|
-
const committed = await input.store.commitRunTerminal({
|
|
1678
|
-
conversationId: candidate.run.conversationId,
|
|
1679
|
-
runId: candidate.run.id,
|
|
1680
|
-
expectedRevision: candidate.run.revision,
|
|
1681
|
-
ownerId: input.runtimeEpoch,
|
|
1682
|
-
...candidate.run.fencingToken !== undefined && {
|
|
1683
|
-
fencingToken: candidate.run.fencingToken
|
|
1684
|
-
},
|
|
1685
|
-
assistant: candidate.assistant,
|
|
1686
|
-
reason: candidate.reason,
|
|
1687
|
-
...candidate.policyName && { policyName: candidate.policyName },
|
|
1688
|
-
...candidate.usage && { usage: candidate.usage },
|
|
1689
|
-
...candidate.absorb?.length && { absorb: candidate.absorb }
|
|
1690
|
-
});
|
|
1691
|
-
if (committed.outcome === "applied") {
|
|
1692
|
-
const terminal2 = canonicalTerminal(viewOfSnapshot(committed.snapshot, candidate.run.id));
|
|
1693
|
-
if (!terminal2)
|
|
1694
|
-
throw new AgentRuntimeConflictError("terminal result projection");
|
|
1695
|
-
const absorbed = committed.snapshot.runs.filter((run) => run.absorbedIntoRunId === candidate.run.id);
|
|
1696
|
-
return {
|
|
1697
|
-
...terminal2,
|
|
1698
|
-
committedByCaller: true,
|
|
1699
|
-
...absorbed.length > 0 && { absorbed }
|
|
1700
|
-
};
|
|
1701
|
-
}
|
|
1702
|
-
if (committed.outcome === "duplicate") {
|
|
1703
|
-
const terminal2 = canonicalTerminal(viewOfSnapshot(committed.snapshot, candidate.run.id, committed.assistant));
|
|
1704
|
-
if (!terminal2)
|
|
1705
|
-
throw new AgentRuntimeConflictError("terminal result projection");
|
|
1706
|
-
return terminal2;
|
|
1707
|
-
}
|
|
1708
|
-
if (committed.outcome !== "conflict") {
|
|
1709
|
-
throw new AgentRuntimeConflictError("terminal commit");
|
|
1710
|
-
}
|
|
1711
|
-
const latest = await input.store.loadRun({
|
|
1712
|
-
conversationId: candidate.run.conversationId,
|
|
1713
|
-
runId: candidate.run.id
|
|
1714
|
-
});
|
|
1715
|
-
const terminal = latest ? canonicalTerminal(viewOfRun(latest, candidate.run.conversationId)) : undefined;
|
|
1716
|
-
if (terminal)
|
|
1717
|
-
return terminal;
|
|
1718
|
-
const current = latest?.run;
|
|
1719
|
-
if (!current || !canRetryTerminal(current, candidate.run, input.runtimeEpoch)) {
|
|
1720
|
-
throw new AgentRuntimeConflictError("terminal commit");
|
|
1721
|
-
}
|
|
1722
|
-
candidate = current.state === "interrupt_requested" ? interruptedCandidate(candidate, current, input.now) : { ...candidate, run: current };
|
|
1723
|
-
}
|
|
1724
|
-
throw new AgentRuntimeConflictError("terminal commit");
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
// src/agent-runtime/runtime-internals.ts
|
|
1728
|
-
function findRun(runs, runId) {
|
|
1729
|
-
const run = runs.find((candidate) => candidate.id === runId);
|
|
1730
|
-
if (!run)
|
|
1731
|
-
throw new AgentRuntimeConflictError("run lookup");
|
|
1732
|
-
return run;
|
|
1733
|
-
}
|
|
1734
|
-
function jsonValue(value) {
|
|
1735
|
-
const parsed = z6.json().safeParse(value);
|
|
1736
|
-
return parsed.success ? parsed.data : { message: "Non-JSON tool output omitted" };
|
|
1737
|
-
}
|
|
1738
|
-
function providerEnvelope(value) {
|
|
1739
|
-
const parsed = AgentJsonObjectSchema.safeParse(value);
|
|
1740
|
-
if (!parsed.success)
|
|
1741
|
-
return;
|
|
1742
|
-
return { schemaVersion: 1, provider: "ai-sdk", data: parsed.data };
|
|
1743
|
-
}
|
|
1744
|
-
function appendText(parts, text) {
|
|
1745
|
-
const previous = parts.at(-1);
|
|
1746
|
-
if (previous?.type === "text") {
|
|
1747
|
-
const next = AgentMessagePartSchema.parse({ ...previous, text: previous.text + text });
|
|
1748
|
-
parts.splice(parts.length - 1, 1, next);
|
|
1749
|
-
return;
|
|
1750
|
-
}
|
|
1751
|
-
parts.push(AgentMessagePartSchema.parse({ type: "text", text }));
|
|
1752
|
-
}
|
|
1753
|
-
function abortTerminalReason(signal) {
|
|
1754
|
-
if (signal.reason === "shutdown")
|
|
1755
|
-
return "shutdown";
|
|
1756
|
-
if (signal.reason === "timeout")
|
|
1757
|
-
return "timeout";
|
|
1758
|
-
if (signal.reason === "supersede")
|
|
1759
|
-
return "superseded";
|
|
1760
|
-
return "interrupted";
|
|
1761
|
-
}
|
|
1762
|
-
function addUsage(total, step) {
|
|
1763
|
-
const sum = (left, right) => {
|
|
1764
|
-
if (left?.value === undefined && right?.value === undefined) {
|
|
1765
|
-
return { provenance: "unavailable" };
|
|
1766
|
-
}
|
|
1767
|
-
if (left?.value === undefined)
|
|
1768
|
-
return { ...right, provenance: "computed" };
|
|
1769
|
-
if (right?.value === undefined)
|
|
1770
|
-
return { ...left, provenance: "computed" };
|
|
1771
|
-
return { value: left.value + right.value, provenance: "computed" };
|
|
1772
|
-
};
|
|
1773
|
-
const sumCost = (left, right) => {
|
|
1774
|
-
if (left?.value === undefined || right?.value === undefined) {
|
|
1775
|
-
return { provenance: "unavailable" };
|
|
1776
|
-
}
|
|
1777
|
-
if (left.currency !== right.currency)
|
|
1778
|
-
return { provenance: "unavailable" };
|
|
1779
|
-
return {
|
|
1780
|
-
value: left.value + right.value,
|
|
1781
|
-
...left.currency && { currency: left.currency },
|
|
1782
|
-
provenance: "computed"
|
|
1783
|
-
};
|
|
1784
|
-
};
|
|
1785
|
-
if (!total)
|
|
1786
|
-
return step;
|
|
1787
|
-
const cost = sumCost(total.cost, step.cost);
|
|
1788
|
-
return {
|
|
1789
|
-
inputTokens: sum(total.inputTokens, step.inputTokens),
|
|
1790
|
-
outputTokens: sum(total.outputTokens, step.outputTokens),
|
|
1791
|
-
reasoningTokens: sum(total.reasoningTokens, step.reasoningTokens),
|
|
1792
|
-
cacheReadTokens: sum(total.cacheReadTokens, step.cacheReadTokens),
|
|
1793
|
-
cacheWriteTokens: sum(total.cacheWriteTokens, step.cacheWriteTokens),
|
|
1794
|
-
...cost && { cost }
|
|
1795
|
-
};
|
|
1796
|
-
}
|
|
1797
|
-
function mergeModelTotals(sdkTotal, accumulated) {
|
|
1798
|
-
const pick = (total, ours) => total?.value !== undefined ? { value: total.value, provenance: "computed" } : ours ?? { provenance: "unavailable" };
|
|
1799
|
-
return {
|
|
1800
|
-
inputTokens: pick(sdkTotal.inputTokens, accumulated?.inputTokens),
|
|
1801
|
-
outputTokens: pick(sdkTotal.outputTokens, accumulated?.outputTokens),
|
|
1802
|
-
reasoningTokens: pick(sdkTotal.reasoningTokens, accumulated?.reasoningTokens),
|
|
1803
|
-
cacheReadTokens: pick(sdkTotal.cacheReadTokens, accumulated?.cacheReadTokens),
|
|
1804
|
-
cacheWriteTokens: pick(sdkTotal.cacheWriteTokens, accumulated?.cacheWriteTokens),
|
|
1805
|
-
cost: accumulated?.cost ?? { provenance: "unavailable" }
|
|
1806
|
-
};
|
|
1807
|
-
}
|
|
1808
|
-
function statedUsage(usage) {
|
|
1809
|
-
const stated = (value) => value ?? { provenance: "unavailable" };
|
|
1810
|
-
if (!usage)
|
|
1811
|
-
return unknownUsage();
|
|
1812
|
-
return {
|
|
1813
|
-
inputTokens: stated(usage.inputTokens),
|
|
1814
|
-
outputTokens: stated(usage.outputTokens),
|
|
1815
|
-
reasoningTokens: stated(usage.reasoningTokens),
|
|
1816
|
-
cacheReadTokens: stated(usage.cacheReadTokens),
|
|
1817
|
-
cacheWriteTokens: stated(usage.cacheWriteTokens),
|
|
1818
|
-
cost: stated(usage.cost)
|
|
1819
|
-
};
|
|
1820
|
-
}
|
|
1821
|
-
function unknownUsage() {
|
|
1822
|
-
const nothing = { provenance: "unavailable" };
|
|
1823
|
-
return {
|
|
1824
|
-
inputTokens: nothing,
|
|
1825
|
-
outputTokens: nothing,
|
|
1826
|
-
reasoningTokens: nothing,
|
|
1827
|
-
cacheReadTokens: nothing,
|
|
1828
|
-
cacheWriteTokens: nothing,
|
|
1829
|
-
cost: nothing
|
|
1830
|
-
};
|
|
1831
|
-
}
|
|
1832
|
-
function normalizeSdkUsage(value) {
|
|
1833
|
-
const reported = (tokens) => tokens === undefined || !Number.isSafeInteger(tokens) || tokens < 0 ? { provenance: "unavailable" } : { value: tokens, provenance: "provider-reported" };
|
|
1834
|
-
return {
|
|
1835
|
-
inputTokens: reported(value.inputTokens),
|
|
1836
|
-
outputTokens: reported(value.outputTokens),
|
|
1837
|
-
reasoningTokens: reported(value.outputTokenDetails.reasoningTokens),
|
|
1838
|
-
cacheReadTokens: reported(value.inputTokenDetails.cacheReadTokens),
|
|
1839
|
-
cacheWriteTokens: reported(value.inputTokenDetails.cacheWriteTokens)
|
|
1840
|
-
};
|
|
1841
|
-
}
|
|
1842
|
-
function createIdleDeadline(parent, timeoutMs) {
|
|
1843
|
-
if (timeoutMs === undefined) {
|
|
1844
|
-
const noop = () => {
|
|
1845
|
-
return;
|
|
1846
|
-
};
|
|
1847
|
-
return { signal: parent, touch: noop, dispose: noop };
|
|
1848
|
-
}
|
|
1849
|
-
const controller = new AbortController;
|
|
1850
|
-
let timer;
|
|
1851
|
-
const touch = () => {
|
|
1852
|
-
if (timer !== undefined)
|
|
1853
|
-
clearTimeout(timer);
|
|
1854
|
-
timer = setTimeout(() => controller.abort("timeout"), timeoutMs);
|
|
1855
|
-
};
|
|
1856
|
-
touch();
|
|
1857
|
-
return {
|
|
1858
|
-
signal: AbortSignal.any([parent, controller.signal]),
|
|
1859
|
-
touch,
|
|
1860
|
-
dispose() {
|
|
1861
|
-
if (timer !== undefined)
|
|
1862
|
-
clearTimeout(timer);
|
|
1863
|
-
}
|
|
1864
|
-
};
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
// src/agent-runtime/run-execution.ts
|
|
1868
|
-
function snapshotForRunPrompt(snapshot, runId) {
|
|
1869
|
-
const runIndex = snapshot.runs.findIndex((candidate) => candidate.id === runId);
|
|
1870
|
-
if (runIndex < 0)
|
|
1871
|
-
throw new AgentRuntimeConflictError("prompt run lookup");
|
|
1872
|
-
const ownedIds = new Set(snapshot.runs.flatMap((candidate) => [
|
|
1873
|
-
...candidate.inputMessageIds,
|
|
1874
|
-
candidate.assistantMessageId
|
|
1875
|
-
]));
|
|
1876
|
-
const eligibleIds = new Set(snapshot.runs.slice(0, runIndex + 1).flatMap((candidate) => [...candidate.inputMessageIds, candidate.assistantMessageId]));
|
|
1877
|
-
return AgentSnapshotSchema.parse({
|
|
1878
|
-
...snapshot,
|
|
1879
|
-
messages: snapshot.messages.filter((message) => !ownedIds.has(message.id) || eligibleIds.has(message.id))
|
|
1880
|
-
});
|
|
1881
|
-
}
|
|
1882
|
-
function createRunExecutor(dependencies) {
|
|
1883
|
-
const {
|
|
1884
|
-
config,
|
|
1885
|
-
publish,
|
|
1886
|
-
runtimeEpoch,
|
|
1887
|
-
generateId,
|
|
1888
|
-
now,
|
|
1889
|
-
checkpointEveryEvents,
|
|
1890
|
-
maxSteps,
|
|
1891
|
-
idleTimeoutMs,
|
|
1892
|
-
injection
|
|
1893
|
-
} = dependencies;
|
|
1894
|
-
return async function executeRun(input) {
|
|
1895
|
-
injection?.withdraw(input.key, input.acceptedRun.id);
|
|
1896
|
-
const queued = await config.store.loadRun({
|
|
1897
|
-
conversationId: input.acceptedRun.conversationId,
|
|
1898
|
-
runId: input.acceptedRun.id
|
|
1899
|
-
});
|
|
1900
|
-
if (!queued)
|
|
1901
|
-
throw new AgentRuntimeConflictError("run lookup");
|
|
1902
|
-
if (queued.run.terminalReason === "absorbed") {
|
|
1903
|
-
const absorbing = queued.run.absorbedIntoRunId ? await config.store.loadRun({
|
|
1904
|
-
conversationId: queued.run.conversationId,
|
|
1905
|
-
runId: queued.run.absorbedIntoRunId
|
|
1906
|
-
}) : undefined;
|
|
1907
|
-
if (!absorbing?.assistant || !absorbing.run.terminalReason) {
|
|
1908
|
-
throw new AgentRuntimeConflictError("absorbed run resolution");
|
|
1909
|
-
}
|
|
1910
|
-
input.onAcquired?.();
|
|
1911
|
-
return {
|
|
1912
|
-
run: absorbing.run,
|
|
1913
|
-
message: absorbing.assistant,
|
|
1914
|
-
reason: absorbing.run.terminalReason,
|
|
1915
|
-
snapshotVersion: absorbing.snapshotVersion,
|
|
1916
|
-
...absorbing.run.terminalPolicyName && {
|
|
1917
|
-
policyName: absorbing.run.terminalPolicyName
|
|
1918
|
-
}
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
const queuedRun = queued.run;
|
|
1922
|
-
const acquired = appliedSnapshot(await config.store.acquireRun({
|
|
1923
|
-
conversationId: queuedRun.conversationId,
|
|
1924
|
-
runId: queuedRun.id,
|
|
1925
|
-
expectedRevision: queuedRun.revision,
|
|
1926
|
-
ownerId: runtimeEpoch
|
|
1927
|
-
}), "run acquisition");
|
|
1928
|
-
let run = findRun(acquired.runs, input.acceptedRun.id);
|
|
1929
|
-
input.onAcquired?.();
|
|
1930
|
-
await publish({
|
|
1931
|
-
type: "run-state",
|
|
1932
|
-
eventId: agentDurableEventId("run-state", run.id, acquired.version),
|
|
1933
|
-
conversationId: run.conversationId,
|
|
1934
|
-
runId: run.id,
|
|
1935
|
-
snapshotVersion: acquired.version,
|
|
1936
|
-
state: run.state,
|
|
1937
|
-
emittedAt: now().toISOString()
|
|
1938
|
-
});
|
|
1939
|
-
const trace = config.observe?.rootTrace();
|
|
1940
|
-
const runStartedAt = performance.now();
|
|
1941
|
-
config.observe?.emit({
|
|
1942
|
-
schemaVersion: 1,
|
|
1943
|
-
eventId: generateId(),
|
|
1944
|
-
type: "run-started",
|
|
1945
|
-
conversationId: run.conversationId,
|
|
1946
|
-
runId: run.id,
|
|
1947
|
-
traceId: trace?.traceId ?? generateId(),
|
|
1948
|
-
spanId: trace?.spanId ?? generateId(),
|
|
1949
|
-
...trace?.parentSpanId && { parentSpanId: trace.parentSpanId },
|
|
1950
|
-
state: run.state,
|
|
1951
|
-
queueWaitMs: Math.max(0, now().getTime() - new Date(run.createdAt).getTime()),
|
|
1952
|
-
emittedAt: now().toISOString()
|
|
1953
|
-
});
|
|
1954
|
-
let assistant = AgentMessageSchema.parse({
|
|
1955
|
-
schemaVersion: 1,
|
|
1956
|
-
id: run.assistantMessageId,
|
|
1957
|
-
conversationId: run.conversationId,
|
|
1958
|
-
runId: run.id,
|
|
1959
|
-
role: "assistant",
|
|
1960
|
-
status: "streaming",
|
|
1961
|
-
parts: [],
|
|
1962
|
-
createdAt: now().toISOString(),
|
|
1963
|
-
updatedAt: now().toISOString()
|
|
1964
|
-
});
|
|
1965
|
-
let snapshot = appliedSnapshot(await config.store.checkpointRunAssistant({
|
|
1966
|
-
conversationId: run.conversationId,
|
|
1967
|
-
runId: run.id,
|
|
1968
|
-
expectedRevision: run.revision,
|
|
1969
|
-
ownerId: runtimeEpoch,
|
|
1970
|
-
...run.fencingToken !== undefined && { fencingToken: run.fencingToken },
|
|
1971
|
-
assistant
|
|
1972
|
-
}), "assistant draft");
|
|
1973
|
-
run = findRun(snapshot.runs, run.id);
|
|
1974
|
-
let observedVersion = snapshot.version;
|
|
1975
|
-
const parts = [];
|
|
1976
|
-
const absorbed = new Map;
|
|
1977
|
-
let eventCount = 0;
|
|
1978
|
-
let sequence = 0;
|
|
1979
|
-
let terminalReason = "success";
|
|
1980
|
-
let nonModelUsage = input.acceptedRun.usage;
|
|
1981
|
-
let modelUsage;
|
|
1982
|
-
let usage = nonModelUsage;
|
|
1983
|
-
let sawProviderFinish = false;
|
|
1984
|
-
let step = 0;
|
|
1985
|
-
let selectedModel;
|
|
1986
|
-
let internalCause;
|
|
1987
|
-
let reasoningPartIndex;
|
|
1988
|
-
let firstOutputAt;
|
|
1989
|
-
let terminalPolicyName;
|
|
1990
|
-
const idleDeadline = createIdleDeadline(input.signal, idleTimeoutMs);
|
|
1991
|
-
const executionSignal = idleDeadline.signal;
|
|
1992
|
-
const updateReasoning = (text, metadata) => {
|
|
1993
|
-
const provider = providerEnvelope(metadata);
|
|
1994
|
-
if (reasoningPartIndex === undefined) {
|
|
1995
|
-
reasoningPartIndex = parts.length;
|
|
1996
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
1997
|
-
type: "reasoning",
|
|
1998
|
-
text,
|
|
1999
|
-
...provider && { provider }
|
|
2000
|
-
}));
|
|
2001
|
-
return;
|
|
2002
|
-
}
|
|
2003
|
-
const current = parts[reasoningPartIndex];
|
|
2004
|
-
if (current?.type !== "reasoning") {
|
|
2005
|
-
throw new AgentRuntimeConflictError("reasoning accumulator");
|
|
2006
|
-
}
|
|
2007
|
-
parts.splice(reasoningPartIndex, 1, AgentMessagePartSchema.parse({
|
|
2008
|
-
...current,
|
|
2009
|
-
text: current.text + text,
|
|
2010
|
-
...provider && { provider }
|
|
2011
|
-
}));
|
|
2012
|
-
};
|
|
2013
|
-
const checkpoint = async () => {
|
|
2014
|
-
assistant = AgentMessageSchema.parse({
|
|
2015
|
-
...assistant,
|
|
2016
|
-
parts,
|
|
2017
|
-
updatedAt: now().toISOString()
|
|
2018
|
-
});
|
|
2019
|
-
snapshot = appliedSnapshot(await config.store.checkpointRunAssistant({
|
|
2020
|
-
conversationId: run.conversationId,
|
|
2021
|
-
runId: run.id,
|
|
2022
|
-
expectedRevision: run.revision,
|
|
2023
|
-
ownerId: runtimeEpoch,
|
|
2024
|
-
...run.fencingToken !== undefined && { fencingToken: run.fencingToken },
|
|
2025
|
-
assistant,
|
|
2026
|
-
usage: statedUsage(usage)
|
|
2027
|
-
}), "assistant checkpoint");
|
|
2028
|
-
observedVersion = snapshot.version;
|
|
2029
|
-
run = findRun(snapshot.runs, run.id);
|
|
2030
|
-
const checkpointMetrics = {
|
|
2031
|
-
partial: true,
|
|
2032
|
-
durationMs: performance.now() - runStartedAt,
|
|
2033
|
-
usage: statedUsage(usage),
|
|
2034
|
-
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt }
|
|
2035
|
-
};
|
|
2036
|
-
await publish({
|
|
2037
|
-
type: "assistant-checkpoint",
|
|
2038
|
-
eventId: agentDurableEventId("assistant-checkpoint", run.id, observedVersion),
|
|
2039
|
-
conversationId: run.conversationId,
|
|
2040
|
-
runId: run.id,
|
|
2041
|
-
snapshotVersion: observedVersion,
|
|
2042
|
-
message: assistant,
|
|
2043
|
-
metrics: checkpointMetrics,
|
|
2044
|
-
emittedAt: now().toISOString()
|
|
2045
|
-
});
|
|
2046
|
-
};
|
|
2047
|
-
try {
|
|
2048
|
-
if (config.history?.compact) {
|
|
2049
|
-
const compacted = await config.history.compact({
|
|
2050
|
-
conversationId: run.conversationId,
|
|
2051
|
-
store: config.store,
|
|
2052
|
-
signal: executionSignal
|
|
2053
|
-
});
|
|
2054
|
-
snapshot = compacted.snapshot;
|
|
2055
|
-
observedVersion = snapshot.version;
|
|
2056
|
-
run = findRun(snapshot.runs, run.id);
|
|
2057
|
-
if (compacted.usage) {
|
|
2058
|
-
nonModelUsage = addUsage(nonModelUsage, compacted.usage);
|
|
2059
|
-
usage = modelUsage ? addUsage(nonModelUsage, modelUsage) : nonModelUsage;
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
const assertCurrent = async () => {
|
|
2063
|
-
if (executionSignal.aborted)
|
|
2064
|
-
return "run_interrupted";
|
|
2065
|
-
const current = await config.store.loadRun({
|
|
2066
|
-
conversationId: run.conversationId,
|
|
2067
|
-
runId: run.id
|
|
2068
|
-
});
|
|
2069
|
-
const currentRun = current?.run;
|
|
2070
|
-
if (!currentRun || currentRun.ownerId !== runtimeEpoch)
|
|
2071
|
-
return "stale_run";
|
|
2072
|
-
if (currentRun.fencingToken !== run.fencingToken)
|
|
2073
|
-
return "stale_run";
|
|
2074
|
-
if (currentRun.state === "interrupt_requested")
|
|
2075
|
-
return "run_interrupted";
|
|
2076
|
-
if (currentRun.state !== "running")
|
|
2077
|
-
return "stale_run";
|
|
2078
|
-
return;
|
|
2079
|
-
};
|
|
2080
|
-
const toolFenceLifecycle = createAgentToolFenceLifecycle({
|
|
2081
|
-
runId: run.id,
|
|
2082
|
-
assertCurrent,
|
|
2083
|
-
context: () => ({
|
|
2084
|
-
...run.fencingToken !== undefined && { fencingToken: run.fencingToken }
|
|
2085
|
-
})
|
|
2086
|
-
});
|
|
2087
|
-
const runtimeContext = {
|
|
2088
|
-
context: input.context,
|
|
2089
|
-
run,
|
|
2090
|
-
signal: executionSignal,
|
|
2091
|
-
toolFenceLifecycle
|
|
2092
|
-
};
|
|
2093
|
-
selectedModel = await config.models.resolve({
|
|
2094
|
-
context: input.context,
|
|
2095
|
-
conversationId: run.conversationId
|
|
2096
|
-
});
|
|
2097
|
-
const promptSnapshot = snapshotForRunPrompt(snapshot, run.id);
|
|
2098
|
-
const [prompt, tools] = await Promise.all([
|
|
2099
|
-
config.prompt({
|
|
2100
|
-
context: input.context,
|
|
2101
|
-
signal: executionSignal,
|
|
2102
|
-
model: selectedModel,
|
|
2103
|
-
snapshot: promptSnapshot
|
|
2104
|
-
}),
|
|
2105
|
-
config.tools(runtimeContext)
|
|
2106
|
-
]);
|
|
2107
|
-
if (prompt.contextDecision === "oversized") {
|
|
2108
|
-
throw new AgentContextOverflowError;
|
|
2109
|
-
}
|
|
2110
|
-
if (prompt.contextDecision === "requires-compaction") {
|
|
2111
|
-
throw new AgentContextOverflowError("Agent context still exceeds the model budget after compaction");
|
|
2112
|
-
}
|
|
2113
|
-
let carriedSystem = [];
|
|
2114
|
-
const projectHistory = async (source) => {
|
|
2115
|
-
if (config.history?.project)
|
|
2116
|
-
return config.history.project(source.messages);
|
|
2117
|
-
const detailed = await projectAgentHistoryDetailed(source.messages, {
|
|
2118
|
-
...config.history?.resolveFile && { resolveFile: config.history.resolveFile },
|
|
2119
|
-
...config.history?.unresolvedFile && {
|
|
2120
|
-
unresolvedFile: config.history.unresolvedFile
|
|
2121
|
-
},
|
|
2122
|
-
...config.history?.interruptedAssistant && {
|
|
2123
|
-
interruptedAssistant: config.history.interruptedAssistant
|
|
2124
|
-
}
|
|
2125
|
-
});
|
|
2126
|
-
carriedSystem = detailed.system;
|
|
2127
|
-
return [...detailed.messages];
|
|
2128
|
-
};
|
|
2129
|
-
const projectInputs = async (source) => {
|
|
2130
|
-
if (config.history?.project)
|
|
2131
|
-
return config.history.project(source);
|
|
2132
|
-
const detailed = await projectAgentHistoryDetailed(source, {
|
|
2133
|
-
...config.history?.resolveFile && { resolveFile: config.history.resolveFile },
|
|
2134
|
-
...config.history?.unresolvedFile && {
|
|
2135
|
-
unresolvedFile: config.history.unresolvedFile
|
|
2136
|
-
},
|
|
2137
|
-
...config.history?.interruptedAssistant && {
|
|
2138
|
-
interruptedAssistant: config.history.interruptedAssistant
|
|
2139
|
-
}
|
|
2140
|
-
});
|
|
2141
|
-
return [...detailed.messages];
|
|
2142
|
-
};
|
|
2143
|
-
const history = await projectHistory(promptSnapshot);
|
|
2144
|
-
const withCarriedSystem = (instructions) => {
|
|
2145
|
-
if (carriedSystem.length === 0)
|
|
2146
|
-
return instructions;
|
|
2147
|
-
const composed = typeof instructions === "string" ? [{ role: "system", content: instructions }] : Array.isArray(instructions) ? instructions : [instructions];
|
|
2148
|
-
return [
|
|
2149
|
-
...composed,
|
|
2150
|
-
...carriedSystem.map((content) => ({ role: "system", content }))
|
|
2151
|
-
];
|
|
2152
|
-
};
|
|
2153
|
-
const maxStepCondition = stepCountIs(maxSteps);
|
|
2154
|
-
const stopConditions = [
|
|
2155
|
-
async (options) => {
|
|
2156
|
-
const stopped = await maxStepCondition(options);
|
|
2157
|
-
if (stopped && terminalPolicyName === undefined)
|
|
2158
|
-
terminalPolicyName = "max-steps";
|
|
2159
|
-
return stopped;
|
|
2160
|
-
}
|
|
2161
|
-
];
|
|
2162
|
-
for (const policy of config.loop?.stopPolicies ?? []) {
|
|
2163
|
-
stopConditions.push(async (options) => {
|
|
2164
|
-
const stopped = await policy.when(options);
|
|
2165
|
-
if (stopped && terminalPolicyName === undefined)
|
|
2166
|
-
terminalPolicyName = policy.name;
|
|
2167
|
-
return stopped;
|
|
2168
|
-
});
|
|
2169
|
-
}
|
|
2170
|
-
const takeInjectedMessages = async () => {
|
|
2171
|
-
const taken = injection?.take(input.key, run.id) ?? [];
|
|
2172
|
-
if (taken.length === 0)
|
|
2173
|
-
return [];
|
|
2174
|
-
const messages = [];
|
|
2175
|
-
for (const entry of taken) {
|
|
2176
|
-
messages.push(...await projectInputs([entry.input]));
|
|
2177
|
-
const ids = absorbed.get(entry.runId) ?? [];
|
|
2178
|
-
ids.push(entry.input.id);
|
|
2179
|
-
absorbed.set(entry.runId, ids);
|
|
2180
|
-
}
|
|
2181
|
-
return messages;
|
|
2182
|
-
};
|
|
2183
|
-
const result = streamText({
|
|
2184
|
-
model: selectedModel.model,
|
|
2185
|
-
tools,
|
|
2186
|
-
instructions: withCarriedSystem(prompt.instructions),
|
|
2187
|
-
messages: history,
|
|
2188
|
-
abortSignal: executionSignal,
|
|
2189
|
-
maxRetries: 0,
|
|
2190
|
-
stopWhen: stopConditions,
|
|
2191
|
-
repairToolCall: deferredToolRepair(config.loop?.prepareStep),
|
|
2192
|
-
...(config.loop?.prepareStep || injection) && {
|
|
2193
|
-
prepareStep: async (options) => {
|
|
2194
|
-
const prepared = await config.loop?.prepareStep?.({
|
|
2195
|
-
...options,
|
|
2196
|
-
...runtimeContext
|
|
2197
|
-
});
|
|
2198
|
-
const injected = await takeInjectedMessages();
|
|
2199
|
-
if (injected.length === 0)
|
|
2200
|
-
return prepared;
|
|
2201
|
-
const base = prepared?.messages ?? options.messages;
|
|
2202
|
-
return { ...prepared, messages: [...base, ...injected] };
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
});
|
|
2206
|
-
for await (const part of result.stream) {
|
|
2207
|
-
idleDeadline.touch();
|
|
2208
|
-
eventCount += 1;
|
|
2209
|
-
sequence += 1;
|
|
2210
|
-
if (firstOutputAt === undefined && ["text-delta", "reasoning-delta", "tool-call", "file", "source"].includes(part.type)) {
|
|
2211
|
-
firstOutputAt = performance.now();
|
|
2212
|
-
}
|
|
2213
|
-
if (part.type === "text-delta") {
|
|
2214
|
-
appendText(parts, part.text);
|
|
2215
|
-
await publish({
|
|
2216
|
-
type: "assistant-delta",
|
|
2217
|
-
conversationId: run.conversationId,
|
|
2218
|
-
runId: run.id,
|
|
2219
|
-
runtimeEpoch,
|
|
2220
|
-
sequence,
|
|
2221
|
-
textDelta: part.text,
|
|
2222
|
-
emittedAt: now().toISOString()
|
|
2223
|
-
});
|
|
2224
|
-
} else if (part.type === "reasoning-start") {
|
|
2225
|
-
reasoningPartIndex = undefined;
|
|
2226
|
-
updateReasoning("", part.providerMetadata);
|
|
2227
|
-
const provider = providerEnvelope(part.providerMetadata);
|
|
2228
|
-
await publish({
|
|
2229
|
-
type: "reasoning-start",
|
|
2230
|
-
conversationId: run.conversationId,
|
|
2231
|
-
runId: run.id,
|
|
2232
|
-
runtimeEpoch,
|
|
2233
|
-
sequence,
|
|
2234
|
-
...provider && { provider },
|
|
2235
|
-
emittedAt: now().toISOString()
|
|
2236
|
-
});
|
|
2237
|
-
} else if (part.type === "reasoning-delta") {
|
|
2238
|
-
updateReasoning(part.text, part.providerMetadata);
|
|
2239
|
-
const provider = providerEnvelope(part.providerMetadata);
|
|
2240
|
-
await publish({
|
|
2241
|
-
type: "reasoning-delta",
|
|
2242
|
-
conversationId: run.conversationId,
|
|
2243
|
-
runId: run.id,
|
|
2244
|
-
runtimeEpoch,
|
|
2245
|
-
sequence,
|
|
2246
|
-
textDelta: part.text,
|
|
2247
|
-
...provider && { provider },
|
|
2248
|
-
emittedAt: now().toISOString()
|
|
2249
|
-
});
|
|
2250
|
-
} else if (part.type === "reasoning-end") {
|
|
2251
|
-
updateReasoning("", part.providerMetadata);
|
|
2252
|
-
reasoningPartIndex = undefined;
|
|
2253
|
-
const provider = providerEnvelope(part.providerMetadata);
|
|
2254
|
-
await publish({
|
|
2255
|
-
type: "reasoning-end",
|
|
2256
|
-
conversationId: run.conversationId,
|
|
2257
|
-
runId: run.id,
|
|
2258
|
-
runtimeEpoch,
|
|
2259
|
-
sequence,
|
|
2260
|
-
...provider && { provider },
|
|
2261
|
-
emittedAt: now().toISOString()
|
|
2262
|
-
});
|
|
2263
|
-
} else if (part.type === "tool-call") {
|
|
2264
|
-
const provider = providerEnvelope(part.providerMetadata);
|
|
2265
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2266
|
-
type: "tool-call",
|
|
2267
|
-
callId: part.toolCallId,
|
|
2268
|
-
toolName: part.toolName,
|
|
2269
|
-
input: jsonValue(part.input),
|
|
2270
|
-
...provider && { provider }
|
|
2271
|
-
}));
|
|
2272
|
-
await publish({
|
|
2273
|
-
type: "tool-status",
|
|
2274
|
-
conversationId: run.conversationId,
|
|
2275
|
-
runId: run.id,
|
|
2276
|
-
runtimeEpoch,
|
|
2277
|
-
sequence,
|
|
2278
|
-
callId: part.toolCallId,
|
|
2279
|
-
toolName: part.toolName,
|
|
2280
|
-
status: "started",
|
|
2281
|
-
input: jsonValue(part.input),
|
|
2282
|
-
emittedAt: now().toISOString()
|
|
2283
|
-
});
|
|
2284
|
-
} else if (part.type === "tool-result") {
|
|
2285
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2286
|
-
type: "tool-result",
|
|
2287
|
-
callId: part.toolCallId,
|
|
2288
|
-
toolName: part.toolName,
|
|
2289
|
-
outcome: "success",
|
|
2290
|
-
output: jsonValue(part.output)
|
|
2291
|
-
}));
|
|
2292
|
-
await publish({
|
|
2293
|
-
type: "tool-status",
|
|
2294
|
-
conversationId: run.conversationId,
|
|
2295
|
-
runId: run.id,
|
|
2296
|
-
runtimeEpoch,
|
|
2297
|
-
sequence,
|
|
2298
|
-
callId: part.toolCallId,
|
|
2299
|
-
toolName: part.toolName,
|
|
2300
|
-
status: "completed",
|
|
2301
|
-
output: jsonValue(part.output),
|
|
2302
|
-
emittedAt: now().toISOString()
|
|
2303
|
-
});
|
|
2304
|
-
} else if (part.type === "tool-error") {
|
|
2305
|
-
internalCause = part.error;
|
|
2306
|
-
if (isToolExecutionControlError(part.error)) {
|
|
2307
|
-
await publish({
|
|
2308
|
-
type: "tool-status",
|
|
2309
|
-
conversationId: run.conversationId,
|
|
2310
|
-
runId: run.id,
|
|
2311
|
-
runtimeEpoch,
|
|
2312
|
-
sequence,
|
|
2313
|
-
callId: part.toolCallId,
|
|
2314
|
-
toolName: part.toolName,
|
|
2315
|
-
status: "interrupted",
|
|
2316
|
-
emittedAt: now().toISOString()
|
|
2317
|
-
});
|
|
2318
|
-
throw part.error;
|
|
2319
|
-
}
|
|
2320
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2321
|
-
type: "tool-result",
|
|
2322
|
-
callId: part.toolCallId,
|
|
2323
|
-
toolName: part.toolName,
|
|
2324
|
-
outcome: "error",
|
|
2325
|
-
output: { message: "Tool execution failed" }
|
|
2326
|
-
}));
|
|
2327
|
-
await publish({
|
|
2328
|
-
type: "tool-status",
|
|
2329
|
-
conversationId: run.conversationId,
|
|
2330
|
-
runId: run.id,
|
|
2331
|
-
runtimeEpoch,
|
|
2332
|
-
sequence,
|
|
2333
|
-
callId: part.toolCallId,
|
|
2334
|
-
toolName: part.toolName,
|
|
2335
|
-
status: "failed",
|
|
2336
|
-
output: { message: "Tool execution failed" },
|
|
2337
|
-
emittedAt: now().toISOString()
|
|
2338
|
-
});
|
|
2339
|
-
} else if (part.type === "tool-output-denied") {
|
|
2340
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2341
|
-
type: "tool-result",
|
|
2342
|
-
callId: part.toolCallId,
|
|
2343
|
-
toolName: part.toolName,
|
|
2344
|
-
outcome: "error",
|
|
2345
|
-
output: { message: "Tool output denied" }
|
|
2346
|
-
}));
|
|
2347
|
-
await publish({
|
|
2348
|
-
type: "tool-status",
|
|
2349
|
-
conversationId: run.conversationId,
|
|
2350
|
-
runId: run.id,
|
|
2351
|
-
runtimeEpoch,
|
|
2352
|
-
sequence,
|
|
2353
|
-
callId: part.toolCallId,
|
|
2354
|
-
toolName: part.toolName,
|
|
2355
|
-
status: "failed",
|
|
2356
|
-
output: { message: "Tool output denied" },
|
|
2357
|
-
emittedAt: now().toISOString()
|
|
2358
|
-
});
|
|
2359
|
-
} else if (part.type === "source") {
|
|
2360
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2361
|
-
type: "source",
|
|
2362
|
-
sourceId: part.id,
|
|
2363
|
-
...part.sourceType === "url" && { url: part.url },
|
|
2364
|
-
...part.title && { title: part.title }
|
|
2365
|
-
}));
|
|
2366
|
-
} else if (part.type === "file" && config.persistGeneratedFile) {
|
|
2367
|
-
const persisted = await config.persistGeneratedFile(part.file);
|
|
2368
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2369
|
-
type: "file",
|
|
2370
|
-
mediaType: part.file.mediaType,
|
|
2371
|
-
reference: persisted.reference,
|
|
2372
|
-
...persisted.filename && { filename: persisted.filename }
|
|
2373
|
-
}));
|
|
2374
|
-
} else if (part.type === "file") {
|
|
2375
|
-
throw new Error("persistGeneratedFile is required for generated file output");
|
|
2376
|
-
} else if (part.type === "reasoning-file" && config.persistGeneratedFile) {
|
|
2377
|
-
const persisted = await config.persistGeneratedFile(part.file);
|
|
2378
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2379
|
-
type: "file",
|
|
2380
|
-
mediaType: part.file.mediaType,
|
|
2381
|
-
reference: persisted.reference,
|
|
2382
|
-
...persisted.filename && { filename: persisted.filename }
|
|
2383
|
-
}));
|
|
2384
|
-
} else if (part.type === "reasoning-file") {
|
|
2385
|
-
throw new Error("persistGeneratedFile is required for generated reasoning files");
|
|
2386
|
-
} else if (part.type === "tool-approval-request" || part.type === "tool-approval-response") {
|
|
2387
|
-
throw new Error("Durable tool approval/resume is outside this runtime version");
|
|
2388
|
-
} else if (part.type === "custom") {
|
|
2389
|
-
const provider = providerEnvelope(part.providerMetadata);
|
|
2390
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2391
|
-
type: "provider",
|
|
2392
|
-
envelope: {
|
|
2393
|
-
schemaVersion: 1,
|
|
2394
|
-
provider: "ai-sdk-custom",
|
|
2395
|
-
data: { kind: part.kind, ...provider && { provider: provider.data } }
|
|
2396
|
-
}
|
|
2397
|
-
}));
|
|
2398
|
-
} else if (part.type === "raw") {
|
|
2399
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2400
|
-
type: "provider",
|
|
2401
|
-
envelope: {
|
|
2402
|
-
schemaVersion: 1,
|
|
2403
|
-
provider: "ai-sdk-raw",
|
|
2404
|
-
data: { value: jsonValue(part.rawValue) }
|
|
2405
|
-
}
|
|
2406
|
-
}));
|
|
2407
|
-
} else if (part.type === "abort") {
|
|
2408
|
-
terminalReason = "interrupted";
|
|
2409
|
-
} else if (part.type === "error") {
|
|
2410
|
-
terminalReason = part.error instanceof AgentContextOverflowError ? "context_overflow" : "provider_failure";
|
|
2411
|
-
internalCause = part.error;
|
|
2412
|
-
} else if (part.type === "finish-step") {
|
|
2413
|
-
const stepTrace = trace ? config.observe?.rootTrace(trace) : undefined;
|
|
2414
|
-
const stepUsage = selectedModel.normalizeUsage?.({
|
|
2415
|
-
usage: part.usage,
|
|
2416
|
-
providerMetadata: part.providerMetadata
|
|
2417
|
-
}) ?? normalizeSdkUsage(part.usage);
|
|
2418
|
-
modelUsage = addUsage(modelUsage, stepUsage);
|
|
2419
|
-
usage = nonModelUsage ? addUsage(nonModelUsage, modelUsage) : modelUsage;
|
|
2420
|
-
config.observe?.emit({
|
|
2421
|
-
schemaVersion: 1,
|
|
2422
|
-
eventId: generateId(),
|
|
2423
|
-
type: "step-finished",
|
|
2424
|
-
conversationId: run.conversationId,
|
|
2425
|
-
runId: run.id,
|
|
2426
|
-
traceId: stepTrace?.traceId ?? trace?.traceId ?? generateId(),
|
|
2427
|
-
spanId: stepTrace?.spanId ?? generateId(),
|
|
2428
|
-
...stepTrace?.parentSpanId && { parentSpanId: stepTrace.parentSpanId },
|
|
2429
|
-
state: run.state,
|
|
2430
|
-
modelId: selectedModel.descriptor.modelId,
|
|
2431
|
-
step,
|
|
2432
|
-
usage: stepUsage,
|
|
2433
|
-
emittedAt: now().toISOString()
|
|
2434
|
-
});
|
|
2435
|
-
step += 1;
|
|
2436
|
-
} else if (part.type === "finish" && part.finishReason !== "stop") {
|
|
2437
|
-
if (terminalReason === "success") {
|
|
2438
|
-
terminalReason = part.finishReason === "error" ? "provider_failure" : "provider_stop";
|
|
2439
|
-
internalCause ??= { finishReason: part.finishReason };
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
if (part.type === "finish") {
|
|
2443
|
-
sawProviderFinish = true;
|
|
2444
|
-
modelUsage = mergeModelTotals(normalizeSdkUsage(part.totalUsage), modelUsage);
|
|
2445
|
-
usage = nonModelUsage ? addUsage(nonModelUsage, modelUsage) : modelUsage;
|
|
2446
|
-
}
|
|
2447
|
-
if (eventCount % checkpointEveryEvents === 0)
|
|
2448
|
-
await checkpoint();
|
|
2449
|
-
}
|
|
2450
|
-
if (terminalPolicyName !== undefined)
|
|
2451
|
-
terminalReason = "policy_stop";
|
|
2452
|
-
if (executionSignal.aborted)
|
|
2453
|
-
terminalReason = abortTerminalReason(executionSignal);
|
|
2454
|
-
if (config.protocol.acceptTerminal && (terminalReason === "success" || terminalReason === "policy_stop" || terminalReason === "provider_stop")) {
|
|
2455
|
-
const candidate = AgentMessageSchema.parse({
|
|
2456
|
-
...assistant,
|
|
2457
|
-
status: assistantStatus(terminalReason),
|
|
2458
|
-
parts,
|
|
2459
|
-
updatedAt: now().toISOString()
|
|
2460
|
-
});
|
|
2461
|
-
if (!await config.protocol.acceptTerminal({
|
|
2462
|
-
message: candidate,
|
|
2463
|
-
reason: terminalReason,
|
|
2464
|
-
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
2465
|
-
})) {
|
|
2466
|
-
terminalReason = "provider_failure";
|
|
2467
|
-
internalCause = new Error("Agent terminal output was rejected by the protocol");
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
} catch (error) {
|
|
2471
|
-
internalCause = error;
|
|
2472
|
-
const latest = await config.store.loadRun({
|
|
2473
|
-
conversationId: run.conversationId,
|
|
2474
|
-
runId: run.id
|
|
2475
|
-
});
|
|
2476
|
-
const latestRun = latest?.run;
|
|
2477
|
-
const durableInterrupt = latestRun?.ownerId === runtimeEpoch && latestRun.state === "interrupt_requested";
|
|
2478
|
-
if (durableInterrupt && latest && latestRun) {
|
|
2479
|
-
observedVersion = latest.snapshotVersion;
|
|
2480
|
-
run = latestRun;
|
|
2481
|
-
}
|
|
2482
|
-
if (isToolExecutionControlError(error) || executionSignal.aborted || durableInterrupt) {
|
|
2483
|
-
terminalReason = executionSignal.aborted ? abortTerminalReason(executionSignal) : "interrupted";
|
|
2484
|
-
parts.push(AgentMessagePartSchema.parse({
|
|
2485
|
-
type: "control",
|
|
2486
|
-
reason: isToolExecutionControlError(error) && error.reason === "stale_run" ? "stale-run" : "run-interrupted"
|
|
2487
|
-
}));
|
|
2488
|
-
} else {
|
|
2489
|
-
terminalReason = error instanceof AgentContextOverflowError ? "context_overflow" : "provider_failure";
|
|
2490
|
-
}
|
|
2491
|
-
} finally {
|
|
2492
|
-
idleDeadline.dispose();
|
|
2493
|
-
}
|
|
2494
|
-
assistant = AgentMessageSchema.parse({
|
|
2495
|
-
...assistant,
|
|
2496
|
-
status: assistantStatus(terminalReason),
|
|
2497
|
-
parts,
|
|
2498
|
-
updatedAt: now().toISOString()
|
|
2499
|
-
});
|
|
2500
|
-
const spent = statedUsage(usage);
|
|
2501
|
-
const emitSpend = (report) => {
|
|
2502
|
-
config.observe?.emit({
|
|
2503
|
-
schemaVersion: 1,
|
|
2504
|
-
eventId: report.eventId,
|
|
2505
|
-
type: "run-terminal",
|
|
2506
|
-
conversationId: run.conversationId,
|
|
2507
|
-
runId: run.id,
|
|
2508
|
-
traceId: trace?.traceId ?? generateId(),
|
|
2509
|
-
spanId: trace?.spanId ?? generateId(),
|
|
2510
|
-
...trace?.parentSpanId && { parentSpanId: trace.parentSpanId },
|
|
2511
|
-
state: report.state,
|
|
2512
|
-
terminalReason: report.reason,
|
|
2513
|
-
...selectedModel && { modelId: selectedModel.descriptor.modelId },
|
|
2514
|
-
durationMs: performance.now() - runStartedAt,
|
|
2515
|
-
usage: spent,
|
|
2516
|
-
...internalCause !== undefined && { internalCause },
|
|
2517
|
-
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt },
|
|
2518
|
-
emittedAt: now().toISOString()
|
|
2519
|
-
});
|
|
2520
|
-
};
|
|
2521
|
-
const unsettledEventId = (version) => agentDurableEventId("terminal", `${run.id}:${runtimeEpoch}`, version);
|
|
2522
|
-
let terminal;
|
|
2523
|
-
try {
|
|
2524
|
-
terminal = await commitAgentRunTerminal({
|
|
2525
|
-
store: config.store,
|
|
2526
|
-
runtimeEpoch,
|
|
2527
|
-
candidate: {
|
|
2528
|
-
run,
|
|
2529
|
-
assistant,
|
|
2530
|
-
reason: terminalReason,
|
|
2531
|
-
...terminalPolicyName && { policyName: terminalPolicyName },
|
|
2532
|
-
usage: spent,
|
|
2533
|
-
...absorbed.size > 0 && runStateForTerminalReason(terminalReason) === "completed" && {
|
|
2534
|
-
absorb: [...absorbed].map(([runId, inputMessageIds]) => ({
|
|
2535
|
-
runId,
|
|
2536
|
-
inputMessageIds
|
|
2537
|
-
}))
|
|
2538
|
-
}
|
|
2539
|
-
},
|
|
2540
|
-
now
|
|
2541
|
-
});
|
|
2542
|
-
} catch (error) {
|
|
2543
|
-
emitSpend({
|
|
2544
|
-
eventId: unsettledEventId(observedVersion),
|
|
2545
|
-
state: run.state,
|
|
2546
|
-
reason: terminalReason
|
|
2547
|
-
});
|
|
2548
|
-
throw error;
|
|
2549
|
-
}
|
|
2550
|
-
observedVersion = terminal.snapshotVersion;
|
|
2551
|
-
run = terminal.run;
|
|
2552
|
-
assistant = terminal.assistant;
|
|
2553
|
-
terminalReason = terminal.reason;
|
|
2554
|
-
terminalPolicyName = terminal.policyName;
|
|
2555
|
-
const terminalMetrics = terminal.committedByCaller ? {
|
|
2556
|
-
partial: !sawProviderFinish,
|
|
2557
|
-
durationMs: performance.now() - runStartedAt,
|
|
2558
|
-
usage: spent,
|
|
2559
|
-
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt }
|
|
2560
|
-
} : undefined;
|
|
2561
|
-
emitSpend({
|
|
2562
|
-
eventId: terminal.committedByCaller ? agentDurableEventId("terminal", run.id, observedVersion) : unsettledEventId(observedVersion),
|
|
2563
|
-
state: run.state,
|
|
2564
|
-
reason: terminalReason
|
|
2565
|
-
});
|
|
2566
|
-
for (const settled of terminal.absorbed ?? []) {
|
|
2567
|
-
await publish({
|
|
2568
|
-
type: "run-state",
|
|
2569
|
-
eventId: agentDurableEventId("run-state", settled.id, observedVersion),
|
|
2570
|
-
conversationId: settled.conversationId,
|
|
2571
|
-
runId: settled.id,
|
|
2572
|
-
snapshotVersion: observedVersion,
|
|
2573
|
-
state: settled.state,
|
|
2574
|
-
emittedAt: now().toISOString()
|
|
2575
|
-
});
|
|
2576
|
-
}
|
|
2577
|
-
if (terminalMetrics) {
|
|
2578
|
-
await publish({
|
|
2579
|
-
type: "terminal",
|
|
2580
|
-
eventId: agentDurableEventId("terminal", run.id, observedVersion),
|
|
2581
|
-
conversationId: run.conversationId,
|
|
2582
|
-
runId: run.id,
|
|
2583
|
-
snapshotVersion: observedVersion,
|
|
2584
|
-
reason: terminalReason,
|
|
2585
|
-
...terminalPolicyName && { policyName: terminalPolicyName },
|
|
2586
|
-
message: assistant,
|
|
2587
|
-
metrics: terminalMetrics,
|
|
2588
|
-
emittedAt: now().toISOString()
|
|
2589
|
-
});
|
|
2590
|
-
}
|
|
2591
|
-
return {
|
|
2592
|
-
run,
|
|
2593
|
-
message: assistant,
|
|
2594
|
-
reason: terminalReason,
|
|
2595
|
-
snapshotVersion: observedVersion,
|
|
2596
|
-
...terminalMetrics && { metrics: terminalMetrics },
|
|
2597
|
-
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
2598
|
-
};
|
|
2599
|
-
};
|
|
2600
|
-
}
|
|
2601
|
-
|
|
2602
|
-
// src/agent-runtime/runtime.ts
|
|
2603
|
-
function createAgentRuntime(config) {
|
|
2604
|
-
const coordinator = createAgentSessionCoordinator();
|
|
2605
|
-
const tickets = new Map;
|
|
2606
|
-
const generateId = config.generateId ?? (() => crypto.randomUUID());
|
|
2607
|
-
const now = config.now ?? (() => new Date);
|
|
2608
|
-
const runtimeEpoch = generateId();
|
|
2609
|
-
const admissionLanes = createRuntimeAdmissionLanes();
|
|
2610
|
-
const injectionPossible = typeof config.runs?.inputPolicy === "function" || config.runs?.inputPolicy === "inject";
|
|
2611
|
-
const injection = injectionPossible ? createAgentInjectionRegistry() : undefined;
|
|
2612
|
-
const reserveAdmission = admissionLanes.reserve;
|
|
2613
|
-
const settleAdmission = admissionLanes.settle;
|
|
2614
|
-
const waitForAdmissionAcceptances = admissionLanes.waitForAcceptances;
|
|
2615
|
-
const checkpointEveryEvents = config.loop?.checkpointEveryEvents ?? 20;
|
|
2616
|
-
const maxSteps = config.loop?.maxSteps ?? 50;
|
|
2617
|
-
const declaredIdleTimeoutMs = config.loop?.idleTimeoutMs;
|
|
2618
|
-
const idleTimeoutMs = declaredIdleTimeoutMs === null ? undefined : declaredIdleTimeoutMs ?? 60000;
|
|
2619
|
-
if (!Number.isSafeInteger(checkpointEveryEvents) || checkpointEveryEvents < 1) {
|
|
2620
|
-
throw new TypeError("checkpointEveryEvents must be a positive safe integer");
|
|
2621
|
-
}
|
|
2622
|
-
if (!Number.isSafeInteger(maxSteps) || maxSteps < 1) {
|
|
2623
|
-
throw new TypeError("maxSteps must be a positive safe integer");
|
|
2624
|
-
}
|
|
2625
|
-
if (idleTimeoutMs !== undefined && (!Number.isSafeInteger(idleTimeoutMs) || idleTimeoutMs < 1)) {
|
|
2626
|
-
throw new TypeError("idleTimeoutMs must be a positive safe integer");
|
|
2627
|
-
}
|
|
2628
|
-
const policyNames = new Set(["max-steps"]);
|
|
2629
|
-
for (const policy of config.loop?.stopPolicies ?? []) {
|
|
2630
|
-
if (!policy.name || policyNames.has(policy.name)) {
|
|
2631
|
-
throw new TypeError("Agent stop policy names must be non-empty and unique");
|
|
2632
|
-
}
|
|
2633
|
-
policyNames.add(policy.name);
|
|
2634
|
-
}
|
|
2635
|
-
const publish = async (event) => {
|
|
2636
|
-
try {
|
|
2637
|
-
await config.publish?.(event);
|
|
2638
|
-
} catch (error) {
|
|
2639
|
-
try {
|
|
2640
|
-
await config.onPublishError?.({ event, error });
|
|
2641
|
-
} catch {}
|
|
2642
|
-
}
|
|
2643
|
-
};
|
|
2644
|
-
const executeRun = createRunExecutor({
|
|
2645
|
-
config,
|
|
2646
|
-
publish,
|
|
2647
|
-
runtimeEpoch,
|
|
2648
|
-
generateId,
|
|
2649
|
-
now,
|
|
2650
|
-
checkpointEveryEvents,
|
|
2651
|
-
maxSteps,
|
|
2652
|
-
...idleTimeoutMs !== undefined && { idleTimeoutMs },
|
|
2653
|
-
...injection && { injection }
|
|
2654
|
-
});
|
|
2655
|
-
let admissionClosed = false;
|
|
2656
|
-
const closedError = () => new Error("[stitchkit] agent runtime is closed and admits no further work");
|
|
2657
|
-
const admissionsInFlight = new Set;
|
|
2658
|
-
const beginAdmission = () => {
|
|
2659
|
-
const handoff = Promise.withResolvers();
|
|
2660
|
-
admissionsInFlight.add(handoff);
|
|
2661
|
-
return () => {
|
|
2662
|
-
if (admissionsInFlight.delete(handoff))
|
|
2663
|
-
handoff.resolve();
|
|
2664
|
-
};
|
|
2665
|
-
};
|
|
2666
|
-
const drainAdmissions = async (budgetMs) => {
|
|
2667
|
-
const pending = [...admissionsInFlight].map((handoff) => handoff.promise);
|
|
2668
|
-
if (pending.length === 0)
|
|
2669
|
-
return 0;
|
|
2670
|
-
let stranded = pending.length;
|
|
2671
|
-
const settled = Promise.all(pending.map((promise) => promise.then(() => {
|
|
2672
|
-
stranded -= 1;
|
|
2673
|
-
})));
|
|
2674
|
-
if (budgetMs === undefined) {
|
|
2675
|
-
await settled;
|
|
2676
|
-
return 0;
|
|
2677
|
-
}
|
|
2678
|
-
let timer;
|
|
2679
|
-
await Promise.race([
|
|
2680
|
-
settled,
|
|
2681
|
-
new Promise((resolve) => {
|
|
2682
|
-
timer = setTimeout(resolve, budgetMs);
|
|
2683
|
-
})
|
|
2684
|
-
]);
|
|
2685
|
-
if (timer !== undefined)
|
|
2686
|
-
clearTimeout(timer);
|
|
2687
|
-
return stranded;
|
|
2688
|
-
};
|
|
2689
|
-
const refuse = () => {
|
|
2690
|
-
const error = closedError();
|
|
2691
|
-
const accepted = Promise.reject(error);
|
|
2692
|
-
const result = Promise.reject(error);
|
|
2693
|
-
accepted.catch(() => {
|
|
2694
|
-
return;
|
|
2695
|
-
});
|
|
2696
|
-
result.catch(() => {
|
|
2697
|
-
return;
|
|
2698
|
-
});
|
|
2699
|
-
return { accepted, result };
|
|
2700
|
-
};
|
|
2701
|
-
const resume = (rawInput) => {
|
|
2702
|
-
if (admissionClosed)
|
|
2703
|
-
return refuse();
|
|
2704
|
-
const context = config.protocol.parseContext(rawInput.context);
|
|
2705
|
-
const accepted = Promise.withResolvers();
|
|
2706
|
-
const result = Promise.withResolvers();
|
|
2707
|
-
let acquisitionSettled = false;
|
|
2708
|
-
const resolveAcquisition = () => {
|
|
2709
|
-
acquisitionSettled = true;
|
|
2710
|
-
accepted.resolve();
|
|
2711
|
-
};
|
|
2712
|
-
const rejectAcquisition = (error) => {
|
|
2713
|
-
if (!acquisitionSettled)
|
|
2714
|
-
accepted.reject(error);
|
|
2715
|
-
};
|
|
2716
|
-
const handedOff = beginAdmission();
|
|
2717
|
-
(async () => {
|
|
2718
|
-
try {
|
|
2719
|
-
const recovered = await config.store.loadRun({
|
|
2720
|
-
conversationId: rawInput.conversationId,
|
|
2721
|
-
runId: rawInput.runId
|
|
2722
|
-
});
|
|
2723
|
-
if (!recovered)
|
|
2724
|
-
throw new AgentRuntimeConflictError("run lookup");
|
|
2725
|
-
const recoveredRun = recovered.run;
|
|
2726
|
-
if (recoveredRun.state !== "queued") {
|
|
2727
|
-
throw new Error("Only a queued recovered agent run can be resumed");
|
|
2728
|
-
}
|
|
2729
|
-
const ticket = coordinator.submit({
|
|
2730
|
-
key: rawInput.conversationKey ?? rawInput.conversationId,
|
|
2731
|
-
policy: "queue",
|
|
2732
|
-
create: (signal) => ({
|
|
2733
|
-
runId: recoveredRun.id,
|
|
2734
|
-
execute: () => executeRun({
|
|
2735
|
-
acceptedRun: recoveredRun,
|
|
2736
|
-
context,
|
|
2737
|
-
signal,
|
|
2738
|
-
key: rawInput.conversationKey ?? rawInput.conversationId,
|
|
2739
|
-
onAcquired: resolveAcquisition
|
|
2740
|
-
})
|
|
2741
|
-
})
|
|
2742
|
-
});
|
|
2743
|
-
ticket.accepted.catch(() => {
|
|
2744
|
-
return;
|
|
2745
|
-
});
|
|
2746
|
-
ticket.result.then(result.resolve, (error) => {
|
|
2747
|
-
rejectAcquisition(error);
|
|
2748
|
-
result.reject(error);
|
|
2749
|
-
});
|
|
2750
|
-
} catch (error) {
|
|
2751
|
-
rejectAcquisition(error);
|
|
2752
|
-
result.reject(error);
|
|
2753
|
-
} finally {
|
|
2754
|
-
handedOff();
|
|
2755
|
-
}
|
|
2756
|
-
})();
|
|
2757
|
-
return { accepted: accepted.promise, result: result.promise };
|
|
2758
|
-
};
|
|
2759
|
-
return {
|
|
2760
|
-
submit(rawInput) {
|
|
2761
|
-
if (admissionClosed) {
|
|
2762
|
-
const refused = refuse();
|
|
2763
|
-
const admission = Promise.reject(closedError());
|
|
2764
|
-
admission.catch(() => {
|
|
2765
|
-
return;
|
|
2766
|
-
});
|
|
2767
|
-
return { ...refused, admission };
|
|
2768
|
-
}
|
|
2769
|
-
const metadata = rawInput.metadata === undefined ? undefined : config.protocol.parseInputMetadata(rawInput.metadata);
|
|
2770
|
-
const input = {
|
|
2771
|
-
conversationId: rawInput.conversationId,
|
|
2772
|
-
idempotencyKey: rawInput.idempotencyKey,
|
|
2773
|
-
context: rawInput.context,
|
|
2774
|
-
parts: rawInput.parts.map((part) => config.protocol.parsePart(part)),
|
|
2775
|
-
...metadata !== undefined && { metadata }
|
|
2776
|
-
};
|
|
2777
|
-
const context = config.protocol.parseContext(input.context);
|
|
2778
|
-
const conversationTickets = tickets.get(input.conversationId);
|
|
2779
|
-
const existingTicket = conversationTickets?.get(input.idempotencyKey);
|
|
2780
|
-
if (existingTicket)
|
|
2781
|
-
return existingTicket;
|
|
2782
|
-
const key = config.runs?.key?.(input) ?? input.conversationId;
|
|
2783
|
-
const policy = typeof config.runs?.inputPolicy === "function" ? config.runs.inputPolicy(input) : config.runs?.inputPolicy ?? "queue";
|
|
2784
|
-
const nowIso = now().toISOString();
|
|
2785
|
-
const inputMessageId = rawInput.recordIds?.inputMessageId ?? generateId();
|
|
2786
|
-
const runId = rawInput.recordIds?.runId ?? generateId();
|
|
2787
|
-
const assistantMessageId = rawInput.recordIds?.assistantMessageId ?? generateId();
|
|
2788
|
-
const userMessage2 = AgentMessageSchema.parse({
|
|
2789
|
-
schemaVersion: 1,
|
|
2790
|
-
id: inputMessageId,
|
|
2791
|
-
conversationId: input.conversationId,
|
|
2792
|
-
role: "user",
|
|
2793
|
-
status: "committed",
|
|
2794
|
-
parts: input.parts,
|
|
2795
|
-
...input.metadata && { metadata: input.metadata },
|
|
2796
|
-
createdAt: nowIso,
|
|
2797
|
-
updatedAt: nowIso
|
|
2798
|
-
});
|
|
2799
|
-
const queuedRun = AgentRunSchema.parse({
|
|
2800
|
-
schemaVersion: 1,
|
|
2801
|
-
id: runId,
|
|
2802
|
-
conversationId: input.conversationId,
|
|
2803
|
-
inputMessageIds: [inputMessageId],
|
|
2804
|
-
assistantMessageId,
|
|
2805
|
-
state: "queued",
|
|
2806
|
-
revision: 0,
|
|
2807
|
-
...policy === "interrupt-next" && { queuePriority: "interrupt-next" },
|
|
2808
|
-
createdAt: nowIso,
|
|
2809
|
-
updatedAt: nowIso
|
|
2810
|
-
});
|
|
2811
|
-
const outerAccepted = Promise.withResolvers();
|
|
2812
|
-
const outerAdmission = Promise.withResolvers();
|
|
2813
|
-
outerAdmission.promise.catch(() => {
|
|
2814
|
-
return;
|
|
2815
|
-
});
|
|
2816
|
-
const outerResult = Promise.withResolvers();
|
|
2817
|
-
const reservation = config.runs?.coalescePending && policy !== "interrupt-next" ? reserveAdmission(key, runId) : undefined;
|
|
2818
|
-
const previousAcceptance = reservation?.lane.acceptanceTail ?? Promise.resolve();
|
|
2819
|
-
const acceptanceDone = Promise.withResolvers();
|
|
2820
|
-
if (reservation) {
|
|
2821
|
-
reservation.lane.acceptanceTail = previousAcceptance.catch(() => {
|
|
2822
|
-
return;
|
|
2823
|
-
}).then(() => acceptanceDone.promise);
|
|
2824
|
-
}
|
|
2825
|
-
const publicTicket = {
|
|
2826
|
-
accepted: outerAccepted.promise,
|
|
2827
|
-
admission: outerAdmission.promise,
|
|
2828
|
-
result: outerResult.promise
|
|
2829
|
-
};
|
|
2830
|
-
const currentConversationTickets = conversationTickets ?? new Map;
|
|
2831
|
-
currentConversationTickets.set(input.idempotencyKey, publicTicket);
|
|
2832
|
-
if (!conversationTickets)
|
|
2833
|
-
tickets.set(input.conversationId, currentConversationTickets);
|
|
2834
|
-
let offeredRunId;
|
|
2835
|
-
const forgetTicket = () => {
|
|
2836
|
-
if (offeredRunId !== undefined)
|
|
2837
|
-
injection?.withdraw(key, offeredRunId);
|
|
2838
|
-
if (currentConversationTickets.get(input.idempotencyKey) !== publicTicket)
|
|
2839
|
-
return;
|
|
2840
|
-
currentConversationTickets.delete(input.idempotencyKey);
|
|
2841
|
-
if (currentConversationTickets.size === 0)
|
|
2842
|
-
tickets.delete(input.conversationId);
|
|
2843
|
-
};
|
|
2844
|
-
outerResult.promise.then(forgetTicket, forgetTicket);
|
|
2845
|
-
const handedOff = beginAdmission();
|
|
2846
|
-
(async () => {
|
|
2847
|
-
try {
|
|
2848
|
-
await previousAcceptance.catch(() => {
|
|
2849
|
-
return;
|
|
2850
|
-
});
|
|
2851
|
-
await config.models.preflight?.({
|
|
2852
|
-
context,
|
|
2853
|
-
conversationId: input.conversationId
|
|
2854
|
-
});
|
|
2855
|
-
if (admissionClosed)
|
|
2856
|
-
throw closedError();
|
|
2857
|
-
const acceptance = await config.store.acceptInputAndAssignRun({
|
|
2858
|
-
idempotencyKey: input.idempotencyKey,
|
|
2859
|
-
input: userMessage2,
|
|
2860
|
-
run: queuedRun,
|
|
2861
|
-
...reservation && !reservation.shouldSchedule && {
|
|
2862
|
-
coalesceIntoRunId: reservation.admission.runId
|
|
2863
|
-
}
|
|
2864
|
-
});
|
|
2865
|
-
const acceptedSnapshot = appliedSnapshot(acceptance, "input acceptance");
|
|
2866
|
-
const assignedRunId = acceptance.outcome === "duplicate" ? acceptance.runId : reservation?.admission.runId ?? runId;
|
|
2867
|
-
const acceptedRun = acceptance.outcome === "duplicate" ? acceptance.run : findRun(acceptedSnapshot.runs, assignedRunId);
|
|
2868
|
-
const actualInputMessageId = acceptance.outcome === "duplicate" ? acceptance.inputMessageId : userMessage2.id;
|
|
2869
|
-
const acceptedInput = acceptance.outcome === "duplicate" ? acceptance.input : acceptedSnapshot.messages.find((candidate) => candidate.id === actualInputMessageId);
|
|
2870
|
-
if (!acceptedInput) {
|
|
2871
|
-
throw new AgentRuntimeConflictError("admission input projection");
|
|
2872
|
-
}
|
|
2873
|
-
const assistantPlaceholder = AgentAssistantPlaceholderSchema.parse({
|
|
2874
|
-
schemaVersion: 1,
|
|
2875
|
-
id: acceptedRun.assistantMessageId,
|
|
2876
|
-
conversationId: acceptedRun.conversationId,
|
|
2877
|
-
runId: acceptedRun.id,
|
|
2878
|
-
status: "pending",
|
|
2879
|
-
createdAt: acceptedRun.createdAt,
|
|
2880
|
-
updatedAt: acceptedRun.updatedAt
|
|
2881
|
-
});
|
|
2882
|
-
const acceptedAssistant = acceptance.outcome === "duplicate" ? acceptance.assistant ?? assistantPlaceholder : assistantPlaceholder;
|
|
2883
|
-
const admission = {
|
|
2884
|
-
inputMessageId: acceptedInput.id,
|
|
2885
|
-
runId: acceptedRun.id,
|
|
2886
|
-
assistantMessageId: assistantPlaceholder.id,
|
|
2887
|
-
input: acceptedInput,
|
|
2888
|
-
run: acceptedRun,
|
|
2889
|
-
assistant: acceptedAssistant,
|
|
2890
|
-
snapshotVersion: acceptedSnapshot.version
|
|
2891
|
-
};
|
|
2892
|
-
outerAdmission.resolve(admission);
|
|
2893
|
-
await publish({
|
|
2894
|
-
type: "admission",
|
|
2895
|
-
eventId: agentDurableEventId("admission", acceptedRun.id, acceptedSnapshot.version),
|
|
2896
|
-
conversationId: acceptedRun.conversationId,
|
|
2897
|
-
runId: acceptedRun.id,
|
|
2898
|
-
snapshotVersion: acceptedSnapshot.version,
|
|
2899
|
-
input: acceptedInput,
|
|
2900
|
-
run: acceptedRun,
|
|
2901
|
-
assistant: acceptedAssistant,
|
|
2902
|
-
emittedAt: now().toISOString()
|
|
2903
|
-
});
|
|
2904
|
-
await publish({
|
|
2905
|
-
type: "run-state",
|
|
2906
|
-
eventId: agentDurableEventId("run-state", acceptedRun.id, acceptedSnapshot.version),
|
|
2907
|
-
conversationId: acceptedRun.conversationId,
|
|
2908
|
-
runId: acceptedRun.id,
|
|
2909
|
-
snapshotVersion: acceptedSnapshot.version,
|
|
2910
|
-
state: acceptedRun.state,
|
|
2911
|
-
emittedAt: now().toISOString()
|
|
2912
|
-
});
|
|
2913
|
-
outerAccepted.resolve();
|
|
2914
|
-
if (acceptance.outcome === "duplicate") {
|
|
2915
|
-
if (!acceptedRun.terminalReason) {
|
|
2916
|
-
const error = new Error("Duplicate input is already owned by another runtime execution");
|
|
2917
|
-
outerResult.reject(error);
|
|
2918
|
-
if (reservation?.shouldSchedule) {
|
|
2919
|
-
reservation.admission.completion.reject(error);
|
|
2920
|
-
settleAdmission(key, reservation.admission);
|
|
2921
|
-
}
|
|
2922
|
-
return;
|
|
2923
|
-
}
|
|
2924
|
-
const message = acceptance.assistant;
|
|
2925
|
-
if (!message) {
|
|
2926
|
-
const error = new Error("Duplicate terminal admission has no retained canonical assistant");
|
|
2927
|
-
outerResult.reject(error);
|
|
2928
|
-
if (reservation?.shouldSchedule) {
|
|
2929
|
-
reservation.admission.completion.reject(error);
|
|
2930
|
-
settleAdmission(key, reservation.admission);
|
|
2931
|
-
}
|
|
2932
|
-
return;
|
|
2933
|
-
}
|
|
2934
|
-
outerResult.resolve({
|
|
2935
|
-
run: acceptedRun,
|
|
2936
|
-
message,
|
|
2937
|
-
reason: acceptedRun.terminalReason,
|
|
2938
|
-
snapshotVersion: acceptedSnapshot.version,
|
|
2939
|
-
...acceptedRun.terminalPolicyName && {
|
|
2940
|
-
policyName: acceptedRun.terminalPolicyName
|
|
2941
|
-
}
|
|
2942
|
-
});
|
|
2943
|
-
if (reservation?.shouldSchedule) {
|
|
2944
|
-
reservation.admission.completion.reject(new Error("Reserved run resolved to a duplicate durable input"));
|
|
2945
|
-
settleAdmission(key, reservation.admission);
|
|
2946
|
-
}
|
|
2947
|
-
return;
|
|
2948
|
-
}
|
|
2949
|
-
if (policy === "inject") {
|
|
2950
|
-
offeredRunId = acceptedRun.id;
|
|
2951
|
-
injection?.offer(key, { runId: acceptedRun.id, input: acceptedInput });
|
|
2952
|
-
}
|
|
2953
|
-
if (reservation && !reservation.shouldSchedule) {
|
|
2954
|
-
reservation.admission.completion.promise.then(outerResult.resolve, outerResult.reject);
|
|
2955
|
-
return;
|
|
2956
|
-
}
|
|
2957
|
-
const ticket = coordinator.submit({
|
|
2958
|
-
key,
|
|
2959
|
-
policy,
|
|
2960
|
-
create: async (signal) => {
|
|
2961
|
-
if (reservation)
|
|
2962
|
-
await waitForAdmissionAcceptances(reservation.lane);
|
|
2963
|
-
return {
|
|
2964
|
-
runId: acceptedRun.id,
|
|
2965
|
-
execute: () => executeRun({ acceptedRun, context, signal, key })
|
|
2966
|
-
};
|
|
2967
|
-
}
|
|
2968
|
-
});
|
|
2969
|
-
ticket.accepted.catch(() => {
|
|
2970
|
-
return;
|
|
2971
|
-
});
|
|
2972
|
-
if (reservation) {
|
|
2973
|
-
ticket.result.then(reservation.admission.completion.resolve, reservation.admission.completion.reject);
|
|
2974
|
-
reservation.admission.completion.promise.then((value) => {
|
|
2975
|
-
settleAdmission(key, reservation.admission);
|
|
2976
|
-
outerResult.resolve(value);
|
|
2977
|
-
}, (error) => {
|
|
2978
|
-
settleAdmission(key, reservation.admission);
|
|
2979
|
-
outerResult.reject(error);
|
|
2980
|
-
});
|
|
2981
|
-
} else {
|
|
2982
|
-
ticket.result.then(outerResult.resolve, outerResult.reject);
|
|
2983
|
-
}
|
|
2984
|
-
} catch (error) {
|
|
2985
|
-
outerAccepted.reject(error);
|
|
2986
|
-
outerAdmission.reject(error);
|
|
2987
|
-
outerResult.reject(error);
|
|
2988
|
-
if (reservation?.shouldSchedule) {
|
|
2989
|
-
reservation.admission.completion.reject(error);
|
|
2990
|
-
settleAdmission(key, reservation.admission);
|
|
2991
|
-
}
|
|
2992
|
-
} finally {
|
|
2993
|
-
acceptanceDone.resolve();
|
|
2994
|
-
handedOff();
|
|
2995
|
-
}
|
|
2996
|
-
})();
|
|
2997
|
-
return publicTicket;
|
|
2998
|
-
},
|
|
2999
|
-
resume,
|
|
3000
|
-
async interrupt(input) {
|
|
3001
|
-
const view = await config.store.loadRun({
|
|
3002
|
-
conversationId: input.conversationId,
|
|
3003
|
-
runId: input.runId
|
|
3004
|
-
});
|
|
3005
|
-
if (!view)
|
|
3006
|
-
throw new AgentRuntimeConflictError("run lookup");
|
|
3007
|
-
const requested = await config.store.requestRunInterrupt({
|
|
3008
|
-
conversationId: input.conversationId,
|
|
3009
|
-
runId: input.runId,
|
|
3010
|
-
expectedRevision: view.run.revision
|
|
3011
|
-
});
|
|
3012
|
-
if (requested.outcome === "applied") {
|
|
3013
|
-
const interruptedRun = findRun(requested.snapshot.runs, input.runId);
|
|
3014
|
-
await publish({
|
|
3015
|
-
type: "run-state",
|
|
3016
|
-
eventId: agentDurableEventId("run-state", interruptedRun.id, requested.snapshot.version),
|
|
3017
|
-
conversationId: interruptedRun.conversationId,
|
|
3018
|
-
runId: interruptedRun.id,
|
|
3019
|
-
snapshotVersion: requested.snapshot.version,
|
|
3020
|
-
state: interruptedRun.state,
|
|
3021
|
-
emittedAt: now().toISOString()
|
|
3022
|
-
});
|
|
3023
|
-
coordinator.stop(input.conversationKey ?? input.conversationId, "user-interrupt");
|
|
3024
|
-
}
|
|
3025
|
-
return requested;
|
|
3026
|
-
},
|
|
3027
|
-
async recover(options) {
|
|
3028
|
-
if (admissionClosed)
|
|
3029
|
-
throw closedError();
|
|
3030
|
-
const pageSize = options.pageSize ?? 100;
|
|
3031
|
-
const maxRuns = options.maxRuns ?? 1000;
|
|
3032
|
-
if (!Number.isSafeInteger(pageSize) || pageSize < 1 || pageSize > 1000) {
|
|
3033
|
-
throw new TypeError("Recovery pageSize must be an integer between 1 and 1000");
|
|
3034
|
-
}
|
|
3035
|
-
if (!Number.isSafeInteger(maxRuns) || maxRuns < 1) {
|
|
3036
|
-
throw new TypeError("Recovery maxRuns must be a positive safe integer");
|
|
3037
|
-
}
|
|
3038
|
-
const recoverable = [];
|
|
3039
|
-
let cursor;
|
|
3040
|
-
while (recoverable.length < maxRuns && !options.signal?.aborted && !admissionClosed) {
|
|
3041
|
-
const page = await config.store.scanRecoverable({
|
|
3042
|
-
...cursor && { cursor },
|
|
3043
|
-
limit: Math.min(pageSize, maxRuns - recoverable.length)
|
|
3044
|
-
});
|
|
3045
|
-
recoverable.push(...page.items);
|
|
3046
|
-
cursor = page.nextCursor;
|
|
3047
|
-
if (!cursor || page.items.length === 0)
|
|
3048
|
-
break;
|
|
3049
|
-
}
|
|
3050
|
-
const grouped = new Map;
|
|
3051
|
-
for (const item of recoverable) {
|
|
3052
|
-
const items = grouped.get(item.conversationId) ?? [];
|
|
3053
|
-
items.push(item);
|
|
3054
|
-
grouped.set(item.conversationId, items);
|
|
3055
|
-
}
|
|
3056
|
-
const ordered = [];
|
|
3057
|
-
for (const [conversationId, items] of grouped) {
|
|
3058
|
-
const active = await config.store.listActiveRuns(conversationId);
|
|
3059
|
-
const position = new Map(active.map((run, index) => [run.id, index]));
|
|
3060
|
-
ordered.push(...items.sort((left, right) => {
|
|
3061
|
-
const leftPosition = position.get(left.run.id);
|
|
3062
|
-
const rightPosition = position.get(right.run.id);
|
|
3063
|
-
if (leftPosition === undefined && rightPosition === undefined)
|
|
3064
|
-
return 0;
|
|
3065
|
-
if (leftPosition === undefined)
|
|
3066
|
-
return 1;
|
|
3067
|
-
if (rightPosition === undefined)
|
|
3068
|
-
return -1;
|
|
3069
|
-
return leftPosition - rightPosition;
|
|
3070
|
-
}));
|
|
3071
|
-
}
|
|
3072
|
-
const outcomes = [];
|
|
3073
|
-
const scheduledRuns = new Map;
|
|
3074
|
-
for (const item of ordered) {
|
|
3075
|
-
if (options.signal?.aborted)
|
|
3076
|
-
break;
|
|
3077
|
-
if (admissionClosed)
|
|
3078
|
-
break;
|
|
3079
|
-
const releaseAdmission = beginAdmission();
|
|
3080
|
-
try {
|
|
3081
|
-
const active = await config.store.listActiveRuns(item.conversationId);
|
|
3082
|
-
const position = active.findIndex((run) => run.id === item.run.id);
|
|
3083
|
-
const scheduled = scheduledRuns.get(item.conversationId) ?? new Set;
|
|
3084
|
-
const blockedByUnscheduledPredecessor = position > 0 && active.slice(0, position).some((run) => !scheduled.has(run.id));
|
|
3085
|
-
if (blockedByUnscheduledPredecessor) {
|
|
3086
|
-
outcomes.push({
|
|
3087
|
-
conversationId: item.conversationId,
|
|
3088
|
-
runId: item.run.id,
|
|
3089
|
-
outcome: "skipped"
|
|
3090
|
-
});
|
|
3091
|
-
continue;
|
|
3092
|
-
}
|
|
3093
|
-
const decision = await options.decide?.(item) ?? (item.run.state === "queued" ? { action: "resume" } : { action: "skip" });
|
|
3094
|
-
if (admissionClosed)
|
|
3095
|
-
throw closedError();
|
|
3096
|
-
if (decision.action === "skip") {
|
|
3097
|
-
outcomes.push({
|
|
3098
|
-
conversationId: item.conversationId,
|
|
3099
|
-
runId: item.run.id,
|
|
3100
|
-
outcome: "skipped"
|
|
3101
|
-
});
|
|
3102
|
-
continue;
|
|
3103
|
-
}
|
|
3104
|
-
if (decision.action === "abandon") {
|
|
3105
|
-
const abandoned = await config.store.recoverRun({
|
|
3106
|
-
conversationId: item.conversationId,
|
|
3107
|
-
runId: item.run.id,
|
|
3108
|
-
expectedRevision: item.run.revision,
|
|
3109
|
-
action: "abandon"
|
|
3110
|
-
});
|
|
3111
|
-
if (abandoned.outcome !== "applied") {
|
|
3112
|
-
throw new AgentRuntimeConflictError("recovery abandon");
|
|
3113
|
-
}
|
|
3114
|
-
outcomes.push({
|
|
3115
|
-
conversationId: item.conversationId,
|
|
3116
|
-
runId: item.run.id,
|
|
3117
|
-
outcome: "abandoned"
|
|
3118
|
-
});
|
|
3119
|
-
continue;
|
|
3120
|
-
}
|
|
3121
|
-
if (decision.action === "requeue") {
|
|
3122
|
-
const requeued = await config.store.recoverRun({
|
|
3123
|
-
conversationId: item.conversationId,
|
|
3124
|
-
runId: item.run.id,
|
|
3125
|
-
expectedRevision: item.run.revision,
|
|
3126
|
-
action: "requeue",
|
|
3127
|
-
replaySafe: true
|
|
3128
|
-
});
|
|
3129
|
-
if (requeued.outcome !== "applied") {
|
|
3130
|
-
throw new AgentRuntimeConflictError("recovery requeue");
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
const context = await options.resolveContext(item);
|
|
3134
|
-
const resumed = resume({
|
|
3135
|
-
conversationId: item.conversationId,
|
|
3136
|
-
runId: item.run.id,
|
|
3137
|
-
context
|
|
3138
|
-
});
|
|
3139
|
-
resumed.result.catch(() => {
|
|
3140
|
-
return;
|
|
3141
|
-
});
|
|
3142
|
-
await resumed.accepted;
|
|
3143
|
-
scheduled.add(item.run.id);
|
|
3144
|
-
scheduledRuns.set(item.conversationId, scheduled);
|
|
3145
|
-
outcomes.push({
|
|
3146
|
-
conversationId: item.conversationId,
|
|
3147
|
-
runId: item.run.id,
|
|
3148
|
-
outcome: decision.action === "requeue" ? "requeued" : "resumed",
|
|
3149
|
-
result: resumed.result
|
|
3150
|
-
});
|
|
3151
|
-
} catch (error) {
|
|
3152
|
-
outcomes.push({
|
|
3153
|
-
conversationId: item.conversationId,
|
|
3154
|
-
runId: item.run.id,
|
|
3155
|
-
outcome: "failed",
|
|
3156
|
-
error
|
|
3157
|
-
});
|
|
3158
|
-
} finally {
|
|
3159
|
-
releaseAdmission();
|
|
3160
|
-
}
|
|
3161
|
-
}
|
|
3162
|
-
return outcomes;
|
|
3163
|
-
},
|
|
3164
|
-
stop: (conversationKey, reason) => coordinator.stop(conversationKey, reason),
|
|
3165
|
-
close: async (options = {}) => {
|
|
3166
|
-
assertCloseBudgets(options);
|
|
3167
|
-
admissionClosed = true;
|
|
3168
|
-
const startedAt = performance.now();
|
|
3169
|
-
injection?.clear();
|
|
3170
|
-
const stranded = await drainAdmissions(options.forceTimeoutMs);
|
|
3171
|
-
const spent = Math.max(0, Math.round(performance.now() - startedAt));
|
|
3172
|
-
const remainingBudget = (value) => value === undefined ? undefined : Math.max(0, value - spent);
|
|
3173
|
-
const grace = remainingBudget(options.gracePeriodMs);
|
|
3174
|
-
const force = remainingBudget(options.forceTimeoutMs);
|
|
3175
|
-
const result = await coordinator.close({
|
|
3176
|
-
...grace !== undefined && { gracePeriodMs: grace },
|
|
3177
|
-
...force !== undefined && { forceTimeoutMs: force }
|
|
3178
|
-
});
|
|
3179
|
-
if (stranded === 0)
|
|
3180
|
-
return result;
|
|
3181
|
-
return { settled: false, timedOut: true, remaining: result.remaining + stranded };
|
|
3182
|
-
}
|
|
3183
|
-
};
|
|
3184
|
-
}
|
|
3185
697
|
export {
|
|
3186
698
|
ACTIVE_AGENT_RUN_STATES,
|
|
3187
699
|
AcceptInputAndAssignRunSchema,
|
|
@@ -3192,6 +704,9 @@ export {
|
|
|
3192
704
|
AgentCheckpointEventSchema,
|
|
3193
705
|
AgentContextOverflowError,
|
|
3194
706
|
AgentControlPartSchema,
|
|
707
|
+
AgentConversationMessagePageSchema,
|
|
708
|
+
AgentConversationPageSchema,
|
|
709
|
+
AgentConversationSummarySchema,
|
|
3195
710
|
AgentCostValueSchema,
|
|
3196
711
|
AgentFilePartSchema,
|
|
3197
712
|
AgentHistoryMutationSchema,
|
|
@@ -3201,8 +716,16 @@ export {
|
|
|
3201
716
|
AgentMessageSchema,
|
|
3202
717
|
AgentMessageStatusSchema,
|
|
3203
718
|
AgentModelCapabilitySchema,
|
|
719
|
+
AgentModelCatalogEntrySchema,
|
|
720
|
+
AgentModelCatalogSchema,
|
|
3204
721
|
AgentModelDescriptorSchema,
|
|
722
|
+
AgentModelMetricSchema,
|
|
723
|
+
AgentModelPopularitySchema,
|
|
724
|
+
AgentModelPriceSchema,
|
|
3205
725
|
AgentModelRegistrySnapshotSchema,
|
|
726
|
+
AgentModelSearchInputSchema,
|
|
727
|
+
AgentModelSearchResultSchema,
|
|
728
|
+
AgentModelSelectionSchema,
|
|
3206
729
|
AgentOpaquePartSchema,
|
|
3207
730
|
AgentProvenanceSchema,
|
|
3208
731
|
AgentProviderEnvelopeSchema,
|
|
@@ -3241,6 +764,8 @@ export {
|
|
|
3241
764
|
AgentTextPartSchema,
|
|
3242
765
|
AgentTimestampSchema,
|
|
3243
766
|
AgentTokenCountSchema,
|
|
767
|
+
AgentToolApprovalRequestPartSchema,
|
|
768
|
+
AgentToolApprovalResponsePartSchema,
|
|
3244
769
|
AgentToolCallPartSchema,
|
|
3245
770
|
AgentToolResultPartSchema,
|
|
3246
771
|
AgentToolStatusEventSchema,
|
|
@@ -3265,13 +790,16 @@ export {
|
|
|
3265
790
|
createAgentSessionCoordinator,
|
|
3266
791
|
createAgentToolFenceLifecycle,
|
|
3267
792
|
createDeferredAgentToolSurface,
|
|
793
|
+
createMemoryAgentModelSelectionStore,
|
|
3268
794
|
createMemoryAgentRuntimeStore,
|
|
3269
795
|
defineAgentProtocol,
|
|
3270
796
|
defineModelRegistry,
|
|
3271
797
|
hasAgentTerminalOutput,
|
|
798
|
+
isAssistantHistoryEvidence,
|
|
3272
799
|
projectAgentHistory,
|
|
3273
800
|
projectAgentHistoryDetailed,
|
|
3274
801
|
runStateForTerminalReason,
|
|
802
|
+
searchAgentModelCatalog,
|
|
3275
803
|
selectAgentHistory,
|
|
3276
804
|
structuredCompaction,
|
|
3277
805
|
validateAgentModelSnapshot
|