stitchkit 0.58.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1724,7 +1724,7 @@ function createAgentRuntime(config) {
1724
1724
  });
1725
1725
  const acceptedSnapshot = appliedSnapshot(acceptance, "input acceptance");
1726
1726
  const assignedRunId = acceptance.outcome === "duplicate" ? acceptance.runId : reservation?.admission.runId ?? runId;
1727
- const acceptedRun = findRun(acceptedSnapshot.runs, assignedRunId);
1727
+ const acceptedRun = acceptance.outcome === "duplicate" ? acceptance.run : findRun(acceptedSnapshot.runs, assignedRunId);
1728
1728
  const actualInputMessageId = acceptance.outcome === "duplicate" ? acceptance.inputMessageId : userMessage2.id;
1729
1729
  const acceptedInput = acceptance.outcome === "duplicate" ? acceptance.input : acceptedSnapshot.messages.find((candidate) => candidate.id === actualInputMessageId);
1730
1730
  if (!acceptedInput) {
@@ -1739,7 +1739,7 @@ function createAgentRuntime(config) {
1739
1739
  createdAt: acceptedRun.createdAt,
1740
1740
  updatedAt: acceptedRun.updatedAt
1741
1741
  });
1742
- const acceptedAssistant = acceptance.outcome === "duplicate" ? acceptedSnapshot.messages.find((candidate) => candidate.id === acceptedRun.assistantMessageId) ?? assistantPlaceholder : assistantPlaceholder;
1742
+ const acceptedAssistant = acceptance.outcome === "duplicate" ? acceptance.assistant ?? assistantPlaceholder : assistantPlaceholder;
1743
1743
  const admission = {
1744
1744
  inputMessageId: acceptedInput.id,
1745
1745
  runId: acceptedRun.id,
@@ -1781,9 +1781,9 @@ function createAgentRuntime(config) {
1781
1781
  }
1782
1782
  return;
1783
1783
  }
1784
- const message = acceptedSnapshot.messages.find((candidate) => candidate.id === acceptedRun.assistantMessageId);
1784
+ const message = acceptance.assistant;
1785
1785
  if (!message) {
1786
- const error = new Error("Duplicate terminal run has no assistant message");
1786
+ const error = new Error("Duplicate terminal admission has no retained canonical assistant");
1787
1787
  outerResult.reject(error);
1788
1788
  if (reservation?.shouldSchedule) {
1789
1789
  reservation.admission.completion.reject(error);
@@ -1996,6 +1996,8 @@ var AgentStoreDuplicateSchema = z6.object({
1996
1996
  inputMessageId: AgentRecordIdSchema,
1997
1997
  runId: AgentRecordIdSchema,
1998
1998
  assistantMessageId: AgentRecordIdSchema,
1999
+ run: AgentRunSchema,
2000
+ assistant: AgentMessageSchema.optional(),
1999
2001
  snapshot: AgentSnapshotSchema
2000
2002
  });
2001
2003
  var AgentStoreMutationResultSchema = z6.discriminatedUnion("outcome", [
@@ -2055,18 +2057,23 @@ var ReplaceCompactedRangeSchema = z6.object({
2055
2057
  });
2056
2058
  // src/agent-runtime/store-driver.ts
2057
2059
  import { z as z7 } from "zod";
2058
- var AgentAdmissionIdentitySchema = z7.object({
2059
- idempotencyKey: z7.string().min(1),
2060
- inputMessageId: AgentRecordIdSchema,
2061
- runId: AgentRecordIdSchema,
2062
- assistantMessageId: AgentRecordIdSchema
2060
+ var AgentRuntimeHeadSchema = z7.object({
2061
+ schemaVersion: z7.literal(1),
2062
+ conversationId: AgentRecordIdSchema,
2063
+ version: AgentRecordVersionSchema
2064
+ });
2065
+ var AgentStoredRunSchema = z7.object({
2066
+ schemaVersion: z7.literal(1),
2067
+ run: AgentRunSchema,
2068
+ terminalAssistant: AgentMessageSchema.optional()
2063
2069
  });
2064
- var AgentStoredStateSchema = z7.object({
2070
+ var AgentAdmissionReceiptSchema = z7.object({
2065
2071
  schemaVersion: z7.literal(1),
2066
2072
  conversationId: AgentRecordIdSchema,
2067
- version: AgentRecordVersionSchema,
2068
- runs: z7.array(AgentRunSchema),
2069
- admissions: z7.array(AgentAdmissionIdentitySchema)
2073
+ idempotencyKey: z7.string().min(1),
2074
+ input: AgentMessageSchema,
2075
+ runId: AgentRecordIdSchema,
2076
+ assistantMessageId: AgentRecordIdSchema
2070
2077
  });
2071
2078
  var AgentHistoryMutationSchema = z7.discriminatedUnion("type", [
2072
2079
  z7.object({ type: z7.literal("admit"), input: AgentMessageSchema }),
@@ -2092,40 +2099,40 @@ var AgentRecoverableScanInputSchema = z7.object({
2092
2099
  cursor: z7.string().min(1).optional(),
2093
2100
  limit: z7.number().int().min(1).max(1000)
2094
2101
  });
2095
- function emptyState(conversationId) {
2096
- return AgentStoredStateSchema.parse({
2102
+ function emptyHead(conversationId) {
2103
+ return AgentRuntimeHeadSchema.parse({
2097
2104
  schemaVersion: 1,
2098
2105
  conversationId,
2099
- version: 0,
2100
- runs: [],
2101
- admissions: []
2106
+ version: 0
2102
2107
  });
2103
2108
  }
2104
- function snapshotOf(state, messages) {
2105
- validateAggregate(state, messages);
2109
+ function snapshotOf(head, messages, records) {
2110
+ validateSnapshot(head, messages, records);
2106
2111
  return AgentSnapshotSchema.parse({
2107
2112
  schemaVersion: 1,
2108
- conversationId: state.conversationId,
2109
- version: state.version,
2113
+ conversationId: head.conversationId,
2114
+ version: head.version,
2110
2115
  messages,
2111
- runs: state.runs
2116
+ runs: records.map((record) => record.run).sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id))
2112
2117
  });
2113
2118
  }
2114
- function validateAggregate(state, messages) {
2119
+ function validateSnapshot(head, messages, records) {
2115
2120
  const runIds = new Set;
2116
2121
  const assistantIds = new Set;
2117
2122
  const messageIds = new Set;
2118
- const idempotencyKeys = new Set;
2119
- const admittedInputIds = new Set;
2120
- for (const run of state.runs) {
2121
- if (run.conversationId !== state.conversationId || runIds.has(run.id) || assistantIds.has(run.assistantMessageId)) {
2122
- throw new TypeError("Stored agent state contains inconsistent run identities");
2123
+ for (const record of records) {
2124
+ const run = record.run;
2125
+ if (run.conversationId !== head.conversationId || runIds.has(run.id) || assistantIds.has(run.assistantMessageId)) {
2126
+ throw new TypeError("Stored agent runs contain inconsistent identities");
2127
+ }
2128
+ 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)) {
2129
+ throw new TypeError("Retained terminal assistant does not match its run");
2123
2130
  }
2124
2131
  runIds.add(run.id);
2125
2132
  assistantIds.add(run.assistantMessageId);
2126
2133
  }
2127
2134
  for (const message of messages) {
2128
- if (message.conversationId !== state.conversationId || messageIds.has(message.id)) {
2135
+ if (message.conversationId !== head.conversationId || messageIds.has(message.id)) {
2129
2136
  throw new TypeError("Stored agent history contains inconsistent message identities");
2130
2137
  }
2131
2138
  messageIds.add(message.id);
@@ -2133,23 +2140,12 @@ function validateAggregate(state, messages) {
2133
2140
  throw new TypeError("Stored history occupies a reserved assistant identity");
2134
2141
  }
2135
2142
  if (message.runId !== undefined) {
2136
- const run = state.runs.find((candidate) => candidate.id === message.runId);
2143
+ const run = records.find((candidate) => candidate.run.id === message.runId)?.run;
2137
2144
  if (!run || message.role !== "assistant" || run.assistantMessageId !== message.id) {
2138
2145
  throw new TypeError("Stored assistant history does not match its reserved run identity");
2139
2146
  }
2140
2147
  }
2141
2148
  }
2142
- for (const admission of state.admissions) {
2143
- const run = state.runs.find((candidate) => candidate.id === admission.runId);
2144
- if (idempotencyKeys.has(admission.idempotencyKey) || admittedInputIds.has(admission.inputMessageId) || !run || run.assistantMessageId !== admission.assistantMessageId || !run.inputMessageIds.includes(admission.inputMessageId)) {
2145
- throw new TypeError("Stored admission identity is inconsistent with its assigned run");
2146
- }
2147
- idempotencyKeys.add(admission.idempotencyKey);
2148
- admittedInputIds.add(admission.inputMessageId);
2149
- }
2150
- }
2151
- function recoverableDescriptors(state) {
2152
- return state.runs.filter((run) => ["queued", "running", "interrupt_requested"].includes(run.state)).map((run) => ({ conversationId: state.conversationId, run }));
2153
2149
  }
2154
2150
  var RecoverableCursorSchema = z7.tuple([AgentRecordIdSchema, AgentRecordIdSchema]);
2155
2151
  function recoverableCursor(input) {
@@ -2187,7 +2183,7 @@ function terminalMessageStatus(reason) {
2187
2183
  }
2188
2184
  return "failed";
2189
2185
  }
2190
- function applied(current, admissions, input, historyMutation) {
2186
+ function applied(current, input, effects) {
2191
2187
  return {
2192
2188
  outcome: "applied",
2193
2189
  snapshot: AgentSnapshotSchema.parse({
@@ -2196,27 +2192,14 @@ function applied(current, admissions, input, historyMutation) {
2196
2192
  runs: input.runs ?? current.runs,
2197
2193
  messages: input.messages ?? current.messages
2198
2194
  }),
2199
- admissions,
2200
- ...historyMutation && { historyMutation }
2195
+ ...effects?.runRecord && { runRecord: effects.runRecord },
2196
+ ...effects?.admissionReceipt && { admissionReceipt: effects.admissionReceipt },
2197
+ ...effects?.historyMutation && { historyMutation: effects.historyMutation }
2201
2198
  };
2202
2199
  }
2203
- function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2200
+ function reduceStore(current, operation) {
2204
2201
  if (operation.type === "accept") {
2205
2202
  const input = operation.input;
2206
- const duplicate = currentAdmissions.find((candidate) => candidate.idempotencyKey === input.idempotencyKey);
2207
- if (duplicate) {
2208
- if (!duplicateInput) {
2209
- throw new Error("Duplicate admission input is unavailable from canonical history");
2210
- }
2211
- return {
2212
- outcome: "duplicate",
2213
- input: duplicateInput,
2214
- inputMessageId: duplicate.inputMessageId,
2215
- runId: duplicate.runId,
2216
- assistantMessageId: duplicate.assistantMessageId,
2217
- snapshot: current
2218
- };
2219
- }
2220
2203
  if (input.expectedVersion !== undefined && input.expectedVersion !== current.version) {
2221
2204
  return conflict(current.version);
2222
2205
  }
@@ -2233,16 +2216,25 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2233
2216
  revision: coalescedRun.revision + 1,
2234
2217
  updatedAt: new Date().toISOString()
2235
2218
  }) : input.run;
2236
- const admission = AgentAdmissionIdentitySchema.parse({
2219
+ const admissionReceipt = AgentAdmissionReceiptSchema.parse({
2220
+ schemaVersion: 1,
2221
+ conversationId: input.input.conversationId,
2237
2222
  idempotencyKey: input.idempotencyKey,
2238
- inputMessageId: input.input.id,
2223
+ input: input.input,
2239
2224
  runId: assignedRun.id,
2240
2225
  assistantMessageId: assignedRun.assistantMessageId
2241
2226
  });
2242
- return applied(current, [...currentAdmissions, admission], {
2227
+ return applied(current, {
2243
2228
  messages: [...current.messages, input.input],
2244
2229
  runs: coalescedRun ? replaceRun(current.runs, assignedRun) : [...current.runs, assignedRun]
2245
- }, { type: "admit", input: input.input });
2230
+ }, {
2231
+ runRecord: AgentStoredRunSchema.parse({
2232
+ schemaVersion: 1,
2233
+ run: assignedRun
2234
+ }),
2235
+ admissionReceipt,
2236
+ historyMutation: { type: "admit", input: input.input }
2237
+ });
2246
2238
  }
2247
2239
  const conversationId = operation.input.conversationId;
2248
2240
  const run = operation.type === "compact" ? undefined : current.runs.find((candidate) => candidate.id === operation.input.runId);
@@ -2262,8 +2254,8 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2262
2254
  revision: run.revision + 1,
2263
2255
  updatedAt: new Date().toISOString()
2264
2256
  });
2265
- return applied(current, currentAdmissions, {
2266
- runs: replaceRun(current.runs, next)
2257
+ return applied(current, { runs: replaceRun(current.runs, next) }, {
2258
+ runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
2267
2259
  });
2268
2260
  }
2269
2261
  if (operation.type === "checkpoint" && run) {
@@ -2276,10 +2268,13 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2276
2268
  revision: run.revision + 1,
2277
2269
  updatedAt: new Date().toISOString()
2278
2270
  });
2279
- return applied(current, currentAdmissions, {
2271
+ return applied(current, {
2280
2272
  runs: replaceRun(current.runs, next),
2281
2273
  messages: replaceMessage(current.messages, input.assistant)
2282
- }, { type: "upsert-assistant", message: input.assistant });
2274
+ }, {
2275
+ runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next }),
2276
+ historyMutation: { type: "upsert-assistant", message: input.assistant }
2277
+ });
2283
2278
  }
2284
2279
  if (operation.type === "interrupt" && run) {
2285
2280
  if (run.revision !== operation.input.expectedRevision || run.state !== "running") {
@@ -2291,8 +2286,8 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2291
2286
  revision: run.revision + 1,
2292
2287
  updatedAt: new Date().toISOString()
2293
2288
  });
2294
- return applied(current, currentAdmissions, {
2295
- runs: replaceRun(current.runs, next)
2289
+ return applied(current, { runs: replaceRun(current.runs, next) }, {
2290
+ runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
2296
2291
  });
2297
2292
  }
2298
2293
  if (operation.type === "recover" && run) {
@@ -2330,13 +2325,20 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2330
2325
  status: "failed",
2331
2326
  updatedAt: new Date().toISOString()
2332
2327
  });
2333
- return applied(current, currentAdmissions, {
2328
+ return applied(current, {
2334
2329
  runs: replaceRun(current.runs, next),
2335
2330
  messages: replaceMessage(current.messages, assistant)
2336
- }, { type: "upsert-assistant", message: assistant });
2331
+ }, {
2332
+ runRecord: AgentStoredRunSchema.parse({
2333
+ schemaVersion: 1,
2334
+ run: next,
2335
+ terminalAssistant: assistant
2336
+ }),
2337
+ historyMutation: { type: "upsert-assistant", message: assistant }
2338
+ });
2337
2339
  }
2338
- return applied(current, currentAdmissions, {
2339
- runs: replaceRun(current.runs, next)
2340
+ return applied(current, { runs: replaceRun(current.runs, next) }, {
2341
+ runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
2340
2342
  });
2341
2343
  }
2342
2344
  if (operation.type === "terminal" && run) {
@@ -2352,10 +2354,17 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2352
2354
  revision: run.revision + 1,
2353
2355
  updatedAt: new Date().toISOString()
2354
2356
  });
2355
- return applied(current, currentAdmissions, {
2357
+ return applied(current, {
2356
2358
  runs: replaceRun(current.runs, next),
2357
2359
  messages: replaceMessage(current.messages, input.assistant)
2358
- }, { type: "upsert-assistant", message: input.assistant });
2360
+ }, {
2361
+ runRecord: AgentStoredRunSchema.parse({
2362
+ schemaVersion: 1,
2363
+ run: next,
2364
+ terminalAssistant: input.assistant
2365
+ }),
2366
+ historyMutation: { type: "upsert-assistant", message: input.assistant }
2367
+ });
2359
2368
  }
2360
2369
  if (operation.type === "compact") {
2361
2370
  const input = operation.input;
@@ -2377,10 +2386,12 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2377
2386
  input.summary,
2378
2387
  ...current.messages.slice(first + positions.length)
2379
2388
  ];
2380
- return applied(current, currentAdmissions, { messages }, {
2381
- type: "replace-compacted-range",
2382
- replacedMessageIds: input.replacedMessageIds,
2383
- summary: input.summary
2389
+ return applied(current, { messages }, {
2390
+ historyMutation: {
2391
+ type: "replace-compacted-range",
2392
+ replacedMessageIds: input.replacedMessageIds,
2393
+ summary: input.summary
2394
+ }
2384
2395
  });
2385
2396
  }
2386
2397
  return { outcome: "not_found" };
@@ -2388,48 +2399,130 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
2388
2399
  function operationConversationId(operation) {
2389
2400
  return operation.type === "accept" ? operation.input.input.conversationId : operation.input.conversationId;
2390
2401
  }
2402
+ function mergeRunRecords(...groups) {
2403
+ const records = new Map;
2404
+ for (const group of groups) {
2405
+ for (const rawRecord of group) {
2406
+ const record = AgentStoredRunSchema.parse(rawRecord);
2407
+ const previous = records.get(record.run.id);
2408
+ if (previous && previous.run.assistantMessageId !== record.run.assistantMessageId) {
2409
+ throw new TypeError("Stored agent run identity changed across normalized records");
2410
+ }
2411
+ records.set(record.run.id, record);
2412
+ }
2413
+ }
2414
+ return [...records.values()];
2415
+ }
2416
+ function referencedRunIds(messages) {
2417
+ return [...new Set(messages.flatMap((message) => message.runId ? [message.runId] : []))];
2418
+ }
2419
+ function validateAdmissionReceipt(receipt, record, conversationId) {
2420
+ const input = receipt.input;
2421
+ const run = record.run;
2422
+ 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)) {
2423
+ throw new TypeError("Admission receipt does not match its canonical run assignment");
2424
+ }
2425
+ }
2391
2426
  function createAgentRuntimeStore(driver) {
2392
2427
  const loadSnapshot = (conversationId) => driver.transaction(async (transaction) => {
2393
- const [stored, messages] = await Promise.all([
2394
- driver.state.load(transaction, conversationId),
2395
- driver.history.load(transaction, conversationId)
2428
+ const [stored, messages, activeRecords] = await Promise.all([
2429
+ driver.head.load(transaction, conversationId),
2430
+ driver.history.load(transaction, conversationId),
2431
+ driver.runs.listActive(transaction, conversationId)
2396
2432
  ]);
2397
- return snapshotOf(stored ?? emptyState(conversationId), messages);
2433
+ const head = AgentRuntimeHeadSchema.parse(stored ?? emptyHead(conversationId));
2434
+ const referencedRecords = await driver.runs.loadMany(transaction, {
2435
+ conversationId,
2436
+ runIds: referencedRunIds(messages)
2437
+ });
2438
+ return snapshotOf(head, messages, mergeRunRecords(activeRecords, referencedRecords));
2398
2439
  });
2399
2440
  const mutate = (operation) => driver.transaction(async (transaction) => {
2400
2441
  const conversationId = operationConversationId(operation);
2401
- const [stored, messages] = await Promise.all([
2402
- driver.state.load(transaction, conversationId),
2403
- driver.history.load(transaction, conversationId)
2442
+ const operationRunId = operation.type === "accept" ? operation.input.coalesceIntoRunId : operation.type === "compact" ? undefined : operation.input.runId;
2443
+ const [stored, messages, activeRecords, operationRecord, duplicateReceipt] = await Promise.all([
2444
+ driver.head.load(transaction, conversationId),
2445
+ driver.history.load(transaction, conversationId),
2446
+ driver.runs.listActive(transaction, conversationId),
2447
+ operationRunId ? driver.runs.load(transaction, { conversationId, runId: operationRunId }) : undefined,
2448
+ operation.type === "accept" ? driver.admissions.load(transaction, {
2449
+ conversationId,
2450
+ idempotencyKey: operation.input.idempotencyKey
2451
+ }) : undefined
2404
2452
  ]);
2405
- const state = AgentStoredStateSchema.parse(stored ?? emptyState(conversationId));
2406
- const current = snapshotOf(state, messages);
2407
- const duplicateIdentity = operation.type === "accept" ? state.admissions.find((candidate) => candidate.idempotencyKey === operation.input.idempotencyKey) : undefined;
2408
- const duplicateInput = duplicateIdentity ? await driver.history.loadById(transaction, {
2453
+ const head = AgentRuntimeHeadSchema.parse(stored ?? emptyHead(conversationId));
2454
+ const referencedRecords = await driver.runs.loadMany(transaction, {
2409
2455
  conversationId,
2410
- messageId: duplicateIdentity.inputMessageId
2411
- }) : undefined;
2412
- if (duplicateIdentity && duplicateInput && (duplicateInput.id !== duplicateIdentity.inputMessageId || duplicateInput.conversationId !== conversationId || duplicateInput.role !== "user" || duplicateInput.status !== "committed" || duplicateInput.runId !== undefined)) {
2413
- throw new TypeError("Canonical duplicate input does not match its admission identity");
2456
+ runIds: referencedRunIds(messages)
2457
+ });
2458
+ const records = mergeRunRecords(activeRecords, referencedRecords, operationRecord ? [operationRecord] : []);
2459
+ const current = snapshotOf(head, messages, records);
2460
+ if (duplicateReceipt) {
2461
+ const duplicateRecord = await driver.runs.load(transaction, {
2462
+ conversationId,
2463
+ runId: duplicateReceipt.runId
2464
+ });
2465
+ if (!duplicateRecord) {
2466
+ throw new TypeError("Admission receipt points to a missing canonical run");
2467
+ }
2468
+ validateAdmissionReceipt(duplicateReceipt, duplicateRecord, conversationId);
2469
+ return {
2470
+ outcome: "duplicate",
2471
+ input: duplicateReceipt.input,
2472
+ inputMessageId: duplicateReceipt.input.id,
2473
+ runId: duplicateReceipt.runId,
2474
+ assistantMessageId: duplicateReceipt.assistantMessageId,
2475
+ run: duplicateRecord.run,
2476
+ ...duplicateRecord.terminalAssistant && {
2477
+ assistant: duplicateRecord.terminalAssistant
2478
+ },
2479
+ snapshot: snapshotOf(head, messages, mergeRunRecords(records, [duplicateRecord]))
2480
+ };
2481
+ }
2482
+ if (operation.type === "accept") {
2483
+ const inputCollision = await driver.admissions.loadByInputMessageId(transaction, {
2484
+ conversationId,
2485
+ inputMessageId: operation.input.input.id
2486
+ });
2487
+ if (inputCollision) {
2488
+ throw new TypeError("Input message identity is already assigned to an admission");
2489
+ }
2490
+ if (!operation.input.coalesceIntoRunId) {
2491
+ const [runCollision, assistantCollision] = await Promise.all([
2492
+ driver.runs.load(transaction, {
2493
+ conversationId,
2494
+ runId: operation.input.run.id
2495
+ }),
2496
+ driver.runs.loadByAssistantMessageId(transaction, {
2497
+ conversationId,
2498
+ assistantMessageId: operation.input.run.assistantMessageId
2499
+ })
2500
+ ]);
2501
+ if (runCollision || assistantCollision) {
2502
+ throw new TypeError("Queued run identities are already reserved");
2503
+ }
2504
+ }
2414
2505
  }
2415
- const reduced = reduceStore(current, state.admissions, operation, duplicateInput);
2506
+ const reduced = reduceStore(current, operation);
2416
2507
  if (reduced.outcome !== "applied")
2417
2508
  return reduced;
2418
- const nextState = AgentStoredStateSchema.parse({
2509
+ const nextHead = AgentRuntimeHeadSchema.parse({
2419
2510
  schemaVersion: 1,
2420
2511
  conversationId,
2421
- version: reduced.snapshot.version,
2422
- runs: reduced.snapshot.runs,
2423
- admissions: reduced.admissions
2512
+ version: reduced.snapshot.version
2424
2513
  });
2425
- const outcome = await driver.state.compareAndSwap(transaction, {
2514
+ const outcome = await driver.head.compareAndSwap(transaction, {
2426
2515
  conversationId,
2427
2516
  expectedVersion: current.version,
2428
- next: nextState,
2429
- recoverable: recoverableDescriptors(nextState)
2517
+ next: nextHead
2430
2518
  });
2431
2519
  if (outcome.outcome === "conflict")
2432
2520
  return conflict(outcome.actualVersion);
2521
+ if (reduced.runRecord)
2522
+ await driver.runs.save(transaction, reduced.runRecord);
2523
+ if (reduced.admissionReceipt) {
2524
+ await driver.admissions.create(transaction, reduced.admissionReceipt);
2525
+ }
2433
2526
  if (reduced.historyMutation) {
2434
2527
  await driver.history.apply(transaction, reduced.historyMutation);
2435
2528
  }
@@ -2481,10 +2574,16 @@ function createAgentRuntimeStore(driver) {
2481
2574
  }
2482
2575
  };
2483
2576
  }
2484
- function cloneStateMap(source) {
2577
+ function cloneHeadMap(source) {
2485
2578
  return new Map([...source].map(([key, value]) => [
2486
2579
  key,
2487
- AgentStoredStateSchema.parse(structuredClone(value))
2580
+ AgentRuntimeHeadSchema.parse(structuredClone(value))
2581
+ ]));
2582
+ }
2583
+ function cloneNestedMap(source, clone) {
2584
+ return new Map([...source].map(([outerKey, values]) => [
2585
+ outerKey,
2586
+ new Map([...values].map(([innerKey, value]) => [innerKey, clone(value)]))
2488
2587
  ]));
2489
2588
  }
2490
2589
  function cloneHistoryMap(source) {
@@ -2494,9 +2593,10 @@ function cloneHistoryMap(source) {
2494
2593
  ]));
2495
2594
  }
2496
2595
  function createMemoryAgentRuntimeStore() {
2497
- let states = new Map;
2596
+ let heads = new Map;
2597
+ let runs = new Map;
2598
+ let admissions = new Map;
2498
2599
  let histories = new Map;
2499
- let archivedMessages = new Map;
2500
2600
  let transactionTail = Promise.resolve();
2501
2601
  const driver = {
2502
2602
  async transaction(work) {
@@ -2509,50 +2609,91 @@ function createMemoryAgentRuntimeStore() {
2509
2609
  return;
2510
2610
  });
2511
2611
  const transaction = {
2512
- states: cloneStateMap(states),
2513
- histories: cloneHistoryMap(histories),
2514
- archivedMessages: new Map([...archivedMessages].map(([conversationId, messages]) => [
2515
- conversationId,
2516
- new Map([...messages].map(([messageId, message]) => [
2517
- messageId,
2518
- AgentMessageSchema.parse(structuredClone(message))
2519
- ]))
2520
- ]))
2612
+ heads: cloneHeadMap(heads),
2613
+ runs: cloneNestedMap(runs, (record) => AgentStoredRunSchema.parse(structuredClone(record))),
2614
+ admissions: cloneNestedMap(admissions, (receipt) => AgentAdmissionReceiptSchema.parse(structuredClone(receipt))),
2615
+ histories: cloneHistoryMap(histories)
2521
2616
  };
2522
2617
  try {
2523
2618
  const result = await work(transaction);
2524
- states = transaction.states;
2619
+ heads = transaction.heads;
2620
+ runs = transaction.runs;
2621
+ admissions = transaction.admissions;
2525
2622
  histories = transaction.histories;
2526
- archivedMessages = transaction.archivedMessages;
2527
2623
  return result;
2528
2624
  } finally {
2529
2625
  release.resolve();
2530
2626
  }
2531
2627
  },
2532
- state: {
2628
+ head: {
2533
2629
  async load(transaction, conversationId) {
2534
- const state = transaction.states.get(conversationId);
2535
- return state ? AgentStoredStateSchema.parse(structuredClone(state)) : undefined;
2630
+ const head = transaction.heads.get(conversationId);
2631
+ return head ? AgentRuntimeHeadSchema.parse(structuredClone(head)) : undefined;
2536
2632
  },
2537
2633
  async compareAndSwap(transaction, input) {
2538
- const current = transaction.states.get(input.conversationId);
2634
+ const current = transaction.heads.get(input.conversationId);
2539
2635
  const actualVersion = current?.version ?? 0;
2540
2636
  if (actualVersion !== input.expectedVersion) {
2541
2637
  return { outcome: "conflict", actualVersion };
2542
2638
  }
2543
- transaction.states.set(input.conversationId, AgentStoredStateSchema.parse(structuredClone(input.next)));
2639
+ transaction.heads.set(input.conversationId, AgentRuntimeHeadSchema.parse(structuredClone(input.next)));
2544
2640
  return { outcome: "applied" };
2545
2641
  }
2546
2642
  },
2643
+ runs: {
2644
+ async load(transaction, input) {
2645
+ const record = transaction.runs.get(input.conversationId)?.get(input.runId);
2646
+ return record ? AgentStoredRunSchema.parse(structuredClone(record)) : undefined;
2647
+ },
2648
+ async loadByAssistantMessageId(transaction, input) {
2649
+ const record = [...transaction.runs.get(input.conversationId)?.values() ?? []].find((candidate) => candidate.run.assistantMessageId === input.assistantMessageId);
2650
+ return record ? AgentStoredRunSchema.parse(structuredClone(record)) : undefined;
2651
+ },
2652
+ async loadMany(transaction, input) {
2653
+ const records = transaction.runs.get(input.conversationId);
2654
+ return input.runIds.flatMap((runId) => {
2655
+ const record = records?.get(runId);
2656
+ return record ? [AgentStoredRunSchema.parse(structuredClone(record))] : [];
2657
+ });
2658
+ },
2659
+ async listActive(transaction, conversationId) {
2660
+ return [...transaction.runs.get(conversationId)?.values() ?? []].filter((record) => ["queued", "running", "interrupt_requested"].includes(record.run.state)).map((record) => AgentStoredRunSchema.parse(structuredClone(record)));
2661
+ },
2662
+ async save(transaction, rawRecord) {
2663
+ const record = AgentStoredRunSchema.parse(structuredClone(rawRecord));
2664
+ const conversationRuns = transaction.runs.get(record.run.conversationId) ?? new Map;
2665
+ const collision = [...conversationRuns.values()].find((candidate) => candidate.run.id !== record.run.id && candidate.run.assistantMessageId === record.run.assistantMessageId);
2666
+ if (collision)
2667
+ throw new TypeError("Assistant message identity is already reserved");
2668
+ conversationRuns.set(record.run.id, record);
2669
+ transaction.runs.set(record.run.conversationId, conversationRuns);
2670
+ }
2671
+ },
2672
+ admissions: {
2673
+ async load(transaction, input) {
2674
+ const receipt = transaction.admissions.get(input.conversationId)?.get(input.idempotencyKey);
2675
+ return receipt ? AgentAdmissionReceiptSchema.parse(structuredClone(receipt)) : undefined;
2676
+ },
2677
+ async loadByInputMessageId(transaction, input) {
2678
+ const receipt = [
2679
+ ...transaction.admissions.get(input.conversationId)?.values() ?? []
2680
+ ].find((candidate) => candidate.input.id === input.inputMessageId);
2681
+ return receipt ? AgentAdmissionReceiptSchema.parse(structuredClone(receipt)) : undefined;
2682
+ },
2683
+ async create(transaction, rawReceipt) {
2684
+ const receipt = AgentAdmissionReceiptSchema.parse(structuredClone(rawReceipt));
2685
+ const conversationAdmissions = transaction.admissions.get(receipt.conversationId) ?? new Map;
2686
+ if (conversationAdmissions.has(receipt.idempotencyKey) || [...conversationAdmissions.values()].some((candidate) => candidate.input.id === receipt.input.id)) {
2687
+ throw new TypeError("Admission identity is already reserved");
2688
+ }
2689
+ conversationAdmissions.set(receipt.idempotencyKey, receipt);
2690
+ transaction.admissions.set(receipt.conversationId, conversationAdmissions);
2691
+ }
2692
+ },
2547
2693
  history: {
2548
2694
  async load(transaction, conversationId) {
2549
2695
  return (transaction.histories.get(conversationId) ?? []).map((message) => AgentMessageSchema.parse(structuredClone(message)));
2550
2696
  },
2551
- async loadById(transaction, input) {
2552
- const active = (transaction.histories.get(input.conversationId) ?? []).find((message2) => message2.id === input.messageId);
2553
- const message = active ?? transaction.archivedMessages.get(input.conversationId)?.get(input.messageId);
2554
- return message ? AgentMessageSchema.parse(structuredClone(message)) : undefined;
2555
- },
2556
2697
  async apply(transaction, rawMutation) {
2557
2698
  const mutation = AgentHistoryMutationSchema.parse(rawMutation);
2558
2699
  const conversationId = mutation.type === "admit" ? mutation.input.conversationId : mutation.type === "upsert-assistant" ? mutation.message.conversationId : mutation.summary.conversationId;
@@ -2570,11 +2711,6 @@ function createMemoryAgentRuntimeStore() {
2570
2711
  const first = positions[0];
2571
2712
  if (first === undefined)
2572
2713
  throw new Error("Compaction history range disappeared");
2573
- const archive = transaction.archivedMessages.get(conversationId) ?? new Map;
2574
- for (const message of current.filter((candidate) => replaced.has(candidate.id))) {
2575
- archive.set(message.id, AgentMessageSchema.parse(structuredClone(message)));
2576
- }
2577
- transaction.archivedMessages.set(conversationId, archive);
2578
2714
  transaction.histories.set(conversationId, [
2579
2715
  ...current.slice(0, first),
2580
2716
  mutation.summary,
@@ -2583,7 +2719,7 @@ function createMemoryAgentRuntimeStore() {
2583
2719
  }
2584
2720
  },
2585
2721
  async scanRecoverable(input) {
2586
- const descriptors = [...states.values()].flatMap((state) => state.runs.filter((run) => ["queued", "running", "interrupt_requested"].includes(run.state)).map((run) => ({ conversationId: state.conversationId, run }))).sort((left, right) => left.conversationId.localeCompare(right.conversationId) || left.run.id.localeCompare(right.run.id));
2722
+ const descriptors = [...runs].flatMap(([conversationId, conversationRuns]) => [...conversationRuns.values()].filter((record) => ["queued", "running", "interrupt_requested"].includes(record.run.state)).map((record) => ({ conversationId, run: record.run }))).sort((left, right) => left.conversationId.localeCompare(right.conversationId) || left.run.id.localeCompare(right.run.id));
2587
2723
  const cursorTuple = input.cursor ? parseRecoverableCursor(input.cursor) : undefined;
2588
2724
  const start = cursorTuple ? descriptors.findIndex((item) => item.conversationId === cursorTuple[0] && item.run.id === cursorTuple[1]) + 1 : 0;
2589
2725
  const items = descriptors.slice(start, start + input.limit);
@@ -2601,7 +2737,7 @@ export {
2601
2737
  AcceptInputAndAssignRunSchema,
2602
2738
  AcquireAgentRunSchema,
2603
2739
  AgentAdmissionEventSchema,
2604
- AgentAdmissionIdentitySchema,
2740
+ AgentAdmissionReceiptSchema,
2605
2741
  AgentAssistantPlaceholderSchema,
2606
2742
  AgentCheckpointEventSchema,
2607
2743
  AgentControlPartSchema,
@@ -2633,6 +2769,7 @@ export {
2633
2769
  AgentRunStateSchema,
2634
2770
  AgentRuntimeEventCursorSchema,
2635
2771
  AgentRuntimeEventSchema,
2772
+ AgentRuntimeHeadSchema,
2636
2773
  AgentSnapshotSchema,
2637
2774
  AgentSourcePartSchema,
2638
2775
  AgentStoreAppliedSchema,
@@ -2640,7 +2777,7 @@ export {
2640
2777
  AgentStoreDuplicateSchema,
2641
2778
  AgentStoreMutationResultSchema,
2642
2779
  AgentStoreNotFoundSchema,
2643
- AgentStoredStateSchema,
2780
+ AgentStoredRunSchema,
2644
2781
  AgentTerminalEventSchema,
2645
2782
  AgentTerminalReasonSchema,
2646
2783
  AgentTextPartSchema,