stitchkit 0.110.1 → 0.110.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/agent-runtime/archive-projection.d.ts +10 -0
  3. package/dist/agent-runtime/archive-projection.d.ts.map +1 -0
  4. package/dist/agent-runtime/archive-spill.d.ts +61 -0
  5. package/dist/agent-runtime/archive-spill.d.ts.map +1 -0
  6. package/dist/agent-runtime/branch-contract.d.ts +102 -0
  7. package/dist/agent-runtime/branch-contract.d.ts.map +1 -0
  8. package/dist/agent-runtime/branch-events.d.ts +18 -0
  9. package/dist/agent-runtime/branch-events.d.ts.map +1 -0
  10. package/dist/agent-runtime/branch-identities.d.ts +20 -0
  11. package/dist/agent-runtime/branch-identities.d.ts.map +1 -0
  12. package/dist/agent-runtime/branch-messages.d.ts +32 -0
  13. package/dist/agent-runtime/branch-messages.d.ts.map +1 -0
  14. package/dist/agent-runtime/branch-observations.d.ts +104 -0
  15. package/dist/agent-runtime/branch-observations.d.ts.map +1 -0
  16. package/dist/agent-runtime/branch-provider.d.ts +130 -0
  17. package/dist/agent-runtime/branch-provider.d.ts.map +1 -0
  18. package/dist/agent-runtime/branch-replay.d.ts +426 -0
  19. package/dist/agent-runtime/branch-replay.d.ts.map +1 -0
  20. package/dist/agent-runtime/branch-source.d.ts +16 -0
  21. package/dist/agent-runtime/branch-source.d.ts.map +1 -0
  22. package/dist/agent-runtime/branch-transitions.d.ts +6 -0
  23. package/dist/agent-runtime/branch-transitions.d.ts.map +1 -0
  24. package/dist/agent-runtime/sqlite-events.d.ts.map +1 -1
  25. package/dist/agent-runtime/sqlite-history.d.ts.map +1 -1
  26. package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -1
  27. package/dist/agent-runtime/store-archive.d.ts +1 -0
  28. package/dist/agent-runtime/store-archive.d.ts.map +1 -1
  29. package/dist/agent-runtime/store-branch.d.ts +5 -0
  30. package/dist/agent-runtime/store-branch.d.ts.map +1 -0
  31. package/dist/agent-runtime/store-conformance-branch.d.ts +4 -0
  32. package/dist/agent-runtime/store-conformance-branch.d.ts.map +1 -0
  33. package/dist/agent-runtime/store-conformance-support.d.ts +5 -5
  34. package/dist/agent-runtime/store-conformance.d.ts.map +1 -1
  35. package/dist/agent-runtime/store-create.d.ts +1 -1
  36. package/dist/agent-runtime/store-create.d.ts.map +1 -1
  37. package/dist/agent-runtime/store-driver-contract.d.ts +6 -0
  38. package/dist/agent-runtime/store-driver-contract.d.ts.map +1 -1
  39. package/dist/agent-runtime/store-events.d.ts +1131 -0
  40. package/dist/agent-runtime/store-events.d.ts.map +1 -1
  41. package/dist/agent-runtime/store-memory-archive.d.ts +8 -0
  42. package/dist/agent-runtime/store-memory-archive.d.ts.map +1 -0
  43. package/dist/agent-runtime/store-memory-events.d.ts +7 -0
  44. package/dist/agent-runtime/store-memory-events.d.ts.map +1 -0
  45. package/dist/agent-runtime/store-memory.d.ts +1 -1
  46. package/dist/agent-runtime/store-memory.d.ts.map +1 -1
  47. package/dist/agent-runtime/store-purge.d.ts +2 -0
  48. package/dist/agent-runtime/store-purge.d.ts.map +1 -1
  49. package/dist/agent-runtime/store-reduce-run.d.ts +2 -2
  50. package/dist/agent-runtime/store-reduce-run.d.ts.map +1 -1
  51. package/dist/agent-runtime/store-reduce-shared.d.ts +1 -0
  52. package/dist/agent-runtime/store-reduce-shared.d.ts.map +1 -1
  53. package/dist/agent-runtime/store-reducer.d.ts +2 -2
  54. package/dist/agent-runtime/store-reducer.d.ts.map +1 -1
  55. package/dist/agent-runtime/store-replay.d.ts +4 -0
  56. package/dist/agent-runtime/store-replay.d.ts.map +1 -0
  57. package/dist/agent-runtime/store.d.ts +3 -0
  58. package/dist/agent-runtime/store.d.ts.map +1 -1
  59. package/dist/durability/events.d.ts +4 -0
  60. package/dist/durability/events.d.ts.map +1 -1
  61. package/dist/entrypoints/agent-runtime/coding-tools.js +1 -2
  62. package/dist/entrypoints/agent-runtime/harness.js +4 -3
  63. package/dist/entrypoints/agent-runtime/sandbox.js +3 -5
  64. package/dist/entrypoints/agent-runtime/sqlite/bun.js +13 -3
  65. package/dist/entrypoints/agent-runtime/sqlite/node.js +13 -3
  66. package/dist/entrypoints/agent-runtime/testing.js +89 -5
  67. package/dist/entrypoints/agent-runtime.d.ts +2 -1
  68. package/dist/entrypoints/agent-runtime.d.ts.map +1 -1
  69. package/dist/entrypoints/agent-runtime.js +27 -15
  70. package/dist/entrypoints/tools.js +5 -3
  71. package/dist/{index-0gs4ymfj.js → index-4fgg1507.js} +31 -2
  72. package/dist/{index-c17xcx4k.js → index-56yx8dxp.js} +19 -96
  73. package/dist/{index-8rstkvcf.js → index-7c046kxr.js} +1 -1
  74. package/dist/{index-tjdsyrys.js → index-bm180f2d.js} +203 -83
  75. package/dist/{index-924500c3.js → index-ddkwkcvn.js} +2 -2
  76. package/dist/{index-c0hffcmj.js → index-dgfah0g8.js} +21 -9
  77. package/dist/index-j1cwtyk1.js +2269 -0
  78. package/dist/index-snef22zr.js +94 -0
  79. package/dist/{index-5p55xreg.js → index-w619fqvv.js} +67 -29
  80. package/llms/agent-runtime.3.txt +4 -0
  81. package/llms/agent-runtime.txt +75 -0
  82. package/llms-full.txt +79 -0
  83. package/llms.txt +1 -1
  84. package/package.json +1 -1
  85. package/dist/index-8ks9b85e.js +0 -129
  86. package/dist/index-d2h99n7d.js +0 -1071
@@ -0,0 +1,2269 @@
1
+ import {
2
+ AgentCodingToolAuthorizationSchema2,
3
+ ShellOutputSchema,
4
+ AgentSandboxRestrictionSchema2,
5
+ AgentSandboxGradeSchema2,
6
+ AGENT_CODING_TOOL_NAMES
7
+ } from "./index-bm180f2d.js";
8
+ import {
9
+ AgentRecordIdSchema2,
10
+ AgentRecordVersionSchema2,
11
+ AgentTimestampSchema2,
12
+ AgentMessageSchema2,
13
+ runStateForTerminalReason2,
14
+ AgentRunSchema2,
15
+ AgentSnapshotSchema2
16
+ } from "./index-8kzct1d6.js";
17
+ import {
18
+ AppendAgentStoreEventSchema2,
19
+ ReadAgentStoreEventsSchema2,
20
+ AgentStoreEventPageSchema2,
21
+ AgentRunViewSchema2,
22
+ AcceptInputAndAssignRunSchema2,
23
+ AcquireAgentRunSchema2,
24
+ CheckpointRunAssistantSchema2,
25
+ RecordRunOperationSchema2,
26
+ CommitRunTerminalSchema2,
27
+ RequestRunInterruptSchema2,
28
+ RecoverAgentRunSchema2,
29
+ ReplaceCompactedRangeSchema2,
30
+ SeedConversationInputSchema2,
31
+ AgentRecoverablePageSchema2,
32
+ AgentStoreTransitionSchema2,
33
+ AgentStoreCheckpointRecordSchema,
34
+ agentStoreEventDraft,
35
+ canonicalAgentJson2,
36
+ encodeAgentConversationArchive2,
37
+ decodeAgentConversationArchive2,
38
+ assistantStatus
39
+ } from "./index-4fgg1507.js";
40
+ import {
41
+ StepEventPayloadSchema,
42
+ ParkEventPayloadSchema,
43
+ DeliveredEventPayloadSchema,
44
+ EffectEventPayloadSchema
45
+ } from "./index-snef22zr.js";
46
+
47
+ // src/agent-runtime/branch-contract.ts
48
+ import { z } from "zod";
49
+ var Hash = z.string().regex(/^[a-f0-9]{64}$/);
50
+ var BranchAgentConversationInputSchema2 = z.object({
51
+ requestKey: AgentRecordIdSchema2,
52
+ destinationConversationId: AgentRecordIdSchema2,
53
+ source: z.discriminatedUnion("kind", [
54
+ z.object({
55
+ kind: z.literal("conversation"),
56
+ conversationId: AgentRecordIdSchema2,
57
+ throughRunId: AgentRecordIdSchema2.optional()
58
+ }).strict(),
59
+ z.object({
60
+ kind: z.literal("archive"),
61
+ bytes: z.custom((value) => value instanceof Uint8Array)
62
+ }).strict()
63
+ ]),
64
+ artifactAuthorization: AgentCodingToolAuthorizationSchema2.optional()
65
+ }).strict();
66
+ var AgentConversationBranchReceiptSchema2 = z.object({
67
+ schemaVersion: z.literal(1),
68
+ requestKey: AgentRecordIdSchema2,
69
+ fingerprint: Hash,
70
+ conversationId: AgentRecordIdSchema2,
71
+ sourceConversationId: AgentRecordIdSchema2,
72
+ sourceKind: z.enum(["conversation", "archive"]),
73
+ throughRunId: AgentRecordIdSchema2.optional(),
74
+ sourceSeq: z.int().nonnegative(),
75
+ sourceVersion: z.int().nonnegative(),
76
+ sourceDigest: Hash,
77
+ createdAt: AgentTimestampSchema2
78
+ }).strict();
79
+ var AgentConversationBranchResultSchema2 = z.object({
80
+ outcome: z.enum(["created", "duplicate"]),
81
+ receipt: AgentConversationBranchReceiptSchema2
82
+ }).strict();
83
+
84
+ // src/agent-runtime/purge.ts
85
+ import { z as z2 } from "zod";
86
+ var AgentConversationPurgeInputSchema2 = z2.strictObject({
87
+ conversationId: AgentRecordIdSchema2,
88
+ expectedVersion: AgentRecordVersionSchema2.optional()
89
+ });
90
+ var AgentConversationPurgeResultSchema2 = z2.discriminatedUnion("outcome", [
91
+ z2.strictObject({ outcome: z2.literal("purged") }),
92
+ z2.strictObject({ outcome: z2.literal("already_purged") }),
93
+ z2.strictObject({ outcome: z2.literal("unsupported") }),
94
+ z2.strictObject({
95
+ outcome: z2.literal("active"),
96
+ runIds: z2.array(AgentRecordIdSchema2).min(1)
97
+ }),
98
+ z2.strictObject({ outcome: z2.literal("conflict"), actualVersion: AgentRecordVersionSchema2 })
99
+ ]);
100
+
101
+ class AgentConversationPurgedError2 extends Error {
102
+ constructor() {
103
+ super("Agent conversation has been purged; use a new conversation ID");
104
+ this.name = "AgentConversationPurgedError";
105
+ }
106
+ }
107
+ async function purgeAgentConversation2(store, input) {
108
+ const parsed = AgentConversationPurgeInputSchema2.parse(input);
109
+ if (!store.purgeConversation)
110
+ return { outcome: "unsupported" };
111
+ return AgentConversationPurgeResultSchema2.parse(await store.purgeConversation(parsed));
112
+ }
113
+
114
+ // src/agent-runtime/store-driver-contract.ts
115
+ import { z as z3 } from "zod";
116
+ var AgentRuntimeHeadSchema2 = z3.object({
117
+ schemaVersion: z3.literal(1),
118
+ conversationId: AgentRecordIdSchema2,
119
+ version: AgentRecordVersionSchema2
120
+ });
121
+ var AgentStoredRunSchema2 = z3.object({
122
+ schemaVersion: z3.literal(1),
123
+ run: AgentRunSchema2,
124
+ terminalAssistant: AgentMessageSchema2.optional()
125
+ });
126
+ var AgentAdmissionReceiptSchema2 = z3.object({
127
+ schemaVersion: z3.literal(1),
128
+ conversationId: AgentRecordIdSchema2,
129
+ idempotencyKey: z3.string().min(1),
130
+ input: AgentMessageSchema2,
131
+ runId: AgentRecordIdSchema2,
132
+ assistantMessageId: AgentRecordIdSchema2
133
+ });
134
+ var AgentSeedReceiptSchema2 = z3.object({
135
+ schemaVersion: z3.literal(1),
136
+ conversationId: AgentRecordIdSchema2,
137
+ seedKey: z3.string().min(1),
138
+ messageIds: z3.array(AgentRecordIdSchema2).min(1)
139
+ });
140
+ var AgentHistoryMutationSchema2 = z3.discriminatedUnion("type", [
141
+ z3.object({ type: z3.literal("admit"), input: AgentMessageSchema2 }),
142
+ z3.object({
143
+ type: z3.literal("upsert-assistant"),
144
+ message: AgentMessageSchema2
145
+ }),
146
+ z3.object({
147
+ type: z3.literal("replace-compacted-range"),
148
+ replacedMessageIds: z3.array(AgentRecordIdSchema2).min(1),
149
+ summary: AgentMessageSchema2
150
+ }),
151
+ z3.object({
152
+ type: z3.literal("seed"),
153
+ message: AgentMessageSchema2
154
+ })
155
+ ]);
156
+ var ACTIVE_AGENT_RUN_STATES2 = [
157
+ "queued",
158
+ "running",
159
+ "interrupt_requested"
160
+ ];
161
+ function isActiveRunState(state) {
162
+ return ACTIVE_AGENT_RUN_STATES2.includes(state);
163
+ }
164
+ function emptyHead(conversationId) {
165
+ return AgentRuntimeHeadSchema2.parse({
166
+ schemaVersion: 1,
167
+ conversationId,
168
+ version: 0
169
+ });
170
+ }
171
+
172
+ // src/agent-runtime/store-create.ts
173
+ import { z as z16 } from "zod";
174
+
175
+ // src/agent-runtime/store-archive.ts
176
+ import { z as z5 } from "zod";
177
+
178
+ // src/agent-runtime/branch-replay.ts
179
+ import { createHash as createHash2 } from "node:crypto";
180
+ import { z as z4 } from "zod";
181
+
182
+ // src/agent-runtime/store-reducer.ts
183
+ import { createHash } from "node:crypto";
184
+
185
+ // src/agent-runtime/store-snapshot.ts
186
+ function historyPositions(messages) {
187
+ const positions = new Map;
188
+ messages.forEach((message, index) => {
189
+ if (!positions.has(message.id))
190
+ positions.set(message.id, index);
191
+ });
192
+ return (run) => {
193
+ let earliest = Number.MAX_SAFE_INTEGER;
194
+ for (const id of [...run.inputMessageIds, run.assistantMessageId]) {
195
+ const position = positions.get(id);
196
+ if (position !== undefined && position < earliest)
197
+ earliest = position;
198
+ }
199
+ return earliest;
200
+ };
201
+ }
202
+ function orderRuns(messages, runs) {
203
+ const positionOf = historyPositions(messages);
204
+ const fallback = (left, right) => left.createdAt.localeCompare(right.createdAt) || positionOf(left) - positionOf(right) || left.id.localeCompare(right.id);
205
+ return [...runs].sort((left, right) => {
206
+ if (left.executionSequence !== undefined && right.executionSequence !== undefined && left.executionSequence !== right.executionSequence) {
207
+ return left.executionSequence - right.executionSequence;
208
+ }
209
+ const leftStarted = left.executionSequence !== undefined || left.state !== "queued";
210
+ const rightStarted = right.executionSequence !== undefined || right.state !== "queued";
211
+ if (leftStarted !== rightStarted)
212
+ return leftStarted ? -1 : 1;
213
+ if (!leftStarted && !rightStarted) {
214
+ const priority = Number(right.queuePriority !== undefined) - Number(left.queuePriority !== undefined);
215
+ if (priority !== 0)
216
+ return priority;
217
+ }
218
+ return fallback(left, right);
219
+ });
220
+ }
221
+ function orderRunMessages(messages, runs) {
222
+ const byId = new Map(messages.map((message) => [message.id, message]));
223
+ const ownedIds = new Set(runs.flatMap((run) => [...run.inputMessageIds, run.assistantMessageId]));
224
+ const orderedOwned = runs.flatMap((run) => [...run.inputMessageIds, run.assistantMessageId].flatMap((id) => {
225
+ const message = byId.get(id);
226
+ return message ? [message] : [];
227
+ }));
228
+ let ownedIndex = 0;
229
+ return messages.map((message) => {
230
+ if (!ownedIds.has(message.id))
231
+ return message;
232
+ const ordered = orderedOwned[ownedIndex];
233
+ if (!ordered)
234
+ throw new TypeError("Stored agent history lost a run-owned message");
235
+ ownedIndex += 1;
236
+ return ordered;
237
+ });
238
+ }
239
+ function snapshotOf(head, messages, records) {
240
+ validateSnapshot(head, messages, records);
241
+ const runs = orderRuns(messages, records.map((record) => record.run));
242
+ return AgentSnapshotSchema2.parse({
243
+ schemaVersion: 1,
244
+ conversationId: head.conversationId,
245
+ version: head.version,
246
+ messages: orderRunMessages(messages, runs),
247
+ runs
248
+ });
249
+ }
250
+ function validateSnapshot(head, messages, records) {
251
+ const runIds = new Set;
252
+ const assistantIds = new Set;
253
+ const messageIds = new Set;
254
+ for (const record of records) {
255
+ const run = record.run;
256
+ if (run.conversationId !== head.conversationId || runIds.has(run.id) || assistantIds.has(run.assistantMessageId)) {
257
+ throw new TypeError("Stored agent runs contain inconsistent identities");
258
+ }
259
+ if (record.terminalAssistant && (record.terminalAssistant.id !== run.assistantMessageId || record.terminalAssistant.conversationId !== run.conversationId || record.terminalAssistant.runId !== run.id || record.terminalAssistant.role !== "assistant" || run.terminalReason === undefined)) {
260
+ throw new TypeError("Retained terminal assistant does not match its run");
261
+ }
262
+ runIds.add(run.id);
263
+ assistantIds.add(run.assistantMessageId);
264
+ }
265
+ for (const message of messages) {
266
+ if (message.conversationId !== head.conversationId || messageIds.has(message.id)) {
267
+ throw new TypeError("Stored agent history contains inconsistent message identities");
268
+ }
269
+ messageIds.add(message.id);
270
+ if (assistantIds.has(message.id) && message.runId === undefined) {
271
+ throw new TypeError("Stored history occupies a reserved assistant identity");
272
+ }
273
+ if (message.runId !== undefined) {
274
+ const run = records.find((candidate) => candidate.run.id === message.runId)?.run;
275
+ if (!run || message.role !== "assistant" || run.assistantMessageId !== message.id) {
276
+ throw new TypeError("Stored assistant history does not match its reserved run identity");
277
+ }
278
+ }
279
+ }
280
+ }
281
+ function replaceRun(runs, next) {
282
+ return runs.map((run) => run.id === next.id ? next : run);
283
+ }
284
+ function replaceMessage(messages, next) {
285
+ return messages.some((message) => message.id === next.id) ? messages.map((message) => message.id === next.id ? next : message) : [...messages, next];
286
+ }
287
+ function mergeRunRecords(...groups) {
288
+ const records = new Map;
289
+ for (const group of groups) {
290
+ for (const rawRecord of group) {
291
+ const record = AgentStoredRunSchema2.parse(rawRecord);
292
+ const previous = records.get(record.run.id);
293
+ if (previous && previous.run.assistantMessageId !== record.run.assistantMessageId) {
294
+ throw new TypeError("Stored agent run identity changed across normalized records");
295
+ }
296
+ records.set(record.run.id, record);
297
+ }
298
+ }
299
+ return [...records.values()];
300
+ }
301
+ function referencedRunIds(messages) {
302
+ return [...new Set(messages.flatMap((message) => message.runId ? [message.runId] : []))];
303
+ }
304
+
305
+ // src/agent-runtime/store-reduce-shared.ts
306
+ function conflict(actualVersion) {
307
+ return { outcome: "conflict", actualVersion };
308
+ }
309
+ function applied(current, input, effects) {
310
+ const runs = input.runs ?? current.runs;
311
+ const messages = input.messages ?? current.messages;
312
+ return {
313
+ outcome: "applied",
314
+ snapshot: AgentSnapshotSchema2.parse({
315
+ ...current,
316
+ version: current.version + 1,
317
+ runs,
318
+ messages: orderRunMessages(messages, runs)
319
+ }),
320
+ ...effects?.runRecords?.length && { runRecords: effects.runRecords },
321
+ ...effects?.admissionReceipt && { admissionReceipt: effects.admissionReceipt },
322
+ ...effects?.seedReceipt && { seedReceipt: effects.seedReceipt },
323
+ ...effects?.historyMutations?.length && { historyMutations: effects.historyMutations }
324
+ };
325
+ }
326
+
327
+ // src/agent-runtime/store-reduce-history.ts
328
+ function reduceAccept(current, input) {
329
+ if (input.expectedVersion !== undefined && input.expectedVersion !== current.version) {
330
+ return conflict(current.version);
331
+ }
332
+ const coalescedRun = input.coalesceIntoRunId ? current.runs.find((candidate) => candidate.id === input.coalesceIntoRunId) : undefined;
333
+ if (input.coalesceIntoRunId !== undefined && (!coalescedRun || coalescedRun.conversationId !== input.input.conversationId || coalescedRun.state !== "queued" || coalescedRun.ownerId !== undefined || coalescedRun.terminalReason !== undefined)) {
334
+ return coalescedRun ? conflict(coalescedRun.revision) : { outcome: "not_found" };
335
+ }
336
+ if (input.run.conversationId !== input.input.conversationId || input.run.inputMessageIds.length !== 1 || input.run.inputMessageIds[0] !== input.input.id || input.run.state !== "queued" || input.run.revision !== 0 || input.run.executionSequence !== undefined || input.run.ownerId !== undefined || input.run.terminalReason !== undefined || input.run.terminalPolicyName !== undefined || input.input.role !== "user" && input.input.role !== "tool" || input.input.status !== "committed" || input.input.runId !== undefined || current.messages.some((message) => message.id === input.input.id) || coalescedRun !== undefined && input.input.id === coalescedRun.assistantMessageId || !coalescedRun && (input.run.assistantMessageId === input.input.id || current.runs.some((candidate) => candidate.id === input.run.id) || current.runs.some((candidate) => candidate.assistantMessageId === input.run.assistantMessageId) || current.messages.some((message) => message.id === input.run.assistantMessageId))) {
337
+ throw new TypeError("Input and queued run do not form one valid assignment");
338
+ }
339
+ const assignedRun = coalescedRun ? AgentRunSchema2.parse({
340
+ ...coalescedRun,
341
+ inputMessageIds: [...coalescedRun.inputMessageIds, input.input.id],
342
+ revision: coalescedRun.revision + 1,
343
+ updatedAt: new Date().toISOString()
344
+ }) : input.run;
345
+ const admissionReceipt = AgentAdmissionReceiptSchema2.parse({
346
+ schemaVersion: 1,
347
+ conversationId: input.input.conversationId,
348
+ idempotencyKey: input.idempotencyKey,
349
+ input: input.input,
350
+ runId: assignedRun.id,
351
+ assistantMessageId: assignedRun.assistantMessageId
352
+ });
353
+ return applied(current, {
354
+ messages: [...current.messages, input.input],
355
+ runs: coalescedRun ? replaceRun(current.runs, assignedRun) : [...current.runs, assignedRun]
356
+ }, {
357
+ runRecords: [AgentStoredRunSchema2.parse({ schemaVersion: 1, run: assignedRun })],
358
+ admissionReceipt,
359
+ historyMutations: [{ type: "admit", input: input.input }]
360
+ });
361
+ }
362
+ function reduceSeed(current, input) {
363
+ const conversationId = input.conversationId;
364
+ if (current.conversationId !== conversationId)
365
+ return { outcome: "not_found" };
366
+ for (const message of input.inputs) {
367
+ if (message.conversationId !== conversationId || message.role !== "user" || message.status !== "committed" || message.runId !== undefined) {
368
+ throw new TypeError("Seeded conversation input must be committed unowned user messages");
369
+ }
370
+ }
371
+ return applied(current, { messages: [...input.inputs, ...current.messages] }, {
372
+ seedReceipt: AgentSeedReceiptSchema2.parse({
373
+ schemaVersion: 1,
374
+ conversationId,
375
+ seedKey: input.seedKey,
376
+ messageIds: input.inputs.map((message) => message.id)
377
+ }),
378
+ historyMutations: [...input.inputs].reverse().map((message) => ({
379
+ type: "seed",
380
+ message
381
+ }))
382
+ });
383
+ }
384
+ function reduceCompact(current, input) {
385
+ if (current.conversationId !== input.conversationId)
386
+ return { outcome: "not_found" };
387
+ if (current.version !== input.expectedVersion)
388
+ return conflict(current.version);
389
+ const replaced = new Set(input.replacedMessageIds);
390
+ if (!input.replacedMessageIds.every((id) => current.messages.some((m) => m.id === id))) {
391
+ return { outcome: "not_found" };
392
+ }
393
+ const liveAssistant = current.runs.find((candidate) => isActiveRunState(candidate.state) && replaced.has(candidate.assistantMessageId));
394
+ if (liveAssistant) {
395
+ throw new TypeError(`Compaction may not replace the assistant message of run ${liveAssistant.id}, which has not finished`);
396
+ }
397
+ const positions = current.messages.map((message, index) => replaced.has(message.id) ? index : undefined).filter((index) => index !== undefined);
398
+ const first = positions[0];
399
+ if (first === undefined || positions.some((position, offset) => position !== first + offset) || input.summary.conversationId !== input.conversationId || input.summary.runId !== undefined || input.summary.role !== "summary" || input.summary.status !== "committed" || current.messages.some((message) => message.id === input.summary.id) || current.runs.some((candidate) => candidate.assistantMessageId === input.summary.id)) {
400
+ throw new TypeError("Compaction replacement must be one valid contiguous history range");
401
+ }
402
+ const messages = [
403
+ ...current.messages.slice(0, first),
404
+ input.summary,
405
+ ...current.messages.slice(first + positions.length)
406
+ ];
407
+ return applied(current, { messages }, {
408
+ historyMutations: [
409
+ {
410
+ type: "replace-compacted-range",
411
+ replacedMessageIds: input.replacedMessageIds,
412
+ summary: input.summary
413
+ }
414
+ ]
415
+ });
416
+ }
417
+
418
+ // src/agent-runtime/store-reduce-run.ts
419
+ function reduceAcquire(current, run, input) {
420
+ const runPosition = current.runs.findIndex((candidate) => candidate.id === run.id);
421
+ const acquisitionBlocked = current.runs.some((candidate, index) => candidate.id !== run.id && (candidate.state === "running" || candidate.state === "interrupt_requested" || index < runPosition && candidate.state === "queued"));
422
+ if (run.revision !== input.expectedRevision || run.state !== "queued" || acquisitionBlocked) {
423
+ return conflict(run.revision);
424
+ }
425
+ const next = AgentRunSchema2.parse({
426
+ ...run,
427
+ state: "running",
428
+ executionSequence: run.executionSequence ?? current.version + 1,
429
+ ownerId: input.ownerId,
430
+ fencingToken: (run.fencingToken ?? 0) + 1,
431
+ revision: run.revision + 1,
432
+ updatedAt: new Date().toISOString()
433
+ });
434
+ return applied(current, { runs: replaceRun(current.runs, next) }, {
435
+ runRecords: [AgentStoredRunSchema2.parse({ schemaVersion: 1, run: next })]
436
+ });
437
+ }
438
+ function reduceCheckpoint(current, run, input) {
439
+ if (run.revision !== input.expectedRevision || run.state !== "running" || run.ownerId !== input.ownerId || input.fencingToken !== undefined && run.fencingToken !== input.fencingToken || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== "streaming") {
440
+ return conflict(run.revision);
441
+ }
442
+ const next = AgentRunSchema2.parse({
443
+ ...run,
444
+ ...input.usage && { usage: input.usage },
445
+ revision: run.revision + 1,
446
+ updatedAt: new Date().toISOString()
447
+ });
448
+ return applied(current, {
449
+ runs: replaceRun(current.runs, next),
450
+ messages: replaceMessage(current.messages, input.assistant)
451
+ }, {
452
+ runRecords: [AgentStoredRunSchema2.parse({ schemaVersion: 1, run: next })],
453
+ historyMutations: [{ type: "upsert-assistant", message: input.assistant }]
454
+ });
455
+ }
456
+ function reduceOperation(current, run, input) {
457
+ if (run.revision !== input.expectedRevision || run.state !== "running" && run.state !== "interrupt_requested" || run.ownerId !== input.ownerId || input.fencingToken !== undefined && run.fencingToken !== input.fencingToken) {
458
+ return conflict(run.revision);
459
+ }
460
+ const next = AgentRunSchema2.parse({
461
+ ...run,
462
+ lastOperation: input.operation,
463
+ revision: run.revision + 1,
464
+ updatedAt: new Date().toISOString()
465
+ });
466
+ return applied(current, { runs: replaceRun(current.runs, next) }, { runRecords: [AgentStoredRunSchema2.parse({ schemaVersion: 1, run: next })] });
467
+ }
468
+ function reduceInterrupt(current, run, input) {
469
+ if (run.revision !== input.expectedRevision || run.state !== "running") {
470
+ return conflict(run.revision);
471
+ }
472
+ const next = AgentRunSchema2.parse({
473
+ ...run,
474
+ state: "interrupt_requested",
475
+ revision: run.revision + 1,
476
+ updatedAt: new Date().toISOString()
477
+ });
478
+ return applied(current, { runs: replaceRun(current.runs, next) }, {
479
+ runRecords: [AgentStoredRunSchema2.parse({ schemaVersion: 1, run: next })]
480
+ });
481
+ }
482
+ function reduceRecover(current, run, input, retainedAssistant) {
483
+ if (run.revision !== input.expectedRevision || !isActiveRunState(run.state)) {
484
+ return conflict(run.revision);
485
+ }
486
+ if (input.action === "requeue" && run.state !== "queued" && input.replaySafe !== true) {
487
+ throw new TypeError("Recovering an acquired run requires explicit replaySafe evidence");
488
+ }
489
+ const { ownerId: _released, ...carried } = run;
490
+ const next = AgentRunSchema2.parse({
491
+ ...carried,
492
+ state: input.action === "requeue" ? "queued" : "abandoned",
493
+ revision: run.revision + 1,
494
+ ...input.action === "abandon" && { terminalReason: "abandoned" },
495
+ updatedAt: new Date().toISOString()
496
+ });
497
+ if (input.action === "abandon") {
498
+ if (retainedAssistant && (retainedAssistant.id !== run.assistantMessageId || retainedAssistant.conversationId !== run.conversationId || retainedAssistant.runId !== run.id || retainedAssistant.role !== "assistant" || retainedAssistant.status !== "failed"))
499
+ throw new TypeError("Abandoned assistant does not match its run");
500
+ const existingAssistant = current.messages.find((message) => message.id === run.assistantMessageId);
501
+ const assistant = retainedAssistant ?? AgentMessageSchema2.parse({
502
+ ...existingAssistant ?? {
503
+ schemaVersion: 1,
504
+ id: run.assistantMessageId,
505
+ conversationId: run.conversationId,
506
+ runId: run.id,
507
+ role: "assistant",
508
+ parts: [],
509
+ createdAt: run.createdAt
510
+ },
511
+ status: "failed",
512
+ updatedAt: new Date().toISOString()
513
+ });
514
+ return applied(current, {
515
+ runs: replaceRun(current.runs, next),
516
+ messages: replaceMessage(current.messages, assistant)
517
+ }, {
518
+ runRecords: [
519
+ AgentStoredRunSchema2.parse({
520
+ schemaVersion: 1,
521
+ run: next,
522
+ terminalAssistant: assistant
523
+ })
524
+ ],
525
+ historyMutations: [{ type: "upsert-assistant", message: assistant }]
526
+ });
527
+ }
528
+ return applied(current, { runs: replaceRun(current.runs, next) }, {
529
+ runRecords: [AgentStoredRunSchema2.parse({ schemaVersion: 1, run: next })]
530
+ });
531
+ }
532
+
533
+ // src/agent-runtime/store-reduce-terminal.ts
534
+ function reduceTerminal(current, run, input) {
535
+ if (input.reason === "absorbed") {
536
+ throw new TypeError("A run is absorbed by another run, never terminalized as absorbed");
537
+ }
538
+ if (run.revision !== input.expectedRevision || run.state !== "running" && run.state !== "interrupt_requested" || run.ownerId !== input.ownerId || input.fencingToken !== undefined && run.fencingToken !== input.fencingToken || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== assistantStatus(input.reason)) {
539
+ return conflict(run.revision);
540
+ }
541
+ if (input.absorb && runStateForTerminalReason2(input.reason) !== "completed") {
542
+ throw new TypeError("Only a completing run may absorb a queued successor");
543
+ }
544
+ const named = new Set((input.absorb ?? []).map((entry) => entry.runId));
545
+ if (named.size !== (input.absorb ?? []).length || named.has(run.id)) {
546
+ throw new TypeError("An absorption names each successor once, and never the absorbing run");
547
+ }
548
+ const absorbable = (input.absorb ?? []).filter((entry) => {
549
+ const candidate = current.runs.find((item) => item.id === entry.runId);
550
+ return candidate !== undefined && candidate.id !== run.id && candidate.state === "queued" && candidate.conversationId === run.conversationId && candidate.inputMessageIds.length === entry.inputMessageIds.length && candidate.inputMessageIds.every((id, index) => id === entry.inputMessageIds[index]);
551
+ });
552
+ const absorbedIds = new Set(absorbable.flatMap((entry) => entry.inputMessageIds));
553
+ const next = AgentRunSchema2.parse({
554
+ ...run,
555
+ state: runStateForTerminalReason2(input.reason),
556
+ terminalReason: input.reason,
557
+ ...input.policyName && { terminalPolicyName: input.policyName },
558
+ ...input.usage && { usage: input.usage },
559
+ ...absorbedIds.size > 0 && {
560
+ inputMessageIds: [
561
+ ...run.inputMessageIds,
562
+ ...[...absorbedIds].filter((id) => !run.inputMessageIds.includes(id))
563
+ ]
564
+ },
565
+ revision: run.revision + 1,
566
+ updatedAt: new Date().toISOString()
567
+ });
568
+ const absorbedRuns = absorbable.map((entry) => {
569
+ const candidate = current.runs.find((item) => item.id === entry.runId);
570
+ if (!candidate)
571
+ throw new TypeError("Absorbed run disappeared inside the reducer");
572
+ return AgentRunSchema2.parse({
573
+ ...candidate,
574
+ state: runStateForTerminalReason2("absorbed"),
575
+ terminalReason: "absorbed",
576
+ absorbedIntoRunId: next.id,
577
+ revision: candidate.revision + 1,
578
+ updatedAt: new Date().toISOString()
579
+ });
580
+ });
581
+ const runs = absorbedRuns.reduce((accumulated, absorbed) => replaceRun(accumulated, absorbed), replaceRun(current.runs, next));
582
+ return applied(current, {
583
+ runs,
584
+ messages: replaceMessage(current.messages, input.assistant)
585
+ }, {
586
+ runRecords: [
587
+ AgentStoredRunSchema2.parse({
588
+ schemaVersion: 1,
589
+ run: next,
590
+ terminalAssistant: input.assistant
591
+ }),
592
+ ...absorbedRuns.map((absorbed) => AgentStoredRunSchema2.parse({ schemaVersion: 1, run: absorbed }))
593
+ ],
594
+ historyMutations: [{ type: "upsert-assistant", message: input.assistant }]
595
+ });
596
+ }
597
+
598
+ // src/agent-runtime/store-reducer.ts
599
+ function transitionRecord(operation, reduced) {
600
+ if (operation.type === "recover" && operation.input.action === "abandon") {
601
+ const assistant = reduced?.runRecords?.find((record) => record.run.id === operation.input.runId)?.terminalAssistant;
602
+ if (assistant)
603
+ return { ...operation, assistant };
604
+ }
605
+ if (operation.type !== "checkpoint")
606
+ return operation;
607
+ const { assistant, ...rest } = operation.input;
608
+ const body = canonicalAgentJson2(assistant);
609
+ return AgentStoreCheckpointRecordSchema.parse({
610
+ type: operation.type,
611
+ input: {
612
+ ...rest,
613
+ assistant: {
614
+ id: assistant.id,
615
+ status: assistant.status,
616
+ sha256: createHash("sha256").update(body).digest("hex"),
617
+ parts: assistant.parts.length,
618
+ bytes: Buffer.byteLength(body)
619
+ }
620
+ }
621
+ });
622
+ }
623
+ function reduceStore(current, operation) {
624
+ if (operation.type === "accept")
625
+ return reduceAccept(current, operation.input);
626
+ if (operation.type === "seed")
627
+ return reduceSeed(current, operation.input);
628
+ if (operation.type === "compact")
629
+ return reduceCompact(current, operation.input);
630
+ const run = current.runs.find((candidate) => candidate.id === operation.input.runId);
631
+ if (!run)
632
+ return { outcome: "not_found" };
633
+ switch (operation.type) {
634
+ case "acquire":
635
+ return reduceAcquire(current, run, operation.input);
636
+ case "checkpoint":
637
+ return reduceCheckpoint(current, run, operation.input);
638
+ case "operation":
639
+ return reduceOperation(current, run, operation.input);
640
+ case "interrupt":
641
+ return reduceInterrupt(current, run, operation.input);
642
+ case "recover":
643
+ return reduceRecover(current, run, operation.input, operation.assistant);
644
+ case "terminal":
645
+ return reduceTerminal(current, run, operation.input);
646
+ }
647
+ }
648
+ function operationConversationId(operation) {
649
+ return operation.type === "accept" ? operation.input.input.conversationId : operation.input.conversationId;
650
+ }
651
+
652
+ // src/agent-runtime/branch-replay.ts
653
+ var BranchBaselineSchema = z4.object({
654
+ migratedAt: z4.iso.datetime({ offset: true }),
655
+ asOf: z4.iso.datetime({ offset: true }).optional(),
656
+ head: AgentRuntimeHeadSchema2,
657
+ messages: z4.array(AgentMessageSchema2),
658
+ compacted: z4.array(z4.string().min(1)).optional(),
659
+ runs: z4.array(AgentStoredRunSchema2),
660
+ admissions: z4.array(AgentAdmissionReceiptSchema2)
661
+ }).strict();
662
+ function branchTransition(event) {
663
+ const checkpoint = AgentStoreCheckpointRecordSchema.safeParse(event.payload);
664
+ if (!checkpoint.success)
665
+ return AgentStoreTransitionSchema2.parse(event.payload);
666
+ const input = checkpoint.data.input;
667
+ if (canonicalAgentJson2(checkpoint.data) !== canonicalAgentJson2(event.payload))
668
+ throw new TypeError("Branch checkpoint contains an unknown opaque field");
669
+ return AgentStoreTransitionSchema2.parse({
670
+ type: "checkpoint",
671
+ input: {
672
+ ...input,
673
+ assistant: {
674
+ schemaVersion: 1,
675
+ id: input.assistant.id,
676
+ conversationId: input.conversationId,
677
+ runId: input.runId,
678
+ role: "assistant",
679
+ status: "streaming",
680
+ parts: [],
681
+ createdAt: event.occurredAt,
682
+ updatedAt: event.occurredAt
683
+ }
684
+ }
685
+ });
686
+ }
687
+ function replayBranchEvents(conversationId, events, retainedSnapshot) {
688
+ const result = {
689
+ snapshot: AgentSnapshotSchema2.parse({
690
+ schemaVersion: 1,
691
+ conversationId,
692
+ version: 0,
693
+ messages: [],
694
+ runs: []
695
+ }),
696
+ effects: [],
697
+ unresolvedDrafts: new Set,
698
+ retainedMessages: new Map
699
+ };
700
+ for (const event of events) {
701
+ if (event.kind === "runtime/baseline") {
702
+ if (event.seq !== 1 || result.snapshot.version !== 0)
703
+ throw new TypeError("Branch baseline must be the first event");
704
+ const baseline = BranchBaselineSchema.parse(event.payload);
705
+ if (baseline.head.conversationId !== conversationId)
706
+ throw new TypeError("Branch baseline has a foreign conversation");
707
+ const compacted = new Set(baseline.compacted);
708
+ snapshotOf(baseline.head, baseline.messages, baseline.runs);
709
+ const keys = new Set;
710
+ for (const record of baseline.runs) {
711
+ if (record.run.inputMessageIds.some((id) => !baseline.messages.some((message) => message.id === id)))
712
+ throw new TypeError("Branch baseline run references missing input");
713
+ }
714
+ for (const admission of baseline.admissions) {
715
+ const run = baseline.runs.find((record) => record.run.id === admission.runId)?.run;
716
+ if (keys.has(admission.idempotencyKey) || admission.conversationId !== conversationId || admission.input.conversationId !== conversationId || !run || !run.inputMessageIds.includes(admission.input.id) || run.assistantMessageId !== admission.assistantMessageId || !baseline.messages.some((message) => message.id === admission.input.id && canonicalAgentJson2(message) === canonicalAgentJson2(admission.input)))
717
+ throw new TypeError("Branch baseline admission is inconsistent");
718
+ keys.add(admission.idempotencyKey);
719
+ }
720
+ if (compacted.size !== (baseline.compacted?.length ?? 0) || [...compacted].some((id) => !baseline.messages.some((m) => m.id === id)))
721
+ throw new TypeError("Branch baseline compacted identities are inconsistent");
722
+ result.snapshot = snapshotOf(baseline.head, baseline.messages.filter((m) => !compacted.has(m.id)), baseline.runs);
723
+ result.baseline = baseline;
724
+ continue;
725
+ }
726
+ if (event.kind === "runtime/branched") {
727
+ const receipt = AgentConversationBranchReceiptSchema2.parse(event.payload);
728
+ if (receipt.conversationId !== conversationId)
729
+ throw new TypeError("Branch receipt has a foreign conversation");
730
+ result.snapshot = {
731
+ ...result.snapshot,
732
+ runs: result.snapshot.runs.map(({ ownerId: _owner, fencingToken: _fence, ...run }) => run)
733
+ };
734
+ continue;
735
+ }
736
+ if (event.kind !== "runtime/transition")
737
+ continue;
738
+ let operation = branchTransition(event);
739
+ if (operation.type === "checkpoint" && retainedSnapshot) {
740
+ const record = AgentStoreCheckpointRecordSchema.safeParse(event.payload);
741
+ const assistantId = operation.input.assistant.id;
742
+ const retained = retainedSnapshot.messages.find((message) => message.id === assistantId);
743
+ if (record.success && retained && record.data.input.assistant.sha256 === createHash2("sha256").update(canonicalAgentJson2(retained)).digest("hex")) {
744
+ operation = { ...operation, input: { ...operation.input, assistant: retained } };
745
+ result.retainedMessages.set(event.eventId, retained);
746
+ }
747
+ }
748
+ if (operation.type === "recover" && operation.input.action === "abandon" && !operation.assistant && retainedSnapshot) {
749
+ const runId = operation.input.runId;
750
+ const run = result.snapshot.runs.find((run) => run.id === runId);
751
+ const retained = retainedSnapshot.messages.find((message) => message.id === run?.assistantMessageId && message.runId === runId && message.role === "assistant" && message.status === "failed");
752
+ if (retained)
753
+ operation = { ...operation, assistant: retained };
754
+ }
755
+ if (operation.type === "recover" && operation.input.action === "abandon" && !operation.assistant)
756
+ throw new TypeError("Branch archive has no exact retained abandoned assistant");
757
+ const inputConversation = operation.type === "accept" ? operation.input.input.conversationId : operation.input.conversationId;
758
+ if (inputConversation !== conversationId)
759
+ throw new TypeError("Branch transition has a foreign conversation");
760
+ const reduced = reduceStore(result.snapshot, operation);
761
+ if (reduced.outcome !== "applied")
762
+ throw new TypeError(`Branch transition at sequence ${event.seq} is inconsistent: ${reduced.outcome}`);
763
+ const runRecords = reduced.runRecords?.map((record) => ({
764
+ ...record,
765
+ run: { ...record.run, updatedAt: event.occurredAt }
766
+ }));
767
+ result.snapshot = {
768
+ ...reduced.snapshot,
769
+ runs: reduced.snapshot.runs.map((run) => runRecords?.find((record) => record.run.id === run.id)?.run ?? run)
770
+ };
771
+ const { snapshot: _snapshot, outcome: _outcome, ...effects } = reduced;
772
+ result.effects.push({ ...effects, ...runRecords && { runRecords } });
773
+ if (operation.type === "recover" && operation.input.action === "abandon") {
774
+ const runId = operation.input.runId;
775
+ const assistant = reduced.runRecords?.find((record) => record.run.id === runId)?.terminalAssistant;
776
+ if (assistant)
777
+ result.retainedMessages.set(event.eventId, assistant);
778
+ if (operation.assistant)
779
+ result.unresolvedDrafts.delete(runId);
780
+ }
781
+ if (operation.type === "checkpoint" && AgentStoreCheckpointRecordSchema.safeParse(event.payload).success)
782
+ result.unresolvedDrafts.add(operation.input.runId);
783
+ if (operation.type === "terminal")
784
+ result.unresolvedDrafts.delete(operation.input.runId);
785
+ }
786
+ return result;
787
+ }
788
+
789
+ // src/agent-runtime/store-reads.ts
790
+ async function snapshotIn(driver, transaction, conversationId) {
791
+ const [stored, messages, activeRecords] = await Promise.all([
792
+ driver.head.load(transaction, conversationId),
793
+ driver.history.load(transaction, conversationId),
794
+ driver.runs.listActive(transaction, conversationId)
795
+ ]);
796
+ const head = AgentRuntimeHeadSchema2.parse(stored ?? emptyHead(conversationId));
797
+ const referencedRecords = await driver.runs.loadMany(transaction, {
798
+ conversationId,
799
+ runIds: referencedRunIds(messages)
800
+ });
801
+ return snapshotOf(head, messages, mergeRunRecords(activeRecords, referencedRecords));
802
+ }
803
+ function loadSnapshot(driver, conversationId) {
804
+ return driver.transaction((transaction) => snapshotIn(driver, transaction, conversationId), {
805
+ access: "read"
806
+ });
807
+ }
808
+ function loadRun(driver, input) {
809
+ return driver.transaction(async (transaction) => {
810
+ const [stored, record] = await Promise.all([
811
+ driver.head.load(transaction, input.conversationId),
812
+ driver.runs.load(transaction, input)
813
+ ]);
814
+ if (!record)
815
+ return;
816
+ const parsed = AgentStoredRunSchema2.parse(record);
817
+ if (parsed.run.conversationId !== input.conversationId || parsed.run.id !== input.runId) {
818
+ throw new TypeError("Stored run does not match the identity it was loaded by");
819
+ }
820
+ const head = AgentRuntimeHeadSchema2.parse(stored ?? emptyHead(input.conversationId));
821
+ return AgentRunViewSchema2.parse({
822
+ snapshotVersion: head.version,
823
+ run: parsed.run,
824
+ ...parsed.terminalAssistant && { assistant: parsed.terminalAssistant }
825
+ });
826
+ }, { access: "read" });
827
+ }
828
+ function listActiveRuns(driver, conversationId) {
829
+ return driver.transaction(async (transaction) => {
830
+ const [records, messages] = await Promise.all([
831
+ driver.runs.listActive(transaction, conversationId),
832
+ driver.history.load(transaction, conversationId)
833
+ ]);
834
+ const runs = records.map((record) => AgentStoredRunSchema2.parse(record).run);
835
+ for (const run of runs) {
836
+ if (run.conversationId !== conversationId) {
837
+ throw new TypeError("Active run belongs to another conversation");
838
+ }
839
+ if (!isActiveRunState(run.state)) {
840
+ throw new TypeError("Active run listing returned a terminal run");
841
+ }
842
+ }
843
+ return orderRuns(messages, runs);
844
+ }, { access: "read" });
845
+ }
846
+ function appendEvent(driver, input) {
847
+ const parsed = AppendAgentStoreEventSchema2.parse(input);
848
+ return driver.transaction((transaction) => appendEventIn(driver, transaction, parsed));
849
+ }
850
+ async function appendEventIn(driver, transaction, parsed) {
851
+ if (await driver.conversations?.isPurged(transaction, parsed.conversationId)) {
852
+ throw new AgentConversationPurgedError2;
853
+ }
854
+ return driver.events.append(transaction, agentStoreEventDraft(parsed));
855
+ }
856
+ function readEvents(driver, input) {
857
+ const parsed = ReadAgentStoreEventsSchema2.parse(input);
858
+ return driver.transaction(async (transaction) => AgentStoreEventPageSchema2.parse(await driver.events.list(transaction, parsed)), { access: "read" });
859
+ }
860
+
861
+ // src/agent-runtime/store-replay.ts
862
+ async function persistRuntimeReplay(driver, tx, replay) {
863
+ if (replay.baseline) {
864
+ const baseline = replay.baseline;
865
+ for (const record of baseline.runs)
866
+ await driver.runs.save(tx, record);
867
+ if (driver.history.restore)
868
+ await driver.history.restore(tx, {
869
+ conversationId: baseline.head.conversationId,
870
+ messages: baseline.messages,
871
+ compactedMessageIds: baseline.compacted ?? []
872
+ });
873
+ else
874
+ for (const message of baseline.messages)
875
+ await driver.history.apply(tx, { type: "admit", input: message });
876
+ for (const receipt of baseline.admissions)
877
+ await driver.admissions.create(tx, receipt);
878
+ }
879
+ for (const effect of replay.effects) {
880
+ for (const record of effect.runRecords ?? [])
881
+ await driver.runs.save(tx, record);
882
+ if (effect.admissionReceipt)
883
+ await driver.admissions.create(tx, effect.admissionReceipt);
884
+ if (effect.seedReceipt)
885
+ await driver.seeds.create(tx, effect.seedReceipt);
886
+ for (const mutation of effect.historyMutations ?? [])
887
+ await driver.history.apply(tx, mutation);
888
+ }
889
+ }
890
+
891
+ // src/agent-runtime/store-archive.ts
892
+ function exportConversation(driver, conversationId) {
893
+ return driver.transaction(async (transaction) => {
894
+ return exportConversationIn(driver, transaction, conversationId);
895
+ }, { access: "read" });
896
+ }
897
+ async function importConversation(driver, bytes) {
898
+ const archive = decodeAgentConversationArchive2(bytes);
899
+ return driver.transaction(async (transaction) => {
900
+ const existing = await driver.events.list(transaction, {
901
+ conversationId: archive.conversationId,
902
+ limit: 1
903
+ });
904
+ if (existing.items.length > 0) {
905
+ throw new TypeError("Conversation event log must be empty before import");
906
+ }
907
+ for (const event of archive.events) {
908
+ const appended = await driver.events.append(transaction, {
909
+ schemaVersion: event.schemaVersion,
910
+ eventId: event.eventId,
911
+ conversationId: event.conversationId,
912
+ kind: event.kind,
913
+ occurredAt: event.occurredAt,
914
+ payload: event.payload,
915
+ ...event.ignorable && { ignorable: true }
916
+ });
917
+ if (appended.seq !== event.seq) {
918
+ throw new TypeError("Conversation archive event sequence is not contiguous");
919
+ }
920
+ }
921
+ const snapshotEntry = archive.projections.find((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry) && entry.archiveType === "runtime-snapshot");
922
+ if (snapshotEntry && typeof snapshotEntry === "object" && !Array.isArray(snapshotEntry)) {
923
+ const snapshot = AgentSnapshotSchema2.parse(snapshotEntry.snapshot);
924
+ if (snapshot.conversationId !== archive.conversationId) {
925
+ throw new TypeError("Conversation archive snapshot belongs to another conversation");
926
+ }
927
+ if (snapshot.version > 0 || snapshot.messages.length > 0 || snapshot.runs.length > 0) {
928
+ const outcome = await driver.head.compareAndSwap(transaction, {
929
+ conversationId: snapshot.conversationId,
930
+ expectedVersion: 0,
931
+ next: AgentRuntimeHeadSchema2.parse({
932
+ schemaVersion: 1,
933
+ conversationId: snapshot.conversationId,
934
+ version: snapshot.version
935
+ })
936
+ });
937
+ if (outcome.outcome !== "applied") {
938
+ throw new TypeError("Conversation archive snapshot target is not empty");
939
+ }
940
+ }
941
+ const replay = replayBranchEvents(archive.conversationId, archive.events, snapshot);
942
+ await persistRuntimeReplay(driver, transaction, replay);
943
+ for (const run of snapshot.runs) {
944
+ const terminalAssistant = snapshot.messages.find((message) => message.id === run.assistantMessageId);
945
+ await driver.runs.save(transaction, AgentStoredRunSchema2.parse({
946
+ schemaVersion: 1,
947
+ run,
948
+ ...terminalAssistant && { terminalAssistant }
949
+ }));
950
+ }
951
+ const restoredIds = new Set((await driver.history.load(transaction, archive.conversationId)).map((message) => message.id));
952
+ for (const message of snapshot.messages) {
953
+ if (message.role === "assistant")
954
+ await driver.history.apply(transaction, { type: "upsert-assistant", message });
955
+ else if (!restoredIds.has(message.id))
956
+ await driver.history.apply(transaction, { type: "admit", input: message });
957
+ }
958
+ }
959
+ await driver.archive?.import(transaction, archive);
960
+ return { conversationId: archive.conversationId, events: archive.events.length };
961
+ });
962
+ }
963
+ async function exportConversationIn(driver, transaction, conversationId) {
964
+ const events = [];
965
+ let fromSeq;
966
+ do {
967
+ const page = AgentStoreEventPageSchema2.parse(await driver.events.list(transaction, ReadAgentStoreEventsSchema2.parse({
968
+ conversationId,
969
+ ...fromSeq && { fromSeq },
970
+ limit: 1e4
971
+ })));
972
+ events.push(...page.items);
973
+ fromSeq = page.nextSeq;
974
+ } while (fromSeq !== undefined);
975
+ const durable = await driver.archive?.export(transaction, conversationId) ?? {
976
+ projections: [],
977
+ spills: []
978
+ };
979
+ const snapshot = await snapshotIn(driver, transaction, conversationId);
980
+ return encodeAgentConversationArchive2({
981
+ format: "stitchkit.agent-conversation",
982
+ formatVersion: 1,
983
+ conversationId,
984
+ events,
985
+ projections: [
986
+ { archiveType: "runtime-snapshot", snapshot: z5.json().parse(snapshot) },
987
+ ...durable.projections
988
+ ],
989
+ spills: durable.spills
990
+ });
991
+ }
992
+
993
+ // src/agent-runtime/store-branch.ts
994
+ import { createHash as createHash7 } from "node:crypto";
995
+ import { z as z14 } from "zod";
996
+
997
+ // src/agent-runtime/branch-events.ts
998
+ import { z as z10 } from "zod";
999
+
1000
+ // src/agent-runtime/branch-messages.ts
1001
+ import { z as z6 } from "zod";
1002
+ function branchOpaqueJson(value) {
1003
+ if (Array.isArray(value))
1004
+ return value.map(branchOpaqueJson);
1005
+ if (value !== null && typeof value === "object") {
1006
+ for (const key of [
1007
+ "reference",
1008
+ "conversationId",
1009
+ "runId",
1010
+ "messageId",
1011
+ "callId",
1012
+ "toolCallId",
1013
+ "approvalId",
1014
+ "operationId",
1015
+ "artifactId"
1016
+ ]) {
1017
+ if (key in value)
1018
+ throw new TypeError(`Branch cannot rekey an opaque payload containing ${key}`);
1019
+ }
1020
+ for (const entry of Object.values(value))
1021
+ branchOpaqueJson(entry);
1022
+ }
1023
+ return value;
1024
+ }
1025
+ var ArtifactReadSchema = z6.object({
1026
+ reference: z6.string(),
1027
+ offset: z6.int().nonnegative(),
1028
+ maxBytes: z6.int().positive().optional()
1029
+ }).strict();
1030
+ var ArtifactSearchSchema = z6.object({
1031
+ reference: z6.string(),
1032
+ query: z6.string(),
1033
+ maxMatches: z6.int().positive().optional()
1034
+ }).strict();
1035
+ var ArtifactReadResultSchema = z6.object({
1036
+ reference: z6.string(),
1037
+ offset: z6.int().nonnegative(),
1038
+ text: z6.string(),
1039
+ bytes: z6.int().nonnegative(),
1040
+ totalBytes: z6.int().nonnegative().optional()
1041
+ }).strict();
1042
+ var ArtifactSearchResultSchema = z6.object({
1043
+ reference: z6.string(),
1044
+ matches: z6.array(z6.object({ line: z6.int().positive(), text: z6.string() }).strict())
1045
+ }).strict();
1046
+ function branchToolJson(ids, name, value, output) {
1047
+ if (name === AGENT_CODING_TOOL_NAMES.readOutput || name === AGENT_CODING_TOOL_NAMES.searchOutput) {
1048
+ const parsed = output ? name === AGENT_CODING_TOOL_NAMES.readOutput ? ArtifactReadResultSchema.parse(value) : ArtifactSearchResultSchema.parse(value) : name === AGENT_CODING_TOOL_NAMES.readOutput ? ArtifactReadSchema.parse(value) : ArtifactSearchSchema.parse(value);
1049
+ return { ...parsed, reference: ids.reference("spill", parsed.reference) };
1050
+ }
1051
+ if (output && name === AGENT_CODING_TOOL_NAMES.runCommand) {
1052
+ const parsed = ShellOutputSchema.parse(value);
1053
+ return {
1054
+ ...parsed,
1055
+ ...parsed.artifact && {
1056
+ artifact: {
1057
+ ...parsed.artifact,
1058
+ reference: ids.reference("spill", parsed.artifact.reference)
1059
+ }
1060
+ }
1061
+ };
1062
+ }
1063
+ return branchOpaqueJson(value);
1064
+ }
1065
+ function branchPart(ids, part) {
1066
+ switch (part.type) {
1067
+ case "file":
1068
+ return { ...part, reference: ids.reference("spill", part.reference) };
1069
+ case "tool-call": {
1070
+ if (part.provider)
1071
+ throw new TypeError("Branch cannot rekey opaque tool provider metadata");
1072
+ return {
1073
+ ...part,
1074
+ callId: ids.reference("call", part.callId),
1075
+ input: branchToolJson(ids, part.toolName, part.input, false)
1076
+ };
1077
+ }
1078
+ case "tool-result":
1079
+ return {
1080
+ ...part,
1081
+ callId: ids.reference("call", part.callId),
1082
+ ...part.output !== undefined && {
1083
+ output: part.outcome === "success" ? branchToolJson(ids, part.toolName, part.output, true) : branchOpaqueJson(part.output)
1084
+ }
1085
+ };
1086
+ case "tool-approval-request":
1087
+ return {
1088
+ type: part.type,
1089
+ approvalId: ids.reference("approval", part.approvalId),
1090
+ callId: ids.reference("call", part.callId)
1091
+ };
1092
+ case "tool-approval-response":
1093
+ return { ...part, approvalId: ids.reference("approval", part.approvalId) };
1094
+ case "provider":
1095
+ throw new TypeError("Branch cannot rekey an opaque provider part");
1096
+ case "reasoning":
1097
+ if (part.provider)
1098
+ throw new TypeError("Branch cannot rekey opaque reasoning provider metadata");
1099
+ return part;
1100
+ default:
1101
+ return part;
1102
+ }
1103
+ }
1104
+ function branchMessage(ids, sourceConversationId, message) {
1105
+ if (message.conversationId !== sourceConversationId)
1106
+ throw new TypeError("Branch message belongs to another conversation");
1107
+ return {
1108
+ ...message,
1109
+ id: ids.reference("message", message.id),
1110
+ conversationId: ids.conversationId,
1111
+ ...message.runId && { runId: ids.reference("run", message.runId) },
1112
+ parts: message.parts.map((part) => branchPart(ids, part)),
1113
+ ...message.metadata && {
1114
+ metadata: z6.record(z6.string(), z6.json()).parse(branchOpaqueJson(message.metadata))
1115
+ }
1116
+ };
1117
+ }
1118
+ function branchRun(ids, sourceConversationId, run, settled) {
1119
+ if (run.conversationId !== sourceConversationId)
1120
+ throw new TypeError("Branch run belongs to another conversation");
1121
+ const { ownerId, fencingToken, ...record } = run;
1122
+ return {
1123
+ ...record,
1124
+ id: ids.reference("run", run.id),
1125
+ conversationId: ids.conversationId,
1126
+ inputMessageIds: run.inputMessageIds.map((id) => ids.reference("message", id)),
1127
+ assistantMessageId: ids.reference("message", run.assistantMessageId),
1128
+ ...run.absorbedIntoRunId && {
1129
+ absorbedIntoRunId: ids.reference("run", run.absorbedIntoRunId)
1130
+ },
1131
+ ...run.lastOperation && {
1132
+ lastOperation: {
1133
+ ...run.lastOperation,
1134
+ operationId: ids.reference("operation", run.lastOperation.operationId)
1135
+ }
1136
+ },
1137
+ ...!settled && ownerId && { ownerId: ids.define("owner", ownerId) },
1138
+ ...!settled && fencingToken !== undefined && { fencingToken }
1139
+ };
1140
+ }
1141
+
1142
+ // src/agent-runtime/branch-observations.ts
1143
+ import { z as z7 } from "zod";
1144
+ var Id = z7.string().min(1);
1145
+ var Hash2 = z7.string().regex(/^[a-f0-9]{64}$/);
1146
+ var State = z7.object({
1147
+ name: Id,
1148
+ version: z7.int().positive(),
1149
+ value: z7.json(),
1150
+ actor: z7.enum(["agent", "human", "system"])
1151
+ }).strict();
1152
+ var Retry = z7.object({
1153
+ attempt: z7.int().positive(),
1154
+ delayMs: z7.number().nonnegative().optional(),
1155
+ reason: z7.string().optional()
1156
+ }).strict();
1157
+ var Spill = z7.object({ reference: Id, bytes: z7.int().nonnegative(), sha256: Hash2.optional() }).strict();
1158
+ var Schedule = z7.object({
1159
+ id: Id,
1160
+ kind: z7.enum(["at", "after", "every"]).optional(),
1161
+ nextAt: z7.string().nullable().optional(),
1162
+ occurrence: z7.int().nonnegative().optional(),
1163
+ lateByMs: z7.number().nonnegative().optional(),
1164
+ state: z7.string().optional(),
1165
+ message: z7.string().optional(),
1166
+ attempts: z7.int().nonnegative().optional(),
1167
+ retryAt: z7.string().nullable().optional(),
1168
+ terminal: z7.boolean().optional(),
1169
+ skipped: z7.boolean().optional()
1170
+ }).strict();
1171
+ var Child = z7.object({
1172
+ childConversationId: Id.optional(),
1173
+ parentConversationId: Id.optional(),
1174
+ seedUptoSeq: z7.int().nonnegative().optional(),
1175
+ budget: z7.json().optional(),
1176
+ state: z7.string().optional(),
1177
+ resultReference: Id.optional(),
1178
+ reason: z7.string().optional(),
1179
+ action: z7.enum(["message-sent", "blocking-presented", "blocking-resolved"]).optional(),
1180
+ approvalId: Id.optional(),
1181
+ childApprovalId: Id.optional(),
1182
+ callId: Id.optional(),
1183
+ toolName: Id.optional(),
1184
+ input: z7.json().optional(),
1185
+ batchId: Id.optional(),
1186
+ kind: z7.string().optional(),
1187
+ presentedAt: z7.string().optional(),
1188
+ approved: z7.boolean().optional()
1189
+ }).strict();
1190
+ var Rejection = z7.object({ approvalId: Id, callId: Id, toolName: Id, reason: z7.string() }).strict();
1191
+ function branchObservation(ids, kind, raw) {
1192
+ if (kind.startsWith("durability/")) {
1193
+ const value = kind === "durability/step" ? StepEventPayloadSchema.parse(raw) : kind === "durability/park" ? ParkEventPayloadSchema.parse(raw) : kind === "durability/event" ? DeliveredEventPayloadSchema.parse(raw) : kind === "durability/effect" ? EffectEventPayloadSchema.parse(raw) : undefined;
1194
+ if (!value || canonicalAgentJson2(value) !== canonicalAgentJson2(raw))
1195
+ throw new TypeError("Branch cannot rekey opaque durability payload");
1196
+ if ("encoded" in value)
1197
+ branchOpaqueJson(z7.json().parse(JSON.parse(value.encoded)));
1198
+ return {
1199
+ ...value,
1200
+ runId: ids.reference("run", value.runId),
1201
+ ..."id" in value && { id: ids.define("key", value.id) }
1202
+ };
1203
+ }
1204
+ if (kind === "sandbox/probed")
1205
+ return z7.object({
1206
+ result: AgentSandboxGradeSchema2,
1207
+ required: z7.array(AgentSandboxRestrictionSchema2),
1208
+ missing: z7.array(AgentSandboxRestrictionSchema2)
1209
+ }).strict().parse(raw);
1210
+ if (kind === "state/set") {
1211
+ const value = State.parse(raw);
1212
+ branchOpaqueJson(value.value);
1213
+ return value;
1214
+ }
1215
+ if (kind === "retry/scheduled" || kind === "retry/started")
1216
+ return Retry.parse(raw);
1217
+ if (kind === "spill/created" || kind === "spill/deleted") {
1218
+ const value = Spill.parse(raw);
1219
+ return { ...value, reference: ids.reference("spill", value.reference) };
1220
+ }
1221
+ if (kind.startsWith("schedule/")) {
1222
+ const value = Schedule.parse(raw);
1223
+ return { ...value, id: ids.define("schedule", value.id) };
1224
+ }
1225
+ if (kind.startsWith("child/")) {
1226
+ const value = Child.parse(raw);
1227
+ if (value.budget)
1228
+ branchOpaqueJson(value.budget);
1229
+ if (value.input)
1230
+ branchOpaqueJson(value.input);
1231
+ return {
1232
+ ...value,
1233
+ ...value.childConversationId && {
1234
+ childConversationId: value.childConversationId === ids.sourceConversationId ? ids.conversationId : ids.define("child", value.childConversationId)
1235
+ },
1236
+ ...value.parentConversationId && {
1237
+ parentConversationId: value.parentConversationId === ids.sourceConversationId ? ids.conversationId : ids.define("child", value.parentConversationId)
1238
+ },
1239
+ ...value.resultReference && {
1240
+ resultReference: ids.reference("spill", value.resultReference)
1241
+ },
1242
+ ...value.approvalId && { approvalId: ids.define("approval", value.approvalId) },
1243
+ ...value.childApprovalId && {
1244
+ childApprovalId: ids.define("approval", value.childApprovalId)
1245
+ },
1246
+ ...value.callId && { callId: ids.reference("call", value.callId) },
1247
+ ...value.batchId && { batchId: ids.define("key", value.batchId) }
1248
+ };
1249
+ }
1250
+ if (kind === "approval/response-rejected") {
1251
+ const value = Rejection.parse(raw);
1252
+ return {
1253
+ ...value,
1254
+ approvalId: ids.reference("approval", value.approvalId),
1255
+ callId: ids.reference("call", value.callId)
1256
+ };
1257
+ }
1258
+ throw new TypeError(`Branch cannot rekey opaque event kind ${kind}`);
1259
+ }
1260
+ var BranchObservationSchema = z7.object({ sourceKind: Id, payload: z7.json() }).strict();
1261
+
1262
+ // src/agent-runtime/branch-provider.ts
1263
+ import { createHash as createHash3 } from "node:crypto";
1264
+ import { z as z8 } from "zod";
1265
+ var Hash3 = z8.string().regex(/^[a-f0-9]{64}$/);
1266
+ var Text = z8.object({ type: z8.enum(["text", "reasoning"]), text: z8.string() }).strict();
1267
+ var Call = z8.object({
1268
+ type: z8.literal("tool-call"),
1269
+ toolCallId: z8.string(),
1270
+ toolName: z8.string(),
1271
+ input: z8.json()
1272
+ }).strict();
1273
+ var Result = z8.object({
1274
+ type: z8.literal("tool-result"),
1275
+ toolCallId: z8.string(),
1276
+ toolName: z8.string(),
1277
+ output: z8.discriminatedUnion("type", [
1278
+ z8.object({ type: z8.enum(["text", "error-text"]), value: z8.string() }).strict(),
1279
+ z8.object({ type: z8.enum(["json", "error-json"]), value: z8.json() }).strict()
1280
+ ])
1281
+ }).strict();
1282
+ var Message = z8.object({
1283
+ role: z8.enum(["user", "assistant", "tool"]),
1284
+ content: z8.union([
1285
+ z8.string(),
1286
+ z8.array(z8.union([
1287
+ Text,
1288
+ Call,
1289
+ Result,
1290
+ z8.object({
1291
+ type: z8.literal("tool-approval-request"),
1292
+ approvalId: z8.string(),
1293
+ toolCallId: z8.string(),
1294
+ signature: z8.string().optional(),
1295
+ isAutomatic: z8.boolean().optional()
1296
+ }).strict(),
1297
+ z8.object({
1298
+ type: z8.literal("tool-approval-response"),
1299
+ approvalId: z8.string(),
1300
+ approved: z8.boolean(),
1301
+ reason: z8.string().optional()
1302
+ }).strict(),
1303
+ z8.object({
1304
+ type: z8.literal("file"),
1305
+ data: z8.union([
1306
+ z8.string(),
1307
+ z8.object({
1308
+ binary: z8.literal(true),
1309
+ sha256: Hash3,
1310
+ bytes: z8.int().nonnegative()
1311
+ }).strict()
1312
+ ]),
1313
+ mediaType: z8.string(),
1314
+ filename: z8.string().optional()
1315
+ }).strict(),
1316
+ z8.object({
1317
+ type: z8.literal("image"),
1318
+ image: z8.union([
1319
+ z8.string(),
1320
+ z8.object({
1321
+ binary: z8.literal(true),
1322
+ sha256: Hash3,
1323
+ bytes: z8.int().nonnegative()
1324
+ }).strict()
1325
+ ]),
1326
+ mediaType: z8.string().optional()
1327
+ }).strict()
1328
+ ]))
1329
+ ])
1330
+ }).strict();
1331
+ var ProviderMessage = z8.object({ sha256: Hash3, message: Message }).strict();
1332
+ var ProviderRequest = z8.object({
1333
+ runId: z8.string(),
1334
+ attempt: z8.int().nonnegative(),
1335
+ stepNumber: z8.int().nonnegative(),
1336
+ modelId: z8.string(),
1337
+ instructionsSha256: Hash3,
1338
+ messageShas: z8.array(Hash3),
1339
+ bodySha256: Hash3
1340
+ }).strict();
1341
+ var ProviderResponse = z8.object({
1342
+ runId: z8.string(),
1343
+ attempt: z8.int().nonnegative(),
1344
+ stepNumber: z8.int().nonnegative(),
1345
+ response: z8.object({ id: z8.string(), provider: z8.string().optional() }).strict()
1346
+ }).strict();
1347
+ function hash(value) {
1348
+ return createHash3("sha256").update(canonicalAgentJson2(value)).digest("hex");
1349
+ }
1350
+
1351
+ class BranchProvider {
1352
+ ids;
1353
+ digests = new Map;
1354
+ constructor(ids) {
1355
+ this.ids = ids;
1356
+ }
1357
+ rekey(kind, raw) {
1358
+ if (kind === "provider/message") {
1359
+ const value = ProviderMessage.parse(raw);
1360
+ if (hash(value.message) !== value.sha256)
1361
+ throw new TypeError("Branch provider message digest is inconsistent");
1362
+ const message = {
1363
+ ...value.message,
1364
+ content: typeof value.message.content === "string" ? value.message.content : value.message.content.map((part) => {
1365
+ if (part.type === "tool-call")
1366
+ return {
1367
+ ...part,
1368
+ toolCallId: this.ids.reference("call", part.toolCallId),
1369
+ input: branchToolJson(this.ids, part.toolName, part.input, false)
1370
+ };
1371
+ if (part.type === "tool-result") {
1372
+ if (part.output.type === "error-json")
1373
+ branchOpaqueJson(part.output.value);
1374
+ return {
1375
+ ...part,
1376
+ toolCallId: this.ids.reference("call", part.toolCallId),
1377
+ output: part.output.type === "json" ? {
1378
+ ...part.output,
1379
+ value: branchToolJson(this.ids, part.toolName, part.output.value, true)
1380
+ } : part.output
1381
+ };
1382
+ }
1383
+ if (part.type === "tool-approval-request")
1384
+ return {
1385
+ type: part.type,
1386
+ approvalId: this.ids.reference("approval", part.approvalId),
1387
+ toolCallId: this.ids.reference("call", part.toolCallId)
1388
+ };
1389
+ if (part.type === "tool-approval-response")
1390
+ return {
1391
+ ...part,
1392
+ approvalId: this.ids.reference("approval", part.approvalId)
1393
+ };
1394
+ return part;
1395
+ })
1396
+ };
1397
+ const sha256 = hash(message);
1398
+ this.digests.set(value.sha256, sha256);
1399
+ return { sha256, message };
1400
+ }
1401
+ if (kind === "provider/request") {
1402
+ const value = ProviderRequest.parse(raw);
1403
+ const sourceHash = createHash3("sha256").update(value.instructionsSha256).update(value.messageShas.join(",")).digest("hex");
1404
+ if (sourceHash !== value.bodySha256)
1405
+ throw new TypeError("Branch provider request digest is inconsistent");
1406
+ const messageShas = value.messageShas.map((sha) => {
1407
+ const target = this.digests.get(sha);
1408
+ if (!target)
1409
+ throw new TypeError("Branch provider request references a missing message");
1410
+ return target;
1411
+ });
1412
+ return {
1413
+ ...value,
1414
+ runId: this.ids.reference("run", value.runId),
1415
+ messageShas,
1416
+ bodySha256: createHash3("sha256").update(value.instructionsSha256).update(messageShas.join(",")).digest("hex")
1417
+ };
1418
+ }
1419
+ const value = ProviderResponse.parse(raw);
1420
+ return { ...value, runId: this.ids.reference("run", value.runId) };
1421
+ }
1422
+ }
1423
+
1424
+ // src/agent-runtime/branch-transitions.ts
1425
+ import { z as z9 } from "zod";
1426
+ function rekeyBranchTransition(ids, source, operation) {
1427
+ const message = (value) => branchMessage(ids, source, value);
1428
+ const input = operation.input;
1429
+ switch (operation.type) {
1430
+ case "accept":
1431
+ return {
1432
+ type: operation.type,
1433
+ input: {
1434
+ ...operation.input,
1435
+ idempotencyKey: ids.define("key", operation.input.idempotencyKey),
1436
+ input: message(operation.input.input),
1437
+ run: branchRun(ids, source, operation.input.run, false),
1438
+ ...operation.input.coalesceIntoRunId && {
1439
+ coalesceIntoRunId: ids.reference("run", operation.input.coalesceIntoRunId)
1440
+ }
1441
+ }
1442
+ };
1443
+ case "seed":
1444
+ return {
1445
+ type: operation.type,
1446
+ input: {
1447
+ ...operation.input,
1448
+ conversationId: ids.conversationId,
1449
+ seedKey: ids.define("key", operation.input.seedKey),
1450
+ inputs: operation.input.inputs.map(message)
1451
+ }
1452
+ };
1453
+ case "compact":
1454
+ return {
1455
+ type: operation.type,
1456
+ input: {
1457
+ ...operation.input,
1458
+ conversationId: ids.conversationId,
1459
+ replacedMessageIds: operation.input.replacedMessageIds.map((id) => ids.reference("message", id)),
1460
+ summary: message(operation.input.summary)
1461
+ }
1462
+ };
1463
+ default: {
1464
+ if (!("runId" in input))
1465
+ throw new TypeError("Branch transition has no run identity");
1466
+ const common = {
1467
+ ...input,
1468
+ conversationId: ids.conversationId,
1469
+ runId: ids.reference("run", input.runId),
1470
+ ..."ownerId" in input && { ownerId: ids.define("owner", input.ownerId) }
1471
+ };
1472
+ return AgentStoreTransitionSchema2.parse({
1473
+ type: operation.type,
1474
+ ...operation.type === "recover" && operation.assistant && { assistant: message(operation.assistant) },
1475
+ input: {
1476
+ ...common,
1477
+ ...operation.type === "checkpoint" || operation.type === "terminal" ? { assistant: message(operation.input.assistant) } : {},
1478
+ ...operation.type === "operation" && {
1479
+ operation: {
1480
+ ...operation.input.operation,
1481
+ operationId: ids.reference("operation", operation.input.operation.operationId)
1482
+ }
1483
+ },
1484
+ ...operation.type === "terminal" && operation.input.absorb && {
1485
+ absorb: operation.input.absorb.map((entry) => ({
1486
+ runId: ids.reference("run", entry.runId),
1487
+ inputMessageIds: entry.inputMessageIds.map((id) => ids.reference("message", id))
1488
+ }))
1489
+ }
1490
+ }
1491
+ });
1492
+ }
1493
+ }
1494
+ }
1495
+ function branchJson(value) {
1496
+ return z9.json().parse(value);
1497
+ }
1498
+
1499
+ // src/agent-runtime/branch-events.ts
1500
+ var Hash4 = z10.string().regex(/^[a-f0-9]{64}$/);
1501
+ function rekeyBranchEvents(ids, source, events, retainedMessages = new Map) {
1502
+ const hydrated = events.map((event) => {
1503
+ const assistant = retainedMessages.get(event.eventId);
1504
+ if (!assistant)
1505
+ return event;
1506
+ ids.collectMessage(assistant);
1507
+ const operation = event.kind === "runtime/transition" ? branchTransition(event) : undefined;
1508
+ if (operation?.type === "recover" && operation.input.action === "abandon" && !operation.assistant)
1509
+ return { ...event, payload: branchJson({ ...operation, assistant }) };
1510
+ return event;
1511
+ });
1512
+ for (const event of hydrated)
1513
+ collectBranchEvent(ids, event);
1514
+ for (const assistant of hydrated.flatMap((event) => retainedMessages.has(event.eventId) ? [retainedMessages.get(event.eventId)] : [])) {
1515
+ if (assistant)
1516
+ branchMessage(ids, source, assistant);
1517
+ }
1518
+ const provider = new BranchProvider(ids);
1519
+ return hydrated.map((event) => rekeyBranchEvent(ids, source, event, provider));
1520
+ }
1521
+ function collectBranchEvent(ids, event) {
1522
+ ids.define("event", event.eventId);
1523
+ if (event.schemaVersion !== 1)
1524
+ throw new TypeError("Branch requires a supported event version");
1525
+ if (event.kind === "runtime/transition")
1526
+ ids.collectOperation(branchTransition(event));
1527
+ if (event.kind === "runtime/baseline") {
1528
+ const baseline = BranchBaselineSchema.parse(event.payload);
1529
+ if (canonicalAgentJson2(baseline) !== canonicalAgentJson2(event.payload))
1530
+ throw new TypeError("Branch baseline contains an unknown opaque field");
1531
+ for (const record of baseline.runs)
1532
+ ids.collectRun(record.run);
1533
+ for (const message of baseline.messages)
1534
+ ids.collectMessage(message);
1535
+ }
1536
+ }
1537
+ function rekeyBranchEvent(ids, source, event, provider) {
1538
+ const envelope = {
1539
+ ...event,
1540
+ eventId: ids.reference("event", event.eventId),
1541
+ conversationId: ids.conversationId
1542
+ };
1543
+ const message = (value) => branchMessage(ids, source, value);
1544
+ if (event.kind === "runtime/transition") {
1545
+ const operation = branchTransition(event);
1546
+ const rekeyed = rekeyBranchTransition(ids, source, operation);
1547
+ const raw = z10.object({
1548
+ type: z10.string(),
1549
+ input: z10.object({
1550
+ assistant: z10.object({
1551
+ sha256: Hash4.optional(),
1552
+ sourceSha256: Hash4.optional(),
1553
+ bytes: z10.int(),
1554
+ parts: z10.int()
1555
+ }).passthrough()
1556
+ }).passthrough()
1557
+ }).passthrough().safeParse(event.payload);
1558
+ if (operation.type === "checkpoint" && raw.success && rekeyed.type === "checkpoint") {
1559
+ const draft = raw.data.input.assistant;
1560
+ return {
1561
+ ...envelope,
1562
+ payload: branchJson({
1563
+ type: "checkpoint",
1564
+ input: {
1565
+ ...rekeyed.input,
1566
+ assistant: {
1567
+ id: rekeyed.input.assistant.id,
1568
+ status: "streaming",
1569
+ sourceSha256: draft.sha256 ?? draft.sourceSha256,
1570
+ parts: draft.parts,
1571
+ bytes: draft.bytes
1572
+ }
1573
+ }
1574
+ })
1575
+ };
1576
+ }
1577
+ if (canonicalAgentJson2(operation) !== canonicalAgentJson2(event.payload))
1578
+ throw new TypeError("Branch transition contains an unknown opaque field");
1579
+ return { ...envelope, payload: branchJson(rekeyed) };
1580
+ }
1581
+ if (event.kind === "runtime/baseline") {
1582
+ const baseline = BranchBaselineSchema.parse(event.payload);
1583
+ return {
1584
+ ...envelope,
1585
+ payload: branchJson({
1586
+ ...baseline,
1587
+ head: { ...baseline.head, conversationId: ids.conversationId },
1588
+ messages: baseline.messages.map(message),
1589
+ ...baseline.compacted && {
1590
+ compacted: baseline.compacted.map((id) => ids.reference("message", id))
1591
+ },
1592
+ runs: baseline.runs.map((record) => ({
1593
+ ...record,
1594
+ run: branchRun(ids, source, record.run, false),
1595
+ ...record.terminalAssistant && {
1596
+ terminalAssistant: message(record.terminalAssistant)
1597
+ }
1598
+ })),
1599
+ admissions: baseline.admissions.map((receipt) => ({
1600
+ ...receipt,
1601
+ conversationId: ids.conversationId,
1602
+ idempotencyKey: ids.define("key", receipt.idempotencyKey),
1603
+ input: message(receipt.input),
1604
+ runId: ids.reference("run", receipt.runId),
1605
+ assistantMessageId: ids.reference("message", receipt.assistantMessageId)
1606
+ }))
1607
+ })
1608
+ };
1609
+ }
1610
+ if (event.kind === "runtime/branched") {
1611
+ const receipt = AgentConversationBranchReceiptSchema2.parse(event.payload);
1612
+ return {
1613
+ ...envelope,
1614
+ payload: branchJson({
1615
+ ...receipt,
1616
+ conversationId: ids.conversationId,
1617
+ requestKey: ids.define("key", receipt.requestKey)
1618
+ })
1619
+ };
1620
+ }
1621
+ if (event.kind === "provider/message" || event.kind === "provider/request" || event.kind === "provider/response")
1622
+ return { ...envelope, payload: branchJson(provider.rekey(event.kind, event.payload)) };
1623
+ const observation = event.kind === "runtime/branch-observation" ? BranchObservationSchema.parse(event.payload) : { sourceKind: event.kind, payload: event.payload };
1624
+ return {
1625
+ ...envelope,
1626
+ kind: "runtime/branch-observation",
1627
+ payload: branchJson({
1628
+ sourceKind: observation.sourceKind,
1629
+ payload: branchObservation(ids, observation.sourceKind, observation.payload)
1630
+ })
1631
+ };
1632
+ }
1633
+
1634
+ // src/agent-runtime/branch-identities.ts
1635
+ import { createHash as createHash4 } from "node:crypto";
1636
+
1637
+ class BranchIdentities {
1638
+ conversationId;
1639
+ requestKey;
1640
+ sourceConversationId;
1641
+ definitions = new Map;
1642
+ references = new Map;
1643
+ constructor(conversationId, requestKey, sourceConversationId) {
1644
+ this.conversationId = conversationId;
1645
+ this.requestKey = requestKey;
1646
+ this.sourceConversationId = sourceConversationId;
1647
+ }
1648
+ define(kind, source) {
1649
+ const values = this.definitions.get(kind) ?? new Map;
1650
+ const target = values.get(source) ?? createHash4("sha256").update(JSON.stringify([this.conversationId, this.requestKey, kind, source])).digest("hex");
1651
+ values.set(source, target);
1652
+ this.definitions.set(kind, values);
1653
+ return target;
1654
+ }
1655
+ reference(kind, source) {
1656
+ const target = this.definitions.get(kind)?.get(source);
1657
+ if (!target)
1658
+ throw new TypeError(`Branch contains a foreign or missing ${kind} reference`);
1659
+ const referenced = this.references.get(kind) ?? new Set;
1660
+ referenced.add(source);
1661
+ this.references.set(kind, referenced);
1662
+ return target;
1663
+ }
1664
+ referenced(kind) {
1665
+ return this.references.get(kind) ?? new Set;
1666
+ }
1667
+ collectMessage(message) {
1668
+ this.define("message", message.id);
1669
+ for (const part of message.parts) {
1670
+ if (part.type === "tool-call")
1671
+ this.define("call", part.callId);
1672
+ if (part.type === "tool-approval-request")
1673
+ this.define("approval", part.approvalId);
1674
+ }
1675
+ }
1676
+ collectRun(run) {
1677
+ this.define("run", run.id);
1678
+ this.define("message", run.assistantMessageId);
1679
+ if (run.lastOperation)
1680
+ this.define("operation", run.lastOperation.operationId);
1681
+ }
1682
+ collectOperation(operation) {
1683
+ switch (operation.type) {
1684
+ case "accept":
1685
+ this.collectRun(operation.input.run);
1686
+ this.collectMessage(operation.input.input);
1687
+ break;
1688
+ case "checkpoint":
1689
+ case "terminal":
1690
+ this.collectMessage(operation.input.assistant);
1691
+ break;
1692
+ case "recover":
1693
+ if (operation.assistant)
1694
+ this.collectMessage(operation.assistant);
1695
+ break;
1696
+ case "compact":
1697
+ this.collectMessage(operation.input.summary);
1698
+ break;
1699
+ case "seed":
1700
+ for (const message of operation.input.inputs)
1701
+ this.collectMessage(message);
1702
+ break;
1703
+ case "operation":
1704
+ this.define("operation", operation.input.operation.operationId);
1705
+ break;
1706
+ }
1707
+ }
1708
+ }
1709
+
1710
+ // src/agent-runtime/branch-source.ts
1711
+ import { createHash as createHash6 } from "node:crypto";
1712
+ import { z as z13 } from "zod";
1713
+
1714
+ // src/agent-runtime/archive-projection.ts
1715
+ import { z as z11 } from "zod";
1716
+ var ArchiveProjectionSchema = z11.object({
1717
+ name: z11.string(),
1718
+ version: z11.int().positive(),
1719
+ uptoSeq: z11.int().nonnegative(),
1720
+ value: z11.json(),
1721
+ updatedAt: z11.string()
1722
+ }).strict();
1723
+
1724
+ // src/agent-runtime/archive-spill.ts
1725
+ import { createHash as createHash5 } from "node:crypto";
1726
+ import { z as z12 } from "zod";
1727
+ var ArchiveSpillSchema = z12.object({
1728
+ reference: z12.string().min(1),
1729
+ runId: z12.string().min(1).optional(),
1730
+ toolCallId: z12.string().min(1).optional(),
1731
+ mediaType: z12.string().min(1),
1732
+ bytes: z12.int().nonnegative(),
1733
+ sha256: z12.string().regex(/^[a-f0-9]{64}$/),
1734
+ createdAt: z12.iso.datetime({ offset: true }),
1735
+ expiresAt: z12.iso.datetime({ offset: true }).optional(),
1736
+ authorization: AgentCodingToolAuthorizationSchema2.optional(),
1737
+ base64: z12.string()
1738
+ }).strict().superRefine((spill, ctx) => {
1739
+ const bytes = Buffer.from(spill.base64, "base64");
1740
+ if (bytes.toString("base64") !== spill.base64 || bytes.byteLength !== spill.bytes || createHash5("sha256").update(bytes).digest("hex") !== spill.sha256) {
1741
+ ctx.addIssue({
1742
+ code: "custom",
1743
+ message: "Conversation archive spill bytes or digest are inconsistent"
1744
+ });
1745
+ }
1746
+ });
1747
+
1748
+ // src/agent-runtime/branch-source.ts
1749
+ var SnapshotProjection = z13.object({ archiveType: z13.literal("runtime-snapshot"), snapshot: AgentSnapshotSchema2 }).strict();
1750
+ function branchHash(value) {
1751
+ return createHash6("sha256").update(canonicalAgentJson2(value)).digest("hex");
1752
+ }
1753
+ function visibleSnapshot(snapshot) {
1754
+ const referenced = new Set(snapshot.messages.flatMap((message) => message.runId ? [message.runId] : []));
1755
+ return {
1756
+ ...snapshot,
1757
+ runs: snapshot.runs.filter((run) => referenced.has(run.id) || isActiveRunState(run.state)).map(({ updatedAt: _time, ...run }) => run).sort((a, b) => a.id.localeCompare(b.id))
1758
+ };
1759
+ }
1760
+ function boundary(events, throughRunId) {
1761
+ if (!throughRunId)
1762
+ return events.length;
1763
+ for (const event of events) {
1764
+ if (event.kind !== "runtime/transition")
1765
+ continue;
1766
+ const candidate = z13.object({
1767
+ type: z13.string(),
1768
+ input: z13.object({
1769
+ runId: z13.string().optional(),
1770
+ reason: z13.string().optional(),
1771
+ action: z13.string().optional(),
1772
+ absorb: z13.array(z13.object({ runId: z13.string() })).optional()
1773
+ })
1774
+ }).safeParse(event.payload);
1775
+ if (!candidate.success)
1776
+ continue;
1777
+ const { type, input } = candidate.data;
1778
+ if (type === "terminal" && (input.runId === throughRunId || input.absorb?.some((entry) => entry.runId === throughRunId)) || type === "recover" && input.action === "abandon" && input.runId === throughRunId)
1779
+ return event.seq;
1780
+ }
1781
+ throw new TypeError("Branch boundary run has no exact settled event");
1782
+ }
1783
+ function prepareBranchSource(bytes, source) {
1784
+ const archive = decodeAgentConversationArchive2(bytes);
1785
+ if (source.kind === "conversation" && source.conversationId !== archive.conversationId)
1786
+ throw new TypeError("Branch source identity changed");
1787
+ for (const [index, event] of archive.events.entries()) {
1788
+ if (event.seq !== index + 1)
1789
+ throw new TypeError("Branch source event sequence is incomplete");
1790
+ }
1791
+ const projections = archive.projections.map((value) => z13.union([SnapshotProjection, ArchiveProjectionSchema]).parse(value));
1792
+ if (projections.some((value, index) => canonicalAgentJson2(value) !== canonicalAgentJson2(archive.projections[index])))
1793
+ throw new TypeError("Branch archive snapshot contains an unknown opaque field");
1794
+ const snapshots = projections.filter((value) => ("archiveType" in value));
1795
+ if (snapshots.length !== 1)
1796
+ throw new TypeError("Branch archive requires exactly one runtime snapshot");
1797
+ const names = new Set;
1798
+ for (const projection of projections) {
1799
+ if (!("name" in projection))
1800
+ continue;
1801
+ if (names.has(projection.name) || projection.uptoSeq > archive.events.length)
1802
+ throw new TypeError("Branch companion projection has an inconsistent checkpoint");
1803
+ names.add(projection.name);
1804
+ }
1805
+ const expected = snapshots[0]?.snapshot;
1806
+ if (!expected || expected.conversationId !== archive.conversationId)
1807
+ throw new TypeError("Branch archive has a foreign snapshot");
1808
+ const allSpills = archive.spills.map((value) => ArchiveSpillSchema.parse(value));
1809
+ if (new Set(allSpills.map((spill) => spill.reference)).size !== allSpills.length)
1810
+ throw new TypeError("Branch archive has duplicate spill identities");
1811
+ const full = replayBranchEvents(archive.conversationId, archive.events, expected);
1812
+ const validationIds = new BranchIdentities("branch-validation", "branch-validation", archive.conversationId);
1813
+ for (const spill of allSpills)
1814
+ validationIds.define("spill", spill.reference);
1815
+ rekeyBranchEvents(validationIds, archive.conversationId, archive.events, full.retainedMessages);
1816
+ for (const spill of allSpills) {
1817
+ if (spill.runId)
1818
+ validationIds.reference("run", spill.runId);
1819
+ if (spill.toolCallId)
1820
+ validationIds.reference("call", spill.toolCallId);
1821
+ }
1822
+ if (canonicalAgentJson2(visibleSnapshot(full.snapshot)) !== canonicalAgentJson2(visibleSnapshot(expected)))
1823
+ throw new TypeError("Branch archive snapshot disagrees with its event log");
1824
+ const seq = boundary(archive.events, source.kind === "conversation" ? source.throughRunId : undefined);
1825
+ const events = archive.events.slice(0, seq);
1826
+ const replay = seq === archive.events.length ? full : replayBranchEvents(archive.conversationId, events, expected);
1827
+ if (replay.snapshot.runs.some((run) => isActiveRunState(run.state)))
1828
+ throw new TypeError("Branch boundary is not settled");
1829
+ if (replay.unresolvedDrafts.size > 0)
1830
+ throw new TypeError("Branch boundary has no full terminal content for a hashed draft");
1831
+ const selectedIds = new BranchIdentities("branch-validation", "branch-validation", archive.conversationId);
1832
+ for (const spill of allSpills)
1833
+ selectedIds.define("spill", spill.reference);
1834
+ rekeyBranchEvents(selectedIds, archive.conversationId, events, replay.retainedMessages);
1835
+ const references = selectedIds.referenced("spill");
1836
+ const spills = allSpills.filter((spill) => references.has(spill.reference));
1837
+ if (spills.length !== references.size)
1838
+ throw new TypeError("Branch source is missing a referenced spill");
1839
+ return {
1840
+ conversationId: archive.conversationId,
1841
+ events,
1842
+ spills,
1843
+ replay,
1844
+ digest: branchHash({
1845
+ conversationId: archive.conversationId,
1846
+ events,
1847
+ spills,
1848
+ snapshot: replay.snapshot
1849
+ })
1850
+ };
1851
+ }
1852
+
1853
+ // src/agent-runtime/store-branch.ts
1854
+ async function destinationEvents(driver, tx, conversationId) {
1855
+ const events = [];
1856
+ let fromSeq;
1857
+ do {
1858
+ const page = await driver.events.list(tx, { conversationId, fromSeq, limit: 1e4 });
1859
+ events.push(...page.items);
1860
+ fromSeq = page.nextSeq;
1861
+ } while (fromSeq !== undefined);
1862
+ return events;
1863
+ }
1864
+ async function branchConversation(driver, raw) {
1865
+ const parsed = BranchAgentConversationInputSchema2.parse(raw);
1866
+ const input = {
1867
+ ...parsed,
1868
+ source: parsed.source.kind === "archive" ? { ...parsed.source, bytes: Uint8Array.from(parsed.source.bytes) } : parsed.source
1869
+ };
1870
+ const fingerprint = branchHash({
1871
+ ...input,
1872
+ source: input.source.kind === "archive" ? {
1873
+ kind: "archive",
1874
+ digest: createHash7("sha256").update(input.source.bytes).digest("hex")
1875
+ } : input.source
1876
+ });
1877
+ return driver.transaction(async (tx) => {
1878
+ if (await driver.conversations?.isPurged(tx, input.destinationConversationId))
1879
+ throw new AgentConversationPurgedError2;
1880
+ const existing = await destinationEvents(driver, tx, input.destinationConversationId);
1881
+ const previous = existing.filter((event) => event.kind === "runtime/branched").at(-1);
1882
+ if (previous) {
1883
+ const receipt = AgentConversationBranchReceiptSchema2.parse(previous.payload);
1884
+ if (receipt.requestKey === input.requestKey && receipt.fingerprint === fingerprint && receipt.conversationId === input.destinationConversationId)
1885
+ return { outcome: "duplicate", receipt };
1886
+ throw new TypeError("Branch request conflicts with the destination receipt");
1887
+ }
1888
+ if (driver.conversations?.isEmpty && !await driver.conversations.isEmpty(tx, input.destinationConversationId) || existing.length > 0 || await driver.head.load(tx, input.destinationConversationId) || (await driver.history.load(tx, input.destinationConversationId)).length > 0)
1889
+ throw new TypeError("Branch destination must be empty");
1890
+ if (input.source.kind === "conversation" && await driver.conversations?.isPurged(tx, input.source.conversationId))
1891
+ throw new AgentConversationPurgedError2;
1892
+ if (input.source.kind === "conversation" && !await driver.head.load(tx, input.source.conversationId) && (await driver.events.list(tx, { conversationId: input.source.conversationId, limit: 1 })).items.length === 0)
1893
+ throw new TypeError("Branch source conversation was not found");
1894
+ const bytes = input.source.kind === "archive" ? input.source.bytes : await exportConversationIn(driver, tx, input.source.conversationId);
1895
+ const source = prepareBranchSource(bytes, input.source);
1896
+ if (source.conversationId === input.destinationConversationId)
1897
+ throw new TypeError("Branch requires a new conversation identity");
1898
+ if (source.spills.length > 0 && (!input.artifactAuthorization || !driver.archive))
1899
+ throw new TypeError("Branch spills require an archive adapter and fresh destination artifact authorization");
1900
+ const ids = new BranchIdentities(input.destinationConversationId, input.requestKey, source.conversationId);
1901
+ for (const spill of source.spills)
1902
+ ids.define("spill", spill.reference);
1903
+ const events = rekeyBranchEvents(ids, source.conversationId, source.events, source.replay.retainedMessages);
1904
+ const replay = replayBranchEvents(input.destinationConversationId, events);
1905
+ if (replay.baseline?.compacted?.length && !driver.history.restore)
1906
+ throw new TypeError("Branch baseline requires inactive history restoration support");
1907
+ const spills = source.spills.map(({ authorization: _sourceAuthority, ...spill }) => ({
1908
+ ...spill,
1909
+ reference: ids.reference("spill", spill.reference),
1910
+ ...spill.runId && { runId: ids.reference("run", spill.runId) },
1911
+ ...spill.toolCallId && { toolCallId: ids.reference("call", spill.toolCallId) },
1912
+ authorization: input.artifactAuthorization
1913
+ }));
1914
+ const receipt = AgentConversationBranchReceiptSchema2.parse({
1915
+ schemaVersion: 1,
1916
+ requestKey: input.requestKey,
1917
+ fingerprint,
1918
+ conversationId: input.destinationConversationId,
1919
+ sourceConversationId: source.conversationId,
1920
+ sourceKind: input.source.kind,
1921
+ ...input.source.kind === "conversation" && input.source.throughRunId && { throughRunId: input.source.throughRunId },
1922
+ sourceSeq: source.events.at(-1)?.seq ?? 0,
1923
+ sourceVersion: source.replay.snapshot.version,
1924
+ sourceDigest: source.digest,
1925
+ createdAt: new Date().toISOString()
1926
+ });
1927
+ const next = {
1928
+ schemaVersion: 1,
1929
+ conversationId: input.destinationConversationId,
1930
+ version: replay.snapshot.version
1931
+ };
1932
+ const cas = await driver.head.compareAndSwap(tx, {
1933
+ conversationId: input.destinationConversationId,
1934
+ expectedVersion: 0,
1935
+ next
1936
+ });
1937
+ if (cas.outcome !== "applied")
1938
+ throw new TypeError("Branch destination changed");
1939
+ await persistRuntimeReplay(driver, tx, replay);
1940
+ for (const run of replay.snapshot.runs) {
1941
+ const stored = await driver.runs.load(tx, {
1942
+ conversationId: run.conversationId,
1943
+ runId: run.id
1944
+ });
1945
+ if (!stored)
1946
+ throw new TypeError("Branch lost a normalized terminal record");
1947
+ const { ownerId: _owner, fencingToken: _fence, ...settled } = stored.run;
1948
+ await driver.runs.save(tx, { ...stored, run: settled });
1949
+ }
1950
+ for (const event of events) {
1951
+ const appended = await driver.events.append(tx, event);
1952
+ if (appended.seq !== event.seq)
1953
+ throw new TypeError("Branch event sequence changed");
1954
+ }
1955
+ await driver.archive?.import(tx, {
1956
+ format: "stitchkit.agent-conversation",
1957
+ formatVersion: 1,
1958
+ conversationId: input.destinationConversationId,
1959
+ events: [],
1960
+ projections: [],
1961
+ spills: z14.array(z14.json()).parse(spills)
1962
+ });
1963
+ await driver.events.append(tx, agentStoreEventDraft({
1964
+ conversationId: input.destinationConversationId,
1965
+ kind: "runtime/branched",
1966
+ payload: z14.json().parse(receipt)
1967
+ }));
1968
+ return { outcome: "created", receipt };
1969
+ });
1970
+ }
1971
+
1972
+ // src/agent-runtime/store-event-once.ts
1973
+ import { createHash as createHash8 } from "node:crypto";
1974
+ function eventOnceId(conversationId, kind, key) {
1975
+ if (!key)
1976
+ throw new TypeError("Event admission key must not be empty");
1977
+ return createHash8("sha256").update(JSON.stringify([conversationId, kind, key])).digest("hex");
1978
+ }
1979
+ async function findEvent(driver, transaction, conversationId, eventId) {
1980
+ if (driver.events.find)
1981
+ return driver.events.find(transaction, { conversationId, eventId });
1982
+ let fromSeq;
1983
+ do {
1984
+ const page = await driver.events.list(transaction, {
1985
+ conversationId,
1986
+ fromSeq,
1987
+ limit: 1e4
1988
+ });
1989
+ const found = page.items.find((event) => event.eventId === eventId);
1990
+ if (found)
1991
+ return found;
1992
+ fromSeq = page.nextSeq;
1993
+ } while (fromSeq !== undefined);
1994
+ return;
1995
+ }
1996
+ function appendEventOnce(driver, input, key) {
1997
+ const parsed = AppendAgentStoreEventSchema2.parse(input);
1998
+ const eventId = eventOnceId(parsed.conversationId, parsed.kind, key);
1999
+ return driver.transaction(async (transaction) => {
2000
+ if (await driver.conversations?.isPurged(transaction, parsed.conversationId)) {
2001
+ throw new AgentConversationPurgedError2;
2002
+ }
2003
+ const previous = await findEvent(driver, transaction, parsed.conversationId, eventId);
2004
+ if (previous)
2005
+ return { outcome: "duplicate", event: previous };
2006
+ const event = await driver.events.append(transaction, {
2007
+ ...agentStoreEventDraft(parsed),
2008
+ eventId
2009
+ });
2010
+ return { outcome: "applied", event };
2011
+ });
2012
+ }
2013
+ function findEventOnce(driver, input) {
2014
+ const eventId = eventOnceId(input.conversationId, input.kind, input.key);
2015
+ return driver.transaction((transaction) => findEvent(driver, transaction, input.conversationId, eventId), { access: "read" });
2016
+ }
2017
+
2018
+ // src/agent-runtime/store-mutate.ts
2019
+ import { z as z15 } from "zod";
2020
+ function validateAdmissionReceipt(receipt, record, conversationId) {
2021
+ const input = receipt.input;
2022
+ const run = record.run;
2023
+ if (receipt.conversationId !== conversationId || input.conversationId !== conversationId || input.role !== "user" || input.status !== "committed" || input.runId !== undefined || receipt.runId !== run.id || receipt.assistantMessageId !== run.assistantMessageId || !run.inputMessageIds.includes(input.id)) {
2024
+ throw new TypeError("Admission receipt does not match its canonical run assignment");
2025
+ }
2026
+ }
2027
+ async function readForMutation(driver, transaction, operation) {
2028
+ const conversationId = operationConversationId(operation);
2029
+ if (await driver.conversations?.isPurged(transaction, conversationId)) {
2030
+ throw new AgentConversationPurgedError2;
2031
+ }
2032
+ const operationRunId = operation.type === "accept" ? operation.input.coalesceIntoRunId : operation.type === "compact" || operation.type === "seed" ? undefined : operation.input.runId;
2033
+ const [stored, messages, activeRecords, operationRecord, duplicateReceipt] = await Promise.all([
2034
+ driver.head.load(transaction, conversationId),
2035
+ driver.history.load(transaction, conversationId),
2036
+ driver.runs.listActive(transaction, conversationId),
2037
+ operationRunId ? driver.runs.load(transaction, { conversationId, runId: operationRunId }) : undefined,
2038
+ operation.type === "accept" ? driver.admissions.load(transaction, {
2039
+ conversationId,
2040
+ idempotencyKey: operation.input.idempotencyKey
2041
+ }) : undefined
2042
+ ]);
2043
+ const head = AgentRuntimeHeadSchema2.parse(stored ?? emptyHead(conversationId));
2044
+ const referencedRecords = await driver.runs.loadMany(transaction, {
2045
+ conversationId,
2046
+ runIds: referencedRunIds(messages)
2047
+ });
2048
+ const records = mergeRunRecords(activeRecords, referencedRecords, operationRecord ? [operationRecord] : []);
2049
+ const current = snapshotOf(head, messages, records);
2050
+ return { conversationId, head, messages, records, current, duplicateReceipt };
2051
+ }
2052
+ async function settledSeed(driver, transaction, read, input) {
2053
+ const { conversationId, current } = read;
2054
+ const existing = await driver.seeds.load(transaction, {
2055
+ conversationId,
2056
+ seedKey: input.seedKey
2057
+ });
2058
+ if (existing)
2059
+ return { outcome: "applied", snapshot: current };
2060
+ let collisions = 0;
2061
+ for (const message of input.inputs) {
2062
+ if (current.messages.some((candidate) => candidate.id === message.id) || driver.history.hasMessage && await driver.history.hasMessage(transaction, {
2063
+ conversationId,
2064
+ messageId: message.id
2065
+ }))
2066
+ collisions++;
2067
+ }
2068
+ if (collisions > 0 && collisions < input.inputs.length) {
2069
+ throw new TypeError("Partial instruction seed collision: restore the complete seed before retrying");
2070
+ }
2071
+ if (collisions > 0) {
2072
+ await driver.seeds.create(transaction, AgentSeedReceiptSchema2.parse({
2073
+ schemaVersion: 1,
2074
+ conversationId,
2075
+ seedKey: input.seedKey,
2076
+ messageIds: input.inputs.map((message) => message.id)
2077
+ }));
2078
+ return { outcome: "applied", snapshot: current };
2079
+ }
2080
+ return;
2081
+ }
2082
+ async function duplicateAdmission(driver, transaction, read, duplicateReceipt) {
2083
+ const { conversationId, head, messages, records } = read;
2084
+ const duplicateRecord = await driver.runs.load(transaction, {
2085
+ conversationId,
2086
+ runId: duplicateReceipt.runId
2087
+ });
2088
+ if (!duplicateRecord) {
2089
+ throw new TypeError("Admission receipt points to a missing canonical run");
2090
+ }
2091
+ validateAdmissionReceipt(duplicateReceipt, duplicateRecord, conversationId);
2092
+ const answering = duplicateRecord.run.absorbedIntoRunId ? await driver.runs.load(transaction, {
2093
+ conversationId,
2094
+ runId: duplicateRecord.run.absorbedIntoRunId
2095
+ }) : undefined;
2096
+ if (duplicateRecord.run.absorbedIntoRunId && !answering) {
2097
+ throw new TypeError("Absorbed run points to a missing absorbing run");
2098
+ }
2099
+ const canonical = answering ?? duplicateRecord;
2100
+ return {
2101
+ outcome: "duplicate",
2102
+ input: duplicateReceipt.input,
2103
+ inputMessageId: duplicateReceipt.input.id,
2104
+ runId: canonical.run.id,
2105
+ assistantMessageId: canonical.run.assistantMessageId,
2106
+ run: canonical.run,
2107
+ ...canonical.terminalAssistant && {
2108
+ assistant: canonical.terminalAssistant
2109
+ },
2110
+ snapshot: snapshotOf(head, messages, mergeRunRecords(records, [duplicateRecord], answering ? [answering] : []))
2111
+ };
2112
+ }
2113
+ async function assertAdmissionIdentitiesFree(driver, transaction, conversationId, input) {
2114
+ const inputCollision = await driver.admissions.loadByInputMessageId(transaction, {
2115
+ conversationId,
2116
+ inputMessageId: input.input.id
2117
+ });
2118
+ if (inputCollision) {
2119
+ throw new TypeError("Input message identity is already assigned to an admission");
2120
+ }
2121
+ if (!input.coalesceIntoRunId) {
2122
+ const [runCollision, assistantCollision] = await Promise.all([
2123
+ driver.runs.load(transaction, {
2124
+ conversationId,
2125
+ runId: input.run.id
2126
+ }),
2127
+ driver.runs.loadByAssistantMessageId(transaction, {
2128
+ conversationId,
2129
+ assistantMessageId: input.run.assistantMessageId
2130
+ })
2131
+ ]);
2132
+ if (runCollision || assistantCollision) {
2133
+ throw new TypeError("Queued run identities are already reserved");
2134
+ }
2135
+ }
2136
+ }
2137
+ async function persistApplied(driver, transaction, read, operation, reduced) {
2138
+ const { conversationId, current } = read;
2139
+ const nextHead = AgentRuntimeHeadSchema2.parse({
2140
+ schemaVersion: 1,
2141
+ conversationId,
2142
+ version: reduced.snapshot.version
2143
+ });
2144
+ const outcome = await driver.head.compareAndSwap(transaction, {
2145
+ conversationId,
2146
+ expectedVersion: current.version,
2147
+ next: nextHead
2148
+ });
2149
+ if (outcome.outcome === "conflict")
2150
+ return conflict(outcome.actualVersion);
2151
+ for (const record of reduced.runRecords ?? []) {
2152
+ await driver.runs.save(transaction, record);
2153
+ }
2154
+ if (reduced.admissionReceipt) {
2155
+ await driver.admissions.create(transaction, reduced.admissionReceipt);
2156
+ }
2157
+ if (reduced.seedReceipt) {
2158
+ await driver.seeds.create(transaction, reduced.seedReceipt);
2159
+ }
2160
+ for (const mutation of reduced.historyMutations ?? []) {
2161
+ await driver.history.apply(transaction, mutation);
2162
+ }
2163
+ await driver.events.append(transaction, agentStoreEventDraft({
2164
+ conversationId,
2165
+ kind: "runtime/transition",
2166
+ payload: z15.json().parse(transitionRecord(operation, reduced))
2167
+ }));
2168
+ return { outcome: "applied", snapshot: reduced.snapshot };
2169
+ }
2170
+ function mutateStore(driver, operation) {
2171
+ return driver.transaction(async (transaction) => {
2172
+ const read = await readForMutation(driver, transaction, operation);
2173
+ if (operation.type === "seed") {
2174
+ const settled = await settledSeed(driver, transaction, read, operation.input);
2175
+ if (settled)
2176
+ return settled;
2177
+ }
2178
+ if (read.duplicateReceipt) {
2179
+ return duplicateAdmission(driver, transaction, read, read.duplicateReceipt);
2180
+ }
2181
+ if (operation.type === "accept") {
2182
+ await assertAdmissionIdentitiesFree(driver, transaction, read.conversationId, operation.input);
2183
+ }
2184
+ const reduced = reduceStore(read.current, operation);
2185
+ if (reduced.outcome !== "applied")
2186
+ return reduced;
2187
+ return persistApplied(driver, transaction, read, operation, reduced);
2188
+ });
2189
+ }
2190
+
2191
+ // src/agent-runtime/store-purge.ts
2192
+ function createStoreConversationPurge(driver, conversations) {
2193
+ return async (input) => {
2194
+ const { conversationId, expectedVersion } = AgentConversationPurgeInputSchema2.parse(input);
2195
+ return driver.transaction(async (transaction) => {
2196
+ if (await conversations.isPurged(transaction, conversationId)) {
2197
+ return { outcome: "already_purged" };
2198
+ }
2199
+ const head = await driver.head.load(transaction, conversationId);
2200
+ const actualVersion = head?.version ?? 0;
2201
+ if (expectedVersion !== undefined && expectedVersion !== actualVersion) {
2202
+ return { outcome: "conflict", actualVersion };
2203
+ }
2204
+ const active = await driver.runs.listActive(transaction, conversationId);
2205
+ if (active.length > 0) {
2206
+ return { outcome: "active", runIds: active.map((record) => record.run.id) };
2207
+ }
2208
+ await conversations.remove(transaction, conversationId);
2209
+ return { outcome: "purged" };
2210
+ });
2211
+ };
2212
+ }
2213
+
2214
+ // src/agent-runtime/store-create.ts
2215
+ var AgentRecoverableScanInputSchema = z16.object({
2216
+ cursor: z16.string().min(1).optional(),
2217
+ limit: z16.number().int().min(1).max(1000)
2218
+ });
2219
+ function createAgentRuntimeStore2(driver) {
2220
+ const mutate = (operation) => mutateStore(driver, operation);
2221
+ return {
2222
+ branchConversation: (input) => branchConversation(driver, input),
2223
+ loadSnapshot: (conversationId) => loadSnapshot(driver, conversationId),
2224
+ loadRun: (input) => loadRun(driver, input),
2225
+ listActiveRuns: (conversationId) => listActiveRuns(driver, conversationId),
2226
+ appendEventOnce: (input, key) => appendEventOnce(driver, input, key),
2227
+ findEventOnce: (input) => findEventOnce(driver, input),
2228
+ appendEvent: (input) => appendEvent(driver, input),
2229
+ readEvents: (input) => readEvents(driver, input),
2230
+ exportConversation: (conversationId) => exportConversation(driver, conversationId),
2231
+ importConversation: (bytes) => importConversation(driver, bytes),
2232
+ ...driver.conversations && {
2233
+ purgeConversation: createStoreConversationPurge(driver, driver.conversations)
2234
+ },
2235
+ acceptInputAndAssignRun: (input) => mutate({
2236
+ type: "accept",
2237
+ input: AcceptInputAndAssignRunSchema2.parse(input)
2238
+ }),
2239
+ acquireRun: (input) => mutate({ type: "acquire", input: AcquireAgentRunSchema2.parse(input) }),
2240
+ checkpointRunAssistant: (input) => mutate({
2241
+ type: "checkpoint",
2242
+ input: CheckpointRunAssistantSchema2.parse(input)
2243
+ }),
2244
+ recordRunOperation: (input) => mutate({
2245
+ type: "operation",
2246
+ input: RecordRunOperationSchema2.parse(input)
2247
+ }),
2248
+ requestRunInterrupt: (input) => mutate({
2249
+ type: "interrupt",
2250
+ input: RequestRunInterruptSchema2.parse(input)
2251
+ }),
2252
+ recoverRun: (input) => mutate({ type: "recover", input: RecoverAgentRunSchema2.parse(input) }),
2253
+ commitRunTerminal: (input) => mutate({ type: "terminal", input: CommitRunTerminalSchema2.parse(input) }),
2254
+ replaceCompactedRange: (input) => mutate({
2255
+ type: "compact",
2256
+ input: ReplaceCompactedRangeSchema2.parse(input)
2257
+ }),
2258
+ seedConversationInput: (input) => mutate({
2259
+ type: "seed",
2260
+ input: SeedConversationInputSchema2.parse(input)
2261
+ }),
2262
+ async scanRecoverable(input) {
2263
+ const parsed = AgentRecoverableScanInputSchema.parse(input);
2264
+ return AgentRecoverablePageSchema2.parse(await driver.scanRecoverable(parsed));
2265
+ }
2266
+ };
2267
+ }
2268
+
2269
+ export { BranchAgentConversationInputSchema2, AgentConversationBranchReceiptSchema2, AgentConversationBranchResultSchema2, AgentConversationPurgeInputSchema2, AgentConversationPurgeResultSchema2, AgentConversationPurgedError2, purgeAgentConversation2, AgentRuntimeHeadSchema2, AgentStoredRunSchema2, AgentAdmissionReceiptSchema2, AgentSeedReceiptSchema2, AgentHistoryMutationSchema2, ACTIVE_AGENT_RUN_STATES2, isActiveRunState, replaceMessage, appendEventIn, ArchiveProjectionSchema, ArchiveSpillSchema, createAgentRuntimeStore2 };