@yielded/agent 0.1.0-beta.181 → 0.1.0-beta.183

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-NvXFPDIM.d.mts → Admin-BgCHCGSl.d.mts} +8 -8
  2. package/dist/{AgentRuntime-Dx5u_FJD.d.mts → AgentRuntime-DhRchFzv.d.mts} +7 -4
  3. package/dist/{AgentRuntime-BAExRlgu.mjs → AgentRuntime-ILtyHuQG.mjs} +61 -45
  4. package/dist/AgentRuntime-ILtyHuQG.mjs.map +1 -0
  5. package/dist/{Digest-DjUgS5PG.d.mts → Digest-1iVcDLVm.d.mts} +2 -2
  6. package/dist/{Digest-ft4ku9pj.mjs → Digest-CzLJcsWv.mjs} +2 -2
  7. package/dist/{Digest-ft4ku9pj.mjs.map → Digest-CzLJcsWv.mjs.map} +1 -1
  8. package/dist/{DurableAgentRuntime-HH2muFrT.mjs → DurableAgentRuntime-Cj263rW9.mjs} +27 -57
  9. package/dist/DurableAgentRuntime-Cj263rW9.mjs.map +1 -0
  10. package/dist/{EventSource-DNyaWmyQ.d.mts → EventSource-CaA6RAtC.d.mts} +3 -3
  11. package/dist/InMemory.d.mts +1 -1
  12. package/dist/{MessageDelivery-CLzWJImC.d.mts → MessageDelivery-rlLpakO8.d.mts} +43 -43
  13. package/dist/{PreparedInputAdmission-BQ9UCLFS.d.mts → PreparedInputAdmission-DjbWz2lW.d.mts} +4 -4
  14. package/dist/{RecordFormat-BNiLpe43.d.mts → RecordFormat-CjMl0uu8.d.mts} +2 -2
  15. package/dist/{Records-BTnGRdcf.d.mts → Records-BP67Yz6n.d.mts} +20 -8
  16. package/dist/{Records-CZRedCE7.mjs → Records-bbGVHprv.mjs} +14 -10
  17. package/dist/Records-bbGVHprv.mjs.map +1 -0
  18. package/dist/{RunContinuation-CL0wBsQE.d.mts → RunContinuation-CIJRN_vC.d.mts} +5 -4
  19. package/dist/{RunContinuation-BF95Ncv2.mjs → RunContinuation-DJ6kkbQG.mjs} +22 -58
  20. package/dist/RunContinuation-DJ6kkbQG.mjs.map +1 -0
  21. package/dist/{RunEvent-C5jVQZ6K.d.mts → RunEvent-Bh1p4zAO.d.mts} +2 -2
  22. package/dist/{RunJournal-oJLo-pjS.mjs → RunJournal-DQr2dUgg.mjs} +4 -7
  23. package/dist/RunJournal-DQr2dUgg.mjs.map +1 -0
  24. package/dist/{RunOptions-BANtohGk.d.mts → RunOptions-aBHR-Eqm.d.mts} +6 -3
  25. package/dist/{RunStorage-DlfvMfHM.mjs → RunStorage-DblN-cmM.mjs} +2 -2
  26. package/dist/{RunStorage-DlfvMfHM.mjs.map → RunStorage-DblN-cmM.mjs.map} +1 -1
  27. package/dist/{Schedule-CDgTz0vj.d.mts → Schedule-CVLtl5rP.d.mts} +2 -2
  28. package/dist/{Scheduling-BebkRVc2.mjs → Scheduling-B6l0lWdr.mjs} +3 -3
  29. package/dist/{Scheduling-BebkRVc2.mjs.map → Scheduling-B6l0lWdr.mjs.map} +1 -1
  30. package/dist/{Subagent-D68fe7mr.mjs → Subagent-Bm842K7J.mjs} +43 -17
  31. package/dist/Subagent-Bm842K7J.mjs.map +1 -0
  32. package/dist/{Subagent-5ulnV6An.d.mts → Subagent-CWWmA1UQ.d.mts} +17 -5
  33. package/dist/{Subscription-D2yn6Eo3.d.mts → Subscription-BRsNy0TA.d.mts} +4 -4
  34. package/dist/{SubscriptionInput-Cog4x7Rd.d.mts → SubscriptionInput-ClCaW_kX.d.mts} +3 -3
  35. package/dist/{ThreadHistory-KPPBJLJb.d.mts → ThreadHistory-ajQhqZDY.d.mts} +2 -2
  36. package/dist/{ThreadImport-BoTfjjrz.mjs → ThreadImport-CkPAxXU5.mjs} +7 -7
  37. package/dist/{ThreadImport-BoTfjjrz.mjs.map → ThreadImport-CkPAxXU5.mjs.map} +1 -1
  38. package/dist/{ThreadImport-em89zmM-.d.mts → ThreadImport-DtRNoHIr.d.mts} +5 -5
  39. package/dist/{ThreadStore-D2rqtt7k.mjs → ThreadStore-C_ofHc7h.mjs} +4 -4
  40. package/dist/{ThreadStore-D2rqtt7k.mjs.map → ThreadStore-C_ofHc7h.mjs.map} +1 -1
  41. package/dist/{ThreadTransfer-BFolkpEp.mjs → ThreadTransfer-C_TRuo_9.mjs} +4 -4
  42. package/dist/{ThreadTransfer-BFolkpEp.mjs.map → ThreadTransfer-C_TRuo_9.mjs.map} +1 -1
  43. package/dist/{WorkerAdmission-DrxDrLII.mjs → WorkerAdmission-9Ka3edXj.mjs} +5 -5
  44. package/dist/{WorkerAdmission-DrxDrLII.mjs.map → WorkerAdmission-9Ka3edXj.mjs.map} +1 -1
  45. package/dist/{agent-registration-BovXT8jc.mjs → agent-registration-7kp1dHT0.mjs} +4 -4
  46. package/dist/{agent-registration-BovXT8jc.mjs.map → agent-registration-7kp1dHT0.mjs.map} +1 -1
  47. package/dist/capabilities/Commands.d.mts +1 -1
  48. package/dist/capabilities/Redaction.d.mts +1 -1
  49. package/dist/capabilities/RunHooks.d.mts +1 -1
  50. package/dist/capabilities/Subagent.d.mts +1 -1
  51. package/dist/capabilities/Subagent.mjs +1 -1
  52. package/dist/capabilities/SubagentReservations.d.mts +4 -1
  53. package/dist/capabilities/SubagentReservations.mjs +4 -1
  54. package/dist/capabilities/SubagentReservations.mjs.map +1 -1
  55. package/dist/core/RunEvent.d.mts +1 -1
  56. package/dist/core/RunEvent.mjs +1 -1
  57. package/dist/core/RunEvent.mjs.map +1 -1
  58. package/dist/durable/ActivityStore.d.mts +1 -1
  59. package/dist/durable/ActivityStore.mjs +1 -1
  60. package/dist/durable/Admin.d.mts +1 -1
  61. package/dist/durable/Admin.mjs +1 -1
  62. package/dist/durable/AgentRegistration.d.mts +1 -1
  63. package/dist/durable/AgentRegistration.mjs +1 -1
  64. package/dist/durable/Certification.d.mts +1 -1
  65. package/dist/durable/CommittedActivity.d.mts +3 -3
  66. package/dist/durable/CommittedActivity.mjs +3 -3
  67. package/dist/durable/Digest.d.mts +1 -1
  68. package/dist/durable/Digest.mjs +1 -1
  69. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  70. package/dist/durable/DurableAgentRuntime.mjs +2 -2
  71. package/dist/durable/EventSource.d.mts +1 -1
  72. package/dist/durable/EventSource.mjs +1 -1
  73. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  74. package/dist/durable/LifecyclePublication.d.mts +1 -1
  75. package/dist/durable/LifecyclePublication.mjs +2 -2
  76. package/dist/durable/MessageDelivery.d.mts +1 -1
  77. package/dist/durable/MessageDelivery.mjs +2 -2
  78. package/dist/durable/MessageDeliveryStoreConformance.d.mts +42 -42
  79. package/dist/durable/MessageDeliveryStoreConformance.mjs +2 -2
  80. package/dist/durable/PersistentHistory.d.mts +2 -2
  81. package/dist/durable/PersistentHistory.mjs +4 -4
  82. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  83. package/dist/durable/RecordFormat.d.mts +1 -1
  84. package/dist/durable/RecordFormat.mjs +1 -1
  85. package/dist/durable/Records.d.mts +1 -1
  86. package/dist/durable/Records.mjs +1 -1
  87. package/dist/durable/Recovery.d.mts +1 -1
  88. package/dist/durable/Recovery.mjs +1 -1
  89. package/dist/durable/RunContinuation.d.mts +3 -3
  90. package/dist/durable/RunContinuation.mjs +3 -3
  91. package/dist/durable/RunJournal.d.mts +1 -1
  92. package/dist/durable/RunJournal.mjs +1 -1
  93. package/dist/durable/RunStorage.d.mts +1 -1
  94. package/dist/durable/RunStorage.mjs +1 -1
  95. package/dist/durable/Schedule.d.mts +1 -1
  96. package/dist/durable/Schedule.mjs +1 -1
  97. package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
  98. package/dist/durable/ScheduleStoreConformance.mjs +1 -1
  99. package/dist/durable/ScheduleTransition.d.mts +1 -1
  100. package/dist/durable/ScheduleTransition.mjs +1 -1
  101. package/dist/durable/Scheduling.d.mts +3 -3
  102. package/dist/durable/Scheduling.mjs +1 -1
  103. package/dist/durable/SettlementPublisher.d.mts +1 -1
  104. package/dist/durable/SettlementPublisher.mjs +3 -3
  105. package/dist/durable/SubmissionLedger.d.mts +1 -1
  106. package/dist/durable/SubmissionLedger.mjs +2 -2
  107. package/dist/durable/SubmissionLedgerConformance.d.mts +2 -2
  108. package/dist/durable/SubmissionLedgerConformance.mjs +5 -5
  109. package/dist/durable/SubmissionStatus.d.mts +1 -1
  110. package/dist/durable/Subscription.d.mts +1 -1
  111. package/dist/durable/Subscription.mjs +1 -1
  112. package/dist/durable/SubscriptionInput.d.mts +1 -1
  113. package/dist/durable/SubscriptionInput.mjs +2 -2
  114. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  115. package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
  116. package/dist/durable/SubscriptionTools.d.mts +4 -4
  117. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  118. package/dist/durable/SubscriptionTransition.mjs +1 -1
  119. package/dist/durable/Subscriptions.d.mts +6 -6
  120. package/dist/durable/Subscriptions.mjs +2 -2
  121. package/dist/durable/ThreadArchiveRange.d.mts +1 -1
  122. package/dist/durable/ThreadArchiveRange.mjs +1 -1
  123. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  124. package/dist/durable/ThreadContextHistory.mjs +2 -2
  125. package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
  126. package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
  127. package/dist/durable/ThreadImport.d.mts +1 -1
  128. package/dist/durable/ThreadImport.mjs +1 -1
  129. package/dist/durable/ThreadInvariants.d.mts +3 -3
  130. package/dist/durable/ThreadInvariants.mjs +4 -4
  131. package/dist/durable/ThreadProjection.d.mts +1 -1
  132. package/dist/durable/ThreadProjection.mjs +1 -1
  133. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  134. package/dist/durable/ThreadReader.d.mts +1 -1
  135. package/dist/durable/ThreadReader.mjs +1 -1
  136. package/dist/durable/ThreadStore.d.mts +2 -2
  137. package/dist/durable/ThreadStore.mjs +2 -2
  138. package/dist/durable/ThreadStoreConformance.d.mts +2 -2
  139. package/dist/durable/ThreadStoreConformance.mjs +4 -4
  140. package/dist/durable/ThreadTransfer.d.mts +2 -2
  141. package/dist/durable/ThreadTransfer.mjs +1 -1
  142. package/dist/durable/ThreadWork.d.mts +1 -1
  143. package/dist/durable/ThreadWork.mjs +5 -5
  144. package/dist/durable/ToolReconciler.mjs +1 -1
  145. package/dist/durable/WorkerAdmission.d.mts +2 -2
  146. package/dist/durable/WorkerAdmission.mjs +1 -1
  147. package/dist/durable/WorkerHost.d.mts +2 -2
  148. package/dist/engine/AgentRuntime.d.mts +1 -1
  149. package/dist/engine/AgentRuntime.mjs +1 -1
  150. package/dist/engine/RunOptions.d.mts +1 -1
  151. package/dist/engine/RunOptions.mjs.map +1 -1
  152. package/dist/engine/ThreadHistory.d.mts +1 -1
  153. package/dist/index.d.mts +17 -17
  154. package/dist/index.mjs +12 -12
  155. package/dist/{run-context-reader-kpO9xhP2.d.mts → run-context-reader-DEU4REfD.d.mts} +3 -3
  156. package/package.json +1 -1
  157. package/src/capabilities/Subagent.ts +83 -28
  158. package/src/capabilities/SubagentReservations.ts +4 -1
  159. package/src/capabilities/internal/subagent-contract.ts +23 -3
  160. package/src/core/RunEvent.ts +1 -1
  161. package/src/durable/DurableAgentRuntime.ts +64 -14
  162. package/src/durable/Records.ts +21 -15
  163. package/src/durable/RunContinuation.ts +64 -125
  164. package/src/durable/RunJournal.ts +4 -12
  165. package/src/durable/internal/history-prompt.ts +10 -4
  166. package/src/durable/internal/initial-context.ts +1 -1
  167. package/src/durable/internal/prompt-history-cache.ts +8 -63
  168. package/src/engine/RunOptions.ts +4 -1
  169. package/src/engine/internal/agent-runtime.ts +422 -387
  170. package/dist/AgentRuntime-BAExRlgu.mjs.map +0 -1
  171. package/dist/DurableAgentRuntime-HH2muFrT.mjs.map +0 -1
  172. package/dist/Records-CZRedCE7.mjs.map +0 -1
  173. package/dist/RunContinuation-BF95Ncv2.mjs.map +0 -1
  174. package/dist/RunJournal-oJLo-pjS.mjs.map +0 -1
  175. package/dist/Subagent-D68fe7mr.mjs.map +0 -1
  176. package/dist/json-BlJVQZxN.mjs +0 -15
  177. package/dist/json-BlJVQZxN.mjs.map +0 -1
@@ -1,5 +1,4 @@
1
1
  import { Context, DateTime, Effect, Option, Result, Schema, Semaphore, Stream } from "effect";
2
- import { Prompt } from "effect/ai";
3
2
 
4
3
  import { AgentPersistenceCapacityError } from "../core/AgentError.ts";
5
4
  import type { RunId, SubmissionId, ThreadId } from "../core/Identifiers.ts";
@@ -118,45 +117,6 @@ const isExecutionProgressRecord = (
118
117
  ): record is RecordEnvelope & { readonly payload: RunContinuation } =>
119
118
  record.payload._tag === "RunContinuation";
120
119
 
121
- const decodeResponsePrompt = Schema.decodeUnknownEffect(Prompt.Prompt);
122
- // The decoded Prompt stays private. Progress and capacity are recomputed on every invocation.
123
- const responsePrompts = new WeakMap<ModelResponseRecorded, Prompt.Prompt>();
124
-
125
- /** Cache eligibility only: freezing does not make accessors or custom iterators immutable. */
126
- const isFrozenJsonData = (input: Schema.Json): boolean => {
127
- const pending: Array<unknown> = [input];
128
- const seen = new WeakSet<object>();
129
-
130
- while (pending.length > 0) {
131
- const value = pending.pop();
132
-
133
- if (value === null) continue;
134
- if (typeof value !== "object") {
135
- if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean")
136
- return false;
137
- continue;
138
- }
139
- if (seen.has(value)) continue;
140
- seen.add(value);
141
- const prototype = Object.getPrototypeOf(value);
142
-
143
- if (
144
- !Object.isFrozen(value) ||
145
- (prototype !== null && prototype !== Object.prototype && prototype !== Array.prototype)
146
- )
147
- return false;
148
- for (const key of Reflect.ownKeys(value)) {
149
- if (typeof key !== "string") return false;
150
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
151
-
152
- if (descriptor === undefined || !("value" in descriptor)) return false;
153
- pending.push(descriptor.value);
154
- }
155
- }
156
-
157
- return true;
158
- };
159
-
160
120
  const validateBatchShape = Schema.decodeUnknownSync(
161
121
  Schema.Struct({
162
122
  batchId: CanonicalBatch.fields.batchId,
@@ -165,11 +125,6 @@ const validateBatchShape = Schema.decodeUnknownSync(
165
125
  }),
166
126
  );
167
127
 
168
- const ContinuationBytes = Schema.Struct({
169
- turnBytes: RunContinuation.fields.turnBytes,
170
- terminalBytes: RunContinuation.fields.terminalBytes,
171
- });
172
-
173
128
  const decodeExecutionFrontier = Schema.decodeUnknownEffect(
174
129
  Schema.Struct({
175
130
  canonicalRecords: ExecutionCheckpoint.fields.canonicalRecords,
@@ -714,7 +669,6 @@ const advanceFacts = Effect.fnUntraced(function* (
714
669
  let terminal = previous?.continuation.terminal;
715
670
  let position = previous?.continuation.position ?? "starting";
716
671
  let response = previous?.response;
717
- let responsePrompt: Prompt.Prompt | undefined;
718
672
  let results = new Map(previous?.results);
719
673
  let childResults = new Map(previous?.childResults);
720
674
  let completionBytes = previous?.completionBytes;
@@ -756,31 +710,10 @@ const advanceFacts = Effect.fnUntraced(function* (
756
710
  if (payload.turn !== accounting.committedTurns + 1)
757
711
  return yield* failure("Canonical Turn progress must advance once");
758
712
 
759
- const promptInput = payload.messages;
760
- const retainedPrompt = responsePrompts.get(payload);
761
-
762
- const messages =
763
- retainedPrompt ??
764
- (yield* decodeResponsePrompt(promptInput).pipe(
765
- Effect.mapError((cause) => failure("Invalid model evidence", cause)),
766
- ));
767
-
768
- if (retainedPrompt === undefined) {
769
- const property = Object.getOwnPropertyDescriptor(payload, "messages");
770
-
771
- if (
772
- Object.isFrozen(payload) &&
773
- property !== undefined &&
774
- "value" in property &&
775
- property.value === promptInput &&
776
- isFrozenJsonData(promptInput)
777
- )
778
- responsePrompts.set(payload, messages);
779
- }
780
- responsePrompt = messages;
713
+ const messages = payload.messages;
781
714
 
782
715
  const calls = messages.content.flatMap((message) =>
783
- message.role === "assistant"
716
+ message.role === "assistant" && typeof message.content !== "string"
784
717
  ? message.content.filter((part) => part.type === "tool-call")
785
718
  : [],
786
719
  );
@@ -910,15 +843,8 @@ const advanceFacts = Effect.fnUntraced(function* (
910
843
  response.turn > accounting.accountedToolTurn &&
911
844
  response.toolOperations.every((operation) => results.has(operation.toolCallId))
912
845
  ) {
913
- const messages =
914
- responsePrompt ??
915
- responsePrompts.get(response) ??
916
- (yield* decodeResponsePrompt(response.messages).pipe(
917
- Effect.mapError((cause) => failure("Invalid batch evidence", cause)),
918
- ));
919
-
920
- const parts = messages.content.flatMap((message) =>
921
- message.role === "assistant" ? message.content : [],
846
+ const parts = response.messages.content.flatMap((message) =>
847
+ message.role === "assistant" && typeof message.content !== "string" ? message.content : [],
922
848
  );
923
849
 
924
850
  for (const call of parts) {
@@ -931,7 +857,8 @@ const advanceFacts = Effect.fnUntraced(function* (
931
857
  if (
932
858
  result === undefined ||
933
859
  !("isFailure" in result) ||
934
- ("budgetRejected" in result && result.budgetRejected === true)
860
+ // Only canonical application settlements carry budget-rejection authority.
861
+ (!call.providerExecuted && "budgetRejected" in result && result.budgetRejected === true)
935
862
  )
936
863
  continue;
937
864
  accounting.consecutiveToolFailures = result.isFailure
@@ -1497,29 +1424,30 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1497
1424
  terminalUsageBytes,
1498
1425
  } = next;
1499
1426
 
1500
- let continuation = yield* RunContinuation.makeEffect({
1501
- version: 1,
1502
- runId,
1503
- submissionId,
1504
- revision: (previous?.continuation.revision ?? 0) + 1,
1505
- recordCount: (previous?.continuation.recordCount ?? originalCount) + facts.length,
1506
- recordBytes: (previous?.continuation.recordBytes ?? initialBytes) + factBytes,
1507
- turn,
1508
- turnBytes: turnBase,
1509
- terminalUsageBytes,
1510
- terminalBytes: next.terminalBytes,
1511
- terminalRecords: next.terminalRecords,
1512
- originalInput,
1513
- ...(savedContext === undefined ? {} : { savedContext }),
1514
- ...(latestResponse === undefined ? {} : { latestResponse }),
1515
- ...(terminal === undefined ? {} : { terminal }),
1516
- lastFact: EvidenceReference.make({ recordId: last.recordId }),
1517
- position,
1518
- accounting,
1519
- }).pipe(
1520
- Effect.mapError(() =>
1521
- capacityFailure("Canonical continuation exceeds its protocol bounds"),
1522
- ),
1427
+ // Facts and prior state are already checked. Validate the complete candidate at
1428
+ // record capture, after byte accounting; preflight has its own boundary below.
1429
+ let continuation = RunContinuation.make(
1430
+ {
1431
+ version: 1,
1432
+ runId,
1433
+ submissionId,
1434
+ revision: (previous?.continuation.revision ?? 0) + 1,
1435
+ recordCount: (previous?.continuation.recordCount ?? originalCount) + facts.length,
1436
+ recordBytes: (previous?.continuation.recordBytes ?? initialBytes) + factBytes,
1437
+ turn,
1438
+ turnBytes: turnBase,
1439
+ terminalUsageBytes,
1440
+ terminalBytes: next.terminalBytes,
1441
+ terminalRecords: next.terminalRecords,
1442
+ originalInput,
1443
+ ...(savedContext === undefined ? {} : { savedContext }),
1444
+ ...(latestResponse === undefined ? {} : { latestResponse }),
1445
+ ...(terminal === undefined ? {} : { terminal }),
1446
+ lastFact: EvidenceReference.make({ recordId: last.recordId }),
1447
+ position,
1448
+ accounting,
1449
+ },
1450
+ { disableChecks: true },
1523
1451
  );
1524
1452
 
1525
1453
  const header = {
@@ -1530,7 +1458,7 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1530
1458
  deploymentId: last.deploymentId,
1531
1459
  };
1532
1460
 
1533
- // The validated continuation fields are JSON; only the envelope DateTime needs encoding.
1461
+ // Continuation fields are JSON; only the envelope DateTime needs encoding.
1534
1462
  // Key order does not change byte width. Measure without capturing a provisional record,
1535
1463
  // then compare against the final Schema encoding before accepting its accounting.
1536
1464
  const provisional = {
@@ -1563,18 +1491,9 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1563
1491
  terminalBytes = nextTerminalBytes;
1564
1492
  }
1565
1493
 
1566
- const checkedBytes = yield* ContinuationBytes.makeEffect({
1567
- turnBytes,
1568
- terminalBytes,
1569
- }).pipe(
1570
- Effect.mapError(() =>
1571
- capacityFailure("Turn exceeds its incremental canonical byte budget"),
1572
- ),
1573
- );
1574
-
1575
- // Every other field was checked above; byte accounting changes only these counters.
1494
+ // Byte accounting changes only these counters; the full candidate is checked below.
1576
1495
  continuation = RunContinuation.make(
1577
- { ...continuation, ...checkedBytes },
1496
+ { ...continuation, turnBytes, terminalBytes },
1578
1497
  { disableChecks: true },
1579
1498
  );
1580
1499
 
@@ -1595,8 +1514,13 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1595
1514
  previous?.checkpoint !== undefined &&
1596
1515
  facts.every(({ payload }) => payload._tag === "ModelResponseRecorded")
1597
1516
  ) {
1598
- // The payload and header are validated, and operational facts are unchanged.
1599
- // Dispatch needs their capacity proof; only a commit needs checkpoint wire.
1517
+ // Preflight does not capture a record. Check its complete payload before
1518
+ // dispatch; a commit receives the same proof from record capture below.
1519
+ continuation = yield* RunContinuation.makeEffect({ ...continuation }).pipe(
1520
+ Effect.mapError(() =>
1521
+ capacityFailure("Canonical continuation exceeds its protocol bounds"),
1522
+ ),
1523
+ );
1600
1524
  yield* decodeExecutionFrontier({
1601
1525
  canonicalRecords,
1602
1526
  throughSequence: after + ownedRecords.filter((fact) => !isOperationalRecord(fact)).length,
@@ -1623,7 +1547,11 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1623
1547
 
1624
1548
  const record = yield* Effect.fromResult(captureRecord(proposedRecord)).pipe(
1625
1549
  Effect.map((record) => record.canonical),
1626
- Effect.mapError((cause) => failure("Cannot capture canonical progress facts", cause)),
1550
+ Effect.mapError((cause) =>
1551
+ Schema.isSchemaError(cause.cause)
1552
+ ? capacityFailure("Canonical continuation exceeds its protocol bounds")
1553
+ : failure("Cannot capture canonical progress facts", cause),
1554
+ ),
1627
1555
  );
1628
1556
 
1629
1557
  if (!isExecutionProgressRecord(record))
@@ -1679,7 +1607,7 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1679
1607
  );
1680
1608
 
1681
1609
  const state = {
1682
- continuation,
1610
+ continuation: record.payload,
1683
1611
  record,
1684
1612
  checkpoint,
1685
1613
  ...(response === undefined ? {} : { response }),
@@ -1803,13 +1731,14 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1803
1731
  if (first === undefined)
1804
1732
  return yield* failure("Settlement must publish a retained terminal fact");
1805
1733
 
1806
- const terminalBatch = yield* CanonicalBatch.makeEffect({
1807
- ...prepared.batch,
1808
- records: [first, ...prepared.batch.records.slice(1)],
1809
- }).pipe(
1810
- Effect.mapError((cause) =>
1811
- failure("Settlement must publish a retained terminal fact", cause),
1812
- ),
1734
+ // prepare retains captured records within the validated batch bound.
1735
+ // The explicit first element proves this retained batch is still non-empty.
1736
+ const terminalBatch = CanonicalBatch.make(
1737
+ {
1738
+ ...prepared.batch,
1739
+ records: [first, ...prepared.batch.records.slice(1)],
1740
+ },
1741
+ { disableChecks: true },
1813
1742
  );
1814
1743
 
1815
1744
  const result = yield* publisher.publishSettlement(terminalBatch, prepared.executions).pipe(
@@ -1840,6 +1769,15 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1840
1769
  const publish = (batch: CanonicalBatch) =>
1841
1770
  captureFacts(batch).pipe(Effect.flatMap(publishCaptured));
1842
1771
 
1772
+ const preflight = (batch: CanonicalBatch) =>
1773
+ gate.withPermits(1)(
1774
+ Effect.gen(function* () {
1775
+ const owned = yield* captureFacts(batch);
1776
+
1777
+ yield* prepare(owned, yield* tail, "preflight");
1778
+ }),
1779
+ );
1780
+
1843
1781
  const reserve = (runId: RunId, probe: CanonicalBatch, bytes: number) =>
1844
1782
  gate.withPermits(1)(
1845
1783
  Effect.gen(function* () {
@@ -1952,6 +1890,7 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1952
1890
  return {
1953
1891
  commit,
1954
1892
  publish,
1893
+ preflight,
1955
1894
  reserve,
1956
1895
  defer,
1957
1896
  check,
@@ -8,7 +8,6 @@ import {
8
8
  ToolCallId,
9
9
  type SubmissionId,
10
10
  } from "../core/Identifiers.ts";
11
- import { copyJson } from "../core/internal/json.ts";
12
11
  import { type ExhaustedLimit } from "../core/RunEvent.ts";
13
12
  import { RunPolicyUsage } from "../core/RunPolicyUsage.ts";
14
13
  import { type Selection, type Snapshot } from "../core/ToolExposure.ts";
@@ -37,7 +36,7 @@ import {
37
36
  CanonicalBatch,
38
37
  ModelResponseRecorded,
39
38
  MAX_PERSISTED_JSON_BYTES,
40
- PersistedJson,
39
+ type PersistedJson,
41
40
  RecordEnvelope,
42
41
  RunCompleted,
43
42
  ToolCallSettled,
@@ -1697,7 +1696,6 @@ export interface TurnCommitInput {
1697
1696
  | undefined;
1698
1697
  }
1699
1698
 
1700
- const decodePersistedJson = Schema.decodeUnknownEffect(PersistedJson);
1701
1699
  const decodeModelResponse = Schema.decodeUnknownEffect(ModelResponseRecorded);
1702
1700
  const decodeToolSettled = Schema.decodeUnknownEffect(ToolCallSettled);
1703
1701
  const encodePrompt = Schema.encodeEffect(Prompt.Prompt);
@@ -1739,14 +1737,8 @@ const modelResponseRecord = Effect.fnUntraced(function* (
1739
1737
  Effect.mapErrorEager((cause) => journalError("Turn Prompt messages failed to encode", cause)),
1740
1738
  );
1741
1739
 
1742
- const messages = yield* decodePersistedJson(encodedMessages).pipe(
1743
- Effect.mapEager(copyJson),
1744
- Effect.mapErrorEager((cause) =>
1745
- journalError("Turn Prompt messages exceed canonical persistence bounds", cause),
1746
- ),
1747
- );
1748
-
1749
- // Validate the detached messages and usage together; no mutable caller data is trusted.
1740
+ // Validate messages and usage together. The progress writer captures the complete record
1741
+ // before acquiring its mutation gate; a separate JSON copy here adds no ownership proof.
1750
1742
  const modelUsage = input.usage?.modelUsage;
1751
1743
 
1752
1744
  const payload = yield* decodeModelResponse({
@@ -1761,7 +1753,7 @@ const modelResponseRecord = Effect.fnUntraced(function* (
1761
1753
  runId: input.runId,
1762
1754
  turnId: input.turnId,
1763
1755
  turn: input.turn,
1764
- messages,
1756
+ messages: encodedMessages,
1765
1757
  ...(runScopedPrefixLength === undefined ? {} : { runScopedPrefixLength }),
1766
1758
  ...(modelUsage === undefined ? {} : { modelUsage }),
1767
1759
  ...(input.unobservedModelCalls === undefined || input.unobservedModelCalls === 0
@@ -23,7 +23,8 @@ function restorePart(part: Prompt.PartEncoded): Prompt.Part {
23
23
  }
24
24
  }
25
25
 
26
- const restoreMessage = (message: Prompt.MessageEncoded): Prompt.Message => {
26
+ // The encoded-message Schema has validated this value; constructors restore brands/defaults.
27
+ export const restoreHistoryMessage = (message: Prompt.MessageEncoded): Prompt.Message => {
27
28
  switch (message.role) {
28
29
  case "system":
29
30
  return Prompt.makeMessage("system", message);
@@ -52,14 +53,19 @@ const restoreMessage = (message: Prompt.MessageEncoded): Prompt.Message => {
52
53
  };
53
54
 
54
55
  /**
55
- * Validate the upstream encoded history once, then restore native values with its constructors.
56
+ * Share the upstream encoded-message boundary between canonical history and checkpoint reads.
56
57
  * The canonical codec remains authoritative at append, import, recovery and explicit verification.
57
58
  */
58
- export const HistoryPrompt = Schema.toEncoded(Prompt.Prompt).pipe(
59
+ export const HistoryMessage = Schema.toEncoded(Prompt.Message);
60
+
61
+ export const HistoryPrompt = Schema.Struct({
62
+ content: Schema.Array(HistoryMessage),
63
+ }).pipe(
59
64
  Schema.decodeTo(
60
65
  Schema.toType(Prompt.Prompt),
61
66
  SchemaTransformation.transformEffect({
62
- decode: (input) => Effect.succeed(Prompt.fromMessages(input.content.map(restoreMessage))),
67
+ decode: (input) =>
68
+ Effect.succeed(Prompt.fromMessages(input.content.map(restoreHistoryMessage))),
63
69
  encode: SchemaParser.encodeEffect(Prompt.Prompt),
64
70
  }),
65
71
  ),
@@ -145,7 +145,7 @@ export const initialContext = Effect.fnUntraced(function* (original: InitialCont
145
145
  )
146
146
  return yield* invalid("Late Tool result differs from its original declaration identity");
147
147
 
148
- const prompt = yield* Schema.decodeUnknownEffect(Prompt.Prompt)(response.messages).pipe(
148
+ const prompt = yield* Schema.decodeEffect(Prompt.Prompt)(response.messages).pipe(
149
149
  Effect.mapError(() => invalid("Late Tool declaration messages are malformed")),
150
150
  );
151
151
 
@@ -23,8 +23,9 @@ import {
23
23
  ThreadStore,
24
24
  ThreadTailRequest,
25
25
  } from "../ThreadStore.ts";
26
+ import { HistoryMessage, restoreHistoryMessage } from "./history-prompt.ts";
26
27
  import type { InitialContextInput } from "./initial-context.ts";
27
- import type { JournalRecordEnvelope } from "./journal-metadata.ts";
28
+ import { makeJournalMetadata, type JournalRecordEnvelope } from "./journal-metadata.ts";
28
29
  import { promptJsonFootprint } from "./prompt-footprint.ts";
29
30
  import { captureRecord } from "./record-encoding.ts";
30
31
  import { digestRunHistory } from "./run-context.ts";
@@ -47,9 +48,7 @@ const decodeCommittedPromptRecord = Schema.decodeUnknownSync(PromptRecord);
47
48
  const decodeEncodedPrompt = Schema.decodeUnknownEffect(
48
49
  Schema.suspend(() =>
49
50
  Schema.Struct({
50
- content: Schema.Array(Schema.toEncoded(Prompt.Message)).check(
51
- Schema.isMaxLength(MAX_ENTRIES),
52
- ),
51
+ content: Schema.Array(HistoryMessage).check(Schema.isMaxLength(MAX_ENTRIES)),
53
52
  }),
54
53
  ),
55
54
  );
@@ -64,64 +63,6 @@ const decodePrompt = Schema.decodeUnknownEffect(
64
63
  ),
65
64
  );
66
65
 
67
- // Inputs have crossed Effect's encoded-message Schema boundary. Native constructors
68
- // supply only the brands and defaults; Effect still owns every message and part type.
69
- function hydratePart(part: Prompt.UserMessagePartEncoded): Prompt.UserMessagePart;
70
- function hydratePart(part: Prompt.ToolMessagePartEncoded): Prompt.ToolMessagePart;
71
- function hydratePart(part: Prompt.AssistantMessagePartEncoded): Prompt.AssistantMessagePart;
72
- function hydratePart(part: Prompt.PartEncoded): Prompt.Part {
73
- switch (part.type) {
74
- case "text":
75
- return Prompt.textPart(part);
76
- case "reasoning":
77
- return Prompt.reasoningPart(part);
78
- case "file":
79
- return Prompt.filePart(part);
80
- case "tool-call":
81
- // Unlike options, providerExecuted is required by Effect's constructors;
82
- // only its Schema decoder supplies the false default.
83
- return Prompt.toolCallPart({ ...part, providerExecuted: part.providerExecuted ?? false });
84
- case "tool-result":
85
- return Prompt.toolResultPart({ ...part, providerExecuted: part.providerExecuted ?? false });
86
- case "tool-approval-request":
87
- return Prompt.toolApprovalRequestPart(part);
88
- case "tool-approval-response":
89
- return Prompt.toolApprovalResponsePart(part);
90
- }
91
-
92
- return part satisfies never;
93
- }
94
-
95
- const hydrateMessage = (message: Prompt.MessageEncoded): Prompt.Message => {
96
- switch (message.role) {
97
- case "system":
98
- return Prompt.systemMessage(message);
99
- case "user":
100
- return Prompt.userMessage({
101
- ...message,
102
- content:
103
- typeof message.content === "string"
104
- ? [Prompt.textPart({ text: message.content })]
105
- : message.content.map((p) => hydratePart(p)),
106
- });
107
- case "assistant":
108
- return Prompt.assistantMessage({
109
- ...message,
110
- content:
111
- typeof message.content === "string"
112
- ? [Prompt.textPart({ text: message.content })]
113
- : message.content.map((p) => hydratePart(p)),
114
- });
115
- case "tool":
116
- return Prompt.toolMessage({
117
- ...message,
118
- content: message.content.map((p) => hydratePart(p)),
119
- });
120
- }
121
-
122
- return message satisfies never;
123
- };
124
-
125
66
  export interface PromptHistory {
126
67
  readonly prompt: Prompt.Prompt;
127
68
  encoded: string | undefined;
@@ -171,7 +112,7 @@ export const decodeCheckpointPrefix = Effect.fnUntraced(function* (
171
112
  if (bytes === undefined) return undefined;
172
113
 
173
114
  const decoded = yield* decodeEncodedPrompt(value).pipe(
174
- Effect.map((prompt) => ({ content: prompt.content.map(hydrateMessage) })),
115
+ Effect.map((prompt) => ({ content: prompt.content.map(restoreHistoryMessage) })),
175
116
  // Keep the canonical codec as fallback for representations the encoded Schema rejects.
176
117
  Effect.catch(() => decodePrompt(value)),
177
118
  );
@@ -399,6 +340,7 @@ const readSuffix = Effect.fnUntraced(function* (
399
340
  ) {
400
341
  const store = yield* ThreadStore;
401
342
  const records: Array<JournalRecordEnvelope> = [];
343
+ const metadata = makeJournalMetadata(undefined);
402
344
  let after = from;
403
345
  let remaining = allowance;
404
346
  let active: RunId | undefined;
@@ -461,6 +403,7 @@ const readSuffix = Effect.fnUntraced(function* (
461
403
  if (footprint === undefined) return undefined;
462
404
  remaining -= 32 * footprint;
463
405
  records.push(entry);
406
+ metadata.add(entry);
464
407
  after = entry.sequence;
465
408
  }
466
409
  if (page.length === 0) break;
@@ -474,6 +417,8 @@ const readSuffix = Effect.fnUntraced(function* (
474
417
  (boundary) => {
475
418
  if (boundary.incomplete) incomplete = true;
476
419
  },
420
+ undefined,
421
+ metadata.finish(),
477
422
  );
478
423
 
479
424
  if (incomplete || delta.prompt.content.length > MAX_ENTRIES) return undefined;
@@ -688,10 +688,13 @@ export interface RunDurabilityHook<Error = never, Requirements = never> {
688
688
  * `committed` accepts the supplied facts canonically. `deferred` is valid only for a Response
689
689
  * with `defer: true`: validation and ownership succeeded, but no canonical acceptance occurred.
690
690
  * The interpreter retains its response and inputs until the Settled commit is accepted.
691
+ * `capacity-refused` is valid only for a fresh Response rejected before any storage append or
692
+ * Tool work. The interpreter settles every application call as not dispatched, atomically with
693
+ * that response. A failed append or an already accepted response must never return this value.
691
694
  */
692
695
  readonly commitTurn: (
693
696
  commit: RunTurnCommit,
694
- ) => Effect.Effect<"committed" | "deferred", Error, Requirements>;
697
+ ) => Effect.Effect<"committed" | "deferred" | "capacity-refused", Error, Requirements>;
695
698
  /** Check the writer fence after approval/authorization and before handler permits; no write. */
696
699
  readonly checkToolDispatch: Effect.Effect<void, Error, Requirements>;
697
700
  readonly step: RunStepHook<Error, Requirements>;