@yielded/agent 0.1.0-beta.174 → 0.1.0-beta.176

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 (177) hide show
  1. package/dist/{Admin-BHmp2V9p.d.mts → Admin-DTDihBra.d.mts} +50 -13
  2. package/dist/{AgentRuntime-BD3ZHAMq.d.mts → AgentRuntime-CHDyRbSf.d.mts} +3 -3
  3. package/dist/{AgentRuntime-B6qZlD3f.mjs → AgentRuntime-DaDKMasp.mjs} +123 -114
  4. package/dist/AgentRuntime-DaDKMasp.mjs.map +1 -0
  5. package/dist/{Digest-EpeqWEdv.mjs → Digest-C3EODOee.mjs} +2 -2
  6. package/dist/{Digest-EpeqWEdv.mjs.map → Digest-C3EODOee.mjs.map} +1 -1
  7. package/dist/{DurableAgentRuntime-CBBjRNJN.mjs → DurableAgentRuntime-BJbuIqQq.mjs} +237 -114
  8. package/dist/DurableAgentRuntime-BJbuIqQq.mjs.map +1 -0
  9. package/dist/{EventSource-C9aaJaXT.d.mts → EventSource-Cc58uEso.d.mts} +2 -2
  10. package/dist/InMemory.d.mts +2 -2
  11. package/dist/{MessageDelivery-BGDm-c46.d.mts → MessageDelivery-ClHg7c0O.d.mts} +42 -42
  12. package/dist/{MessagingHost-CRUK4JBi.mjs → MessagingHost-CZCyrnvJ.mjs} +27 -26
  13. package/dist/MessagingHost-CZCyrnvJ.mjs.map +1 -0
  14. package/dist/{PreparedInputAdmission-BvDqlgiP.d.mts → PreparedInputAdmission-CjcX1OP0.d.mts} +3 -3
  15. package/dist/{Records-BiuJDWNe.mjs → Records-DzL4Kbtw.mjs} +2 -2
  16. package/dist/{Records-BiuJDWNe.mjs.map → Records-DzL4Kbtw.mjs.map} +1 -1
  17. package/dist/{RunContinuation-4ZATcVLB.mjs → RunContinuation-CWHuciup.mjs} +74 -40
  18. package/dist/RunContinuation-CWHuciup.mjs.map +1 -0
  19. package/dist/{RunContinuation-CTJ1jGrO.d.mts → RunContinuation-DSnUTSSb.d.mts} +11 -5
  20. package/dist/{RunJournal-CNZqA3Ge.mjs → RunJournal-DGblbFas.mjs} +86 -81
  21. package/dist/RunJournal-DGblbFas.mjs.map +1 -0
  22. package/dist/{RunStorage-BHjuuERI.mjs → RunStorage-BF2YgWkg.mjs} +44 -33
  23. package/dist/RunStorage-BF2YgWkg.mjs.map +1 -0
  24. package/dist/{Scheduling-NG8slKdI.mjs → Scheduling-07_dsGED.mjs} +3 -3
  25. package/dist/{Scheduling-NG8slKdI.mjs.map → Scheduling-07_dsGED.mjs.map} +1 -1
  26. package/dist/{Subagent-CTIpnzlB.mjs → Subagent-CexndZSp.mjs} +2 -2
  27. package/dist/{Subagent-CTIpnzlB.mjs.map → Subagent-CexndZSp.mjs.map} +1 -1
  28. package/dist/{Subagent-DKhZA72u.d.mts → Subagent-xhN_aD1r.d.mts} +3 -3
  29. package/dist/{Subscription-BNg9VCRl.d.mts → Subscription-DuC_PcGx.d.mts} +2 -2
  30. package/dist/{SubscriptionInput-BUoXv-_K.d.mts → SubscriptionInput-B1IeAEDi.d.mts} +2 -2
  31. package/dist/{Thread-dk-QiNml.d.mts → Thread-DzdJAY4J.d.mts} +4 -3
  32. package/dist/{ThreadHistory-ByxS8HU1.d.mts → ThreadHistory-KPPBJLJb.d.mts} +12 -2
  33. package/dist/{ThreadImport-CMgzttvr.d.mts → ThreadImport-D_Biid6w.d.mts} +42 -42
  34. package/dist/{ThreadImport-CodYoJoa.mjs → ThreadImport-ZgWxf7bB.mjs} +8 -8
  35. package/dist/{ThreadImport-CodYoJoa.mjs.map → ThreadImport-ZgWxf7bB.mjs.map} +1 -1
  36. package/dist/{ThreadStore-59uDGXul.mjs → ThreadStore-BC4V-mY-.mjs} +42 -23
  37. package/dist/ThreadStore-BC4V-mY-.mjs.map +1 -0
  38. package/dist/{ThreadTransfer-3jigUl3g.mjs → ThreadTransfer-S7FnTRHt.mjs} +4 -4
  39. package/dist/{ThreadTransfer-3jigUl3g.mjs.map → ThreadTransfer-S7FnTRHt.mjs.map} +1 -1
  40. package/dist/{WorkerAdmission-DDYJdOsU.mjs → WorkerAdmission-C47Vy_cK.mjs} +5 -5
  41. package/dist/{WorkerAdmission-DDYJdOsU.mjs.map → WorkerAdmission-C47Vy_cK.mjs.map} +1 -1
  42. package/dist/{agent-registration-DRbyhAu-.mjs → agent-registration-a6QYaG3h.mjs} +4 -4
  43. package/dist/{agent-registration-DRbyhAu-.mjs.map → agent-registration-a6QYaG3h.mjs.map} +1 -1
  44. package/dist/capabilities/Messaging.mjs +1 -1
  45. package/dist/capabilities/ModelContext.d.mts +1 -1
  46. package/dist/capabilities/RunHooks.d.mts +1 -1
  47. package/dist/capabilities/Subagent.d.mts +1 -1
  48. package/dist/capabilities/Subagent.mjs +1 -1
  49. package/dist/core/Thread.d.mts +2 -2
  50. package/dist/core/Thread.mjs +5 -3
  51. package/dist/core/Thread.mjs.map +1 -1
  52. package/dist/durable/ActivityStore.mjs +1 -1
  53. package/dist/durable/Admin.d.mts +1 -1
  54. package/dist/durable/Admin.mjs +1 -1
  55. package/dist/durable/AgentRegistration.d.mts +1 -1
  56. package/dist/durable/AgentRegistration.mjs +1 -1
  57. package/dist/durable/Certification.d.mts +1 -1
  58. package/dist/durable/CommittedActivity.d.mts +1 -1
  59. package/dist/durable/CommittedActivity.mjs +3 -3
  60. package/dist/durable/Digest.mjs +1 -1
  61. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  62. package/dist/durable/DurableAgentRuntime.mjs +2 -2
  63. package/dist/durable/EventSource.d.mts +1 -1
  64. package/dist/durable/EventSource.mjs +1 -1
  65. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  66. package/dist/durable/LifecyclePublication.d.mts +1 -1
  67. package/dist/durable/LifecyclePublication.mjs +2 -2
  68. package/dist/durable/MessageDelivery.d.mts +1 -1
  69. package/dist/durable/MessageDelivery.mjs +2 -2
  70. package/dist/durable/MessageDeliveryStoreConformance.d.mts +40 -40
  71. package/dist/durable/MessageDeliveryStoreConformance.mjs +2 -2
  72. package/dist/durable/PersistentHistory.d.mts +2 -2
  73. package/dist/durable/PersistentHistory.mjs +4 -4
  74. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  75. package/dist/durable/RecordFormat.mjs +1 -1
  76. package/dist/durable/Records.mjs +1 -1
  77. package/dist/durable/Recovery.d.mts +1 -1
  78. package/dist/durable/Recovery.mjs +1 -1
  79. package/dist/durable/RunContinuation.d.mts +2 -2
  80. package/dist/durable/RunContinuation.mjs +3 -3
  81. package/dist/durable/RunJournal.d.mts +2 -2
  82. package/dist/durable/RunJournal.mjs +2 -2
  83. package/dist/durable/RunStorage.d.mts +1 -1
  84. package/dist/durable/RunStorage.mjs +1 -1
  85. package/dist/durable/Schedule.mjs +1 -1
  86. package/dist/durable/ScheduleStoreConformance.mjs +1 -1
  87. package/dist/durable/ScheduleTransition.mjs +1 -1
  88. package/dist/durable/Scheduling.d.mts +1 -1
  89. package/dist/durable/Scheduling.mjs +1 -1
  90. package/dist/durable/SettlementPublisher.d.mts +1 -1
  91. package/dist/durable/SettlementPublisher.mjs +3 -3
  92. package/dist/durable/SqlMemoryStore.mjs +5 -3
  93. package/dist/durable/SqlMemoryStore.mjs.map +1 -1
  94. package/dist/durable/SubmissionLedger.d.mts +1 -1
  95. package/dist/durable/SubmissionLedger.mjs +7 -3
  96. package/dist/durable/SubmissionLedger.mjs.map +1 -1
  97. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  98. package/dist/durable/SubmissionLedgerConformance.mjs +6 -6
  99. package/dist/durable/SubmissionStatus.d.mts +1 -1
  100. package/dist/durable/Subscription.d.mts +1 -1
  101. package/dist/durable/Subscription.mjs +2 -2
  102. package/dist/durable/SubscriptionInput.d.mts +1 -1
  103. package/dist/durable/SubscriptionInput.mjs +2 -2
  104. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  105. package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
  106. package/dist/durable/SubscriptionTools.d.mts +2 -2
  107. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  108. package/dist/durable/SubscriptionTransition.mjs +1 -1
  109. package/dist/durable/Subscriptions.d.mts +5 -5
  110. package/dist/durable/Subscriptions.mjs +2 -2
  111. package/dist/durable/ThreadArchiveRange.d.mts +1 -1
  112. package/dist/durable/ThreadArchiveRange.mjs +1 -1
  113. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  114. package/dist/durable/ThreadContextHistory.mjs +2 -2
  115. package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
  116. package/dist/durable/ThreadImport.d.mts +1 -1
  117. package/dist/durable/ThreadImport.mjs +1 -1
  118. package/dist/durable/ThreadInvariants.d.mts +3 -3
  119. package/dist/durable/ThreadInvariants.mjs +4 -4
  120. package/dist/durable/ThreadProjection.mjs +1 -1
  121. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  122. package/dist/durable/ThreadReader.d.mts +1 -1
  123. package/dist/durable/ThreadReader.mjs +1 -1
  124. package/dist/durable/ThreadStore.d.mts +1 -1
  125. package/dist/durable/ThreadStore.mjs +2 -2
  126. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  127. package/dist/durable/ThreadStoreConformance.mjs +4 -4
  128. package/dist/durable/ThreadTransfer.d.mts +1 -1
  129. package/dist/durable/ThreadTransfer.mjs +1 -1
  130. package/dist/durable/ThreadWork.d.mts +2 -2
  131. package/dist/durable/ThreadWork.mjs +12 -6
  132. package/dist/durable/ThreadWork.mjs.map +1 -1
  133. package/dist/durable/ToolReconciler.mjs +1 -1
  134. package/dist/durable/WakeScheduler.mjs +23 -39
  135. package/dist/durable/WakeScheduler.mjs.map +1 -1
  136. package/dist/durable/WorkerAdmission.d.mts +68 -68
  137. package/dist/durable/WorkerAdmission.mjs +1 -1
  138. package/dist/durable/WorkerHost.d.mts +1 -1
  139. package/dist/engine/AgentRuntime.d.mts +1 -1
  140. package/dist/engine/AgentRuntime.mjs +1 -1
  141. package/dist/engine/MessagingHost.mjs +1 -1
  142. package/dist/engine/Output.mjs +1 -1
  143. package/dist/engine/ThreadHistory.d.mts +1 -1
  144. package/dist/engine/ThreadHistory.mjs +3 -2
  145. package/dist/engine/ThreadHistory.mjs.map +1 -1
  146. package/dist/index.d.mts +12 -12
  147. package/dist/index.mjs +13 -13
  148. package/dist/{output-contract-CMIhu4Cw.mjs → output-contract-DCnLYCdp.mjs} +12 -15
  149. package/dist/{output-contract-CMIhu4Cw.mjs.map → output-contract-DCnLYCdp.mjs.map} +1 -1
  150. package/dist/{run-context-reader-CC7ZD95F.d.mts → run-context-reader-DvTEq2RC.d.mts} +2 -2
  151. package/package.json +1 -1
  152. package/src/core/Thread.ts +143 -146
  153. package/src/durable/DurableAgentRuntime.ts +549 -260
  154. package/src/durable/RunContinuation.ts +115 -64
  155. package/src/durable/RunJournal.ts +107 -112
  156. package/src/durable/RunStorage.ts +90 -61
  157. package/src/durable/SqlMemoryStore.ts +8 -6
  158. package/src/durable/SubmissionLedger.ts +15 -0
  159. package/src/durable/ThreadStore.ts +48 -26
  160. package/src/durable/ThreadWork.ts +10 -0
  161. package/src/durable/WakeScheduler.ts +34 -66
  162. package/src/durable/internal/initial-context.ts +17 -2
  163. package/src/durable/internal/prompt-history-cache.ts +13 -8
  164. package/src/durable/internal/usage-totals.ts +28 -31
  165. package/src/engine/MessagingHost.ts +4 -2
  166. package/src/engine/ThreadHistory.ts +47 -48
  167. package/src/engine/internal/agent-runtime.ts +234 -202
  168. package/src/engine/internal/output-contract.ts +15 -15
  169. package/src/engine/internal/primitive-delta.ts +2 -3
  170. package/src/engine/internal/tool-telemetry.ts +3 -5
  171. package/dist/AgentRuntime-B6qZlD3f.mjs.map +0 -1
  172. package/dist/DurableAgentRuntime-CBBjRNJN.mjs.map +0 -1
  173. package/dist/MessagingHost-CRUK4JBi.mjs.map +0 -1
  174. package/dist/RunContinuation-4ZATcVLB.mjs.map +0 -1
  175. package/dist/RunJournal-CNZqA3Ge.mjs.map +0 -1
  176. package/dist/RunStorage-BHjuuERI.mjs.map +0 -1
  177. package/dist/ThreadStore-59uDGXul.mjs.map +0 -1
@@ -13,7 +13,7 @@ import { type ExhaustedLimit } from "../core/RunEvent.ts";
13
13
  import { RunPolicyUsage } from "../core/RunPolicyUsage.ts";
14
14
  import { type Selection, type Snapshot } from "../core/ToolExposure.ts";
15
15
  import type { ToolParameterRejection } from "../core/ToolResult.ts";
16
- import { ModelCallUsage, summarizeModelUsage, type RunUsageSummary } from "../core/Usage.ts";
16
+ import { type ModelCallUsage, summarizeModelUsage, type RunUsageSummary } from "../core/Usage.ts";
17
17
  import type { WorkerRef } from "../core/Worker.ts";
18
18
  import {
19
19
  CLEARED_TOOL_RESULT,
@@ -436,6 +436,36 @@ export interface RunJournalProjection {
436
436
  readonly pendingContextToolCallId?: string | undefined;
437
437
  }
438
438
 
439
+ const initialPolicyUsage = () => ({
440
+ committedTurns: 0,
441
+ toolCalls: 0,
442
+ programmaticToolCalls: 0,
443
+ consecutiveToolFailures: 0,
444
+ finalizationUsed: false,
445
+ modelRestarts: 0,
446
+ });
447
+
448
+ const initialUsage = (modelUsage: Array<ModelCallUsage>) => ({
449
+ modelCalls: 0,
450
+ inputTokens: 0,
451
+ outputTokens: 0,
452
+ lastInputTokens: 0,
453
+ lastOutputTokens: 0,
454
+ costMicrousd: 0,
455
+ modelUsage,
456
+ });
457
+
458
+ /** Internal first-Run projection after exact input/checkpoint-absence verification. The
459
+ * new UserInputRecorded contributes no model message; the engine evaluates it later. */
460
+ export const initialRunJournal = (history: Prompt.Prompt): RunJournalProjection => ({
461
+ policyUsage: RunPolicyUsage.make(initialPolicyUsage()),
462
+ usage: initialUsage([]),
463
+ prompt: history,
464
+ historyBefore: history,
465
+ historyFrom: CanonicalSequence.make(1),
466
+ committedTurns: 0,
467
+ });
468
+
439
469
  interface ProjectedResponseUsage {
440
470
  readonly modelCalls: number;
441
471
  readonly inputTokens: number;
@@ -1050,15 +1080,7 @@ export const projectRunJournalStream = Effect.fnUntraced(function* <
1050
1080
  const modelUsage: Array<ModelCallUsage> = [];
1051
1081
  let unobservedModelCalls = 0;
1052
1082
 
1053
- const usage = {
1054
- modelCalls: 0,
1055
- inputTokens: 0,
1056
- outputTokens: 0,
1057
- lastInputTokens: 0,
1058
- lastOutputTokens: 0,
1059
- costMicrousd: 0,
1060
- modelUsage,
1061
- };
1083
+ const usage = initialUsage(modelUsage);
1062
1084
 
1063
1085
  let toolSelection: Selection | undefined;
1064
1086
  let usageTurn = 0;
@@ -1080,14 +1102,7 @@ export const projectRunJournalStream = Effect.fnUntraced(function* <
1080
1102
  let pendingContextToolCallId: string | undefined;
1081
1103
  let ownerTerminated = false;
1082
1104
 
1083
- const policyUsage = {
1084
- committedTurns: 0,
1085
- toolCalls: 0,
1086
- programmaticToolCalls: 0,
1087
- consecutiveToolFailures: 0,
1088
- finalizationUsed: false,
1089
- modelRestarts: 0,
1090
- };
1105
+ const policyUsage = initialPolicyUsage();
1091
1106
 
1092
1107
  const accountResponse = Effect.fnUntraced(function* (
1093
1108
  envelope: JournalRecordEnvelope,
@@ -1639,18 +1654,6 @@ export const promptFromCanonicalRecords = (
1639
1654
  );
1640
1655
 
1641
1656
  /** Everything one committed Turn contributes to its canonical batch. */
1642
- /**
1643
- * Staged usage is validated, never repaired: clamping negatives or truncating
1644
- * fractions would under-record canonical usage, and NaN/Infinity must fail
1645
- * typed instead of escaping as a record-construction defect (RUN-023).
1646
- */
1647
- const validStagedUsage = (label: string, value: number): Effect.Effect<number, RunJournalError> =>
1648
- Number.isSafeInteger(value) && value >= 0
1649
- ? Effect.succeed(value)
1650
- : Effect.fail(
1651
- journalError(`Staged ${label} must be a non-negative safe integer, got ${String(value)}`),
1652
- );
1653
-
1654
1657
  export interface TurnCommitInput {
1655
1658
  readonly toolResultMaxBytes?: number | undefined;
1656
1659
  readonly toolSelectionMaxBytes?: number | undefined;
@@ -1695,8 +1698,9 @@ export interface TurnCommitInput {
1695
1698
  }
1696
1699
 
1697
1700
  const decodePersistedJson = Schema.decodeUnknownEffect(PersistedJson);
1701
+ const decodeModelResponse = Schema.decodeUnknownEffect(ModelResponseRecorded);
1702
+ const decodeToolSettled = Schema.decodeUnknownEffect(ToolCallSettled);
1698
1703
  const encodePrompt = Schema.encodeEffect(Prompt.Prompt);
1699
- const decodeModelUsage = Schema.decodeUnknownEffect(Schema.Array(ModelCallUsage));
1700
1704
 
1701
1705
  const requireCanonicalTurn = (turn: number): Effect.Effect<void, RunJournalError> =>
1702
1706
  !Number.isInteger(turn) || turn <= 0
@@ -1732,40 +1736,74 @@ const modelResponseRecord = Effect.fnUntraced(function* (
1732
1736
  }
1733
1737
 
1734
1738
  const encodedMessages = yield* encodePrompt(Prompt.fromMessages([...promptMessages])).pipe(
1735
- Effect.mapError((cause) => journalError("Turn Prompt messages failed to encode", cause)),
1739
+ Effect.mapErrorEager((cause) => journalError("Turn Prompt messages failed to encode", cause)),
1736
1740
  );
1737
1741
 
1738
1742
  const messages = yield* decodePersistedJson(encodedMessages).pipe(
1739
- Effect.map(copyJson),
1740
- Effect.mapError((cause) =>
1743
+ Effect.mapEager(copyJson),
1744
+ Effect.mapErrorEager((cause) =>
1741
1745
  journalError("Turn Prompt messages exceed canonical persistence bounds", cause),
1742
1746
  ),
1743
1747
  );
1744
1748
 
1745
- const modelUsage =
1746
- input.usage?.modelUsage === undefined
1747
- ? undefined
1748
- : yield* decodeModelUsage(input.usage.modelUsage).pipe(
1749
- Effect.mapError((cause) => journalError("Turn model usage failed to decode", cause)),
1750
- );
1749
+ // Validate the detached messages and usage together; no mutable caller data is trusted.
1750
+ const modelUsage = input.usage?.modelUsage;
1751
+
1752
+ const payload = yield* decodeModelResponse({
1753
+ _tag: "ModelResponseRecorded",
1754
+ toolResultMaxBytes: input.toolResultMaxBytes ?? MAX_PERSISTED_JSON_BYTES,
1755
+ toolSelectionMaxBytes: input.toolSelectionMaxBytes ?? 0,
1756
+ toolOperations: input.toolOperations ?? [],
1757
+ ...(input.toolParameterRejections === undefined || input.toolParameterRejections.length === 0
1758
+ ? {}
1759
+ : { toolParameterRejections: input.toolParameterRejections }),
1760
+ ...(input.toolExposure === undefined ? {} : { toolExposure: input.toolExposure }),
1761
+ runId: input.runId,
1762
+ turnId: input.turnId,
1763
+ turn: input.turn,
1764
+ messages,
1765
+ ...(runScopedPrefixLength === undefined ? {} : { runScopedPrefixLength }),
1766
+ ...(modelUsage === undefined ? {} : { modelUsage }),
1767
+ ...(input.unobservedModelCalls === undefined || input.unobservedModelCalls === 0
1768
+ ? {}
1769
+ : {
1770
+ unobservedModelCalls: input.unobservedModelCalls,
1771
+ }),
1772
+ ...(input.usage === undefined
1773
+ ? {}
1774
+ : {
1775
+ inputTokens: input.usage.inputTokens,
1776
+ outputTokens: input.usage.outputTokens,
1777
+ // Written only when non-zero: absent re-seeds as zero, so the
1778
+ // no-estimator case stays byte-identical to pre-cost histories.
1779
+ ...(input.usage.costMicrousd === undefined || input.usage.costMicrousd === 0
1780
+ ? {}
1781
+ : {
1782
+ costMicrousd: input.usage.costMicrousd,
1783
+ }),
1784
+ }),
1785
+ }).pipe(
1786
+ Effect.mapErrorEager((cause) =>
1787
+ journalError("Invalid model response operation evidence", cause),
1788
+ ),
1789
+ );
1751
1790
 
1752
- if (modelUsage !== undefined) {
1753
- if (modelUsage.length === 0) {
1791
+ if (payload.modelUsage !== undefined) {
1792
+ if (payload.modelUsage.length === 0)
1754
1793
  return yield* journalError("Turn model usage must contain at least one completed call");
1755
- }
1756
1794
 
1757
- const summary = yield* continuationUsage(modelUsage).pipe(
1758
- Effect.mapError((cause) => journalError("Turn model usage exceeds accounting bounds", cause)),
1795
+ const summary = yield* continuationUsage(payload.modelUsage).pipe(
1796
+ Effect.mapErrorEager((cause) =>
1797
+ journalError("Turn model usage exceeds accounting bounds", cause),
1798
+ ),
1759
1799
  );
1760
1800
 
1761
1801
  if (
1762
- input.usage === undefined ||
1763
- input.usage.inputTokens !== summary.inputTokens ||
1764
- input.usage.outputTokens !== summary.outputTokens ||
1765
- (input.usage.costMicrousd ?? 0) !== summary.costMicrousd
1766
- ) {
1802
+ payload.inputTokens !== summary.inputTokens ||
1803
+ payload.outputTokens !== summary.outputTokens ||
1804
+ (payload.costMicrousd ?? 0) !== summary.costMicrousd
1805
+ )
1767
1806
  return yield* journalError("Turn detailed and aggregate model usage disagree");
1768
- }
1769
1807
  }
1770
1808
 
1771
1809
  return RecordEnvelope.make({
@@ -1774,44 +1812,7 @@ const modelResponseRecord = Effect.fnUntraced(function* (
1774
1812
  schemaVersion: 1,
1775
1813
  createdAt: input.createdAt,
1776
1814
  deploymentId: input.deploymentId,
1777
- payload: yield* ModelResponseRecorded.makeEffect({
1778
- toolResultMaxBytes: input.toolResultMaxBytes ?? MAX_PERSISTED_JSON_BYTES,
1779
- toolSelectionMaxBytes: input.toolSelectionMaxBytes ?? 0,
1780
- toolOperations: input.toolOperations ?? [],
1781
- ...(input.toolParameterRejections === undefined || input.toolParameterRejections.length === 0
1782
- ? {}
1783
- : { toolParameterRejections: input.toolParameterRejections }),
1784
- ...(input.toolExposure === undefined ? {} : { toolExposure: input.toolExposure }),
1785
- runId: input.runId,
1786
- turnId: input.turnId,
1787
- turn: input.turn,
1788
- messages,
1789
- ...(runScopedPrefixLength === undefined ? {} : { runScopedPrefixLength }),
1790
- ...(modelUsage === undefined ? {} : { modelUsage }),
1791
- ...(input.unobservedModelCalls === undefined || input.unobservedModelCalls === 0
1792
- ? {}
1793
- : {
1794
- unobservedModelCalls: yield* validStagedUsage(
1795
- "unobservedModelCalls",
1796
- input.unobservedModelCalls,
1797
- ),
1798
- }),
1799
- ...(input.usage === undefined
1800
- ? {}
1801
- : {
1802
- inputTokens: yield* validStagedUsage("inputTokens", input.usage.inputTokens),
1803
- outputTokens: yield* validStagedUsage("outputTokens", input.usage.outputTokens),
1804
- // Written only when non-zero: absent re-seeds as zero, so the
1805
- // no-estimator case stays byte-identical to pre-cost histories.
1806
- ...(input.usage.costMicrousd === undefined || input.usage.costMicrousd === 0
1807
- ? {}
1808
- : {
1809
- costMicrousd: yield* validStagedUsage("costMicrousd", input.usage.costMicrousd),
1810
- }),
1811
- }),
1812
- }).pipe(
1813
- Effect.mapError((cause) => journalError("Invalid model response operation evidence", cause)),
1814
- ),
1815
+ payload,
1815
1816
  });
1816
1817
  });
1817
1818
 
@@ -1821,33 +1822,27 @@ const toolSettledRecords = Effect.fnUntraced(function* (
1821
1822
  const toolRecords: Array<RecordEnvelope> = [];
1822
1823
 
1823
1824
  for (const part of input.toolResults) {
1824
- const result = yield* decodePersistedJson(part.result).pipe(
1825
- Effect.mapError((cause) =>
1826
- journalError(`Tool result ${part.toolCallId} exceeds canonical persistence bounds`, cause),
1827
- ),
1825
+ const payload = yield* decodeToolSettled({
1826
+ _tag: "ToolCallSettled",
1827
+ ...(part.toolSelection === undefined ? {} : { toolSelection: part.toolSelection }),
1828
+ runId: input.runId,
1829
+ toolCallId: part.toolCallId,
1830
+ toolName: part.toolName,
1831
+ result: part.result,
1832
+ isFailure: part.isFailure,
1833
+ ...(part.budgetRejected === true ? { budgetRejected: true } : {}),
1834
+ }).pipe(
1835
+ Effect.mapError((cause) => journalError(`Invalid Tool result ${part.toolCallId}`, cause)),
1828
1836
  );
1829
1837
 
1830
- const toolCallId = yield* Effect.try({
1831
- try: () => decodeToolCallId(part.toolCallId),
1832
- catch: (cause) => journalError(`Invalid Tool Call ID ${part.toolCallId}`, cause),
1833
- });
1834
-
1835
1838
  toolRecords.push(
1836
1839
  RecordEnvelope.make({
1837
- recordId: toolCallSettledRecordId(input.runId, input.turn, toolCallId),
1840
+ recordId: toolCallSettledRecordId(input.runId, input.turn, payload.toolCallId),
1838
1841
  family: "thread",
1839
1842
  schemaVersion: 1,
1840
1843
  createdAt: input.createdAt,
1841
1844
  deploymentId: input.deploymentId,
1842
- payload: ToolCallSettled.make({
1843
- ...(part.toolSelection === undefined ? {} : { toolSelection: part.toolSelection }),
1844
- runId: input.runId,
1845
- toolCallId,
1846
- toolName: part.toolName,
1847
- result,
1848
- isFailure: part.isFailure,
1849
- ...(part.budgetRejected === true ? { budgetRejected: true } : {}),
1850
- }),
1845
+ payload,
1851
1846
  }),
1852
1847
  );
1853
1848
  }
@@ -1855,7 +1850,7 @@ const toolSettledRecords = Effect.fnUntraced(function* (
1855
1850
  return toolRecords;
1856
1851
  });
1857
1852
 
1858
- const runCompletionRecord = Effect.fnUntraced(function* (input: TurnCommitInput) {
1853
+ const runCompletionRecord = (input: TurnCommitInput) => {
1859
1854
  if (input.runCompletion === undefined) return undefined;
1860
1855
  const completion = { runId: input.runId, ...input.runCompletion };
1861
1856
 
@@ -1869,7 +1864,7 @@ const runCompletionRecord = Effect.fnUntraced(function* (input: TurnCommitInput)
1869
1864
  ...completion,
1870
1865
  }),
1871
1866
  });
1872
- });
1867
+ };
1873
1868
 
1874
1869
  /**
1875
1870
  * Build a canonical batch from interpreter-validated Turn facts: one
@@ -1887,7 +1882,7 @@ export const turnCanonicalBatch = Effect.fnUntraced(function* (
1887
1882
  yield* requireCanonicalTurn(input.turn);
1888
1883
  const modelResponse = yield* modelResponseRecord(input);
1889
1884
  const toolRecords = yield* toolSettledRecords(input);
1890
- const completionRecord = yield* runCompletionRecord(input);
1885
+ const completionRecord = runCompletionRecord(input);
1891
1886
 
1892
1887
  return CanonicalBatch.make({
1893
1888
  batchId: turnBatchId(input.runId, input.turn),
@@ -1931,7 +1926,7 @@ export const turnResultsBatch = Effect.fnUntraced(function* (
1931
1926
  yield* requireCanonicalTurn(input.turn);
1932
1927
  const toolRecords = yield* toolSettledRecords(input);
1933
1928
  const first = toolRecords[0];
1934
- const completionRecord = yield* runCompletionRecord(input);
1929
+ const completionRecord = runCompletionRecord(input);
1935
1930
 
1936
1931
  if (first === undefined) {
1937
1932
  if (completionRecord === undefined) {
@@ -113,6 +113,13 @@ export interface RunWriter {
113
113
 
114
114
  export interface RunStorageSession extends RunWriter, RunOwnership {
115
115
  readonly claim: Claim;
116
+ /** Atomic original input/context plus the ledger marker, when the adapter supports it. */
117
+ readonly appendInput?: RunWriter["append"];
118
+ /** Library-only initialization, before application/model IO. Hold progress before mutation
119
+ * so the captured controls and this owner's acknowledged input remain one read view. */
120
+ readonly withInitialization?: <A, E, R>(
121
+ effect: Effect.Effect<A, E, R>,
122
+ ) => Effect.Effect<A, E | OwnershipLost, R>;
116
123
  readonly refresh: Effect.Effect<void, ThreadStoreFailure>;
117
124
  readonly publishSettlement: (
118
125
  batch: CanonicalBatch,
@@ -171,69 +178,75 @@ const makeWriter = Effect.fnUntraced(function* (threadId: ThreadId) {
171
178
  }),
172
179
  );
173
180
 
174
- const append: RunWriter["append"] = Effect.fnUntraced(function* (batch, executions) {
175
- const captured = yield* PreparedAppend.capture(
176
- AppendRequest.make({
177
- threadId,
178
- expectedTailSequence: tail.sequence,
179
- batch,
180
- ...(executions === undefined ? {} : { executions }),
181
- }),
182
- ).pipe(
183
- Effect.mapError((cause) =>
184
- ThreadStoreError.make({
185
- operation: "capture append",
186
- message: cause.message,
187
- cause,
181
+ const makeAppend = Effect.gen(function* () {
182
+ const publisher = yield* ThreadStore;
183
+
184
+ const append: RunWriter["append"] = Effect.fnUntraced(function* (batch, executions) {
185
+ const captured = yield* PreparedAppend.capture(
186
+ AppendRequest.make({
187
+ threadId,
188
+ expectedTailSequence: tail.sequence,
189
+ batch,
190
+ ...(executions === undefined ? {} : { executions }),
188
191
  }),
189
- ),
190
- );
192
+ ).pipe(
193
+ Effect.mapError((cause) =>
194
+ ThreadStoreError.make({
195
+ operation: "capture append",
196
+ message: cause.message,
197
+ cause,
198
+ }),
199
+ ),
200
+ );
191
201
 
192
- return yield* owner.withProgress(
193
- threadId,
194
- owner.withMutation(
195
- Effect.gen(function* () {
196
- yield* checkActive;
197
- for (let retries = 0; ; retries++) {
198
- const result = yield* store
199
- .append(
200
- captured.expectedTailSequence === tail.sequence
201
- ? captured
202
- : AppendRequest.make({
203
- ...captured,
204
- expectedTailSequence: tail.sequence,
205
- }),
206
- )
207
- .pipe(
208
- Effect.catchTag("AppendConflict", (conflict) => {
209
- if (
210
- conflict.reason !== "tail" ||
211
- conflict.actualTailSequence === undefined ||
212
- retries >= 8
213
- )
214
- return Effect.fail(conflict);
215
- tail = { sequence: conflict.actualTailSequence };
216
- if (captured.executions !== undefined) return Effect.fail(conflict);
217
-
218
- return Effect.succeed(undefined);
219
- }),
220
- );
221
-
222
- if (result === undefined) continue;
223
- // A historical replay returns its original coordinates, not today's canonical tail.
224
- if (result.replayed) yield* refresh;
225
- else tail = { sequence: result.lastSequence };
226
-
227
- return result;
228
- }
229
- }).pipe(
230
- // Revoke uncertain writes before releasing the writer's mutation gate.
231
- Effect.onError((cause) =>
232
- isDefiniteWriteFailure(cause) ? Effect.void : ownership.release,
202
+ return yield* owner.withProgress(
203
+ threadId,
204
+ owner.withMutation(
205
+ Effect.gen(function* () {
206
+ yield* checkActive;
207
+ for (let retries = 0; ; retries++) {
208
+ const result = yield* publisher
209
+ .append(
210
+ captured.expectedTailSequence === tail.sequence
211
+ ? captured
212
+ : AppendRequest.make({
213
+ ...captured,
214
+ expectedTailSequence: tail.sequence,
215
+ }),
216
+ )
217
+ .pipe(
218
+ Effect.catchTag("AppendConflict", (conflict) => {
219
+ if (
220
+ conflict.reason !== "tail" ||
221
+ conflict.actualTailSequence === undefined ||
222
+ retries >= 8
223
+ )
224
+ return Effect.fail(conflict);
225
+ tail = { sequence: conflict.actualTailSequence };
226
+ if (captured.executions !== undefined) return Effect.fail(conflict);
227
+
228
+ return Effect.succeed(undefined);
229
+ }),
230
+ );
231
+
232
+ if (result === undefined) continue;
233
+ // A historical replay returns its original coordinates, not today's canonical tail.
234
+ if (result.replayed) yield* refresh;
235
+ else tail = { sequence: result.lastSequence };
236
+
237
+ return result;
238
+ }
239
+ }).pipe(
240
+ // Revoke uncertain writes before releasing the writer's mutation gate.
241
+ Effect.onError((cause) =>
242
+ isDefiniteWriteFailure(cause) ? Effect.void : ownership.release,
243
+ ),
233
244
  ),
234
245
  ),
235
- ),
236
- );
246
+ );
247
+ });
248
+
249
+ return append;
237
250
  });
238
251
 
239
252
  const writer = {
@@ -242,13 +255,14 @@ const makeWriter = Effect.fnUntraced(function* (threadId: ThreadId) {
242
255
  withProgress: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
243
256
  owner.withProgress(threadId, Effect.andThen(checkActive, effect)),
244
257
  tail: Effect.sync(() => tail),
245
- append,
258
+ append: yield* makeAppend,
246
259
  checkActive: owner.withMutation(checkActive),
247
260
  refresh,
248
261
  };
249
262
 
250
263
  return {
251
264
  writer,
265
+ makeAppend,
252
266
  adoptTail: (sequence: CanonicalSequence) => {
253
267
  tail = { sequence };
254
268
  },
@@ -305,7 +319,7 @@ export const make = Effect.gen(function* () {
305
319
  });
306
320
  yield* store.materialize(ThreadMaterialization.make({ threadId: request.threadId }));
307
321
 
308
- const { writer, adoptTail } = yield* makeWriter(request.threadId).pipe(
322
+ const { writer, makeAppend, adoptTail } = yield* makeWriter(request.threadId).pipe(
309
323
  Effect.provideService(WriterOwnership, generation),
310
324
  Effect.provideService(ThreadStore, store),
311
325
  Effect.provideService(StorageOwner, owner),
@@ -313,9 +327,24 @@ export const make = Effect.gen(function* () {
313
327
 
314
328
  const owned = <A, E>(effect: Effect.Effect<A, E>) => generation.use(effect);
315
329
 
330
+ const inputPublisher = ledger.appendInput;
331
+
332
+ const appendInput =
333
+ inputPublisher === undefined
334
+ ? undefined
335
+ : yield* makeAppend.pipe(
336
+ Effect.provideService(ThreadStore, {
337
+ ...store,
338
+ append: (request) => inputPublisher(claimed.submissionId, request),
339
+ }),
340
+ );
341
+
316
342
  const session: RunStorageSession = {
317
343
  ...writer,
318
344
  claim: claimed,
345
+ ...(appendInput === undefined ? {} : { appendInput }),
346
+ withInitialization: (effect) =>
347
+ owner.withProgress(request.threadId, generation.use(effect)),
319
348
  release: generation.release,
320
349
  refresh: writer.refresh,
321
350
  markInputApplied: (marker) =>
@@ -33,11 +33,13 @@ const EncodedMemoryChange = Schema.Struct({
33
33
  resultJson: StoredJson,
34
34
  });
35
35
 
36
- const equivalentContent = Schema.toEquivalence(
37
- Schema.toEncoded(MemoryWrite.Wire.members[0].fields.content),
38
- );
36
+ const makeEquivalentContent = () =>
37
+ Schema.toEquivalence(Schema.toEncoded(MemoryWrite.Wire.members[0].fields.content));
38
+
39
+ let equivalentContent: ReturnType<typeof makeEquivalentContent> | undefined;
39
40
 
40
- const equivalentScopes = Schema.toEquivalence(MemoryWrite.Wire.members[0].fields.scopes);
41
+ const makeEquivalentScopes = () => Schema.toEquivalence(MemoryWrite.Wire.members[0].fields.scopes);
42
+ let equivalentScopes: ReturnType<typeof makeEquivalentScopes> | undefined;
41
43
 
42
44
  /** Ordered commands committed atomically by the SQL memory adapter. */
43
45
  export const SqlMemoryWriteBatch = Schema.Array(MemoryWrite.Wire).check(Schema.isMaxLength(128));
@@ -388,8 +390,8 @@ const validateReceiptResult = Effect.fnUntraced(function* (
388
390
  return yield* storageError(operation, "corrupt");
389
391
  }
390
392
  if (
391
- !equivalentContent(command.content, result.content) ||
392
- !equivalentScopes(command.scopes, result.scopes)
393
+ !(equivalentContent ??= makeEquivalentContent())(command.content, result.content) ||
394
+ !(equivalentScopes ??= makeEquivalentScopes())(command.scopes, result.scopes)
393
395
  ) {
394
396
  return yield* storageError(operation, "corrupt");
395
397
  }
@@ -36,6 +36,7 @@ import {
36
36
  ToolCallUnknown,
37
37
  WorkerAdmission,
38
38
  } from "./Records.ts";
39
+ import type { AppendRequest, AppendResult, ThreadStoreFailure } from "./ThreadStore.ts";
39
40
 
40
41
  export { IdempotencyKey, JoinedToHost, QueueSequence, Principal } from "../core/Receipt.ts";
41
42
 
@@ -989,6 +990,10 @@ export type SubmissionLedgerFailure =
989
990
  * An optional `parentLinkage` is recorded immutably at first admission (SUB-016); a replay
990
991
  * whose linkage diverges (present vs. absent, or different identities) fails with
991
992
  * `AdmissionConflict` even when the input digest matches.
993
+ * With `readyIfInitialized`, an adapter may insert a fresh ordinary Submission as ready
994
+ * when it proves Thread initialization under the admission's owner transaction. Readiness,
995
+ * its lifecycle fact and source progress commit with the receipt. Otherwise it returns
996
+ * admitted and the caller completes materialization and `markReady` as usual.
992
997
  * - `resolveAdmission` — authoritative tri-state admission lookup by scoped idempotency key
993
998
  * (SUB-031): `NotAdmitted` proves absence, `Admitted` carries the full snapshot, and
994
999
  * `Indeterminate` states that the adapter cannot currently prove either. Only `NotAdmitted`
@@ -1093,6 +1098,7 @@ export class SubmissionLedger extends Context.Service<
1093
1098
  readonly capabilities: Effect.Effect<LedgerCapabilities>;
1094
1099
  readonly admit: (
1095
1100
  request: AdmissionRequest,
1101
+ options?: { readonly readyIfInitialized?: boolean },
1096
1102
  ) => Effect.Effect<AdmissionResult, AdmissionConflict | AdmissionPolicyError | LedgerError>;
1097
1103
  readonly markReady: (request: MarkReadyRequest) => Effect.Effect<void, LedgerError>;
1098
1104
  readonly lookup: (
@@ -1108,6 +1114,13 @@ export class SubmissionLedger extends Context.Service<
1108
1114
  readonly selectHead: (
1109
1115
  request: ClaimRequest,
1110
1116
  ) => Effect.Effect<Option.Option<Claim>, LedgerError>;
1117
+ /** Atomically publish a captured original input, active context and applied marker.
1118
+ * Optional adapters retain the ordinary append-then-repair protocol. The captured input
1119
+ * must be this owner's deterministic admission; a moved context boundary is refused. */
1120
+ readonly appendInput?: (
1121
+ submissionId: SubmissionId,
1122
+ request: AppendRequest,
1123
+ ) => Effect.Effect<AppendResult, ThreadStoreFailure>;
1111
1124
  readonly markInputApplied: (
1112
1125
  request: MarkInputAppliedRequest,
1113
1126
  ) => Effect.Effect<void, OwnershipLost | LedgerError>;
@@ -1168,6 +1181,8 @@ export class SubmissionLedger extends Context.Service<
1168
1181
  request: ReleaseChildBudgetRequest,
1169
1182
  ) => Effect.Effect<ChildBudgetReservationSnapshot, ChildReservationConflict | LedgerError>;
1170
1183
  readonly scanNonterminal: Stream.Stream<SubmissionWorkItem, LedgerError>;
1184
+ /** Optional native snapshot for hosts that need the complete control worklist. */
1185
+ readonly listNonterminal?: Effect.Effect<ReadonlyArray<SubmissionWorkItem>, LedgerError>;
1171
1186
  readonly loadRecoverySnapshot: (
1172
1187
  request: RecoverySnapshotRequest,
1173
1188
  ) => Effect.Effect<RecoverySnapshot, LedgerError>;