stitchkit 0.84.1 → 0.85.1

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 (32) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/agent-runtime/control-schema.d.ts +61 -0
  3. package/dist/agent-runtime/control-schema.d.ts.map +1 -1
  4. package/dist/agent-runtime/event-schema.d.ts +88 -1
  5. package/dist/agent-runtime/event-schema.d.ts.map +1 -1
  6. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  7. package/dist/agent-runtime/run-operation-lifecycle.d.ts +25 -0
  8. package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -0
  9. package/dist/agent-runtime/schemas.d.ts +67 -0
  10. package/dist/agent-runtime/schemas.d.ts.map +1 -1
  11. package/dist/agent-runtime/store-driver.d.ts +18 -0
  12. package/dist/agent-runtime/store-driver.d.ts.map +1 -1
  13. package/dist/agent-runtime/store.d.ts +208 -0
  14. package/dist/agent-runtime/store.d.ts.map +1 -1
  15. package/dist/agent-runtime/terminal-commit.d.ts +9 -0
  16. package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
  17. package/dist/agent-runtime-browser.js +11 -3
  18. package/dist/agent-runtime-harness.js +4 -4
  19. package/dist/agent-runtime-sqlite-bun.js +3 -3
  20. package/dist/agent-runtime-sqlite-node.js +3 -3
  21. package/dist/agent-runtime.d.ts +2 -2
  22. package/dist/agent-runtime.d.ts.map +1 -1
  23. package/dist/agent-runtime.js +14 -4
  24. package/dist/{index-85vfqd7m.js → index-4hk633vz.js} +11 -3
  25. package/dist/{index-devfkwfm.js → index-66nxrgy0.js} +2 -2
  26. package/dist/{index-5s3zajp8.js → index-ff0kcqvp.js} +9 -3
  27. package/dist/{index-11vzj2sk.js → index-fq492hg0.js} +28 -2
  28. package/dist/{index-st8v8739.js → index-jpp7jd1p.js} +206 -26
  29. package/dist/{index-x1th9s8c.js → index-x15ss2dx.js} +50 -1
  30. package/dist/testing.js +26 -2
  31. package/llms-full.txt +61 -5
  32. package/package.json +2 -2
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  AgentRuntimeEventSchema
3
- } from "./index-85vfqd7m.js";
3
+ } from "./index-4hk633vz.js";
4
4
  import {
5
5
  AgentMessagePartSchema,
6
6
  AgentSnapshotSchema
7
- } from "./index-x1th9s8c.js";
7
+ } from "./index-x15ss2dx.js";
8
8
 
9
9
  // src/agent-runtime/control-schema.ts
10
10
  import { z } from "zod";
@@ -82,7 +82,13 @@ var AgentMultiSessionCursorSchema = z.object({
82
82
  }).strict())
83
83
  }).strict();
84
84
  function durable(event) {
85
- return ["admission", "assistant-checkpoint", "run-state", "terminal"].includes(event.type);
85
+ return [
86
+ "admission",
87
+ "assistant-checkpoint",
88
+ "run-state",
89
+ "run-operation",
90
+ "terminal"
91
+ ].includes(event.type);
86
92
  }
87
93
  function advanceAgentMultiSessionCursor(raw, event) {
88
94
  const cursor = AgentMultiSessionCursorSchema.parse(raw);
@@ -5,12 +5,13 @@ import {
5
5
  AgentMessageSchema,
6
6
  AgentRecordIdSchema,
7
7
  AgentRecordVersionSchema,
8
+ AgentRunOperationSchema,
8
9
  AgentRunSchema,
9
10
  AgentSnapshotSchema,
10
11
  AgentTerminalReasonSchema,
11
12
  AgentUsageSchema,
12
13
  runStateForTerminalReason
13
- } from "./index-x1th9s8c.js";
14
+ } from "./index-x15ss2dx.js";
14
15
 
15
16
  // src/agent-runtime/conversations.ts
16
17
  import { z } from "zod";
@@ -114,6 +115,14 @@ var CheckpointRunAssistantSchema = z3.object({
114
115
  assistant: AgentMessageSchema,
115
116
  usage: AgentUsageSchema.optional()
116
117
  });
118
+ var RecordRunOperationSchema = z3.object({
119
+ conversationId: AgentRecordIdSchema,
120
+ runId: AgentRecordIdSchema,
121
+ expectedRevision: AgentRecordVersionSchema,
122
+ ownerId: z3.string().min(1),
123
+ fencingToken: AgentRecordVersionSchema.optional(),
124
+ operation: AgentRunOperationSchema
125
+ });
117
126
  var CommitRunTerminalSchema = z3.object({
118
127
  conversationId: AgentRecordIdSchema,
119
128
  runId: AgentRecordIdSchema,
@@ -437,6 +446,19 @@ function reduceStore(current, operation) {
437
446
  historyMutations: [{ type: "upsert-assistant", message: input.assistant }]
438
447
  });
439
448
  }
449
+ if (operation.type === "operation" && run) {
450
+ const input = operation.input;
451
+ if (run.revision !== input.expectedRevision || run.state !== "running" && run.state !== "interrupt_requested" || run.ownerId !== input.ownerId || input.fencingToken !== undefined && run.fencingToken !== input.fencingToken) {
452
+ return conflict(run.revision);
453
+ }
454
+ const next = AgentRunSchema.parse({
455
+ ...run,
456
+ lastOperation: input.operation,
457
+ revision: run.revision + 1,
458
+ updatedAt: new Date().toISOString()
459
+ });
460
+ return applied(current, { runs: replaceRun(current.runs, next) }, { runRecords: [AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })] });
461
+ }
440
462
  if (operation.type === "interrupt" && run) {
441
463
  if (run.revision !== operation.input.expectedRevision || run.state !== "running") {
442
464
  return conflict(run.revision);
@@ -794,6 +816,10 @@ function createAgentRuntimeStore(driver) {
794
816
  type: "checkpoint",
795
817
  input: CheckpointRunAssistantSchema.parse(input)
796
818
  }),
819
+ recordRunOperation: (input) => mutate({
820
+ type: "operation",
821
+ input: RecordRunOperationSchema.parse(input)
822
+ }),
797
823
  requestRunInterrupt: (input) => mutate({
798
824
  type: "interrupt",
799
825
  input: RequestRunInterruptSchema.parse(input)
@@ -988,4 +1014,4 @@ function createMemoryAgentRuntimeStore() {
988
1014
  return createAgentRuntimeStore(driver);
989
1015
  }
990
1016
 
991
- export { AgentConversationSummarySchema, AgentConversationPageSchema, AgentConversationMessagePageSchema, AgentConversationPurgeInputSchema, AgentConversationPurgeResultSchema, AgentConversationPurgedError, purgeAgentConversation, AgentStoreConflictSchema, AgentStoreNotFoundSchema, AgentStoreAppliedSchema, AgentStoreDuplicateSchema, AgentStoreMutationResultSchema, AgentRunViewSchema, AcceptInputAndAssignRunSchema, AcquireAgentRunSchema, CheckpointRunAssistantSchema, CommitRunTerminalSchema, RequestRunInterruptSchema, RecoverAgentRunSchema, ReplaceCompactedRangeSchema, AgentRecoverableDescriptorSchema, AgentRecoverablePageSchema, AgentRuntimeHeadSchema, AgentStoredRunSchema, AgentAdmissionReceiptSchema, AgentHistoryMutationSchema, ACTIVE_AGENT_RUN_STATES, createAgentRuntimeStore, createMemoryAgentRuntimeStore };
1017
+ export { AgentConversationSummarySchema, AgentConversationPageSchema, AgentConversationMessagePageSchema, AgentConversationPurgeInputSchema, AgentConversationPurgeResultSchema, AgentConversationPurgedError, purgeAgentConversation, AgentStoreConflictSchema, AgentStoreNotFoundSchema, AgentStoreAppliedSchema, AgentStoreDuplicateSchema, AgentStoreMutationResultSchema, AgentRunViewSchema, AcceptInputAndAssignRunSchema, AcquireAgentRunSchema, CheckpointRunAssistantSchema, RecordRunOperationSchema, CommitRunTerminalSchema, RequestRunInterruptSchema, RecoverAgentRunSchema, ReplaceCompactedRangeSchema, AgentRecoverableDescriptorSchema, AgentRecoverablePageSchema, AgentRuntimeHeadSchema, AgentStoredRunSchema, AgentAdmissionReceiptSchema, AgentHistoryMutationSchema, ACTIVE_AGENT_RUN_STATES, createAgentRuntimeStore, createMemoryAgentRuntimeStore };
@@ -12,17 +12,18 @@ import {
12
12
  import {
13
13
  AgentRuntimeEventSchema,
14
14
  agentDurableEventId
15
- } from "./index-85vfqd7m.js";
15
+ } from "./index-4hk633vz.js";
16
16
  import {
17
17
  AgentAssistantPlaceholderSchema,
18
18
  AgentJsonObjectSchema,
19
19
  AgentMessagePartSchema,
20
20
  AgentMessageSchema,
21
21
  AgentProvenanceSchema,
22
+ AgentRunOperationSchema,
22
23
  AgentRunSchema,
23
24
  AgentSnapshotSchema,
24
25
  runStateForTerminalReason
25
- } from "./index-x1th9s8c.js";
26
+ } from "./index-x15ss2dx.js";
26
27
  import {
27
28
  isAgentToolError
28
29
  } from "./index-3xnq72rz.js";
@@ -933,6 +934,9 @@ function repairedSearchCall(toolCall, searchName) {
933
934
  };
934
935
  }
935
936
 
937
+ // src/agent-runtime/run-operation-lifecycle.ts
938
+ import { wrapLanguageModel } from "ai";
939
+
936
940
  // src/agent-runtime/runtime-internals.ts
937
941
  import { z as z2 } from "zod";
938
942
  function findRun(runs, runId) {
@@ -1074,6 +1078,117 @@ function createIdleDeadline(parent, timeoutMs) {
1074
1078
  };
1075
1079
  }
1076
1080
 
1081
+ // src/agent-runtime/run-operation-lifecycle.ts
1082
+ function createAgentRunOperationLifecycle(config) {
1083
+ let active;
1084
+ let providerCallId;
1085
+ let checkpointedStep = -1;
1086
+ const checkpointWaiters = new Map;
1087
+ const record = async (operation) => {
1088
+ const current = config.currentRun();
1089
+ const snapshot = appliedSnapshot(await config.store.recordRunOperation({
1090
+ conversationId: current.conversationId,
1091
+ runId: current.id,
1092
+ expectedRevision: current.revision,
1093
+ ownerId: config.runtimeEpoch,
1094
+ ...current.fencingToken !== undefined && { fencingToken: current.fencingToken },
1095
+ operation
1096
+ }), "run operation");
1097
+ config.acceptSnapshot(snapshot);
1098
+ const run = findRun(snapshot.runs, current.id);
1099
+ active = operation;
1100
+ await config.publish({
1101
+ type: "run-operation",
1102
+ eventId: agentDurableEventId("run-operation", run.id, snapshot.version),
1103
+ conversationId: run.conversationId,
1104
+ runId: run.id,
1105
+ snapshotVersion: snapshot.version,
1106
+ operation,
1107
+ emittedAt: config.now().toISOString()
1108
+ });
1109
+ };
1110
+ const startModelRequest = (callId, step) => record(AgentRunOperationSchema.parse({
1111
+ operationId: `${callId}:${step}`,
1112
+ kind: "model-request",
1113
+ phase: "started",
1114
+ step,
1115
+ startedAt: config.now().toISOString()
1116
+ }));
1117
+ return {
1118
+ noteProviderCall(callId) {
1119
+ providerCallId = callId;
1120
+ },
1121
+ async prepareModel(model, step, fallbackCallId) {
1122
+ if (step > checkpointedStep + 1) {
1123
+ const waiter = checkpointWaiters.get(step - 1) ?? Promise.withResolvers();
1124
+ checkpointWaiters.set(step - 1, waiter);
1125
+ await waiter.promise;
1126
+ }
1127
+ if (typeof model === "string") {
1128
+ await startModelRequest(fallbackCallId, step);
1129
+ return model;
1130
+ }
1131
+ return wrapLanguageModel({
1132
+ model,
1133
+ middleware: {
1134
+ specificationVersion: "v4",
1135
+ wrapStream: async ({ doStream }) => {
1136
+ const callId = providerCallId;
1137
+ providerCallId = undefined;
1138
+ if (!callId) {
1139
+ throw new Error("AgentRuntime model call reached the provider without lifecycle identity");
1140
+ }
1141
+ await startModelRequest(callId, step);
1142
+ return doStream();
1143
+ }
1144
+ }
1145
+ });
1146
+ },
1147
+ checkpointStep(step) {
1148
+ checkpointedStep = Math.max(checkpointedStep, step);
1149
+ for (const [waitingFor, waiter] of checkpointWaiters) {
1150
+ if (waitingFor > checkpointedStep)
1151
+ continue;
1152
+ checkpointWaiters.delete(waitingFor);
1153
+ waiter.resolve();
1154
+ }
1155
+ },
1156
+ failStepCheckpoints(error) {
1157
+ for (const waiter of checkpointWaiters.values())
1158
+ waiter.reject(error);
1159
+ checkpointWaiters.clear();
1160
+ },
1161
+ startCompaction(operationId) {
1162
+ return record(AgentRunOperationSchema.parse({
1163
+ operationId,
1164
+ kind: "compaction",
1165
+ phase: "started",
1166
+ startedAt: config.now().toISOString()
1167
+ }));
1168
+ },
1169
+ firstOutput() {
1170
+ if (active?.kind !== "model-request" || active.phase !== "started") {
1171
+ return Promise.resolve();
1172
+ }
1173
+ return record(AgentRunOperationSchema.parse({
1174
+ ...active,
1175
+ phase: "first-output",
1176
+ firstOutputAt: config.now().toISOString()
1177
+ }));
1178
+ },
1179
+ finish(phase) {
1180
+ if (!active || ["completed", "failed", "cancelled"].includes(active.phase)) {
1181
+ return Promise.resolve();
1182
+ }
1183
+ return record(AgentRunOperationSchema.parse({
1184
+ ...active,
1185
+ phase,
1186
+ finishedAt: config.now().toISOString()
1187
+ }));
1188
+ }
1189
+ };
1190
+ }
1191
+
1077
1192
  // src/agent-runtime/run-execution.ts
1078
1193
  function snapshotForRunPrompt(snapshot, runId) {
1079
1194
  const runIndex = snapshot.runs.findIndex((candidate) => candidate.id === runId);
@@ -1184,7 +1299,7 @@ function createRunExecutor(dependencies) {
1184
1299
  let observedVersion = snapshot.version;
1185
1300
  const parts = [];
1186
1301
  const absorbed = new Map;
1187
- let eventCount = 0;
1302
+ let eventsSinceCheckpoint = 0;
1188
1303
  let sequence = 0;
1189
1304
  let terminalReason = "success";
1190
1305
  let nonModelUsage = input.acceptedRun.usage;
@@ -1199,6 +1314,18 @@ function createRunExecutor(dependencies) {
1199
1314
  let terminalPolicyName;
1200
1315
  const idleDeadline = createIdleDeadline(input.signal, idleTimeoutMs);
1201
1316
  const executionSignal = idleDeadline.signal;
1317
+ const operationLifecycle = createAgentRunOperationLifecycle({
1318
+ store: config.store,
1319
+ runtimeEpoch,
1320
+ currentRun: () => run,
1321
+ acceptSnapshot: (next) => {
1322
+ snapshot = next;
1323
+ observedVersion = next.version;
1324
+ run = findRun(next.runs, run.id);
1325
+ },
1326
+ publish,
1327
+ now
1328
+ });
1202
1329
  const updateReasoning = (text, metadata) => {
1203
1330
  const provider = providerEnvelope(metadata);
1204
1331
  if (reasoningPartIndex === undefined) {
@@ -1256,14 +1383,30 @@ function createRunExecutor(dependencies) {
1256
1383
  };
1257
1384
  try {
1258
1385
  if (config.history?.compact) {
1259
- const compacted = await config.history.compact({
1260
- conversationId: run.conversationId,
1261
- store: config.store,
1262
- signal: executionSignal
1263
- });
1264
- snapshot = compacted.snapshot;
1265
- observedVersion = snapshot.version;
1266
- run = findRun(snapshot.runs, run.id);
1386
+ await operationLifecycle.startCompaction(generateId());
1387
+ let compacted;
1388
+ try {
1389
+ compacted = await config.history.compact({
1390
+ conversationId: run.conversationId,
1391
+ store: config.store,
1392
+ signal: executionSignal
1393
+ });
1394
+ snapshot = compacted.snapshot;
1395
+ observedVersion = snapshot.version;
1396
+ run = findRun(snapshot.runs, run.id);
1397
+ await operationLifecycle.finish("completed");
1398
+ } catch (error) {
1399
+ const latest = await config.store.loadRun({
1400
+ conversationId: run.conversationId,
1401
+ runId: run.id
1402
+ });
1403
+ if (latest?.run.ownerId === runtimeEpoch) {
1404
+ observedVersion = latest.snapshotVersion;
1405
+ run = latest.run;
1406
+ }
1407
+ await operationLifecycle.finish(executionSignal.aborted ? "cancelled" : "failed");
1408
+ throw error;
1409
+ }
1267
1410
  if (compacted.usage) {
1268
1411
  nonModelUsage = addUsage(nonModelUsage, compacted.usage);
1269
1412
  usage = modelUsage ? addUsage(nonModelUsage, modelUsage) : nonModelUsage;
@@ -1410,14 +1553,18 @@ function createRunExecutor(dependencies) {
1410
1553
  }
1411
1554
  return messages;
1412
1555
  };
1556
+ const fallbackModel = selectedModel.model;
1413
1557
  const result = streamAgentTextBoundary({
1414
- model: selectedModel.model,
1558
+ model: fallbackModel,
1415
1559
  tools,
1416
1560
  instructions: withCarriedSystem(prompt.instructions),
1417
1561
  messages: history,
1418
1562
  abortSignal: executionSignal,
1419
1563
  maxRetries: 0,
1420
1564
  stopWhen: stopConditions,
1565
+ onLanguageModelCallStart: (event) => {
1566
+ operationLifecycle.noteProviderCall(event.callId);
1567
+ },
1421
1568
  repairToolCall: deferredToolRepair(config.loop?.prepareStep),
1422
1569
  ...config.loop?.toolApproval && {
1423
1570
  toolApproval: config.loop.toolApproval,
@@ -1426,27 +1573,39 @@ function createRunExecutor(dependencies) {
1426
1573
  ...config.loop?.toolApprovalSecret && {
1427
1574
  experimental_toolApprovalSecret: config.loop.toolApprovalSecret
1428
1575
  },
1429
- ...(config.loop?.prepareStep || injection) && {
1430
- prepareStep: async (options) => {
1431
- const prepared = await config.loop?.prepareStep?.({
1432
- ...options,
1433
- ...runtimeContext
1434
- });
1435
- const injected = await takeInjectedMessages();
1436
- if (injected.length === 0)
1437
- return prepared;
1438
- const base = prepared?.messages ?? options.messages;
1439
- return { ...prepared, messages: [...base, ...injected] };
1576
+ prepareStep: async (options) => {
1577
+ const previousStep = options.steps.at(-1);
1578
+ if (previousStep) {
1579
+ lastPromptTokens = selectedModel?.normalizeUsage?.({
1580
+ usage: previousStep.usage,
1581
+ providerMetadata: previousStep.providerMetadata
1582
+ })?.inputTokens ?? normalizeSdkUsage(previousStep.usage).inputTokens;
1440
1583
  }
1584
+ const prepared = await config.loop?.prepareStep?.({
1585
+ ...options,
1586
+ ...runtimeContext
1587
+ });
1588
+ const injected = await takeInjectedMessages();
1589
+ const preparedStep = injected.length === 0 ? prepared : {
1590
+ ...prepared,
1591
+ messages: [...prepared?.messages ?? options.messages, ...injected]
1592
+ };
1593
+ return {
1594
+ ...preparedStep,
1595
+ model: await operationLifecycle.prepareModel(preparedStep?.model ?? fallbackModel, options.stepNumber, generateId())
1596
+ };
1441
1597
  }
1442
1598
  });
1443
1599
  for await (const part of result.stream) {
1444
1600
  idleDeadline.touch();
1445
- eventCount += 1;
1601
+ eventsSinceCheckpoint += 1;
1446
1602
  sequence += 1;
1447
- if (firstOutputAt === undefined && ["text-delta", "reasoning-delta", "tool-call", "file", "source"].includes(part.type)) {
1603
+ if (firstOutputAt === undefined && (part.type === "text-delta" && part.text.length > 0 || part.type === "reasoning-delta" && part.text.length > 0 || part.type === "tool-input-delta" && part.delta.length > 0 || part.type === "tool-call")) {
1448
1604
  firstOutputAt = performance.now();
1449
1605
  }
1606
+ if (part.type === "text-delta" && part.text.length > 0 || part.type === "reasoning-delta" && part.text.length > 0 || part.type === "tool-input-delta" && part.delta.length > 0 || part.type === "tool-call") {
1607
+ await operationLifecycle.firstOutput();
1608
+ }
1450
1609
  if (part.type === "text-delta") {
1451
1610
  appendText(parts, part.text);
1452
1611
  await publish({
@@ -1552,6 +1711,7 @@ function createRunExecutor(dependencies) {
1552
1711
  status: "interrupted",
1553
1712
  emittedAt: now().toISOString()
1554
1713
  });
1714
+ await checkpoint();
1555
1715
  throw part.error;
1556
1716
  }
1557
1717
  const output = isAgentToolError(part.error) ? jsonValue(part.error.output) : { message: "Tool execution failed" };
@@ -1661,6 +1821,7 @@ function createRunExecutor(dependencies) {
1661
1821
  terminalReason = part.error instanceof AgentContextOverflowError ? "context_overflow" : "provider_failure";
1662
1822
  internalCause = part.error;
1663
1823
  } else if (part.type === "finish-step") {
1824
+ await operationLifecycle.finish(part.finishReason === "error" ? "failed" : "completed");
1664
1825
  const stepTrace = trace ? config.observe?.rootTrace(trace) : undefined;
1665
1826
  const stepUsage = selectedModel.normalizeUsage?.({
1666
1827
  usage: part.usage,
@@ -1696,9 +1857,23 @@ function createRunExecutor(dependencies) {
1696
1857
  modelUsage = mergeModelTotals(normalizeSdkUsage(part.totalUsage), modelUsage);
1697
1858
  usage = nonModelUsage ? addUsage(nonModelUsage, modelUsage) : modelUsage;
1698
1859
  }
1699
- if (eventCount % checkpointEveryEvents === 0)
1860
+ const structuralBoundary = [
1861
+ "tool-call",
1862
+ "tool-result",
1863
+ "tool-error",
1864
+ "tool-output-denied",
1865
+ "tool-approval-request",
1866
+ "tool-approval-response",
1867
+ "finish-step"
1868
+ ].includes(part.type);
1869
+ if (structuralBoundary || eventsSinceCheckpoint >= checkpointEveryEvents) {
1700
1870
  await checkpoint();
1871
+ eventsSinceCheckpoint = 0;
1872
+ if (part.type === "finish-step")
1873
+ operationLifecycle.checkpointStep(step - 1);
1874
+ }
1701
1875
  }
1876
+ await operationLifecycle.finish(executionSignal.aborted ? "cancelled" : terminalReason === "provider_failure" ? "failed" : "completed");
1702
1877
  if (terminalPolicyName !== undefined)
1703
1878
  terminalReason = "policy_stop";
1704
1879
  if (executionSignal.aborted)
@@ -1721,6 +1896,7 @@ function createRunExecutor(dependencies) {
1721
1896
  }
1722
1897
  }
1723
1898
  } catch (error) {
1899
+ operationLifecycle.failStepCheckpoints(error);
1724
1900
  internalCause = error;
1725
1901
  const latest = await config.store.loadRun({
1726
1902
  conversationId: run.conversationId,
@@ -1732,6 +1908,10 @@ function createRunExecutor(dependencies) {
1732
1908
  observedVersion = latest.snapshotVersion;
1733
1909
  run = latestRun;
1734
1910
  }
1911
+ const staleOwner = isToolExecutionControlError(error) && error.reason === "stale_run";
1912
+ if (!staleOwner) {
1913
+ await operationLifecycle.finish(executionSignal.aborted || durableInterrupt ? "cancelled" : "failed");
1914
+ }
1735
1915
  if (isToolExecutionControlError(error) || executionSignal.aborted || durableInterrupt) {
1736
1916
  terminalReason = executionSignal.aborted ? abortTerminalReason(executionSignal) : "interrupted";
1737
1917
  parts.push(AgentMessagePartSchema.parse({
@@ -125,6 +125,54 @@ var AgentRunStateSchema = z.enum([
125
125
  "abandoned"
126
126
  ]);
127
127
  var AgentRunQueuePrioritySchema = z.enum(["interrupt-next"]);
128
+ var AgentRunOperationKindSchema = z.enum(["model-request", "compaction"]);
129
+ var AgentRunOperationPhaseSchema = z.enum([
130
+ "started",
131
+ "first-output",
132
+ "completed",
133
+ "failed",
134
+ "cancelled"
135
+ ]);
136
+ var AgentRunOperationFieldsSchema = z.object({
137
+ operationId: AgentRecordIdSchema,
138
+ kind: AgentRunOperationKindSchema,
139
+ phase: AgentRunOperationPhaseSchema,
140
+ step: z.int().nonnegative().optional(),
141
+ startedAt: AgentTimestampSchema,
142
+ firstOutputAt: AgentTimestampSchema.optional(),
143
+ finishedAt: AgentTimestampSchema.optional()
144
+ });
145
+ var AgentRunOperationSchema = AgentRunOperationFieldsSchema.superRefine((operation, ctx) => {
146
+ if (operation.kind === "model-request" !== (operation.step !== undefined)) {
147
+ ctx.addIssue({
148
+ code: "custom",
149
+ path: ["step"],
150
+ message: "Only a model request has a step identity"
151
+ });
152
+ }
153
+ if (operation.kind === "compaction" && operation.phase === "first-output") {
154
+ ctx.addIssue({
155
+ code: "custom",
156
+ path: ["phase"],
157
+ message: "Compaction has no first-output phase"
158
+ });
159
+ }
160
+ if (operation.phase === "first-output" && operation.firstOutputAt === undefined || operation.phase === "started" && operation.firstOutputAt !== undefined || operation.kind === "compaction" && operation.firstOutputAt !== undefined) {
161
+ ctx.addIssue({
162
+ code: "custom",
163
+ path: ["firstOutputAt"],
164
+ message: "Only model output phases carry the observed first-output timestamp"
165
+ });
166
+ }
167
+ const terminal = ["completed", "failed", "cancelled"].includes(operation.phase);
168
+ if (terminal !== (operation.finishedAt !== undefined)) {
169
+ ctx.addIssue({
170
+ code: "custom",
171
+ path: ["finishedAt"],
172
+ message: "A terminal operation phase carries its observed finish timestamp"
173
+ });
174
+ }
175
+ });
128
176
  var AgentTerminalReasonSchema = z.enum([
129
177
  "success",
130
178
  "policy_stop",
@@ -204,6 +252,7 @@ var AgentRunFieldsSchema = z.object({
204
252
  terminalPolicyName: z.string().min(1).optional(),
205
253
  absorbedIntoRunId: AgentRecordIdSchema.optional(),
206
254
  usage: AgentUsageSchema.optional(),
255
+ lastOperation: AgentRunOperationSchema.optional(),
207
256
  createdAt: AgentTimestampSchema,
208
257
  updatedAt: AgentTimestampSchema
209
258
  });
@@ -277,4 +326,4 @@ var AgentRunMetricsSchema = z.object({
277
326
  ttftMs: z.number().nonnegative().optional()
278
327
  });
279
328
 
280
- export { AgentRecordIdSchema, AgentRecordVersionSchema, AgentTimestampSchema, AgentJsonObjectSchema, AgentProviderEnvelopeSchema, AgentTextPartSchema, AgentReasoningPartSchema, AgentFilePartSchema, AgentSourcePartSchema, AgentToolCallPartSchema, AgentToolResultPartSchema, AgentToolApprovalRequestPartSchema, AgentToolApprovalResponsePartSchema, AgentOpaquePartSchema, AgentControlPartSchema, AgentMessagePartSchema, AgentMessageRoleSchema, AgentMessageStatusSchema, AgentMessageSchema, AgentAssistantPlaceholderSchema, AgentRunStateSchema, AgentRunQueuePrioritySchema, AgentTerminalReasonSchema, runStateForTerminalReason, AgentProvenanceSchema, AgentUsageValueSchema, AgentCostValueSchema, AgentUsageSchema, AgentRunSchema, AgentSnapshotSchema, AgentRunMetricsSchema };
329
+ export { AgentRecordIdSchema, AgentRecordVersionSchema, AgentTimestampSchema, AgentJsonObjectSchema, AgentProviderEnvelopeSchema, AgentTextPartSchema, AgentReasoningPartSchema, AgentFilePartSchema, AgentSourcePartSchema, AgentToolCallPartSchema, AgentToolResultPartSchema, AgentToolApprovalRequestPartSchema, AgentToolApprovalResponsePartSchema, AgentOpaquePartSchema, AgentControlPartSchema, AgentMessagePartSchema, AgentMessageRoleSchema, AgentMessageStatusSchema, AgentMessageSchema, AgentAssistantPlaceholderSchema, AgentRunStateSchema, AgentRunQueuePrioritySchema, AgentRunOperationKindSchema, AgentRunOperationPhaseSchema, AgentRunOperationSchema, AgentTerminalReasonSchema, runStateForTerminalReason, AgentProvenanceSchema, AgentUsageValueSchema, AgentCostValueSchema, AgentUsageSchema, AgentRunSchema, AgentSnapshotSchema, AgentRunMetricsSchema };
package/dist/testing.js CHANGED
@@ -7,7 +7,7 @@ import"./index-3z73fh2c.js";
7
7
  import {
8
8
  AgentMessageSchema,
9
9
  AgentRunSchema
10
- } from "./index-x1th9s8c.js";
10
+ } from "./index-x15ss2dx.js";
11
11
  import {
12
12
  RealtimeRejectDirectionSchema,
13
13
  RealtimeRejectFaultSchema,
@@ -450,10 +450,34 @@ async function conformanceScenario(store, conversationIds) {
450
450
  if (!recoverable.items.some((item) => item.run.id === running.id)) {
451
451
  throw new Error("A running run must appear in a recoverable scan");
452
452
  }
453
+ const operation = await store.recordRunOperation({
454
+ conversationId,
455
+ runId: running.id,
456
+ expectedRevision: checkpointedRun.revision,
457
+ ownerId: "conformance-owner",
458
+ fencingToken: checkpointedRun.fencingToken,
459
+ operation: {
460
+ operationId: "model-call-1:0",
461
+ kind: "model-request",
462
+ phase: "first-output",
463
+ step: 0,
464
+ startedAt: "2026-08-22T00:00:01.800Z",
465
+ firstOutputAt: "2026-08-22T00:00:01.900Z"
466
+ }
467
+ });
468
+ requireOutcome(operation, "applied");
469
+ const operatedRun = operation.snapshot.runs.find((run) => run.id === running.id);
470
+ if (operatedRun?.lastOperation?.operationId !== "model-call-1:0") {
471
+ throw new Error("Run operation identity did not survive its durable mutation");
472
+ }
473
+ const operatedView = await store.loadRun({ conversationId, runId: running.id });
474
+ if (operatedView?.run.lastOperation?.startedAt !== "2026-08-22T00:00:01.800Z") {
475
+ throw new Error("loadRun did not retain the original operation timestamp");
476
+ }
453
477
  const interrupted = await store.requestRunInterrupt({
454
478
  conversationId,
455
479
  runId: running.id,
456
- expectedRevision: checkpointedRun.revision
480
+ expectedRevision: operatedRun.revision
457
481
  });
458
482
  requireOutcome(interrupted, "applied");
459
483
  const interruptedRun = interrupted.snapshot.runs.find((run) => run.id === running.id);