@yielded/agent 0.1.0-beta.177 → 0.1.0-beta.179

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 (103) hide show
  1. package/dist/{Admin-Bpg24yCK.d.mts → Admin-NvXFPDIM.d.mts} +26 -6
  2. package/dist/{DurableAgentRuntime-CGW93k81.mjs → DurableAgentRuntime-CWhu1exp.mjs} +49 -69
  3. package/dist/DurableAgentRuntime-CWhu1exp.mjs.map +1 -0
  4. package/dist/{EventSource-A6TB7t-f.d.mts → EventSource-DNyaWmyQ.d.mts} +2 -2
  5. package/dist/{MessageDelivery-BkWrcQUd.d.mts → MessageDelivery-CLzWJImC.d.mts} +42 -42
  6. package/dist/{PreparedInputAdmission-DKIfqRZT.d.mts → PreparedInputAdmission-BQ9UCLFS.d.mts} +3 -3
  7. package/dist/{RunContinuation-D42RFqjS.mjs → RunContinuation-BF95Ncv2.mjs} +9 -11
  8. package/dist/RunContinuation-BF95Ncv2.mjs.map +1 -0
  9. package/dist/{RunContinuation-Ce2pR8VD.d.mts → RunContinuation-CL0wBsQE.d.mts} +3 -6
  10. package/dist/{RunStorage-BRGP3Tcu.mjs → RunStorage-DlfvMfHM.mjs} +20 -6
  11. package/dist/RunStorage-DlfvMfHM.mjs.map +1 -0
  12. package/dist/{Subscription-D5sOrmlI.d.mts → Subscription-D2yn6Eo3.d.mts} +2 -2
  13. package/dist/{SubscriptionInput-CxSXZUiC.d.mts → SubscriptionInput-Cog4x7Rd.d.mts} +2 -2
  14. package/dist/{ThreadImport-DUcPj5jC.mjs → ThreadImport-BoTfjjrz.mjs} +4 -4
  15. package/dist/{ThreadImport-DUcPj5jC.mjs.map → ThreadImport-BoTfjjrz.mjs.map} +1 -1
  16. package/dist/{ThreadImport-C6IiPJMW.d.mts → ThreadImport-em89zmM-.d.mts} +4 -4
  17. package/dist/{ThreadStore-Cn6FDWX9.mjs → ThreadStore-D2rqtt7k.mjs} +13 -8
  18. package/dist/ThreadStore-D2rqtt7k.mjs.map +1 -0
  19. package/dist/{ThreadTransfer-BWnAHIge.mjs → ThreadTransfer-BFolkpEp.mjs} +2 -2
  20. package/dist/{ThreadTransfer-BWnAHIge.mjs.map → ThreadTransfer-BFolkpEp.mjs.map} +1 -1
  21. package/dist/{WorkerAdmission-cfiK9aRi.mjs → WorkerAdmission-CtX1HRkm.mjs} +2 -2
  22. package/dist/{WorkerAdmission-cfiK9aRi.mjs.map → WorkerAdmission-CtX1HRkm.mjs.map} +1 -1
  23. package/dist/capabilities/BrowserUse.d.mts +36 -18
  24. package/dist/capabilities/BrowserUse.mjs +43 -11
  25. package/dist/capabilities/BrowserUse.mjs.map +1 -1
  26. package/dist/capabilities/CodeMode.mjs +1 -0
  27. package/dist/capabilities/CodeMode.mjs.map +1 -1
  28. package/dist/durable/Admin.d.mts +1 -1
  29. package/dist/durable/AgentRegistration.d.mts +1 -1
  30. package/dist/durable/Certification.d.mts +1 -1
  31. package/dist/durable/CommittedActivity.d.mts +1 -1
  32. package/dist/durable/CommittedActivity.mjs +1 -1
  33. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  34. package/dist/durable/DurableAgentRuntime.mjs +2 -2
  35. package/dist/durable/EventSource.d.mts +1 -1
  36. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  37. package/dist/durable/LifecyclePublication.d.mts +1 -1
  38. package/dist/durable/LifecyclePublication.mjs +1 -1
  39. package/dist/durable/MessageDelivery.d.mts +1 -1
  40. package/dist/durable/MessageDeliveryStoreConformance.d.mts +2 -2
  41. package/dist/durable/PersistentHistory.d.mts +1 -1
  42. package/dist/durable/PersistentHistory.mjs +2 -2
  43. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  44. package/dist/durable/Recovery.d.mts +1 -1
  45. package/dist/durable/RunContinuation.d.mts +2 -2
  46. package/dist/durable/RunContinuation.mjs +2 -2
  47. package/dist/durable/RunJournal.d.mts +1 -1
  48. package/dist/durable/RunStorage.d.mts +1 -1
  49. package/dist/durable/RunStorage.mjs +1 -1
  50. package/dist/durable/Scheduling.d.mts +1 -1
  51. package/dist/durable/SettlementPublisher.d.mts +1 -1
  52. package/dist/durable/SettlementPublisher.mjs +1 -1
  53. package/dist/durable/SubmissionLedger.d.mts +1 -1
  54. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  55. package/dist/durable/SubmissionLedgerConformance.mjs +2 -2
  56. package/dist/durable/SubmissionStatus.d.mts +1 -1
  57. package/dist/durable/Subscription.d.mts +1 -1
  58. package/dist/durable/SubscriptionInput.d.mts +1 -1
  59. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  60. package/dist/durable/SubscriptionTools.d.mts +2 -2
  61. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  62. package/dist/durable/Subscriptions.d.mts +5 -5
  63. package/dist/durable/ThreadArchiveRange.d.mts +1 -1
  64. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  65. package/dist/durable/ThreadContextHistory.mjs +1 -1
  66. package/dist/durable/ThreadImport.d.mts +1 -1
  67. package/dist/durable/ThreadImport.mjs +1 -1
  68. package/dist/durable/ThreadInvariants.d.mts +3 -3
  69. package/dist/durable/ThreadInvariants.mjs +3 -3
  70. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  71. package/dist/durable/ThreadReader.d.mts +1 -1
  72. package/dist/durable/ThreadReader.mjs +1 -1
  73. package/dist/durable/ThreadStore.d.mts +2 -2
  74. package/dist/durable/ThreadStore.mjs +2 -2
  75. package/dist/durable/ThreadStoreConformance.d.mts +2 -1
  76. package/dist/durable/ThreadStoreConformance.mjs +2 -2
  77. package/dist/durable/ThreadTransfer.d.mts +1 -1
  78. package/dist/durable/ThreadTransfer.mjs +1 -1
  79. package/dist/durable/ThreadWork.d.mts +1 -1
  80. package/dist/durable/ThreadWork.mjs +2 -2
  81. package/dist/durable/WorkerAdmission.d.mts +1 -1
  82. package/dist/durable/WorkerAdmission.mjs +1 -1
  83. package/dist/durable/WorkerHost.d.mts +1 -1
  84. package/dist/index.d.mts +8 -8
  85. package/dist/index.mjs +6 -6
  86. package/dist/{run-context-reader-BtBPX_nC.d.mts → run-context-reader-kpO9xhP2.d.mts} +2 -2
  87. package/dist/sandbox/CodeExecutor.d.mts +3 -1
  88. package/dist/sandbox/CodeExecutor.mjs +3 -1
  89. package/dist/sandbox/CodeExecutor.mjs.map +1 -1
  90. package/package.json +1 -1
  91. package/src/capabilities/BrowserUse.ts +53 -15
  92. package/src/capabilities/CodeMode.ts +1 -0
  93. package/src/durable/DurableAgentRuntime.ts +78 -143
  94. package/src/durable/RunContinuation.ts +44 -32
  95. package/src/durable/RunStorage.ts +56 -15
  96. package/src/durable/ThreadStore.ts +11 -0
  97. package/src/durable/internal/prompt-history-cache.ts +19 -3
  98. package/src/durable/internal/record-encoding.ts +8 -7
  99. package/src/sandbox/CodeExecutor.ts +3 -1
  100. package/dist/DurableAgentRuntime-CGW93k81.mjs.map +0 -1
  101. package/dist/RunContinuation-D42RFqjS.mjs.map +0 -1
  102. package/dist/RunStorage-BRGP3Tcu.mjs.map +0 -1
  103. package/dist/ThreadStore-Cn6FDWX9.mjs.map +0 -1
@@ -174,6 +174,7 @@ import {
174
174
  type CommittedPromptSuffix,
175
175
  type PromptHistory,
176
176
  } from "./internal/prompt-history-cache.ts";
177
+ import { captureRecord } from "./internal/record-encoding.ts";
177
178
  import { RunContextReader } from "./internal/run-context-reader.ts";
178
179
  import { readRunContext } from "./internal/run-context.ts";
179
180
  import * as ThreadInitialization from "./internal/thread-initialization.ts";
@@ -403,7 +404,6 @@ import {
403
404
  getRunInput,
404
405
  ThreadReader,
405
406
  MAX_THREAD_EXPORT_PAGE_BYTES,
406
- PreparedAppend,
407
407
  type CommitBatch,
408
408
  type ExecutionUpdate,
409
409
  } from "./ThreadStore.ts";
@@ -4918,6 +4918,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
4918
4918
  progressThrough: CanonicalSequence | undefined,
4919
4919
  continuation: RunContinuation | undefined,
4920
4920
  priorContext: RunJournalContext | undefined,
4921
+ operational: ReadonlyArray<RecordEnvelope>,
4921
4922
  journalMetadata: JournalMetadata | undefined,
4922
4923
  lineage: AttemptLineage,
4923
4924
  approvalDecisions: ReadonlyArray<ApprovalDecisionIntent>,
@@ -5453,13 +5454,9 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
5453
5454
  );
5454
5455
  });
5455
5456
 
5456
- const activeExecution = yield* reader.readExecution({ threadId: ctx.threadId, runId });
5457
-
5458
- const recordedCompletion = Option.isSome(activeExecution)
5459
- ? activeExecution.value.operational.find(
5460
- (record) => record.recordId === runCompletedRecordId(runId),
5461
- )?.payload
5462
- : undefined;
5457
+ const recordedCompletion = operational.find(
5458
+ (record) => record.recordId === runCompletedRecordId(runId),
5459
+ )?.payload;
5463
5460
 
5464
5461
  if (recordedCompletion !== undefined) {
5465
5462
  if (recordedCompletion._tag !== "RunCompleted" || recordedCompletion.runId !== runId) {
@@ -7830,19 +7827,26 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
7830
7827
  ...(config.estimateCostMicrousd === undefined
7831
7828
  ? {}
7832
7829
  : { estimateCostMicrousd: config.estimateCostMicrousd }),
7833
- resumeUsage: {
7834
- ...journal.usage,
7835
- ...journal.policyUsage,
7836
- ...(journal.usage.modelCalls === 0 &&
7837
- (journal.usage.unobservedModelCalls ?? 0) === 0 &&
7838
- !records.some(
7839
- ({ record }) =>
7840
- record.payload._tag === "ModelResponseInterrupted" && record.payload.runId === runId,
7841
- )
7842
- ? emptyRunTotals()
7843
- : runTotalsFromSummary(yield* currentUsageSummary())),
7844
- children: childUsageReportsOf(subagentState),
7845
- },
7830
+ // An exact committed history prefix followed only by this input creates the
7831
+ // initial journal above. There is no resumed accounting to reconstruct or decode.
7832
+ ...(freshPrefix !== undefined && continuation === undefined
7833
+ ? {}
7834
+ : {
7835
+ resumeUsage: {
7836
+ ...journal.usage,
7837
+ ...journal.policyUsage,
7838
+ ...(journal.usage.modelCalls === 0 &&
7839
+ (journal.usage.unobservedModelCalls ?? 0) === 0 &&
7840
+ !records.some(
7841
+ ({ record }) =>
7842
+ record.payload._tag === "ModelResponseInterrupted" &&
7843
+ record.payload.runId === runId,
7844
+ )
7845
+ ? emptyRunTotals()
7846
+ : runTotalsFromSummary(yield* currentUsageSummary())),
7847
+ children: childUsageReportsOf(subagentState),
7848
+ },
7849
+ }),
7846
7850
  ...(journal.contextWindowId === undefined
7847
7851
  ? {}
7848
7852
  : { initialContextWindowId: journal.contextWindowId }),
@@ -8301,136 +8305,66 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8301
8305
  const { claim, threadId } = session;
8302
8306
  const submissionId = claim.submissionId;
8303
8307
 
8304
- // The hint only selects this preparation. A real claim, current controls, exact
8305
- // evidence and checkpoint absence are verified under its owner before any reuse.
8306
- // No binding, instructions, model, Tool or cross-Thread operation runs in the window.
8307
- let prepared =
8308
- freshlyAdmitted && session.withInitialization !== undefined
8309
- ? yield* session.withInitialization(
8310
- Effect.gen(function* () {
8311
- const snapshot = yield* ledger.loadRecoverySnapshot(
8312
- RecoverySnapshotRequest.make({ submissionId }),
8313
- );
8314
-
8315
- const submission = snapshot.submission;
8308
+ // The hint selects a read under the existing owner. Only exact empty-Run evidence
8309
+ // permits startup; the initial commit still checks its tail and revision-zero CAS.
8310
+ const preparation = freshlyAdmitted ? yield* session.prepareStart ?? Effect.void : undefined;
8316
8311
 
8317
- if (
8318
- submission.state !== "ready" ||
8319
- submission.workerAdmission !== undefined ||
8320
- submission.messageAdmission !== undefined ||
8321
- submission.parentLinkage !== undefined ||
8322
- snapshot.inputApplied !== undefined ||
8323
- snapshot.abortIntent !== undefined ||
8324
- snapshot.approvalDecisions.length !== 0
8325
- )
8326
- return undefined;
8327
- yield* ensureThreadCreated(threadId, submission.agentId, submission.agentDigests);
8328
- yield* session.refresh;
8329
- const before = (yield* session.tail).sequence;
8330
-
8331
- const view = yield* recoveryView(
8332
- threadId,
8333
- before,
8334
- [submissionId],
8335
- runIdForSubmission(submissionId),
8336
- );
8337
-
8338
- if (
8339
- view.progress !== undefined ||
8340
- view.context !== undefined ||
8341
- view.operational.length !== 0 ||
8342
- view.records.some(({ record }) => record.payload._tag !== "ThreadCreated")
8343
- )
8344
- return undefined;
8345
- if (session.appendInput !== undefined) {
8346
- const captured = yield* PreparedAppend.capture(
8347
- AppendRequest.make({
8348
- threadId,
8349
- expectedTailSequence: before,
8350
- batch: CanonicalBatch.make({
8351
- batchId: submissionInputBatchId(submissionId),
8352
- producerId: config.producerId,
8353
- records: [yield* prepareOriginalInput(submission)],
8354
- }),
8355
- }),
8356
- );
8357
-
8358
- const record = captured.records[0]!.canonical;
8312
+ let prepared:
8313
+ | {
8314
+ snapshot: RecoverySnapshot;
8315
+ through: CanonicalSequence;
8316
+ view: Omit<Effect.Success<ReturnType<typeof recoveryView>>, "records"> & {
8317
+ readonly records: ReadonlyArray<CanonicalRecordEnvelope>;
8318
+ };
8319
+ start: Effect.Success<ReturnType<typeof makeRunStart>>;
8320
+ input: InitialContextInput;
8321
+ }
8322
+ | undefined;
8359
8323
 
8360
- if (!isOriginalInputRecord(record))
8361
- return yield* RunJournalError.make({
8362
- message: "Captured startup has no original input",
8363
- });
8364
- pendingOriginal = {
8365
- threadId,
8366
- sequence: CanonicalSequence.make(before + 1),
8367
- record,
8368
- };
8324
+ if (preparation !== undefined) {
8325
+ const { snapshot, identity } = preparation;
8326
+ const before = identity.tailSequence;
8369
8327
 
8370
- const start = yield* makeRunStart(
8371
- submission,
8372
- Duration.toMillis(registeredAgent.definition.policy.maxDuration),
8373
- view.records,
8374
- view.operational,
8375
- record,
8376
- ).pipe(Effect.provideService(CurrentRunWriter, session));
8328
+ const captured = yield* Effect.fromResult(
8329
+ captureRecord(yield* prepareOriginalInput(snapshot.submission)),
8330
+ ).pipe(
8331
+ Effect.mapError((cause) =>
8332
+ ThreadStoreError.make({
8333
+ operation: "capture startup input",
8334
+ message: cause.message,
8335
+ cause,
8336
+ }),
8337
+ ),
8338
+ );
8377
8339
 
8378
- return { snapshot, through: before, view, start, input: pendingOriginal };
8379
- }
8380
- const progress = progressWriters.get(session);
8340
+ const record = captured.canonical;
8381
8341
 
8382
- const applyInput = applyCanonicalInput(
8383
- session,
8384
- submission,
8385
- session,
8386
- view.records,
8387
- undefined,
8388
- );
8342
+ if (!isOriginalInputRecord(record))
8343
+ return yield* RunJournalError.make({ message: "Captured startup has no original input" });
8344
+ pendingOriginal = {
8345
+ threadId,
8346
+ sequence: CanonicalSequence.make(before + 1),
8347
+ record,
8348
+ };
8389
8349
 
8390
- yield* progress === undefined
8391
- ? applyInput
8392
- : progress
8393
- .withVerifiedEmptyRun(runIdForSubmission(submissionId), before, applyInput)
8394
- .pipe(Effect.provideService(CurrentRunWriter, promptSession));
8395
- const through = (yield* session.tail).sequence;
8396
- const added = yield* selectedRange(threadId, [submissionId], through, before);
8397
- const records = [...view.records, ...added];
8350
+ const view = {
8351
+ records: identity.records,
8352
+ context: undefined,
8353
+ progress: undefined,
8354
+ progressThrough: undefined,
8355
+ operational: [],
8356
+ };
8398
8357
 
8399
- const accepted = progressWriters
8400
- .get(session)
8401
- ?.acceptedCheckpoint(runIdForSubmission(submissionId));
8358
+ const start = yield* makeRunStart(
8359
+ snapshot.submission,
8360
+ Duration.toMillis(registeredAgent.definition.policy.maxDuration),
8361
+ view.records,
8362
+ view.operational,
8363
+ record,
8364
+ ).pipe(Effect.provideService(CurrentRunWriter, session));
8402
8365
 
8403
- if (
8404
- accepted === undefined ||
8405
- accepted.throughSequence !== through ||
8406
- accepted.progress.runId !== runIdForSubmission(submissionId) ||
8407
- accepted.progress.submissionId !== submissionId
8408
- )
8409
- return undefined;
8410
-
8411
- const start = yield* makeRunStart(
8412
- submission,
8413
- Duration.toMillis(registeredAgent.definition.policy.maxDuration),
8414
- records,
8415
- view.operational,
8416
- ).pipe(Effect.provideService(CurrentRunWriter, session));
8417
-
8418
- return {
8419
- snapshot,
8420
- through,
8421
- view: {
8422
- ...view,
8423
- records,
8424
- progress: accepted.progress,
8425
- progressThrough: accepted.throughSequence,
8426
- operational: accepted.operational,
8427
- },
8428
- start,
8429
- input: undefined,
8430
- };
8431
- }),
8432
- )
8433
- : undefined;
8366
+ prepared = { snapshot, through: before, view, start, input: pendingOriginal };
8367
+ }
8434
8368
 
8435
8369
  const preparedInput = prepared !== undefined;
8436
8370
 
@@ -8941,6 +8875,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
8941
8875
  view.progressThrough,
8942
8876
  view.progress,
8943
8877
  view.context,
8878
+ view.operational,
8944
8879
  takeJournalMetadata(),
8945
8880
  lineage,
8946
8881
  approvalDecisionIntents,
@@ -184,18 +184,25 @@ const captureFacts = (batch: CommitBatch) =>
184
184
  Result.gen(function* () {
185
185
  const inputs = batch.records;
186
186
 
187
- const { batchId, producerId } = validateBatchShape({
187
+ const {
188
+ batchId,
189
+ producerId,
190
+ records: slots,
191
+ } = validateBatchShape({
188
192
  batchId: batch.batchId,
189
193
  producerId: batch.producerId,
190
194
  records: inputs,
191
195
  });
192
196
 
193
- const encodings = yield* Result.all(inputs.map(captureRecord));
194
- const records = encodings.map((record) => record.canonical);
197
+ const first = yield* captureRecord(inputs[0]!);
198
+ const records: [RecordEnvelope, ...Array<RecordEnvelope>] = [first.canonical];
199
+
200
+ for (let index = 1; index < slots.length; index++)
201
+ records.push((yield* captureRecord(inputs[index]!)).canonical);
195
202
 
196
203
  return Object.freeze(
197
204
  CanonicalBatch.make(
198
- { batchId, producerId, records: [records[0]!, ...records.slice(1)] },
205
+ { batchId, producerId, records: Object.freeze(records) },
199
206
  { disableChecks: true },
200
207
  ),
201
208
  );
@@ -1646,25 +1653,30 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1646
1653
  operational.set(fact.recordId, fact);
1647
1654
  }
1648
1655
 
1649
- const checkpoint = yield* ExecutionCheckpoint.makeEffect({
1650
- version: 2,
1651
- progressRecord: record,
1652
- operational: [...operational.values()],
1653
- canonicalRecords,
1654
- originalSequence:
1655
- originalSequence ??
1656
- CanonicalSequence.make(
1657
- after +
1658
- ownedRecords
1659
- .filter((fact) => !isOperationalRecord(fact))
1660
- .findIndex((fact) => fact.recordId === originalInput.recordId) +
1661
- 1,
1656
+ // The records above already belong to this writer. PreparedAppend captures the
1657
+ // complete update at the storage boundary, preserving their private encodings.
1658
+ const checkpoint = ExecutionCheckpoint.make(
1659
+ {
1660
+ version: 2,
1661
+ progressRecord: record,
1662
+ operational: [...operational.values()],
1663
+ canonicalRecords,
1664
+ originalSequence:
1665
+ originalSequence ??
1666
+ CanonicalSequence.make(
1667
+ after +
1668
+ ownedRecords
1669
+ .filter((fact) => !isOperationalRecord(fact))
1670
+ .findIndex((fact) => fact.recordId === originalInput.recordId) +
1671
+ 1,
1672
+ ),
1673
+ throughSequence: CanonicalSequence.make(
1674
+ after + ownedRecords.filter((fact) => !isOperationalRecord(fact)).length,
1662
1675
  ),
1663
- throughSequence: CanonicalSequence.make(
1664
- after + ownedRecords.filter((fact) => !isOperationalRecord(fact)).length,
1665
- ),
1666
- batchId: batch.batchId,
1667
- }).pipe(Effect.mapError((cause) => failure("Active execution exceeds its bounds", cause)));
1676
+ batchId: batch.batchId,
1677
+ },
1678
+ { disableChecks: true },
1679
+ );
1668
1680
 
1669
1681
  const state = {
1670
1682
  continuation,
@@ -1687,13 +1699,16 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1687
1699
  );
1688
1700
  yield* checkFutureShapes(state);
1689
1701
  executions.push(
1690
- ExecutionUpdate.make({
1691
- previousRevision: previous?.continuation.revision ?? 0,
1692
- checkpoint,
1693
- facts: changed,
1694
- // Pending operations and handoffs retain their own recovery rows and original facts.
1695
- retire: position === "settled",
1696
- }),
1702
+ ExecutionUpdate.make(
1703
+ {
1704
+ previousRevision: previous?.continuation.revision ?? 0,
1705
+ checkpoint,
1706
+ facts: changed,
1707
+ // Pending operations and handoffs retain their own recovery rows and original facts.
1708
+ retire: position === "settled",
1709
+ },
1710
+ { disableChecks: true },
1711
+ ),
1697
1712
  );
1698
1713
  accepted.set(runId, state);
1699
1714
  }
@@ -1940,9 +1955,6 @@ export const makeProgressWriter = Effect.fnUntraced(function* (
1940
1955
  reserve,
1941
1956
  defer,
1942
1957
  check,
1943
- /** Internal read view immediately after this writer's acknowledged commit, while
1944
- * holding its initialization gates. Never authority across ownership or writes. */
1945
- acceptedCheckpoint: (runId: RunId) => cached.get(runId)?.checkpoint,
1946
1958
  /** Scope a verified empty execution/evidence view to one owned initialization body.
1947
1959
  * The caller holds progress+mutation gates; a changed writer or tail never reuses it. */
1948
1960
  withVerifiedEmptyRun: <A, E, R>(
@@ -17,6 +17,8 @@ import {
17
17
  type Claim,
18
18
  type ClaimRequest,
19
19
  type InputAppliedMarker,
20
+ type RecoverySnapshot,
21
+ RecoverySnapshotRequest,
20
22
  LedgerError,
21
23
  MarkInputAppliedRequest,
22
24
  MarkJoinedRequest,
@@ -34,6 +36,8 @@ import {
34
36
  type CommitBatch,
35
37
  type ExecutionUpdate,
36
38
  PreparedAppend,
39
+ FreshRunRead,
40
+ type ThreadIdentity,
37
41
  ThreadMaterialization,
38
42
  ThreadNotMaterialized,
39
43
  ThreadStore,
@@ -115,11 +119,12 @@ export interface RunStorageSession extends RunWriter, RunOwnership {
115
119
  readonly claim: Claim;
116
120
  /** Atomic original input/context plus the ledger marker, when the adapter supports it. */
117
121
  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>;
122
+ /** Read current controls, root identity and empty-Run evidence under this owner.
123
+ * Its tail is adopted by the writer; append still checks the tail and revision. */
124
+ readonly prepareStart?: Effect.Effect<
125
+ { readonly snapshot: RecoverySnapshot; readonly identity: ThreadIdentity } | undefined,
126
+ LedgerError | ThreadStoreFailure
127
+ >;
123
128
  readonly refresh: Effect.Effect<void, ThreadStoreFailure>;
124
129
  readonly publishSettlement: (
125
130
  batch: CanonicalBatch,
@@ -182,14 +187,12 @@ const makeWriter = Effect.fnUntraced(function* (threadId: ThreadId) {
182
187
  const publisher = yield* ThreadStore;
183
188
 
184
189
  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 }),
191
- }),
192
- ).pipe(
190
+ const captured = yield* PreparedAppend.capture({
191
+ threadId,
192
+ expectedTailSequence: tail.sequence,
193
+ batch,
194
+ ...(executions === undefined ? {} : { executions }),
195
+ }).pipe(
193
196
  Effect.mapError((cause) =>
194
197
  ThreadStoreError.make({
195
198
  operation: "capture append",
@@ -343,8 +346,46 @@ export const make = Effect.gen(function* () {
343
346
  ...writer,
344
347
  claim: claimed,
345
348
  ...(appendInput === undefined ? {} : { appendInput }),
346
- withInitialization: (effect) =>
347
- owner.withProgress(request.threadId, generation.use(effect)),
349
+ ...(store.readFreshRun === undefined || appendInput === undefined
350
+ ? {}
351
+ : {
352
+ prepareStart: owner.withProgress(
353
+ request.threadId,
354
+ generation.use(
355
+ Effect.gen(function* () {
356
+ const snapshot = yield* ledger.loadRecoverySnapshot(
357
+ RecoverySnapshotRequest.make({ submissionId: claimed.submissionId }),
358
+ );
359
+
360
+ const submission = snapshot.submission;
361
+
362
+ if (
363
+ submission.threadId !== request.threadId ||
364
+ submission.state !== "ready" ||
365
+ submission.workerAdmission !== undefined ||
366
+ submission.messageAdmission !== undefined ||
367
+ submission.parentLinkage !== undefined ||
368
+ snapshot.inputApplied !== undefined ||
369
+ snapshot.abortIntent !== undefined ||
370
+ snapshot.approvalDecisions.length !== 0
371
+ )
372
+ return undefined;
373
+
374
+ const identity = yield* store.readFreshRun!(
375
+ FreshRunRead.make({
376
+ threadId: request.threadId,
377
+ submissionId: claimed.submissionId,
378
+ }),
379
+ );
380
+
381
+ if (Option.isNone(identity)) return undefined;
382
+ adoptTail(identity.value.tailSequence);
383
+
384
+ return { snapshot, identity: identity.value };
385
+ }),
386
+ ),
387
+ ),
388
+ }),
348
389
  release: generation.release,
349
390
  refresh: writer.refresh,
350
391
  markInputApplied: (marker) =>
@@ -836,6 +836,11 @@ export class ThreadIdentityRequest extends Schema.Class<ThreadIdentityRequest>(
836
836
  "@effect-agent/thread/ThreadIdentityRequest",
837
837
  )({ threadId: ThreadId }) {}
838
838
 
839
+ export class FreshRunRead extends Schema.Class<FreshRunRead>("@effect-agent/thread/FreshRunRead")({
840
+ threadId: ThreadId,
841
+ submissionId: SubmissionId,
842
+ }) {}
843
+
839
844
  /**
840
845
  * One consistent snapshot: Thread ID, canonical tail, admission presence, first record,
841
846
  * exact worker origin, then exact subagent lineage. A row selected twice appears only
@@ -1246,6 +1251,12 @@ export class ThreadStore extends Context.Service<
1246
1251
  readonly readIdentity: (
1247
1252
  request: ThreadIdentityRequest,
1248
1253
  ) => Effect.Effect<ThreadIdentity, ThreadStoreError | ThreadNotMaterialized>;
1254
+ /** Owner-held preparation for an admitted root Run. Some proves an initialized root
1255
+ * identity and no checkpoint, Run membership or exact input/abort/settlement record.
1256
+ * None requires ordinary recovery; it never authorizes overwriting existing evidence. */
1257
+ readonly readFreshRun?: (
1258
+ request: FreshRunRead,
1259
+ ) => Effect.Effect<Option.Option<ThreadIdentity>, ThreadStoreError | ThreadNotMaterialized>;
1249
1260
  readonly readWorkerCapacity?: (
1250
1261
  request: ThreadWorkerCapacityRequest,
1251
1262
  ) => Effect.Effect<ThreadWorkerCapacity, ThreadStoreError | ThreadNotMaterialized>;
@@ -294,8 +294,9 @@ export const makeCommittedPromptSuffix = (
294
294
  return;
295
295
  }
296
296
  // Prepared append facts already have privately frozen encodings; the original input and
297
- // winning settlement are captured here before retention. Decode the same prompt projection
298
- // as readPrompt, never raw provider values. Failure invalidates this derivative, not the commit.
297
+ // winning settlement are captured here before retention. An input contributes only its
298
+ // owned identity; other facts still decode the prompt projection used by readPrompt.
299
+ // Failure invalidates this derivative, not the commit.
299
300
  try {
300
301
  for (const fact of facts) {
301
302
  const captured = captureRecord(fact);
@@ -332,10 +333,25 @@ export const makeCommittedPromptSuffix = (
332
333
  return;
333
334
  }
334
335
 
336
+ const record: PromptRecord =
337
+ payload._tag === "UserInputRecorded"
338
+ ? {
339
+ recordId: owned.canonical.recordId,
340
+ payload: {
341
+ _tag: payload._tag,
342
+ kind: payload.kind,
343
+ ...(payload.runId === undefined ? {} : { runId: payload.runId }),
344
+ ...(payload.submissionId === undefined
345
+ ? {}
346
+ : { submissionId: payload.submissionId }),
347
+ },
348
+ }
349
+ : decodeCommittedPromptRecord(owned.wire);
350
+
335
351
  const entry: PromptRecordEnvelope = {
336
352
  threadId: original.threadId,
337
353
  sequence: CanonicalSequence.make(through + 1),
338
- record: decodeCommittedPromptRecord(owned.wire),
354
+ record,
339
355
  };
340
356
 
341
357
  const footprint = suffixRecordFootprint(entry, remaining);
@@ -118,14 +118,14 @@ const capturePayload = (payload: CanonicalRecordPayload) => {
118
118
  Object.setPrototypeOf(copy, Object.getPrototypeOf(payload));
119
119
  const copies = new WeakMap<object, object>([[payload, copy]]);
120
120
 
121
- const pending: Array<object> = [copy];
122
-
123
121
  const owned: Array<object> = [copy];
124
122
 
125
- while (pending.length > 0) {
126
- const next = pending.pop()!;
123
+ for (let index = 0; index < owned.length; index++) {
124
+ const next = owned[index]!;
125
+
126
+ for (const key of Object.keys(next)) {
127
+ const value = Reflect.get(next, key);
127
128
 
128
- for (const [key, value] of Object.entries(next)) {
129
129
  if (!Predicate.isObjectOrArray(value)) continue;
130
130
  let child = copies.get(value);
131
131
 
@@ -134,7 +134,6 @@ const capturePayload = (payload: CanonicalRecordPayload) => {
134
134
 
135
135
  copies.set(value, created);
136
136
  owned.push(created);
137
- pending.push(created);
138
137
  child = created;
139
138
  }
140
139
  setOwn(next, key, child);
@@ -152,7 +151,9 @@ const ownWire = <Wire extends RecordJson>(wire: Wire, copies: WeakMap<object, ob
152
151
  while (pending.length > 0) {
153
152
  const next = pending.pop()!;
154
153
 
155
- for (const [key, value] of Object.entries(next)) {
154
+ for (const key of Object.keys(next)) {
155
+ const value = Reflect.get(next, key);
156
+
156
157
  if (!Predicate.isObjectOrArray(value)) continue;
157
158
  const copy = copies.get(value);
158
159
 
@@ -159,7 +159,9 @@ export class CodeHostCallSuccess extends Schema.TaggedClass<CodeHostCallSuccess>
159
159
  /**
160
160
  * A host call settled with a Schema-encoded typed failure envelope. Inside the
161
161
  * generated program the call's Promise rejects with the envelope value, so a
162
- * program can catch and branch on expected failures.
162
+ * program can catch and branch on expected failures. Object and array failures
163
+ * stringify to their encoded JSON, preserving recovery details in String(error)
164
+ * without changing the enumerable fields returned or rethrown by the program.
163
165
  */
164
166
  export class CodeHostCallFailure extends Schema.TaggedClass<CodeHostCallFailure>()(
165
167
  "CodeHostCallFailure",