effect-agent 0.1.0-beta.97 → 0.1.0-beta.99

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 (143) hide show
  1. package/dist/{AgentRuntime-B64362Id.d.mts → AgentRuntime-BMzrJos6.d.mts} +2 -2
  2. package/dist/{AgentRuntime-Cdvqw7l_.mjs → AgentRuntime-BWOnNc0d.mjs} +78 -42
  3. package/dist/AgentRuntime-BWOnNc0d.mjs.map +1 -0
  4. package/dist/{DurableAgentRuntime-DbUvZHDZ.mjs → DurableAgentRuntime-BE_OjQjz.mjs} +471 -252
  5. package/dist/DurableAgentRuntime-BE_OjQjz.mjs.map +1 -0
  6. package/dist/{DurableFailpoint-DQJet828.d.mts → DurableFailpoint-mx3-nsxU.d.mts} +3 -3
  7. package/dist/{EventSource-D6PAAEyK.d.mts → EventSource-BZ73J0s5.d.mts} +3 -3
  8. package/dist/{PreparedInputAdmission-omMbwd--.d.mts → PreparedInputAdmission-BkeI-Xvr.d.mts} +4 -4
  9. package/dist/{Records-CV49DgQK.d.mts → Records-BIiRLqlz.d.mts} +92 -128
  10. package/dist/{Records-BAutSSnJ.mjs → Records-DR1xtbXY.mjs} +35 -8
  11. package/dist/Records-DR1xtbXY.mjs.map +1 -0
  12. package/dist/{RunJournal-DRRRZmG7.mjs → RunJournal-DNs3PM4K.mjs} +80 -23
  13. package/dist/RunJournal-DNs3PM4K.mjs.map +1 -0
  14. package/dist/{RunOptions-Clj8cLsp.d.mts → RunOptions-Bruk54SE.d.mts} +13 -4
  15. package/dist/{Schedule-DePbGfgd.d.mts → Schedule-BNFlluAl.d.mts} +2 -2
  16. package/dist/{Scheduling-C3QpYn0W.mjs → Scheduling-HFH0pST1.mjs} +2 -2
  17. package/dist/{Scheduling-C3QpYn0W.mjs.map → Scheduling-HFH0pST1.mjs.map} +1 -1
  18. package/dist/{Subagent-Cbfzylz1.mjs → Subagent-C2s8PFrd.mjs} +2 -2
  19. package/dist/{Subagent-Cbfzylz1.mjs.map → Subagent-C2s8PFrd.mjs.map} +1 -1
  20. package/dist/{Subagent-Bm1dRecN.d.mts → Subagent-m7ywTCe4.d.mts} +3 -3
  21. package/dist/{Subscription-BR_8Cxh3.d.mts → Subscription-BCQ3L9Zp.d.mts} +3 -3
  22. package/dist/{SubscriptionInput-B95sQbuk.d.mts → SubscriptionInput-Cqu4_RW3.d.mts} +3 -3
  23. package/dist/{ToolReconciler-DQVaJfpE.d.mts → ToolReconciler-BmVRAHKs.d.mts} +8 -4
  24. package/dist/agent-registration-cXpB8cBU.mjs +199 -0
  25. package/dist/agent-registration-cXpB8cBU.mjs.map +1 -0
  26. package/dist/capabilities/Commands.d.mts +1 -1
  27. package/dist/capabilities/RunHooks.d.mts +1 -1
  28. package/dist/capabilities/Subagent.d.mts +1 -1
  29. package/dist/capabilities/Subagent.mjs +1 -1
  30. package/dist/durable/ActivityStore.d.mts +1 -1
  31. package/dist/durable/ActivityStore.mjs +1 -1
  32. package/dist/durable/Admin.d.mts +1 -1
  33. package/dist/durable/Admin.mjs +12 -10
  34. package/dist/durable/Admin.mjs.map +1 -1
  35. package/dist/durable/AgentRegistration.d.mts +2 -2
  36. package/dist/durable/AgentRegistration.mjs +4 -8
  37. package/dist/durable/AgentRegistration.mjs.map +1 -1
  38. package/dist/durable/Certification.d.mts +2 -2
  39. package/dist/durable/CommittedActivity.d.mts +1 -1
  40. package/dist/durable/CommittedActivity.mjs +1 -1
  41. package/dist/durable/Digest.d.mts +1 -1
  42. package/dist/durable/Digest.mjs +1 -1
  43. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  44. package/dist/durable/DurableAgentRuntime.mjs +1 -1
  45. package/dist/durable/DurableFailpoint.d.mts +1 -1
  46. package/dist/durable/DurableFailpoint.mjs +2 -0
  47. package/dist/durable/DurableFailpoint.mjs.map +1 -1
  48. package/dist/durable/DurableFailpointTestControl.d.mts +1 -1
  49. package/dist/durable/EventSource.d.mts +1 -1
  50. package/dist/durable/EventSource.mjs +1 -1
  51. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  52. package/dist/durable/GitHubWorkflowSource.mjs +1 -1
  53. package/dist/durable/MessageDelivery.d.mts +3 -3
  54. package/dist/durable/MessageDelivery.mjs +1 -1
  55. package/dist/durable/MessageDeliveryStoreConformance.d.mts +39 -39
  56. package/dist/durable/MessageDeliveryStoreConformance.mjs +1 -1
  57. package/dist/durable/PersistentHistory.d.mts +1 -1
  58. package/dist/durable/PersistentHistory.mjs +2 -2
  59. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  60. package/dist/durable/Records.d.mts +2 -2
  61. package/dist/durable/Records.mjs +2 -2
  62. package/dist/durable/Recovery.d.mts +1 -1
  63. package/dist/durable/Recovery.mjs +9 -9
  64. package/dist/durable/Recovery.mjs.map +1 -1
  65. package/dist/durable/RunJournal.d.mts +2 -2
  66. package/dist/durable/RunJournal.mjs +2 -2
  67. package/dist/durable/Schedule.d.mts +1 -1
  68. package/dist/durable/Schedule.mjs +1 -1
  69. package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
  70. package/dist/durable/ScheduleStoreConformance.mjs +1 -1
  71. package/dist/durable/ScheduleTransition.d.mts +1 -1
  72. package/dist/durable/ScheduleTransition.mjs +1 -1
  73. package/dist/durable/Scheduling.d.mts +2 -2
  74. package/dist/durable/Scheduling.mjs +1 -1
  75. package/dist/durable/SqlStorageV2Upgrade.d.mts +2 -2
  76. package/dist/durable/SqlStorageV2Upgrade.mjs +1 -1
  77. package/dist/durable/SqlSubscriptionStore.d.mts +2 -2
  78. package/dist/durable/SqlSubscriptionStore.mjs +1 -1
  79. package/dist/durable/SubmissionLedger.d.mts +1 -1
  80. package/dist/durable/SubmissionLedger.mjs +14 -13
  81. package/dist/durable/SubmissionLedger.mjs.map +1 -1
  82. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  83. package/dist/durable/SubmissionLedgerConformance.mjs +9 -2
  84. package/dist/durable/SubmissionLedgerConformance.mjs.map +1 -1
  85. package/dist/durable/SubmissionStatus.d.mts +1 -1
  86. package/dist/durable/Subscription.d.mts +1 -1
  87. package/dist/durable/Subscription.mjs +1 -1
  88. package/dist/durable/SubscriptionInput.d.mts +1 -1
  89. package/dist/durable/SubscriptionInput.mjs +2 -2
  90. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  91. package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
  92. package/dist/durable/SubscriptionTools.d.mts +3 -3
  93. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  94. package/dist/durable/SubscriptionTransition.mjs +1 -1
  95. package/dist/durable/Subscriptions.d.mts +5 -5
  96. package/dist/durable/Subscriptions.mjs +1 -1
  97. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  98. package/dist/durable/ThreadContextHistory.mjs +1 -1
  99. package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
  100. package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
  101. package/dist/durable/ThreadInvariants.d.mts +4 -2
  102. package/dist/durable/ThreadInvariants.mjs +49 -3
  103. package/dist/durable/ThreadInvariants.mjs.map +1 -1
  104. package/dist/durable/ThreadProjection.d.mts +1 -1
  105. package/dist/durable/ThreadProjection.mjs +1 -1
  106. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  107. package/dist/durable/ThreadStore.d.mts +1 -1
  108. package/dist/durable/ThreadStore.mjs +1 -1
  109. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  110. package/dist/durable/ThreadStoreConformance.mjs +1 -1
  111. package/dist/durable/ToolReconciler.d.mts +1 -1
  112. package/dist/durable/ToolReconciler.mjs +9 -5
  113. package/dist/durable/ToolReconciler.mjs.map +1 -1
  114. package/dist/durable/WorkerHost.d.mts +1 -1
  115. package/dist/engine/AgentRuntime.d.mts +1 -1
  116. package/dist/engine/AgentRuntime.mjs +1 -1
  117. package/dist/engine/RunOptions.d.mts +1 -1
  118. package/dist/engine/RunOptions.mjs.map +1 -1
  119. package/dist/index.d.mts +11 -11
  120. package/dist/index.mjs +6 -6
  121. package/package.json +1 -1
  122. package/src/durable/Admin.ts +20 -11
  123. package/src/durable/AgentRegistration.ts +1 -5
  124. package/src/durable/DurableAgentRuntime.ts +947 -464
  125. package/src/durable/DurableFailpoint.ts +2 -0
  126. package/src/durable/Records.ts +30 -5
  127. package/src/durable/Recovery.ts +8 -8
  128. package/src/durable/RunJournal.ts +128 -51
  129. package/src/durable/SubmissionLedger.ts +13 -12
  130. package/src/durable/SubmissionLedgerConformance.ts +13 -2
  131. package/src/durable/ThreadInvariants.ts +70 -5
  132. package/src/durable/ToolReconciler.ts +7 -3
  133. package/src/durable/internal/agent-registration.ts +65 -414
  134. package/src/durable/internal/messaging-host.ts +0 -2
  135. package/src/durable/internal/worker-host.ts +34 -96
  136. package/src/engine/RunOptions.ts +13 -3
  137. package/src/engine/internal/agent-runtime.ts +160 -106
  138. package/dist/AgentRuntime-Cdvqw7l_.mjs.map +0 -1
  139. package/dist/DurableAgentRuntime-DbUvZHDZ.mjs.map +0 -1
  140. package/dist/Records-BAutSSnJ.mjs.map +0 -1
  141. package/dist/RunJournal-DRRRZmG7.mjs.map +0 -1
  142. package/dist/agent-registration-CXGDwvJH.mjs +0 -379
  143. package/dist/agent-registration-CXGDwvJH.mjs.map +0 -1
@@ -7,24 +7,25 @@ import { Update, UpdateError } from "./core/AgentUpdates.mjs";
7
7
  import { AgentApprovalDenied, AgentApprovalPending, AgentInputError, AgentToolAuthorizationDenied, PolicyLimit } from "./core/AgentError.mjs";
8
8
  import { IdGenerator as IdGenerator$1 } from "./core/IdGenerator.mjs";
9
9
  import { DelegationDepth, SubagentBudgetReservation, SubagentDelegationCaps, SubagentGrant, SubagentParentLink, SubagentReservationAmounts, getToolExecutionKind } from "./core/SubagentContract.mjs";
10
- import { o as Selection } from "./ToolExposure-C93U1sPu.mjs";
10
+ import { o as Selection, s as Snapshot } from "./ToolExposure-C93U1sPu.mjs";
11
11
  import { ChildRunUsage, InputTokenUsage, ModelUsageGroup, OutputTokenUsage, RunUsageReport, RunUsageSummary, emptyRunTotals, runTotalsFromSummary, sumRunTotals, summarizeModelUsage, unknownRunTotals } from "./core/Usage.mjs";
12
12
  import { FrameworkMessage, WorkerCompletion, WorkerError, WorkerHistoryEntry, WorkerRef, WorkerUpdate } from "./core/Worker.mjs";
13
13
  import { a as PeerMessageCapacity, d as MessageAdmission, f as MessageRef, h as PeerName, i as PeerDeliveryLifetime, m as MessagingError, o as PeerRoutes, p as MessageStatus, r as PeerAuthorizer, t as MessagingHost } from "./MessagingHost-CWi_PP3g.mjs";
14
14
  import { SubagentHost } from "./engine/SubagentHost.mjs";
15
15
  import { ThreadHistory } from "./engine/ThreadHistory.mjs";
16
16
  import { n as RunToolVisibility } from "./ToolExposure-9uF_Yo0T.mjs";
17
- import { c as decodeFinalOutput, d as projectCompletionOutput, f as renderInputPrompt, l as encodeRunDisposition, n as AgentChildPending, u as projectCompletionFromToolOutput, y as streamWithUsageAccountingUnknown } from "./AgentRuntime-Cdvqw7l_.mjs";
17
+ import { f as renderInputPrompt, n as AgentChildPending, y as streamWithUsageAccountingUnknown } from "./AgentRuntime-BWOnNc0d.mjs";
18
+ import { ContextRolloverTool } from "./engine/ContextWindow.mjs";
18
19
  import { getToolExecutionClass } from "./engine/DurableStep.mjs";
19
20
  import { CompactionError, ContextCompactor } from "./engine/ContextCompactor.mjs";
20
21
  import { RunPolicyUsage } from "./core/RunPolicyUsage.mjs";
21
22
  import { AgentUpdateAcceptance, CurrentToolFailureObserver, ModelUsageAccounting, RunContextPreparation, RunContextPreparationPassthrough, RunToolAuthorization, RunToolScheduling } from "./engine/RunOptions.mjs";
22
- import { $ as ToolCallSettled, B as SubagentJoined, E as ProducerEpoch, G as SubmissionSettledRecord, H as SubagentRequested, I as RunPolicyUsageReserved, J as ToolApprovalDecided, K as SubtreeBudgetReserved, L as RunStartedRecord, O as RecordEnvelope, P as RunDurationExhausted, Q as ToolCallResolved, R as SettlementFailureDiagnostic, T as PersistedJson, U as SubagentStarted, V as SubagentLineageRecorded, W as SubmissionSettled, X as ToolCallPrepared, Y as ToolApprovalRequested, at as WorkerInputRequested, c as CanonicalRecordEnvelope, ct as WorkerReportPrepared, d as CompactionCreated, et as ToolCallUnknown, h as Digest, i as BatchId, it as WorkerInputCompleted, j as RepairAnnotated, k as RecordId, lt as WorkerReportRefused, n as AgentUpdateEmitted, nt as UserInputRecorded, o as CanonicalBatch, ot as WorkerOrigin, p as DefinitionDigests, q as ThreadCreated, rt as WorkerAdmission, st as WorkerOriginRecorded, t as AbortRequested, tt as ToolStepSettled, u as CanonicalSequence, ut as WorkerReportingIntent, w as PeerMessagePrepared, x as ModelResponseInterrupted } from "./Records-BAutSSnJ.mjs";
23
+ import { $ as ToolCallSettled, B as SubagentJoined, E as ProducerEpoch, G as SubmissionSettledRecord, H as SubagentRequested, I as RunPolicyUsageReserved, J as ToolApprovalDecided, K as SubtreeBudgetReserved, L as RunStartedRecord, O as RecordEnvelope, P as RunDurationExhausted, Q as ToolCallResolved, R as SettlementFailureDiagnostic, T as PersistedJson, U as SubagentStarted, V as SubagentLineageRecorded, W as SubmissionSettled, X as ToolCallPrepared, Y as ToolApprovalRequested, at as WorkerAdmission, c as CanonicalRecordEnvelope, ct as WorkerOrigin, d as CompactionCreated, dt as WorkerReportRefused, et as ToolCallUnknown, ft as WorkerReportingIntent, h as Digest, i as BatchId, it as UserInputRecorded, j as RepairAnnotated, k as RecordId, lt as WorkerOriginRecorded, n as AgentUpdateEmitted, nt as ToolStepSettled, o as CanonicalBatch, ot as WorkerInputCompleted, p as DefinitionDigests, q as ThreadCreated, rt as ToolUnavailable, st as WorkerInputRequested, t as AbortRequested, tt as ToolOperation, u as CanonicalSequence, ut as WorkerReportPrepared, w as PeerMessagePrepared, x as ModelResponseInterrupted } from "./Records-DR1xtbXY.mjs";
23
24
  import { AbortCommand, AbortIntentRequest, AdmissionPolicyError, AdmissionRequest, ApprovalDecisionCommand, ApprovalPendingSuspension, AttachChildToReservationRequest, BeginChildBudgetReleaseRequest, ChildBudgetReservationRequest, ChildReservationId, ChildSettledNotification, ClaimJoiningRequest, ClaimRequest, LedgerError, MarkInputAppliedRequest, MarkJoinedRequest, MarkReadyRequest, MarkUnknownRequest, OwnershipToken, ParentLinkage, RecoverySnapshotRequest, ReleaseChildBudgetRequest, ReleaseOwnershipRequest, RenewOwnershipRequest, RevertJoiningRequest, Settlement, SettlementFinalization, SettlementReservation, SubmissionLedger, SubmissionLookupById, SubmissionLookupByKey, SuspendRequest, UnknownResolutionCommand, WaitingChild, WaitingForChildSuspension, submissionAbortBatchId, submissionAbortRecordId, submissionInputBatchId, submissionInputRecordId, submissionSettlementBatchId, submissionSettlementId, submissionSettlementRecordId } from "./durable/SubmissionLedger.mjs";
24
25
  import { DeclaredPendingBatchEvidence, OpenDelegationCallEvidence, OpenToolCallEvidence, PendingApprovalEvidence, RecoveryDecision, RecoveryEvidence, classifyRecovery } from "./durable/Recovery.mjs";
25
26
  import { ExplainedEvidence, ExplainedSubmission, ExplainedUnknownCall, IntegrityCheck, IntegrityReport, ObligationEntry, ObligationReport, RecoveryExplanation, RetryRefused, obligationSeverityOf, predictRecoveryDisposition, recoveryDecisionMeaning } from "./durable/Admin.mjs";
26
27
  import { digestJson } from "./durable/Digest.mjs";
27
- import { a as bindingSupports, c as definitionDigestsEqual, d as resolveDefinitionBinding, f as resolveWorkerBinding, r as BindingUnavailable, s as compileRegistrations, u as makeLegacyWorkerBinding } from "./agent-registration-CXGDwvJH.mjs";
28
+ import { a as definitionDigestsEqual, c as resolveWorkerBinding, i as compileRegistrations, l as toolReplayContracts, o as makeLegacyWorkerBinding, s as resolveDefinitionBinding, t as BindingUnavailable } from "./agent-registration-cXpB8cBU.mjs";
28
29
  import { FencedAppendRequest, LoadCheckpointRequest, SaveRecoveryCheckpointRequest, ThreadCheckpoint, ThreadExportRequest, ThreadMaterialization, ThreadObservation, ThreadRead, ThreadStore, ThreadStoreError, ThreadTailRequest } from "./durable/ThreadStore.mjs";
29
30
  import { DurableRuntimeFailpoint } from "./durable/DurableFailpoint.mjs";
30
31
  import { ScheduledInputRefused, ScheduledInputRetryable } from "./durable/Schedule.mjs";
@@ -32,7 +33,7 @@ import { PreparedInputAdmission } from "./durable/PreparedInputAdmission.mjs";
32
33
  import { PreparedInput } from "./durable/Subscription.mjs";
33
34
  import { MessageDeliveryDriver, MessageDeliveryFailpoint, MessageDeliveryRecord, MessageDeliveryStore, messageDeliveryCapacity, messageDeliveryUsesCapacity, prepareMessageDelivery } from "./durable/MessageDelivery.mjs";
34
35
  import { n as safeUnknownString, t as inspectForeignDiagnostic } from "./foreign-diagnostic-DBQ1XHKh.mjs";
35
- import { A as toolApprovalRequestRecordId, C as subagentLineageBatchId, D as subagentStartedBatchId, E as subagentRequestedRecordId, F as toolCallSettledRecordId, H as turnIdForRun, I as toolCallUnknownRecordId, J as makeJournalMetadata, K as turnResultsBatch, L as toolStepSettledBatchId, M as toolCallResolutionBatchId, N as toolCallResolvedRecordId, O as subagentStartedRecordId, P as toolCallResultBatchId, R as toolStepSettledRecordId, S as subagentJoinedRecordId, T as subagentRequestedBatchId, U as turnPreparedBatchId, V as turnCanonicalBatch, W as turnResponseBatch, _ as runDurationRecordId, a as childThreadIdFor, b as runStartedRecordId, c as markUnknownBatchId, d as modelResponseRecordId, g as runDurationBatchId, h as runCompletedRecordId, i as childIdempotencyKeyFor, j as toolCallPreparedRecordId, k as toolApprovalDecisionRecordId, l as modelResponseInterruptedBatchId, o as compactionBatchId, p as projectRunJournalStream, r as approvalDecisionBatchId, s as compactionRecordId, t as RunJournalError, u as modelResponseInterruptedRecordId, v as runIdForSubmission, w as subagentLineageRecordId, x as subagentJoinBatchId, y as runStartedBatchId, z as turnApprovalsBatchId } from "./RunJournal-DRRRZmG7.mjs";
36
+ import { A as toolApprovalDecisionRecordId, B as turnApprovalsBatchId, C as subagentJoinedRecordId, D as subagentRequestedRecordId, E as subagentRequestedBatchId, F as toolCallResultBatchId, G as turnResponseBatch, H as turnCanonicalBatch, I as toolCallSettledRecordId, L as toolCallUnknownRecordId, M as toolCallPreparedRecordId, N as toolCallResolutionBatchId, O as subagentStartedBatchId, P as toolCallResolvedRecordId, R as toolStepSettledBatchId, S as subagentJoinBatchId, T as subagentLineageRecordId, U as turnIdForRun, W as turnPreparedBatchId, Y as makeJournalMetadata, _ as runDurationBatchId, a as childThreadIdFor, b as runStartedBatchId, c as markUnknownBatchId, d as modelResponseRecordId, g as runCompletionDigest, h as runCompletedRecordId, i as childIdempotencyKeyFor, j as toolApprovalRequestRecordId, k as subagentStartedRecordId, l as modelResponseInterruptedBatchId, o as compactionBatchId, p as projectRunJournalStream, q as turnResultsBatch, r as approvalDecisionBatchId, s as compactionRecordId, t as RunJournalError, u as modelResponseInterruptedRecordId, v as runDurationRecordId, w as subagentLineageBatchId, x as runStartedRecordId, y as runIdForSubmission, z as toolStepSettledRecordId } from "./RunJournal-DNs3PM4K.mjs";
36
37
  import { WorkerBudgetAuthorizer, WorkerConcurrencyLimit, WorkerConcurrencyResolver, WorkerHostAuthorizer, WorkerHostConfig, WorkerPolicyResolver } from "./durable/WorkerHost.mjs";
37
38
  import { OperationAuthorizationRequest, OperationAuthorizer, OperationDenied } from "./durable/OperationAuthorizer.mjs";
38
39
  import { PendingSubmission, SettledSubmission } from "./durable/SubmissionStatus.mjs";
@@ -375,7 +376,7 @@ const makeMessagingRuntime = Effect.fn("MessagingHost.make")(function* (options)
375
376
  if (prior !== void 0) {
376
377
  saved = yield* decodeProof(prior);
377
378
  const metadata = saved.envelope.messageAdmission;
378
- if (!Schema.is(MessageAdmission)(metadata) || metadata.peerName !== request.name || saved.envelope.agentId !== target.agentId || !bindingSupports(target, saved.envelope.definitions, void 0, saved.envelope.input) || !sameJson$1(saved.envelope.input, input) || (inReplyTo === void 0 || metadata.inReplyTo === void 0 ? inReplyTo !== metadata.inReplyTo : !sameRef(inReplyTo, metadata.inReplyTo))) return yield* failure$1(operation, "conflict");
379
+ if (!Schema.is(MessageAdmission)(metadata) || metadata.peerName !== request.name || saved.envelope.agentId !== target.agentId || !sameJson$1(saved.envelope.input, input) || (inReplyTo === void 0 || metadata.inReplyTo === void 0 ? inReplyTo !== metadata.inReplyTo : !sameRef(inReplyTo, metadata.inReplyTo))) return yield* failure$1(operation, "conflict");
379
380
  yield* authorize(operation, "send", request, {
380
381
  threadId: saved.envelope.threadId,
381
382
  agentId: target.agentId
@@ -676,15 +677,15 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
676
677
  ...report.destination === void 0 ? {} : { destinationDelegationId: report.destination.delegationId }
677
678
  };
678
679
  });
679
- const firstThreadInput = (history) => {
680
- const first = history.records.find(({ record }) => record.payload._tag === "UserInputRecorded")?.record.payload;
681
- return first?._tag === "UserInputRecorded" ? first.input : void 0;
680
+ const currentBinding = (agentId) => {
681
+ const matches = deps.bindings.filter((entry) => entry.agentId === agentId);
682
+ return matches.length === 1 ? matches[0] : void 0;
682
683
  };
683
684
  const sourceAuthority = Effect.fn("WorkerHost.sourceAuthority")(function* (threadId, submissionId) {
684
685
  const current = yield* read(threadId, "start");
685
686
  const created = current.records[0]?.record.payload;
686
687
  if (created?._tag !== "ThreadCreated") return yield* failure("start", "not-found");
687
- const binding = deps.bindings.find((entry) => entry.agentId === created.agentId && bindingSupports(entry, created.definitions, void 0, firstThreadInput(current)));
688
+ const binding = currentBinding(created.agentId);
688
689
  const worker = current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
689
690
  const attached = current.records.find(({ record }) => record.payload._tag === "SubagentLineageRecorded")?.record.payload;
690
691
  const nested = worker?._tag === "WorkerOriginRecorded" || attached?._tag === "SubagentLineageRecorded";
@@ -697,7 +698,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
697
698
  if (worker?._tag === "WorkerOriginRecorded" && (submission.value.workerAdmission === void 0 || !sameOrigin(submission.value.workerAdmission.origin, worker.origin))) return yield* failure("start", "denied");
698
699
  if (attached?._tag === "SubagentLineageRecorded" && (submission.value.parentLinkage?.parentSubmissionId !== attached.parentSubmissionId || submission.value.parentLinkage.parentToolCallId !== attached.parentLink.parentToolCallId)) return yield* failure("start", "denied");
699
700
  }
700
- const ownerBinding = ownerSubmission === void 0 ? void 0 : deps.bindings.find((entry) => entry.agentId === ownerSubmission.agentId && bindingSupports(entry, ownerSubmission.agentDigests, void 0, ownerSubmission.inputPayload));
701
+ const ownerBinding = ownerSubmission === void 0 ? void 0 : currentBinding(ownerSubmission.agentId);
701
702
  const selectedBinding = ownerBinding ?? binding;
702
703
  if (worker?._tag === "WorkerOriginRecorded") return {
703
704
  current,
@@ -806,7 +807,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
806
807
  }
807
808
  return yield* failure("start", "storage");
808
809
  });
809
- const reservation = Effect.fn("WorkerHost.reserveInput")(function* (admission, inputDigest, input, principal) {
810
+ const reservation = Effect.fn("WorkerHost.reserveInput")(function* (admission, inputDigest, input, principal, retainedFrameworkInput) {
810
811
  const origin = admission.origin;
811
812
  const id = `worker-input:${admission.messageId}`;
812
813
  for (let attempt = 0; attempt < 16; attempt++) {
@@ -827,9 +828,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
827
828
  if (prior === void 0 && admission.messageId !== origin.firstMessageId) return yield* failure("start", "not-found");
828
829
  const caps = origin.budget.caps;
829
830
  const sourceBinding = source.binding;
830
- const targetBinding = deps.bindings.find((entry) => entry.agentId === origin.worker.targetAgentId && bindingSupports(entry, origin.targetDigests, void 0, input));
831
+ const targetBinding = currentBinding(origin.worker.targetAgentId);
831
832
  if (sourceBinding === void 0 || targetBinding === void 0) return yield* failure("start", "declaration-unavailable");
832
- yield* Schema.decodeEffect(Schema.toEncoded(targetBinding.definition.input))(input).pipe(Effect.mapError(() => failure("start", "corrupt")));
833
+ if (!retainedFrameworkInput) yield* Schema.decodeEffect(Schema.toEncoded(targetBinding.definition.input))(input).pipe(Effect.mapError(() => failure("start", "corrupt")));
833
834
  const targetRequest = {
834
835
  definition: targetBinding.definition,
835
836
  definitions: targetBinding.digests,
@@ -926,7 +927,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
926
927
  });
927
928
  yield* authorizeBudget(admission.origin, options.principal);
928
929
  if (admission.origin.worker.threadId !== options.threadId || admission.origin.worker.targetAgentId !== agentId || !definitionDigestsEqual(admission.origin.targetDigests, options.definitions) || options.idempotencyKey !== admission.messageId || admission.deliveryPrincipal !== void 0 && admission.deliveryPrincipal !== options.principal) return yield* failure("start", "worker-mismatch");
929
- yield* reservation(admission, inputDigest, input, options.principal);
930
+ const retainedFrameworkInput = Schema.is(FrameworkMessage)(options.messageAdmission);
931
+ if (retainedFrameworkInput && admission.reportKind !== "update") yield* validateCompletion(options.messageAdmission, options, agentId, inputDigest);
932
+ yield* reservation(admission, inputDigest, input, options.principal, retainedFrameworkInput);
930
933
  return admission;
931
934
  });
932
935
  const prepareUpdate = Effect.fn("WorkerHost.prepareUpdate")(function* (update, submission, messageId) {
@@ -938,9 +941,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
938
941
  const first = requests(sourceHistory.records).find((row) => row.admission.messageId === origin.firstMessageId);
939
942
  if (first === void 0 || !sameOrigin(first.admission.origin, origin)) return yield* failure("followUp", "corrupt");
940
943
  const source = yield* sourceAuthority(origin.source.threadId, first.admission.sourceSubmissionId);
941
- if (source.binding === void 0 || source.submission === void 0 || !bindingSupports(source.binding, intent.sourceDigests, void 0, source.submission?.inputPayload)) return yield* failure("followUp", "declaration-unavailable");
942
- const sourceBinding = deps.bindings.find((entry) => entry.agentId === origin.source.agentId && bindingSupports(entry, intent.sourceDigests, void 0, source.submission?.inputPayload));
943
- const targetBinding = deps.bindings.find((entry) => entry.agentId === origin.worker.targetAgentId && bindingSupports(entry, origin.targetDigests, void 0, submission.inputPayload));
944
+ if (source.binding === void 0 || source.submission === void 0) return yield* failure("followUp", "declaration-unavailable");
945
+ const sourceBinding = currentBinding(origin.source.agentId);
946
+ const targetBinding = currentBinding(origin.worker.targetAgentId);
944
947
  const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
945
948
  if (sourceBinding === void 0 || targetBinding === void 0 || reports.length !== 1 || reports[0]?.mode !== "standard" || reports[0].target !== targetBinding.definition || !definitionDigestsEqual(submission.agentDigests, origin.targetDigests)) return yield* failure("followUp", "declaration-unavailable");
946
949
  const recorded = source.current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
@@ -949,8 +952,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
949
952
  const now = yield* Clock.currentTimeMillis;
950
953
  const deadlineAtMillis = Math.min(origin.expiresAtMillis, receivingOrigin?.expiresAtMillis ?? Infinity);
951
954
  if (now >= deadlineAtMillis) return yield* failure("followUp", "denied");
952
- const sourceSubmission = source.submission;
953
- const input = yield* Schema.decodeEffect(Schema.toEncoded(sourceBinding.definition.input))(source.submission.inputPayload).pipe(Effect.flatMap(() => Schema.decodeEffect(PersistedJson)(sourceSubmission.inputPayload)), Effect.mapError(() => failure("followUp", "corrupt")));
955
+ const input = source.submission.inputPayload;
954
956
  let workerAdmission;
955
957
  let principal = submission.principal;
956
958
  if (receivingOrigin !== void 0) {
@@ -1041,9 +1043,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1041
1043
  const firstInput = requests(sourceHistory.records).find((row) => row.admission.messageId === origin.firstMessageId);
1042
1044
  if (firstInput === void 0 || !sameOrigin(firstInput.admission.origin, origin)) return yield* failure("inspect", "corrupt");
1043
1045
  const source = yield* sourceAuthority(origin.source.threadId, firstInput.admission.sourceSubmissionId);
1044
- if (source.binding === void 0 || !bindingSupports(source.binding, intent.sourceDigests, void 0, source.submission?.inputPayload)) return refused("declaration-unavailable");
1045
- const sourceBinding = deps.bindings.find((entry) => entry.agentId === origin.source.agentId && bindingSupports(entry, intent.sourceDigests, void 0, source.submission?.inputPayload));
1046
- const targetBinding = deps.bindings.find((entry) => entry.agentId === origin.worker.targetAgentId && bindingSupports(entry, origin.targetDigests, void 0, hostSubmission.inputPayload));
1046
+ if (source.binding === void 0) return refused("declaration-unavailable");
1047
+ const sourceBinding = currentBinding(origin.source.agentId);
1048
+ const targetBinding = currentBinding(origin.worker.targetAgentId);
1047
1049
  const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
1048
1050
  const descriptor = reports[0];
1049
1051
  if (sourceBinding === void 0 || targetBinding === void 0 || descriptor === void 0 || reports.length !== 1 || !Object.is(descriptor.target, targetBinding.definition) || descriptor.mode !== "standard" && !Object.is(descriptor.input, sourceBinding.definition.input) || descriptor.mode !== intent.mode || descriptor.destination?.delegationId !== intent.destinationDelegationId || descriptor.destination !== void 0 && !Object.is(descriptor.destination.target, sourceBinding.definition)) return refused("declaration-unavailable");
@@ -1091,7 +1093,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1091
1093
  if (!Schema.toEquivalence(WorkerRef)(message.report.worker, origin.worker) || !Schema.toEquivalence(Receipt)(message.report.receipt, report.observation.receipt) || message.report.runId !== runId || message.report.settlementId !== host.settlementId || message.report.outcome !== host.outcome || message.budgetExhausted !== report.observation.budgetExhausted) return refused("input");
1092
1094
  } else if (projection.value.message !== void 0) return refused("input");
1093
1095
  const reportInput = descriptor.mode === "standard" ? source.submission?.inputPayload : projection.value.encodedInput;
1094
- const validated = yield* Schema.decodeEffect(Schema.toEncoded(sourceBinding.definition.input))(reportInput).pipe(Effect.flatMap(() => Schema.decodeUnknownEffect(PersistedJson)(reportInput)), Effect.option);
1096
+ const validated = yield* (descriptor.mode === "standard" ? Schema.decodeUnknownEffect(PersistedJson)(reportInput) : Schema.decodeEffect(Schema.toEncoded(sourceBinding.definition.input))(reportInput).pipe(Effect.flatMap(() => Schema.decodeUnknownEffect(PersistedJson)(reportInput)))).pipe(Effect.option);
1095
1097
  if (Option.isNone(validated)) return refused("input");
1096
1098
  const input = validated.value;
1097
1099
  const inputDigest = yield* withCrypto(digestJson(input)).pipe(Effect.mapError(storageFailure("inspect")));
@@ -1422,7 +1424,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1422
1424
  threadId: Schema.decodeSync(ThreadId)(messageId)
1423
1425
  },
1424
1426
  source: existing?.envelope.workerAdmission?.origin.source ?? context.source,
1425
- targetDigests: resolved.digests,
1427
+ targetDigests: previousOrigin?.targetDigests ?? resolved.digests,
1426
1428
  policy: request.policy,
1427
1429
  budget: request.budget,
1428
1430
  ...request.budgetScope === void 0 ? {} : { budgetScope: request.budgetScope },
@@ -1575,7 +1577,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1575
1577
  const created = current.records[0]?.record.payload;
1576
1578
  if (created?._tag !== "ThreadCreated") return yield* failure("context", "not-found");
1577
1579
  const selected = request.sourceSubmissionId === void 0 ? void 0 : yield* sourceAuthority(sourceThreadId, request.sourceSubmissionId);
1578
- const resolved = selected === void 0 ? deps.bindings.find((entry) => entry.agentId === created.agentId && bindingSupports(entry, created.definitions, void 0, firstThreadInput(current))) : selected.binding;
1580
+ const resolved = selected === void 0 ? currentBinding(created.agentId) : selected.binding;
1579
1581
  if (resolved === void 0) return yield* failure("context", "declaration-unavailable");
1580
1582
  const origin = current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
1581
1583
  const attached = current.records.find(({ record }) => record.payload._tag === "SubagentLineageRecorded")?.record.payload;
@@ -1733,7 +1735,7 @@ var RecoveryReport = class extends Schema.Class("@effect-agent/thread/RecoveryRe
1733
1735
  decision: RecoveryDecision,
1734
1736
  /**
1735
1737
  * `repaired` = executed; `deferred` = a claiming worker must finish it; `none` = settled;
1736
- * `unknown` = the lane is durably blocked on an Unknown Outcome awaiting the authorized
1738
+ * `unknown` = this Submission is parked on an Unknown Outcome awaiting the authorized
1737
1739
  * DUR-017 resolution path — the settlement obligation stays visible while no worker permit
1738
1740
  * is consumed (durability §16).
1739
1741
  */
@@ -1870,18 +1872,6 @@ const declaredToolCalls = Effect.fn("DurableAgentRuntime.declaredToolCalls")((me
1870
1872
  })));
1871
1873
  /** Application calls alone drive durable preparation, settlement, and batch resume. */
1872
1874
  const declaredApplicationCalls = Effect.fn("DurableAgentRuntime.declaredApplicationCalls")((messages) => declaredToolCalls(messages).pipe(Effect.map(({ application }) => application)));
1873
- /** Rebuild the exact JSON text that the engine decoded from one terminal no-Tool response. */
1874
- const terminalAssistantText = Effect.fn("DurableAgentRuntime.terminalAssistantText")((messages) => decodePrompt(messages).pipe(Effect.mapError((cause) => RunJournalError.make({
1875
- message: "ModelResponseRecorded messages are not Schema-encoded Prompt messages",
1876
- cause
1877
- })), Effect.map((prompt) => {
1878
- let text = "";
1879
- for (const message of prompt.content) {
1880
- if (message.role !== "assistant") continue;
1881
- for (const part of message.content) if (part.type === "text") text += part.text;
1882
- }
1883
- return text;
1884
- })));
1885
1875
  const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
1886
1876
  const registeredBindings = [...bindings];
1887
1877
  const ledger = yield* SubmissionLedger;
@@ -1903,6 +1893,34 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
1903
1893
  const operationAuthorizer = yield* OperationAuthorizer;
1904
1894
  const toolFailureObserver = yield* CurrentToolFailureObserver;
1905
1895
  const withCrypto = (effect) => Effect.provideService(effect, Crypto.Crypto, crypto);
1896
+ const contractsFor = (definition) => {
1897
+ const registered = registeredBindings.filter((binding) => binding.agentId === definition.id);
1898
+ const binding = registered.length === 1 ? registered[0] : void 0;
1899
+ const current = binding?.definition.toolkit === definition.toolkit && binding.definition.completion === definition.completion && binding.definition.completionFromTools === definition.completionFromTools ? binding : void 0;
1900
+ const contracts = current?.digests.replay?.tools;
1901
+ return contracts === void 0 ? withCrypto(toolReplayContracts(definition, void 0, current?.digests.tools ?? config.deploymentId)) : Effect.succeed(contracts);
1902
+ };
1903
+ const currentOperationsFor = Effect.fnUntraced(function* (agentId) {
1904
+ const registered = registeredBindings.filter((binding) => binding.agentId === agentId);
1905
+ const binding = registered.length === 1 ? registered[0] : void 0;
1906
+ return binding === void 0 ? void 0 : {
1907
+ definition: binding.definition,
1908
+ contracts: yield* contractsFor(binding.definition)
1909
+ };
1910
+ });
1911
+ const operationsFor = (records, runId) => {
1912
+ const operations = /* @__PURE__ */ new Map();
1913
+ for (const { record: { payload } } of records) {
1914
+ if (payload._tag !== "ModelResponseRecorded" || payload.runId !== runId) continue;
1915
+ for (const operation of payload.toolOperations ?? []) operations.set(operation.toolCallId, operation);
1916
+ }
1917
+ return operations;
1918
+ };
1919
+ const supportsOperation = (submission, call, operation, prepared, current) => {
1920
+ const tool = current?.definition.toolkit.tools[call.toolName];
1921
+ const replay = operation?.replay ?? prepared?.replay ?? submission.agentDigests.replay?.tools[call.toolName];
1922
+ return tool !== void 0 && replay !== void 0 && current?.contracts[call.toolName] === replay && (operation === void 0 || operation.toolName === call.toolName && operation.executionClass === getToolExecutionClass(tool) && operation.executionKind === getToolExecutionKind(tool.annotations)) && (prepared === void 0 || prepared.toolName === call.toolName && (prepared.executionClass === void 0 || prepared.executionClass === getToolExecutionClass(tool)) && (prepared.executionKind ?? "ordinary") === getToolExecutionKind(tool.annotations));
1923
+ };
1906
1924
  const hit = (location) => failpoint.hit(location);
1907
1925
  const makeEnvelope = (recordId, payload) => Effect.map(nowUtc, (createdAt) => RecordEnvelope.make({
1908
1926
  recordId,
@@ -1983,7 +2001,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
1983
2001
  return yield* Stream.runCollect(view.canonical.pipe(Stream.filter(controlRecords(submissionIds))));
1984
2002
  });
1985
2003
  /** A checkpoint is useful only for its explicitly retained submissions and exact definitions. */
1986
- const recoveryView = Effect.fn("DurableAgentRuntime.recoveryView")(function* (threadId, throughSequence, submissionIds) {
2004
+ const recoveryView = Effect.fn("DurableAgentRuntime.recoveryView")(function* (threadId, throughSequence, submissionIds, journalOwner) {
1987
2005
  const full = { canonical: canonicalRange(threadId, throughSequence) };
1988
2006
  if (store.recoveryCheckpoints === void 0) return full;
1989
2007
  const loaded = yield* store.recoveryCheckpoints.load(LoadCheckpointRequest.make({
@@ -1996,7 +2014,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
1996
2014
  const decoded = yield* Schema.decodeUnknownEffect(RecoveryCheckpointContents)(checkpoint.state).pipe(Effect.option);
1997
2015
  if (Option.isNone(decoded)) return full;
1998
2016
  const { state, digest } = decoded.value;
1999
- if (!submissionIds.every((id) => state.submissionIds.includes(id)) || state.seed.runId !== runIdForSubmission(state.submissionId)) return full;
2017
+ if (!submissionIds.every((id) => state.submissionIds.includes(id)) || state.seed.runId !== runIdForSubmission(state.submissionId) || journalOwner !== void 0 && state.seed.runId !== journalOwner) return full;
2000
2018
  const encoded = yield* Schema.encodeEffect(RecoveryCheckpointState)(state).pipe(Effect.mapError((cause) => ThreadStoreError.make({
2001
2019
  operation: "decode recovery checkpoint",
2002
2020
  message: "Invalid recovery state",
@@ -2123,6 +2141,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2123
2141
  const preparedTurns = /* @__PURE__ */ new Set();
2124
2142
  const settledIds = /* @__PURE__ */ new Set();
2125
2143
  const resolvedIds = /* @__PURE__ */ new Set();
2144
+ const unknownIds = /* @__PURE__ */ new Set();
2126
2145
  const requested = [];
2127
2146
  const decidedIds = /* @__PURE__ */ new Set();
2128
2147
  let lastResponse;
@@ -2163,8 +2182,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2163
2182
  case "ToolCallSettled":
2164
2183
  if (payload.runId === runId) settledIds.add(payload.toolCallId);
2165
2184
  break;
2185
+ case "ToolCallUnknown":
2186
+ if (payload.runId === runId) unknownIds.add(payload.toolCallId);
2187
+ break;
2166
2188
  case "ToolCallResolved":
2167
- if (payload.runId === runId) resolvedIds.add(payload.toolCallId);
2189
+ if (payload.runId === runId && (payload.resolution === "completed-with-result" || payload.resolution === "failed-with-error")) resolvedIds.add(payload.toolCallId);
2168
2190
  break;
2169
2191
  case "ToolApprovalRequested":
2170
2192
  if (payload.runId !== runId) break;
@@ -2245,8 +2267,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2245
2267
  }));
2246
2268
  }
2247
2269
  const isPreparedWorker = (call) => preparedKinds.get(toolCallPreparedRecordId(runId, call.turn, call.toolCallId)) === "orchestration";
2248
- const openWorkerCalls = allOpenCalls.filter(isPreparedWorker);
2249
- const openToolCalls = allOpenCalls.filter((call) => !isPreparedDelegation(call) && !isPreparedWorker(call));
2270
+ const openWorkerCalls = allOpenCalls.filter((call) => isPreparedWorker(call) && !unknownIds.has(call.toolCallId));
2271
+ const openToolCalls = allOpenCalls.filter((call) => unknownIds.has(call.toolCallId) || !isPreparedDelegation(call) && !isPreparedWorker(call));
2250
2272
  let declaredPendingBatch;
2251
2273
  if (lastResponse !== void 0 && !preparedTurns.has(lastResponse.turn)) {
2252
2274
  const declared = yield* declaredApplicationCalls(lastResponse.messages);
@@ -2271,6 +2293,25 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2271
2293
  ...hostSettlementOutcome === void 0 ? {} : { hostSettlementOutcome }
2272
2294
  });
2273
2295
  });
2296
+ /** Verify immutable preparation against the original declaration before any recovery side effect. */
2297
+ const validatePreparedCall = Effect.fnUntraced(function* (prepared, declared, operations) {
2298
+ const call = declared.application.find((call) => call.id === prepared.toolCallId);
2299
+ if (call === void 0 || prepared.toolName !== call.name) return yield* RunJournalError.make({ message: `Prepared Tool ${prepared.toolCallId} differs from its original declaration` });
2300
+ const parameters = yield* decodePersisted(call.params).pipe(Effect.mapError((cause) => RunJournalError.make({
2301
+ message: "Invalid canonical Tool parameters",
2302
+ cause
2303
+ })));
2304
+ const declarationDigest = yield* withCrypto(digestJson(parameters)).pipe(Effect.mapError((cause) => RunJournalError.make({
2305
+ message: "Cannot verify declared Tool parameters",
2306
+ cause
2307
+ })));
2308
+ const preparedDigest = yield* withCrypto(digestJson(prepared.parameters)).pipe(Effect.mapError((cause) => RunJournalError.make({
2309
+ message: "Cannot verify prepared Tool parameters",
2310
+ cause
2311
+ })));
2312
+ const operation = operations?.find((entry) => entry.toolCallId === prepared.toolCallId);
2313
+ if (prepared.parametersDigest !== declarationDigest || preparedDigest !== declarationDigest || operation !== void 0 && (prepared.executionClass !== void 0 && prepared.executionClass !== operation.executionClass || (prepared.executionKind ?? "ordinary") !== operation.executionKind || prepared.replay !== void 0 && prepared.replay !== operation.replay)) return yield* RunJournalError.make({ message: `Prepared Tool ${prepared.toolCallId} differs from its original operation` });
2314
+ });
2274
2315
  /**
2275
2316
  * The declared-but-unsettled Tool batch of the Run's LAST committed Turn (§2.4). Keyed on
2276
2317
  * SETTLED coverage deliberately: a call closed by a never-started/safe-retry `ToolCallResolved`
@@ -2289,12 +2330,15 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2289
2330
  turn: payload.turn,
2290
2331
  messages: payload.messages,
2291
2332
  toolParameterRejections: payload.toolParameterRejections,
2333
+ toolOperations: payload.toolOperations,
2292
2334
  ...payload.toolExposure === void 0 ? {} : { toolExposure: payload.toolExposure }
2293
2335
  };
2294
2336
  }
2295
2337
  continue;
2296
2338
  }
2297
2339
  if (payload._tag === "ToolCallSettled" && payload.runId === runId) settledByCallId.set(payload.toolCallId, {
2340
+ recordId: envelope.record.recordId,
2341
+ toolName: payload.toolName,
2298
2342
  result: payload.result,
2299
2343
  isFailure: payload.isFailure,
2300
2344
  ...payload.budgetRejected === true ? { budgetRejected: true } : {},
@@ -2305,6 +2349,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2305
2349
  const declared = yield* declaredToolCalls(lastResponse.messages);
2306
2350
  if (declared.application.length === 0) return void 0;
2307
2351
  const calls = declared.all;
2352
+ if (lastResponse.toolOperations !== void 0) {
2353
+ const identities = /* @__PURE__ */ new Set();
2354
+ for (const operation of lastResponse.toolOperations) {
2355
+ if (identities.has(operation.toolCallId) || !declared.application.some((call) => call.id === operation.toolCallId && call.name === operation.toolName)) return yield* RunJournalError.make({ message: "Operation evidence differs from the declared Tool batch" });
2356
+ identities.add(operation.toolCallId);
2357
+ }
2358
+ if (identities.size !== declared.application.length) return yield* RunJournalError.make({ message: "Declared Tool batch has incomplete operation evidence" });
2359
+ }
2360
+ for (const { record: { payload } } of records) {
2361
+ if (payload._tag !== "ToolCallPrepared" || payload.runId !== runId || payload.turn !== lastResponse.turn) continue;
2362
+ yield* validatePreparedCall(payload, declared, lastResponse.toolOperations);
2363
+ }
2308
2364
  for (const part of declared.providerResults) {
2309
2365
  const result = yield* decodePersisted(part.result).pipe(Effect.mapError((cause) => RunJournalError.make({
2310
2366
  message: `Invalid canonical provider result ${part.id}`,
@@ -2319,10 +2375,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2319
2375
  for (const call of calls) {
2320
2376
  const recorded = settledByCallId.get(call.id);
2321
2377
  if (call.providerExecuted === true && recorded === void 0) return yield* RunJournalError.make({ message: `Pending Turn lacks canonical provider result for ${call.id}` });
2322
- if (recorded !== void 0) settled.push({
2323
- id: call.id,
2324
- ...recorded
2325
- });
2378
+ if (recorded !== void 0) {
2379
+ const { recordId, toolName, ...result } = recorded;
2380
+ if (call.providerExecuted !== true && (toolName !== call.name || recordId !== toolCallSettledRecordId(runId, lastResponse.turn, Schema.decodeSync(ToolCallId)(call.id)))) return yield* RunJournalError.make({ message: `Settled Tool ${call.id} differs from its original declaration` });
2381
+ settled.push({
2382
+ id: call.id,
2383
+ ...result
2384
+ });
2385
+ }
2326
2386
  }
2327
2387
  const completionCall = declared.application[0];
2328
2388
  const projectSettledCompletion = declared.application.length === 1 && completionCall !== void 0 && completionTools.includes(completionCall.name) && settledByCallId.get(completionCall.id)?.isFailure === false && !records.some(({ record }) => record.payload._tag === "RunCompleted" && record.payload.runId === runId);
@@ -2336,6 +2396,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2336
2396
  responseRecordId: modelResponseRecordId(runId, lastResponse.turn),
2337
2397
  messages: lastResponse.messages,
2338
2398
  toolParameterRejections: lastResponse.toolParameterRejections,
2399
+ toolOperations: lastResponse.toolOperations,
2339
2400
  ...lastResponse.toolExposure === void 0 ? {} : { toolExposure: lastResponse.toolExposure }
2340
2401
  };
2341
2402
  });
@@ -2396,9 +2457,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2396
2457
  };
2397
2458
  });
2398
2459
  /**
2399
- * Append context at the Thread's CURRENT fence, without a claim. Used only where no live
2400
- * owner can exist (an `unknown` head is never claimable, WP2 claim rule): record-identity
2401
- * dedupe absorbs a racing recovery process, and a fence advance defers to the advancing owner.
2460
+ * Existing ownership-free settlement protocol for queued aborts and joined outcomes.
2461
+ * Its canonical settlement reservation authorizes the append; it never authorizes tool
2462
+ * execution, unknown-resolution appends, or repair audits beside a live writer.
2402
2463
  */
2403
2464
  const attemptContextAtTail = Effect.fn("DurableAgentRuntime.attemptContextAtTail")(function* (threadId) {
2404
2465
  const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
@@ -2542,16 +2603,28 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2542
2603
  * `Uncertain` collects for Unknown marking, and a reconciler FAILURE is no proof at all (the
2543
2604
  * call stays open and blocked without being marked). Nothing here ever executes a handler.
2544
2605
  */
2545
- const reconcileOpenCalls = Effect.fn("DurableAgentRuntime.reconcileOpenCalls")(function* (ctx, submission, snapshot, records, openCalls, knownIds, executionClassFor) {
2606
+ const reconcileOpenCalls = Effect.fn("DurableAgentRuntime.reconcileOpenCalls")(function* (ctx, submission, snapshot, records, openCalls, knownIds, current) {
2546
2607
  const submissionId = submission.submissionId;
2547
2608
  const runId = runIdForSubmission(submissionId);
2609
+ yield* pendingToolBatchFor(records, runId);
2610
+ const operations = operationsFor(records, runId);
2548
2611
  const preparedByCallId = /* @__PURE__ */ new Map();
2549
- for (const envelope of records) {
2550
- const payload = envelope.record.payload;
2551
- if (payload._tag === "ToolCallPrepared" && payload.runId === runId) preparedByCallId.set(payload.toolCallId, payload);
2612
+ for (const { record } of records) {
2613
+ const payload = record.payload;
2614
+ if (payload._tag === "ToolCallPrepared" && payload.runId === runId && openCalls.some((call) => call.toolCallId === payload.toolCallId && call.turn === payload.turn)) {
2615
+ if (preparedByCallId.has(payload.toolCallId) || record.recordId !== toolCallPreparedRecordId(runId, payload.turn, payload.toolCallId)) return yield* RunJournalError.make({ message: `Prepared Tool ${payload.toolCallId} has inconsistent canonical identity` });
2616
+ preparedByCallId.set(payload.toolCallId, payload);
2617
+ }
2552
2618
  }
2553
- const intents = /* @__PURE__ */ new Map();
2554
- for (const intent of snapshot.unknownResolutions) intents.set(intent.toolCallId, intent);
2619
+ for (const call of openCalls) {
2620
+ const prepared = preparedByCallId.get(call.toolCallId);
2621
+ const responses = records.filter(({ record }) => record.payload._tag === "ModelResponseRecorded" && record.payload.runId === runId && record.payload.turn === call.turn);
2622
+ const response = responses[0]?.record;
2623
+ if (prepared === void 0 || prepared.turn !== call.turn || prepared.toolName !== call.toolName || responses.length !== 1 || response?.payload._tag !== "ModelResponseRecorded" || response.recordId !== modelResponseRecordId(runId, call.turn) || response.payload.turnId !== turnIdForRun(runId, call.turn)) return yield* RunJournalError.make({ message: `Prepared Tool ${call.toolCallId} has no unique original response` });
2624
+ if ((yield* withCrypto(digestJson(response.payload.messages))) !== response.payload.messagesDigest) return yield* RunJournalError.make({ message: `Original response for Tool ${call.toolCallId} has an invalid digest` });
2625
+ yield* validatePreparedCall(prepared, yield* declaredToolCalls(response.payload.messages), response.payload.toolOperations);
2626
+ }
2627
+ const intents = new Map(snapshot.unknownResolutions.map((intent) => [intent.toolCallId, intent]));
2555
2628
  const review = {
2556
2629
  uncertain: [],
2557
2630
  unproven: [],
@@ -2560,7 +2633,39 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2560
2633
  };
2561
2634
  let recovered = 0;
2562
2635
  for (const call of openCalls) {
2636
+ const prepared = preparedByCallId.get(call.toolCallId);
2637
+ const operation = operations.get(call.toolCallId);
2638
+ const supported = supportsOperation(submission, call, operation, prepared, current);
2563
2639
  const intent = intents.get(call.toolCallId);
2640
+ const author = intent?.author ?? RECONCILER_AUTHOR;
2641
+ const reason = intent?.reason ?? "A registered reconciliation policy recovered the external outcome";
2642
+ const neverStarted = Effect.gen(function* () {
2643
+ if (current === void 0) {
2644
+ review.unproven.push(call);
2645
+ return;
2646
+ }
2647
+ if (supported) {
2648
+ review.retryable.push(call);
2649
+ return;
2650
+ }
2651
+ const unavailable = yield* Schema.encodeEffect(ToolUnavailable)(ToolUnavailable.make({
2652
+ toolName: call.toolName,
2653
+ execution: "not-executed",
2654
+ message: "This operation never started and its original implementation is unavailable. Continue with the current tools."
2655
+ })).pipe(Effect.flatMap(decodePersisted), Effect.orDie);
2656
+ yield* hit("tools:before-unavailable-append");
2657
+ yield* appendClosedCall(ctx, submissionId, knownIds, call, {
2658
+ result: {
2659
+ value: unavailable,
2660
+ isFailure: true
2661
+ },
2662
+ resolution: "never-started",
2663
+ author,
2664
+ reason
2665
+ });
2666
+ yield* hit("tools:after-unavailable-append");
2667
+ recovered += 1;
2668
+ });
2564
2669
  if (intent !== void 0) switch (intent.resolution._tag) {
2565
2670
  case "AbortSubmission": continue;
2566
2671
  case "CompletedWithResult":
@@ -2570,33 +2675,29 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2570
2675
  isFailure: intent.resolution.isFailure
2571
2676
  },
2572
2677
  resolution: intent.resolution.isFailure ? "failed-with-error" : "completed-with-result",
2573
- author: intent.author,
2574
- reason: intent.reason
2678
+ author,
2679
+ reason
2575
2680
  });
2576
2681
  recovered += 1;
2577
2682
  continue;
2578
2683
  case "NeverHappened":
2579
- yield* appendClosedCall(ctx, submissionId, knownIds, call, {
2580
- resolution: "never-started",
2581
- author: intent.author,
2582
- reason: intent.reason
2583
- });
2584
- review.retryable.push(call);
2684
+ yield* neverStarted;
2585
2685
  continue;
2586
2686
  case "SafeToRetry":
2587
- yield* appendClosedCall(ctx, submissionId, knownIds, call, {
2588
- resolution: "safe-retry",
2589
- author: intent.author,
2590
- reason: intent.reason
2591
- });
2592
- review.retryable.push(call);
2687
+ if (current === void 0) review.unproven.push(call);
2688
+ else if (supported) review.retryable.push(call);
2689
+ else review.uncertain.push(call);
2593
2690
  continue;
2594
2691
  }
2595
- if (executionClassFor(call.toolName) === "idempotent") {
2692
+ const tool = current?.definition.toolkit.tools[call.toolName];
2693
+ if (current === void 0 && (operation?.executionClass ?? prepared?.executionClass) === "idempotent") {
2694
+ review.unproven.push(call);
2695
+ continue;
2696
+ }
2697
+ if (supported && tool !== void 0 && getToolExecutionClass(tool) === "idempotent") {
2596
2698
  review.retryable.push(call);
2597
2699
  continue;
2598
2700
  }
2599
- const prepared = preparedByCallId.get(call.toolCallId);
2600
2701
  if (prepared === void 0) {
2601
2702
  review.unproven.push(call);
2602
2703
  continue;
@@ -2609,7 +2710,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2609
2710
  toolCallId: prepared.toolCallId,
2610
2711
  toolName: prepared.toolName,
2611
2712
  parameters: prepared.parameters,
2612
- parametersDigest: prepared.parametersDigest
2713
+ parametersDigest: prepared.parametersDigest,
2714
+ ...prepared.executionKind === void 0 ? {} : { executionKind: prepared.executionKind },
2715
+ ...prepared.executionClass === void 0 ? {} : { executionClass: prepared.executionClass },
2716
+ ...prepared.replay === void 0 ? {} : { replay: prepared.replay }
2613
2717
  })).pipe(Effect.map(Option.some), Effect.catchTag("ToolReconcilerError", () => Effect.succeed(Option.none())));
2614
2718
  if (Option.isNone(reconciled)) {
2615
2719
  review.unproven.push(call);
@@ -2624,14 +2728,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2624
2728
  isFailure: decision.isFailure
2625
2729
  },
2626
2730
  resolution: decision.isFailure ? "failed-with-error" : "completed-with-result",
2627
- author: RECONCILER_AUTHOR,
2628
- reason: "A registered reconciliation policy recovered the external outcome"
2731
+ author,
2732
+ reason
2629
2733
  });
2630
2734
  recovered += 1;
2631
2735
  continue;
2632
2736
  case "NeverStarted":
2737
+ yield* neverStarted;
2738
+ continue;
2633
2739
  case "SafeToRetry":
2634
- review.retryable.push(call);
2740
+ if (current === void 0) review.unproven.push(call);
2741
+ else if (supported) review.retryable.push(call);
2742
+ else review.uncertain.push(call);
2635
2743
  continue;
2636
2744
  case "Uncertain":
2637
2745
  review.uncertain.push(call);
@@ -3172,11 +3280,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3172
3280
  * Join a settled child after parent abort or expiry without running application handlers.
3173
3281
  * The canonical child outcome stays intact; the parent result records why it cannot continue.
3174
3282
  */
3175
- const joinSettledChildForClosedParent = Effect.fn("DurableAgentRuntime.joinSettledChildForClosedParent")(function* (ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, reason) {
3283
+ const joinSettledChildWithoutHandler = Effect.fn("DurableAgentRuntime.joinSettledChildWithoutHandler")(function* (ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, reason) {
3176
3284
  const runId = runIdForSubmission(parent.submissionId);
3177
3285
  const request = subagent.requested.get(toolCallId);
3178
3286
  if (request === void 0) return yield* LedgerError.make({
3179
- operation: "joinSettledChildForClosedParent",
3287
+ operation: "joinSettledChildWithoutHandler",
3180
3288
  message: `Tool Call ${toolCallId} has an attached child but no canonical SubagentRequested record`
3181
3289
  });
3182
3290
  const joinedRecordId = subagentJoinedRecordId(runId, toolCallId);
@@ -3186,16 +3294,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3186
3294
  else {
3187
3295
  const verification = yield* verifySettledChild(parent, request, childSubmissionId);
3188
3296
  if (verification._tag === "mismatch") return yield* LedgerError.make({
3189
- operation: "joinSettledChildForClosedParent",
3297
+ operation: "joinSettledChildWithoutHandler",
3190
3298
  message: `Child Settlement verification failed for Tool Call ${toolCallId}: ${verification.message}`
3191
3299
  });
3192
3300
  const verified = verification.value;
3193
- const boundedResult = yield* decodePersisted({
3301
+ const boundedResult = yield* decodePersisted(reason === "unavailable" ? {
3302
+ _tag: "ToolUnavailable",
3303
+ toolName: subagent.preparedNames.get(toolCallId) ?? request.delegationId,
3304
+ execution: "unavailable",
3305
+ message: `The original delegation implementation is unavailable. Its original child settled ${verified.outcome}; no replacement child was started.`
3306
+ } : {
3194
3307
  errorTag: reason === "aborted" ? "SubagentParentAborted" : "SubagentParentDurationExceeded",
3195
3308
  message: boundedText(`The parent Submission ${reason === "aborted" ? "aborted" : "exceeded its duration"}; attached child ${childSubmissionId} settled ${verified.outcome}`)
3196
3309
  }).pipe(Effect.orDie);
3197
3310
  finalAccounting = yield* decodePersisted({
3198
- basis: reason === "aborted" ? "aborted-conservative" : "duration-conservative",
3311
+ basis: `${reason}-conservative`,
3199
3312
  allocation: reservation.allocation
3200
3313
  }).pipe(Effect.orDie);
3201
3314
  const childRunId = runIdForSubmission(childSubmissionId);
@@ -3266,25 +3379,51 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3266
3379
  return open;
3267
3380
  });
3268
3381
  /** Release unused reservations or repair and join existing children, never admit new work. */
3269
- const reconcileExpiredChildren = Effect.fn("DurableAgentRuntime.reconcileExpiredChildren")(function* (ctx, parent, ownershipToken) {
3382
+ const reconcileRetainedChildren = Effect.fn("DurableAgentRuntime.reconcileRetainedChildren")(function* (ctx, parent, ownershipToken, unavailableCalls) {
3270
3383
  const snapshot = yield* ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId: parent.submissionId }));
3271
3384
  const records = yield* readControl(parent.threadId, [parent.submissionId]);
3272
3385
  const runId = runIdForSubmission(parent.submissionId);
3273
3386
  const pending = yield* pendingToolBatchFor(records, runId);
3274
3387
  const knownIds = knownRecordIdsOf(records);
3275
3388
  const subagent = subagentRecordsOf(records, runId);
3389
+ const notExecuted = /* @__PURE__ */ new Set();
3390
+ const waiting = [];
3391
+ const indeterminate = /* @__PURE__ */ new Set();
3392
+ for (const toolCallId of unavailableCalls ?? []) {
3393
+ if (snapshot.childReservations.some((reservation) => reservation.parentToolCallId === toolCallId)) continue;
3394
+ if (subagent.requested.has(toolCallId) || subagent.started.has(toolCallId)) return yield* LedgerError.make({
3395
+ operation: "reconcileRetainedChildren",
3396
+ message: "Child request has no original reservation"
3397
+ });
3398
+ notExecuted.add(toolCallId);
3399
+ }
3276
3400
  for (const reservation of snapshot.childReservations) {
3277
3401
  const toolCallId = reservation.parentToolCallId;
3278
- if (reservation.status !== "reserved" || subagent.joined.has(toolCallId)) continue;
3402
+ if (unavailableCalls !== void 0 && !unavailableCalls.has(toolCallId)) continue;
3403
+ const joined = subagent.joined.get(toolCallId);
3404
+ if (joined !== void 0) {
3405
+ yield* applyReservationRelease(reservation.reservationId, joined.finalAccounting);
3406
+ continue;
3407
+ }
3408
+ if (reservation.status !== "reserved") {
3409
+ if (reservation.accounting !== void 0) yield* applyReservationRelease(reservation.reservationId, reservation.accounting);
3410
+ if (reservation.childSubmissionId !== void 0 || subagent.started.has(toolCallId)) return yield* LedgerError.make({
3411
+ operation: "reconcileRetainedChildren",
3412
+ message: "Unjoined child reservation was already released"
3413
+ });
3414
+ notExecuted.add(toolCallId);
3415
+ continue;
3416
+ }
3279
3417
  let started = subagent.started.get(toolCallId);
3280
3418
  const request = subagent.requested.get(toolCallId);
3281
3419
  if (started === void 0 && request === void 0 && reservation.childSubmissionId === void 0) {
3282
3420
  yield* releaseOrphanReservation(reservation.reservationId);
3421
+ notExecuted.add(toolCallId);
3283
3422
  continue;
3284
3423
  }
3285
3424
  const call = pending?.calls.find((candidate) => candidate.id === toolCallId);
3286
3425
  if (pending === void 0 || call === void 0 || request === void 0 || subagent.preparedNames.get(toolCallId) !== call.name || request.delegationId !== call.name || reservation.parentSubmissionId !== parent.submissionId || request.reservationId !== reservation.reservationId || request.reservationDigest !== reservation.allocationDigest || request.turn !== pending.turn || request.turnId !== pending.turnId) return yield* LedgerError.make({
3287
- operation: "reconcileExpiredChildren",
3426
+ operation: "reconcileRetainedChildren",
3288
3427
  message: `Tool Call ${toolCallId} has inconsistent canonical child request evidence`
3289
3428
  });
3290
3429
  if (started === void 0) {
@@ -3293,17 +3432,26 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3293
3432
  principal: decodePrincipalSync(request.childPrincipal),
3294
3433
  idempotencyKey: decodeIdempotencyKeySync(request.childIdempotencyKey)
3295
3434
  }));
3296
- if (resolution._tag === "Indeterminate") continue;
3435
+ if (resolution._tag === "Indeterminate") {
3436
+ indeterminate.add(toolCallId);
3437
+ continue;
3438
+ }
3297
3439
  if (resolution._tag === "NotAdmitted") {
3298
3440
  if (reservation.childSubmissionId !== void 0) return yield* LedgerError.make({
3299
- operation: "reconcileExpiredChildren",
3441
+ operation: "reconcileRetainedChildren",
3300
3442
  message: `Tool Call ${toolCallId} has an attachment but no admitted child`
3301
3443
  });
3302
- yield* releaseOrphanReservation(reservation.reservationId);
3444
+ if (unavailableCalls === void 0) {
3445
+ yield* releaseOrphanReservation(reservation.reservationId);
3446
+ notExecuted.add(toolCallId);
3447
+ continue;
3448
+ }
3449
+ }
3450
+ const admission = yield* establishChildFromRequest(parent, request, unavailableCalls !== void 0);
3451
+ if (admission._tag === "indeterminate") {
3452
+ indeterminate.add(toolCallId);
3303
3453
  continue;
3304
3454
  }
3305
- const admission = yield* establishChildFromRequest(parent, request, false);
3306
- if (admission._tag === "indeterminate") continue;
3307
3455
  started = SubagentStarted.make({
3308
3456
  runId,
3309
3457
  toolCallId,
@@ -3325,7 +3473,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3325
3473
  }
3326
3474
  const child = yield* ledger.lookup(SubmissionLookupById.make({ submissionId: started.childSubmissionId }));
3327
3475
  if (Option.isNone(child) || reservation.childSubmissionId !== void 0 && reservation.childSubmissionId !== started.childSubmissionId || request.childThreadId !== started.childThreadId || started.childThreadId !== child.value.threadId || started.childReceiptId !== child.value.receiptId || started.childRunId !== runIdForSubmission(started.childSubmissionId)) return yield* LedgerError.make({
3328
- operation: "reconcileExpiredChildren",
3476
+ operation: "reconcileRetainedChildren",
3329
3477
  message: `Tool Call ${call.id} has inconsistent canonical child attachment evidence`
3330
3478
  });
3331
3479
  yield* ledger.attachChildToReservation(AttachChildToReservationRequest.make({
@@ -3333,9 +3481,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3333
3481
  ownershipToken,
3334
3482
  childSubmissionId: started.childSubmissionId
3335
3483
  })).pipe(Effect.catchTag("ChildReservationConflict", conflictToLedgerError("attachChildToReservation")));
3336
- if (child.value.state !== "settled") continue;
3337
- yield* joinSettledChildForClosedParent(ctx, parent, knownIds, subagent, reservation, started.toolCallId, started.childSubmissionId, "duration");
3484
+ if (child.value.state !== "settled") {
3485
+ waiting.push(WaitingChild.make({
3486
+ toolCallId,
3487
+ childSubmissionId: started.childSubmissionId
3488
+ }));
3489
+ yield* wake.notify(child.value.threadId);
3490
+ continue;
3491
+ }
3492
+ yield* joinSettledChildWithoutHandler(ctx, parent, knownIds, subagent, reservation, started.toolCallId, started.childSubmissionId, unavailableCalls === void 0 ? "duration" : "unavailable");
3338
3493
  }
3494
+ return {
3495
+ notExecuted,
3496
+ waiting,
3497
+ indeterminate
3498
+ };
3339
3499
  });
3340
3500
  /**
3341
3501
  * Request-abort-and-join over every attached child of an aborting parent (spec §13.1,
@@ -3389,7 +3549,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3389
3549
  message: `Attached child ${childSubmissionId} is unknown to the ledger`
3390
3550
  });
3391
3551
  if (child.value.state === "settled") {
3392
- yield* joinSettledChildForClosedParent(ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, "aborted");
3552
+ yield* joinSettledChildWithoutHandler(ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, "aborted");
3393
3553
  continue;
3394
3554
  }
3395
3555
  yield* ledger.requestAbort(AbortCommand.make({
@@ -3617,14 +3777,20 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3617
3777
  if (submission.parentLinkage !== void 0 && (childLineage?._tag !== "SubagentLineageRecorded" || childLineage.parentSubmissionId !== submission.parentLinkage.parentSubmissionId || childLineage.parentLink.parentToolCallId !== submission.parentLinkage.parentToolCallId || !definitionDigestsEqual(childLineage.childDefinitionDigests, submission.agentDigests) || childLineage.childInputDigest !== submission.inputDigest)) return yield* RunJournalError.make({ message: "The child Run has no matching canonical lineage and execution bounds" });
3618
3778
  const pending = yield* pendingToolBatchFor(records, runId, agent.definition.completionFromTools?.map((declaration) => declaration.tool));
3619
3779
  const resumeProjection = pending === void 0 ? journal : yield* projectRunJournalStream(withoutPendingBatch(canonical, pending, runId), runId, void 0, journalSeed);
3620
- const tools = agent.definition.toolkit.tools;
3621
- for (const envelope of records) {
3622
- const payload = envelope.record.payload;
3623
- if (payload._tag !== "ToolCallPrepared" || payload.runId !== runId) continue;
3624
- const tool = tools[payload.toolName];
3625
- const kind = tool === void 0 ? "ordinary" : getToolExecutionKind(tool.annotations);
3626
- if (tool === void 0 || (payload.executionKind ?? "ordinary") !== kind) return yield* RunJournalError.make({ message: `Prepared Tool ${payload.toolCallId} classification conflicts with the resolved binding` });
3627
- }
3780
+ const currentContracts = yield* contractsFor(agent.definition);
3781
+ const rolloverOperation = journal.pendingContextToolCallId === void 0 ? void 0 : operationsFor(records, runId).get(journal.pendingContextToolCallId);
3782
+ const rolloverTool = rolloverOperation === void 0 ? void 0 : agent.definition.toolkit.tools[rolloverOperation.toolName];
3783
+ const pendingContextToolCallId = rolloverOperation !== void 0 && rolloverTool !== void 0 && Context.get(rolloverTool.annotations, ContextRolloverTool) && currentContracts[rolloverOperation.toolName] === rolloverOperation.replay ? journal.pendingContextToolCallId : void 0;
3784
+ const applicationCalls = pending?.calls.filter((call) => !call.providerExecuted);
3785
+ const completionCandidate = applicationCalls?.length === 1 && agent.definition.completionFromTools?.some((declaration) => declaration.tool === applicationCalls[0]?.name) && pending?.settled.some((result) => result.id === applicationCalls[0]?.id && !result.isFailure) ? applicationCalls[0] : void 0;
3786
+ const settledCompletion = pending !== void 0 && completionCandidate !== void 0 && supportsOperation(submission, OpenToolCallEvidence.make({
3787
+ toolCallId: Schema.decodeSync(ToolCallId)(completionCandidate.id),
3788
+ toolName: completionCandidate.name,
3789
+ turn: pending.turn
3790
+ }), pending.toolOperations?.find((operation) => operation.toolCallId === completionCandidate.id), void 0, {
3791
+ definition: agent.definition,
3792
+ contracts: currentContracts
3793
+ }) ? Schema.decodeSync(ToolCallId)(completionCandidate.id) : void 0;
3628
3794
  const knownIds = knownRecordIdsOf(records);
3629
3795
  const stepOutputs = /* @__PURE__ */ new Map();
3630
3796
  for (const envelope of records) {
@@ -3744,51 +3910,12 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3744
3910
  const response = [...records].reverse().find((envelope) => envelope.record.payload._tag === "ModelResponseRecorded" && envelope.record.payload.runId === runId)?.record.payload;
3745
3911
  if (response?._tag !== "ModelResponseRecorded") return yield* RunJournalError.make({ message: `Run ${runId} has a terminal completion marker without a response` });
3746
3912
  const calls = (yield* declaredToolCalls(response.messages)).application;
3747
- let expectedOutput;
3748
3913
  if (calls.length > 0) {
3749
- const completion = agent.definition.completion;
3750
3914
  const call = calls[0];
3751
- const actionCompletion = agent.definition.completionFromTools?.find((declaration) => declaration.tool === call?.name);
3752
- const settled = call === void 0 ? void 0 : records.find((envelope) => envelope.record.payload._tag === "ToolCallSettled" && envelope.record.payload.runId === runId && envelope.record.payload.toolCallId === call.id)?.record.payload;
3753
- if (calls.length !== 1 || call === void 0 || call.name !== completion?.tool && actionCompletion === void 0 || settled?._tag !== "ToolCallSettled" || settled.toolName !== call.name || settled.isFailure) return yield* RunJournalError.make({ message: `Run ${runId} has a terminal completion marker without one successful declared completion Tool result` });
3754
- const projected = yield* (actionCompletion !== void 0 ? projectCompletionFromToolOutput(agent, actionCompletion, call.params, settled.result) : completion !== void 0 ? Effect.map(projectCompletionOutput(agent, completion, call.params, settled.result), Option.some) : Effect.succeed(Option.none())).pipe(Effect.mapError((cause) => RunJournalError.make({
3755
- message: `Run ${runId} completion Tool output failed recovery projection`,
3756
- cause
3757
- })));
3758
- if (Option.isNone(projected)) return yield* RunJournalError.make({ message: `Run ${runId} completion Tool declined its recorded terminal output` });
3759
- expectedOutput = projected.value;
3760
- } else {
3761
- const responseText = yield* terminalAssistantText(response.messages);
3762
- expectedOutput = yield* decodeFinalOutput(agent, responseText).pipe(Effect.mapError((cause) => RunJournalError.make({
3763
- message: `Run ${runId} final response output failed recovery decoding`,
3764
- cause
3765
- })));
3766
- }
3767
- const boundedExpectedOutput = yield* decodePersisted(expectedOutput.encoded).pipe(Effect.mapError((cause) => RunJournalError.make({
3768
- message: `Run ${runId} reconstructed output exceeds canonical persistence bounds`,
3769
- cause
3770
- })));
3771
- const [expectedOutputDigest, recordedOutputDigest] = yield* Effect.all([withCrypto(digestJson(boundedExpectedOutput)), withCrypto(digestJson(recordedCompletion.output))]).pipe(Effect.mapError((cause) => RunJournalError.make({
3772
- message: `Run ${runId} completion output comparison failed`,
3773
- cause
3774
- })));
3775
- if (expectedOutputDigest !== recordedOutputDigest) return yield* RunJournalError.make({ message: `Run ${runId} terminal completion output disagrees with its canonical response or Tool result` });
3776
- const expectedRunDisposition = recordedCompletion.finishReason === "budget-exhausted" ? void 0 : yield* encodeRunDisposition(agent, expectedOutput.decoded).pipe(Effect.mapError((cause) => RunJournalError.make({
3777
- message: `Run ${runId} run disposition failed recovery projection`,
3778
- cause
3779
- })));
3780
- if (expectedRunDisposition === void 0 !== (recordedCompletion.runDisposition === void 0)) return yield* RunJournalError.make({ message: `Run ${runId} terminal run disposition disagrees with its reconstructed output` });
3781
- if (expectedRunDisposition !== void 0 && recordedCompletion.runDisposition !== void 0) {
3782
- const boundedExpectedRunDisposition = yield* decodePersisted(expectedRunDisposition).pipe(Effect.mapError((cause) => RunJournalError.make({
3783
- message: `Run ${runId} reconstructed run disposition exceeds canonical persistence bounds`,
3784
- cause
3785
- })));
3786
- const [expectedRunDispositionDigest, recordedRunDispositionDigest] = yield* Effect.all([withCrypto(digestJson(boundedExpectedRunDisposition)), withCrypto(digestJson(recordedCompletion.runDisposition))]).pipe(Effect.mapError((cause) => RunJournalError.make({
3787
- message: `Run ${runId} run disposition comparison failed`,
3788
- cause
3789
- })));
3790
- if (expectedRunDispositionDigest !== recordedRunDispositionDigest) return yield* RunJournalError.make({ message: `Run ${runId} terminal run disposition disagrees with its reconstructed output` });
3915
+ const settled = call === void 0 ? void 0 : records.find(({ record }) => record.payload._tag === "ToolCallSettled" && record.payload.runId === runId && record.payload.toolCallId === call.id)?.record.payload;
3916
+ if (calls.length !== 1 || call === void 0 || settled?._tag !== "ToolCallSettled" || settled.toolName !== call.name || settled.isFailure) return yield* RunJournalError.make({ message: `Run ${runId} has a terminal completion marker without one successful declared Tool result` });
3791
3917
  }
3918
+ if (recordedCompletion.resultDigest !== void 0 && recordedCompletion.resultDigest !== (yield* withCrypto(runCompletionDigest(recordedCompletion)))) return yield* RunJournalError.make({ message: "RunCompleted terminal values failed their integrity check" });
3792
3919
  return {
3793
3920
  _tag: "completed",
3794
3921
  result: recordedCompletion.output,
@@ -3826,7 +3953,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3826
3953
  cause
3827
3954
  })));
3828
3955
  const firstAssistant = pendingMessages.content.findIndex((message) => message.role === "assistant");
3829
- if (firstAssistant > 0) resumeLeadingMessages = Prompt.fromMessages(pendingMessages.content.slice(0, firstAssistant));
3956
+ if (firstAssistant > 0) resumeLeadingMessages = Prompt.fromMessages(pendingMessages.content.slice(0, firstAssistant).filter((message) => message.role !== "system"));
3830
3957
  if (firstAssistant >= 0) resumeResponseMessages = Prompt.fromMessages(pendingMessages.content.slice(firstAssistant));
3831
3958
  }
3832
3959
  let currentToolTurn = pending === void 0 ? void 0 : {
@@ -3863,7 +3990,24 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3863
3990
  ...state,
3864
3991
  history
3865
3992
  });
3866
- const resumeContext = { prepare: ({ source }) => Ref.get(stateRef).pipe(Effect.map((state) => ({ prompt: Prompt.fromMessages([...resumeProjection.prompt.content, ...source.content.slice(state.baseLen ?? source.content.length)]) }))) };
3993
+ const instructionView = (messages, instructions, insertWhenAbsent) => {
3994
+ const projected = [];
3995
+ const lengths = [0];
3996
+ let inserted = false;
3997
+ for (const message of messages) {
3998
+ if (message.role === "system") {
3999
+ if (!inserted) projected.push(...instructions);
4000
+ inserted = true;
4001
+ } else projected.push(message);
4002
+ lengths.push(projected.length);
4003
+ }
4004
+ if (!inserted && insertWhenAbsent) projected.push(...instructions);
4005
+ return {
4006
+ messages: projected,
4007
+ prefixLength: (length) => lengths[length] ?? projected.length
4008
+ };
4009
+ };
4010
+ const resumeContext = { prepare: ({ source }) => Ref.get(stateRef).pipe(Effect.map((state) => ({ prompt: Prompt.fromMessages([...instructionView(resumeProjection.prompt.content, source.content.slice(0, state.baseLen).filter((message) => message.role === "system"), true).messages, ...source.content.slice(state.baseLen ?? source.content.length)]) }))) };
3867
4011
  const externalContext = runContextPreparation.hook;
3868
4012
  const preparedContext = externalContext === void 0 ? journal.committedTurns === 0 ? void 0 : resumeContext : { prepare: (request) => (journal.committedTurns === 0 ? Effect.succeed({ prompt: request.source }) : resumeContext.prepare(request)).pipe(Effect.flatMap(({ prompt }) => externalContext.prepare({
3869
4013
  ...request,
@@ -3908,6 +4052,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3908
4052
  const createdAt = yield* nowUtc;
3909
4053
  const batch = yield* withCrypto(turnResponseBatch({
3910
4054
  toolExposure: commit.toolExposure,
4055
+ toolOperations: commit.calls.map((call) => ToolOperation.make({
4056
+ toolCallId: call.toolCallId,
4057
+ toolName: call.toolName,
4058
+ executionClass: call.executionClass,
4059
+ executionKind: call.executionKind,
4060
+ replay: currentContracts[call.toolName]
4061
+ })),
3911
4062
  toolParameterRejections: commit.toolParameterRejections,
3912
4063
  runId,
3913
4064
  turn: canonicalTurn,
@@ -3946,7 +4097,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3946
4097
  toolName: call.toolName,
3947
4098
  parameters,
3948
4099
  parametersDigest,
3949
- executionKind: call.executionKind
4100
+ executionKind: call.executionKind,
4101
+ executionClass: call.executionClass,
4102
+ replay: currentContracts[call.toolName]
3950
4103
  })));
3951
4104
  }
3952
4105
  const head = preparedRecords[0];
@@ -4014,14 +4167,16 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4014
4167
  }
4015
4168
  const coverable = sourceBoundaries.filter((boundary) => commit.kind !== "summarize" || ownerFirstSequence === void 0 || boundary.sequence < ownerFirstSequence);
4016
4169
  if (coverable.length === 0) return yield* CompactionError.make({ message: commit.kind === "rollover" ? "Durable rollover requires complete canonical records" : "Durable compaction requires eligible canonical records" });
4170
+ const state = yield* Ref.get(stateRef);
4171
+ const comparisonView = instructionView(sourceJournal.prompt.content, state.history?.content.slice(0, state.baseLen).filter((message) => message.role === "system") ?? [], false);
4017
4172
  let matchingPrefix = 0;
4018
4173
  let requiredThrough = 0;
4019
- const comparisonLimit = Math.min(sourceJournal.prompt.content.length, commit.through);
4174
+ const comparisonLimit = Math.min(comparisonView.messages.length, commit.through);
4020
4175
  for (let index = 0; index < commit.through; index += 1) {
4021
4176
  const message = commit.source.content[index];
4022
4177
  if (message !== void 0 && (commit.kind === "clear-tool-results" ? message.role === "tool" : message.role !== "system")) requiredThrough = index + 1;
4023
4178
  if (index >= comparisonLimit || index !== matchingPrefix || message === void 0) continue;
4024
- const canonicalMessage = sourceJournal.prompt.content[index];
4179
+ const canonicalMessage = comparisonView.messages[index];
4025
4180
  if (canonicalMessage === void 0) continue;
4026
4181
  const encode = (entry) => Schema.encodeEffect(Prompt.Message)(entry).pipe(Effect.mapError((cause) => CompactionError.make({
4027
4182
  message: "Could not encode the canonical compaction prefix",
@@ -4037,7 +4192,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4037
4192
  if (candidate === void 0) continue;
4038
4193
  const following = coverable[index + 1];
4039
4194
  if (following !== void 0 && following.tag !== "ModelResponseRecorded") continue;
4040
- const length = candidate.promptLength;
4195
+ const length = comparisonView.prefixLength(candidate.promptLength);
4041
4196
  if (length === 0 || length > commit.through) continue;
4042
4197
  if (length < requiredThrough || length > matchingPrefix) continue;
4043
4198
  lastCovered = candidate;
@@ -4580,6 +4735,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4580
4735
  history: pending === void 0 ? journal.historyBefore : resumeProjection.historyBefore,
4581
4736
  onHistory,
4582
4737
  input,
4738
+ ...journal.committedTurns > 0 || Schema.is(FrameworkMessage)(submission.messageAdmission) ? { retainedInput: submission.inputPayload } : {},
4583
4739
  ...Schema.is(FrameworkMessage)(submission.messageAdmission) ? { frameworkMessage: submission.messageAdmission } : {},
4584
4740
  approval,
4585
4741
  toolAuthorization,
@@ -4595,12 +4751,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4595
4751
  ...submission.workerAdmission === void 0 ? {} : { toolCallAllowance: submission.workerAdmission.origin.toolCallAllowance },
4596
4752
  ...submission.parentLinkage !== void 0 && childLineage?._tag === "SubagentLineageRecorded" ? { toolCallAllowance: childLineage.toolCallAllowance } : {},
4597
4753
  ...pending === void 0 ? {} : { resume: {
4754
+ ...settledCompletion === void 0 ? {} : { settledCompletion },
4598
4755
  turn: pending.turn,
4599
4756
  turnId: pending.turnId,
4600
4757
  calls: pending.calls,
4601
4758
  toolParameterRejections: pending.toolParameterRejections,
4602
4759
  settled: pending.settled,
4603
- ...pending.toolExposure === void 0 ? {} : { toolExposure: pending.toolExposure },
4760
+ toolExposure: pending.toolExposure ?? Snapshot.make({ exposedToolNames: [...new Set(pending.calls.map((call) => call.name))] }),
4604
4761
  ...resumeLeadingMessages === void 0 ? {} : { leadingMessages: resumeLeadingMessages },
4605
4762
  ...resumeResponseMessages === void 0 ? {} : { responseMessages: resumeResponseMessages }
4606
4763
  } },
@@ -4622,7 +4779,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4622
4779
  },
4623
4780
  ...journal.contextWindowId === void 0 ? {} : { initialContextWindowId: journal.contextWindowId },
4624
4781
  ...journal.protectedContext === void 0 ? {} : { protectedContext: journal.protectedContext },
4625
- ...journal.pendingContextToolCallId === void 0 ? {} : { pendingContextToolCallId: journal.pendingContextToolCallId }
4782
+ ...pendingContextToolCallId === void 0 ? {} : { pendingContextToolCallId }
4626
4783
  };
4627
4784
  const commitPendingTurn = Effect.gen(function* () {
4628
4785
  const state = yield* Ref.get(stateRef);
@@ -4668,7 +4825,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4668
4825
  const completion = agent.definition.completion;
4669
4826
  const completionPart = resultParts[0];
4670
4827
  const runCompletion = resultParts.length === 1 && (completionPart?.name === completion?.tool || agent.definition.completionFromTools?.some((declaration) => declaration.tool === completionPart?.name)) && completionPart.isFailure !== true && completedRun !== void 0 ? completedRun : void 0;
4671
- const batch = yield* turnResultsBatch({
4828
+ const batch = yield* withCrypto(turnResultsBatch({
4672
4829
  toolSelections: stagedToolSelections,
4673
4830
  budgetRejectedCalls,
4674
4831
  runId,
@@ -4679,7 +4836,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4679
4836
  deploymentId: config.deploymentId,
4680
4837
  createdAt,
4681
4838
  ...runCompletion === void 0 ? {} : { runCompletion }
4682
- });
4839
+ }));
4683
4840
  let pendingBatch = batch;
4684
4841
  if (settledCompletionPart !== void 0) {
4685
4842
  const completionRecord = batch.records.find((record) => record.payload._tag === "RunCompleted");
@@ -4950,7 +5107,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4950
5107
  const ctx = yield* attemptContextFor(threadId, claim.producerEpoch);
4951
5108
  let controlThrough = (yield* store.inspectTail(ThreadTailRequest.make({ threadId }))).tailSequence;
4952
5109
  const initialThrough = controlThrough;
4953
- const initialView = yield* recoveryView(threadId, initialThrough, [submissionId]);
5110
+ const initialView = yield* recoveryView(threadId, initialThrough, [submissionId], runIdForSubmission(submissionId));
4954
5111
  let journalMetadata = initialView.seed === void 0 ? makeJournalMetadata(runIdForSubmission(submissionId)) : void 0;
4955
5112
  const retainControl = controlRecords([submissionId]);
4956
5113
  const collectControl = (record) => {
@@ -5009,7 +5166,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5009
5166
  };
5010
5167
  let expiredChildObligation = false;
5011
5168
  if ((yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(runTiming.deadline)) {
5012
- yield* reconcileExpiredChildren(ctx, submission, yield* Ref.get(tokenRef));
5169
+ yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef));
5013
5170
  expiredChildObligation = yield* completeJoinedReleases(submission);
5014
5171
  }
5015
5172
  const ordinaryCallsResolved = Effect.gen(function* () {
@@ -5018,10 +5175,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5018
5175
  const reconciliationEvidence = yield* evidenceFor(reconciliationRecords, submissionId, true, reconciliationSnapshot.hostSubmissionId);
5019
5176
  if (reconciliationEvidence.openToolCalls.length === 0) return true;
5020
5177
  for (const envelope of reconciliationRecords) knownIds.add(envelope.record.recordId);
5021
- const tools = agent.definition.toolkit.tools;
5022
- const review = yield* reconcileOpenCalls(ctx, submission, reconciliationSnapshot, reconciliationRecords, reconciliationEvidence.openToolCalls, knownIds, (toolName) => {
5023
- const tool = tools[toolName];
5024
- return tool === void 0 ? void 0 : getToolExecutionClass(tool);
5178
+ const review = yield* reconcileOpenCalls(ctx, submission, reconciliationSnapshot, reconciliationRecords, reconciliationEvidence.openToolCalls, knownIds, {
5179
+ definition: agent.definition,
5180
+ contracts: yield* contractsFor(agent.definition)
5025
5181
  });
5026
5182
  if (review.uncertain.length > 0 || review.unproven.length > 0) {
5027
5183
  if (review.uncertain.length > 0) yield* markCallsUnknown(ctx, submissionId, knownIds, review.uncertain, "An ordinary Tool call may have executed without a canonical outcome");
@@ -5035,6 +5191,74 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5035
5191
  return true;
5036
5192
  });
5037
5193
  if (!expiredChildObligation && !(yield* ordinaryCallsResolved)) return Option.none();
5194
+ const continuationRecords = yield* refreshControl();
5195
+ const continuation = yield* pendingToolBatchFor(continuationRecords, runIdForSubmission(submissionId));
5196
+ if (continuation !== void 0) {
5197
+ const current = {
5198
+ definition: agent.definition,
5199
+ contracts: yield* contractsFor(agent.definition)
5200
+ };
5201
+ const operations = operationsFor(continuationRecords, runIdForSubmission(submissionId));
5202
+ const settled = new Set(continuation.settled.map((call) => call.id));
5203
+ const rejected = new Set(continuation.toolParameterRejections?.map((call) => call.toolCallId));
5204
+ const unsupported = [];
5205
+ const unavailableDelegations = /* @__PURE__ */ new Set();
5206
+ for (const call of continuation.calls) {
5207
+ const original = continuationRecords.map(({ record }) => record.payload).find((payload) => payload._tag === "ToolCallPrepared" && payload.runId === runIdForSubmission(submissionId) && payload.toolCallId === call.id);
5208
+ if (original?._tag === "ToolCallPrepared" && original.executionKind === "delegation" && !settled.has(call.id) && !supportsOperation(submission, OpenToolCallEvidence.make(original), operations.get(call.id), original, current)) unavailableDelegations.add(original.toolCallId);
5209
+ }
5210
+ const children = unavailableDelegations.size === 0 ? void 0 : yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef), unavailableDelegations);
5211
+ for (const envelope of yield* refreshControl()) knownIds.add(envelope.record.recordId);
5212
+ for (const call of continuation.calls) {
5213
+ if (call.providerExecuted || settled.has(call.id) || rejected.has(Schema.decodeSync(ToolCallId)(call.id))) continue;
5214
+ const evidence = OpenToolCallEvidence.make({
5215
+ toolCallId: Schema.decodeSync(ToolCallId)(call.id),
5216
+ toolName: call.name,
5217
+ turn: continuation.turn
5218
+ });
5219
+ const operation = operations.get(call.id);
5220
+ const prepared = continuationRecords.map(({ record }) => record.payload).find((payload) => payload._tag === "ToolCallPrepared" && payload.runId === runIdForSubmission(submissionId) && payload.turn === continuation.turn && payload.toolCallId === call.id);
5221
+ const preparation = prepared?._tag === "ToolCallPrepared" ? prepared : void 0;
5222
+ if (supportsOperation(submission, evidence, operation, preparation, current)) continue;
5223
+ if (preparation !== void 0 && !children?.notExecuted.has(evidence.toolCallId)) {
5224
+ if (!unavailableDelegations.has(evidence.toolCallId)) unsupported.push(evidence);
5225
+ continue;
5226
+ }
5227
+ const execution = children?.notExecuted.has(evidence.toolCallId) || operation !== void 0 && (operation.executionClass !== "readonly" || operation.executionKind !== "ordinary") ? "not-executed" : "unavailable";
5228
+ const result = yield* Schema.encodeEffect(ToolUnavailable)(ToolUnavailable.make({
5229
+ toolName: call.name,
5230
+ execution,
5231
+ message: execution === "not-executed" ? "This operation was not executed and its original implementation is unavailable. Continue with the current tools." : "The original operation is unavailable. No mutating handler was dispatched; readonly work may have run without a recorded result."
5232
+ })).pipe(Effect.flatMap(decodePersisted), Effect.orDie);
5233
+ yield* hit("tools:before-unavailable-append");
5234
+ yield* appendClosedCall(ctx, submissionId, knownIds, evidence, {
5235
+ result: {
5236
+ value: result,
5237
+ isFailure: true
5238
+ },
5239
+ resolution: execution === "not-executed" ? "never-started" : "failed-with-error",
5240
+ author: "runtime",
5241
+ reason: "The declared operation cannot execute under its original contract"
5242
+ });
5243
+ yield* hit("tools:after-unavailable-append");
5244
+ }
5245
+ const firstWaiting = children?.waiting[0];
5246
+ if (children !== void 0 && firstWaiting !== void 0) {
5247
+ const disposition = yield* ledger.suspend(SuspendRequest.make({
5248
+ submissionId,
5249
+ ownershipToken: yield* Ref.get(tokenRef),
5250
+ reason: WaitingForChildSuspension.make({ children: [firstWaiting, ...children.waiting.slice(1)] })
5251
+ }));
5252
+ yield* hit("subagent:after-suspend");
5253
+ if (disposition === "resume-immediately") yield* wake.notify(threadId);
5254
+ return Option.none();
5255
+ }
5256
+ if ((children?.indeterminate.size ?? 0) > 0) return Option.none();
5257
+ if (unsupported.length > 0) {
5258
+ yield* markCallsUnknown(ctx, submissionId, knownIds, unsupported, "The unfinished operation has no supported recovery implementation; its original identity and evidence remain unresolved");
5259
+ return Option.none();
5260
+ }
5261
+ }
5038
5262
  const lineage = {
5039
5263
  attemptId: claim.attemptId,
5040
5264
  supersededEpoch,
@@ -5103,7 +5327,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5103
5327
  }));
5104
5328
  yield* hit("run:after-duration-append");
5105
5329
  }
5106
- yield* reconcileExpiredChildren(ctx, submission, yield* Ref.get(tokenRef));
5330
+ yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef));
5107
5331
  }
5108
5332
  if (yield* completeJoinedReleases(submission)) {
5109
5333
  if (outcome._tag === "failed") {
@@ -5172,24 +5396,24 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5172
5396
  const resolution = yield* resolve(submission).pipe(Effect.map((binding) => ({
5173
5397
  _tag: "resolved",
5174
5398
  binding
5175
- })), Effect.catchTags({
5176
- BindingUnavailable: (failure) => Effect.succeed({
5177
- _tag: "refused",
5178
- failure
5179
- }),
5180
- BindingDigestMismatch: (failure) => Effect.succeed({
5181
- _tag: "refused",
5182
- failure
5183
- })
5184
- }));
5399
+ })), Effect.catchTags({ BindingUnavailable: (failure) => Effect.succeed({
5400
+ _tag: "refused",
5401
+ failure
5402
+ }) }));
5185
5403
  if (resolution._tag === "refused") return yield* resolution.failure;
5186
5404
  return yield* resolution.binding.attempt((agent, attemptThreadId, attemptClaim) => runAttempt(agent, attemptThreadId, attemptClaim, tokenRef, options?.yieldAfter), threadId, claim);
5187
5405
  })).pipe(withThreadHeadSpan);
5406
+ const eligibleThreadHead = (threadId) => Stream.runHead(ledger.scanNonterminal.pipe(Stream.filter((entry) => entry.threadId === threadId), Stream.filterEffect((entry) => entry.state !== "unknown" ? Effect.succeed(true) : ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId: entry.submissionId })).pipe(Effect.map((snapshot) => snapshot.abortIntent !== void 0)))));
5188
5407
  const drainThread = (resolve, threadId) => Effect.gen(function* () {
5189
5408
  const settlements = [];
5190
5409
  while (true) {
5410
+ const before = yield* eligibleThreadHead(threadId);
5191
5411
  const settlement = yield* processThreadHead(resolve, threadId);
5192
- if (Option.isNone(settlement)) return settlements;
5412
+ if (Option.isNone(settlement)) {
5413
+ const after = yield* eligibleThreadHead(threadId);
5414
+ if (Option.isSome(before) && Option.isSome(after) && before.value.submissionId !== after.value.submissionId) continue;
5415
+ return settlements;
5416
+ }
5193
5417
  settlements.push(settlement.value);
5194
5418
  }
5195
5419
  });
@@ -5200,25 +5424,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5200
5424
  message: `This worker is bound to Agent ${binding.agentId}; the claimed head belongs to Agent ${submission.agentId} (SUB-023)`
5201
5425
  })), threadId);
5202
5426
  });
5203
- const resolveRetainedBinding = Effect.fn("DurableAgentRuntime.resolveRetainedBinding")(function* (submission) {
5204
- const exact = registeredBindings.find((binding) => binding.agentId === submission.agentId && definitionDigestsEqual(binding.digests, submission.agentDigests));
5205
- if (exact !== void 0) return exact;
5206
- const candidates = registeredBindings.filter((binding) => binding.agentId === submission.agentId);
5207
- const candidate = candidates[0];
5208
- if (candidate === void 0 || candidates.length !== 1) return yield* resolveWorkerBinding(registeredBindings, submission.agentId, submission.agentDigests);
5209
- if (bindingSupports(candidate, submission.agentDigests, void 0, submission.inputPayload)) return candidate;
5210
- const records = yield* Effect.gen(function* () {
5211
- const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId: submission.threadId }));
5212
- return yield* Stream.runCollect(canonicalRange(submission.threadId, tail.tailSequence).pipe(Stream.filter(controlRecords([submission.submissionId]))));
5213
- }).pipe(Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed([])));
5214
- const completionTools = candidate === void 0 ? [] : [...candidate.definition.completion === void 0 ? [] : [candidate.definition.completion.tool], ...candidate.definition.completionFromTools?.map((entry) => entry.tool) ?? []];
5215
- const pending = yield* pendingToolBatchFor(records, runIdForSubmission(submission.submissionId), completionTools);
5216
- return yield* resolveWorkerBinding(registeredBindings, submission.agentId, submission.agentDigests, pending?.calls.filter((call) => call.providerExecuted !== true || completionTools.includes(call.name)).map((call) => call.name) ?? [], submission.inputPayload);
5217
- });
5218
- const processThreadResolvedImpl = (threadId) => drainThread(resolveRetainedBinding, threadId);
5219
- const processThreadHeadImpl = (threadId, options) => processThreadHead(resolveRetainedBinding, threadId, options);
5220
- const claimFor = Effect.fn("DurableAgentRuntime.claimFor")(function* (submission) {
5221
- const head = yield* Stream.runHead(ledger.scanNonterminal.pipe(Stream.filter((entry) => entry.threadId === submission.threadId)));
5427
+ const resolveCurrentBinding = (submission) => resolveWorkerBinding(registeredBindings, submission.agentId);
5428
+ const processThreadResolvedImpl = (threadId) => drainThread(resolveCurrentBinding, threadId);
5429
+ const processThreadHeadImpl = (threadId, options) => processThreadHead(resolveCurrentBinding, threadId, options);
5430
+ const claimFor = Effect.fn("DurableAgentRuntime.claimFor")(function* (submission, decision) {
5431
+ const head = yield* eligibleThreadHead(submission.threadId);
5222
5432
  if (Option.isNone(head) || head.value.submissionId !== submission.submissionId) return Option.none();
5223
5433
  const claimed = yield* acquireClaim(submission.threadId);
5224
5434
  if (Option.isNone(claimed)) return Option.none();
@@ -5229,35 +5439,40 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5229
5439
  })).pipe(Effect.catchTag("OwnershipLost", () => Effect.void));
5230
5440
  return Option.none();
5231
5441
  }
5442
+ yield* annotateRepair(submission.threadId, decision, claimed.value.claim.producerEpoch);
5232
5443
  return Option.some(claimed.value.claim);
5233
5444
  });
5234
5445
  /**
5235
- * DUR-013 audit: one deterministic `RepairAnnotated` record per executed (submission, decision)
5236
- * pair. Conflicts mean the annotation is already canonical (or a newer epoch owns the lane), so
5237
- * they never fail the already-idempotent repair itself.
5446
+ * Best-effort DUR-013 audit of an owned repair attempt, before it can release its claim.
5447
+ * The fixed granted epoch never borrows a later writer's authority. State-only repairs
5448
+ * omit canonical annotations; their decision and operator author/reason remain in telemetry.
5238
5449
  */
5239
- const annotateRepair = (threadId, submissionId, decision) => Effect.gen(function* () {
5450
+ const annotateRepair = (threadId, decision, producerEpoch) => Effect.gen(function* () {
5451
+ yield* store.materialize(ThreadMaterialization.make({
5452
+ threadId,
5453
+ producerEpoch
5454
+ }));
5455
+ const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
5456
+ if (tail.tailSequence === ZERO_SEQUENCE) return;
5240
5457
  const encodedDecision = yield* Schema.encodeEffect(RecoveryDecision)(decision).pipe(Effect.orDie);
5241
5458
  const details = yield* Schema.decodeEffect(PersistedJson)(encodedDecision).pipe(Effect.orDie);
5242
- const envelope = yield* makeEnvelope(recoveryRepairRecordId(submissionId, decision._tag), RepairAnnotated.make({
5459
+ const envelope = yield* makeEnvelope(recoveryRepairRecordId(decision.submissionId, decision._tag), RepairAnnotated.make({
5243
5460
  reason: `recovery:${decision._tag}`,
5244
5461
  details
5245
5462
  }));
5246
- const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
5247
5463
  yield* store.append(FencedAppendRequest.make({
5248
5464
  threadId,
5249
5465
  batch: CanonicalBatch.make({
5250
- batchId: recoveryRepairBatchId(submissionId, decision._tag),
5466
+ batchId: recoveryRepairBatchId(decision.submissionId, decision._tag),
5251
5467
  producerId: config.producerId,
5252
5468
  records: [envelope]
5253
5469
  }),
5254
5470
  expectedTailSequence: tail.tailSequence,
5255
5471
  expectedTailDigest: tail.tailDigest,
5256
- producerEpoch: tail.producerEpoch
5472
+ producerEpoch
5257
5473
  }));
5258
- yield* wake.notify(threadId);
5259
5474
  }).pipe(Effect.ignore);
5260
- const settleAbortedForRecovery = Effect.fn("DurableAgentRuntime.settleAbortedForRecovery")(function* (snapshot, evidence, records, _decision) {
5475
+ const settleAbortedForRecovery = Effect.fn("DurableAgentRuntime.settleAbortedForRecovery")(function* (snapshot, evidence, records, decision) {
5261
5476
  const intent = snapshot.abortIntent;
5262
5477
  if (intent === void 0) return "deferred";
5263
5478
  const submission = snapshot.submission;
@@ -5284,7 +5499,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5284
5499
  }
5285
5500
  }
5286
5501
  }
5287
- const claimed = yield* claimFor(submission);
5502
+ const claimed = yield* claimFor(submission, decision);
5288
5503
  if (Option.isNone(claimed)) {
5289
5504
  if (submission.state === "ready" && snapshot.ownership === void 0) return yield* Effect.gen(function* () {
5290
5505
  yield* materializeAtLeast(submission.threadId, ZERO_EPOCH);
@@ -5312,15 +5527,15 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5312
5527
  return "repaired";
5313
5528
  });
5314
5529
  /**
5315
- * Execute the reconcile-then-mark flow for a `MarkUnknown` decision (plan §2.2). The recovery
5316
- * pass has no Agent Binding, so no execution-class annotation is visible here — every call
5317
- * without a durable intent or reconciler proof stays fail-closed uncertain. A lane whose open
5318
- * calls all closed reports `repaired`; provably-retryable calls defer to a worker's batch
5319
- * resume; the rest become Unknown Outcomes and report the `unknown` disposition.
5530
+ * Execute the reconcile-then-mark flow for a `MarkUnknown` decision (plan §2.2). Current
5531
+ * registration metadata checks retry support when available. Low-level drivers supply their
5532
+ * Agent at claim time, so retry proofs defer that check without permitting execution. Calls
5533
+ * without a durable intent, original idempotent declaration, or reconciler proof stay uncertain.
5534
+ * Closed calls report `repaired`; deferred checks report `deferred`; uncertain calls become Unknown.
5320
5535
  */
5321
5536
  const markUnknownForRecovery = Effect.fn("DurableAgentRuntime.markUnknownForRecovery")(function* (snapshot, evidence, records, decision) {
5322
5537
  const submission = snapshot.submission;
5323
- const claimed = yield* claimFor(submission);
5538
+ const claimed = yield* claimFor(submission, decision);
5324
5539
  if (Option.isNone(claimed)) return "deferred";
5325
5540
  const claim = claimed.value;
5326
5541
  yield* store.materialize(ThreadMaterialization.make({
@@ -5330,7 +5545,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5330
5545
  const ctx = yield* attemptContextFor(submission.threadId, claim.producerEpoch);
5331
5546
  const knownIds = knownRecordIdsOf(records);
5332
5547
  const markableIds = new Set(decision.openToolCallIds);
5333
- const review = yield* reconcileOpenCalls(ctx, submission, snapshot, records, evidence.openToolCalls.filter((call) => markableIds.has(call.toolCallId)), knownIds, () => void 0);
5548
+ const review = yield* reconcileOpenCalls(ctx, submission, snapshot, records, evidence.openToolCalls.filter((call) => markableIds.has(call.toolCallId)), knownIds, yield* currentOperationsFor(submission.agentId));
5334
5549
  let disposition;
5335
5550
  if (review.uncertain.length > 0) {
5336
5551
  yield* markCallsUnknown(ctx, submission.submissionId, knownIds, review.uncertain, decision.reason);
@@ -5343,17 +5558,25 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5343
5558
  })).pipe(Effect.catchTag("OwnershipLost", () => Effect.void));
5344
5559
  return disposition;
5345
5560
  });
5561
+ /** A retry intent permits only the saved operation under its supported current contract. */
5562
+ const hasUnsupportedRetry = Effect.fnUntraced(function* (snapshot, records) {
5563
+ if (!snapshot.unknownResolutions.some((intent) => intent.resolution._tag === "SafeToRetry")) return false;
5564
+ const submission = snapshot.submission;
5565
+ const current = yield* currentOperationsFor(submission.agentId);
5566
+ const operations = operationsFor(records, runIdForSubmission(submission.submissionId));
5567
+ for (const intent of snapshot.unknownResolutions) if (intent.resolution._tag === "SafeToRetry") {
5568
+ const prepared = records.map(({ record }) => record.payload).find((payload) => payload._tag === "ToolCallPrepared" && payload.runId === runIdForSubmission(submission.submissionId) && payload.toolCallId === intent.toolCallId);
5569
+ if (prepared?._tag !== "ToolCallPrepared" || current !== void 0 && !supportsOperation(submission, OpenToolCallEvidence.make(prepared), operations.get(intent.toolCallId), prepared, current)) return true;
5570
+ }
5571
+ return false;
5572
+ });
5346
5573
  /**
5347
- * Apply covering DUR-017 resolution intents canonically and wake the lane. The `unknown` head
5348
- * is never claimable, so the appends run unfenced at the current tail (no live owner can
5349
- * exist); the wake itself is the ledger's idempotent re-check on intent replay (WP2), so a
5350
- * crash anywhere in this pass converges on the next one.
5574
+ * Replay covering resolution intents as state-only wakes. Canonical outcomes wait for an
5575
+ * actual claim, so resolving an older request cannot append using a later writer's epoch.
5351
5576
  */
5352
- const applyUnknownResolutionsForRecovery = Effect.fn("DurableAgentRuntime.applyUnknownResolutionsForRecovery")(function* (snapshot, evidence, records) {
5577
+ const applyUnknownResolutionsForRecovery = Effect.fn("DurableAgentRuntime.applyUnknownResolutionsForRecovery")(function* (snapshot, _evidence, records) {
5578
+ if (yield* hasUnsupportedRetry(snapshot, records)) return "unknown";
5353
5579
  const submission = snapshot.submission;
5354
- const ctx = yield* attemptContextAtTail(submission.threadId);
5355
- const knownIds = knownRecordIdsOf(records);
5356
- if (!(yield* reconcileOpenCalls(ctx, submission, snapshot, records, evidence.openToolCalls, knownIds, () => void 0).pipe(Effect.as(true), Effect.catchTag("FenceRejected", () => Effect.succeed(false))))) return "deferred";
5357
5580
  for (const intent of snapshot.unknownResolutions) yield* ledger.recordUnknownResolution(UnknownResolutionCommand.make({
5358
5581
  submissionId: intent.submissionId,
5359
5582
  toolCallId: intent.toolCallId,
@@ -5378,14 +5601,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5378
5601
  * permit, and stays `deferred`. Decisions that raced ahead of the repair leave the lane to a
5379
5602
  * worker's batch resume (`deferred`).
5380
5603
  */
5381
- const awaitApprovalForRecovery = Effect.fn("DurableAgentRuntime.awaitApprovalForRecovery")(function* (snapshot, evidence) {
5604
+ const awaitApprovalForRecovery = Effect.fn("DurableAgentRuntime.awaitApprovalForRecovery")(function* (snapshot, evidence, decision) {
5382
5605
  const submission = snapshot.submission;
5383
5606
  if (submission.state === "suspended") return "deferred";
5384
5607
  const decided = new Set(snapshot.approvalDecisions.map((decision) => decision.toolCallId));
5385
5608
  const undecided = evidence.approvalsPending.filter((pending) => !decided.has(pending.toolCallId));
5386
5609
  const first = undecided[0];
5387
5610
  if (first === void 0) return "deferred";
5388
- const claimed = yield* claimFor(submission);
5611
+ const claimed = yield* claimFor(submission, decision);
5389
5612
  if (Option.isNone(claimed)) return "deferred";
5390
5613
  const claim = claimed.value;
5391
5614
  const outcome = yield* ledger.suspend(SuspendRequest.make({
@@ -5420,7 +5643,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5420
5643
  const runId = runIdForSubmission(submission.submissionId);
5421
5644
  const start = yield* canonicalRunStartFromRecords(records, runId);
5422
5645
  if (start?.payload._tag === "RunStarted" && start.payload.runId === runId && (records.some(({ record }) => record.payload._tag === "RunDurationExhausted" && record.payload.runId === runId) || submission.workerAdmission !== void 0 && (yield* Clock.currentTimeMillis) >= submission.workerAdmission.origin.expiresAtMillis) && (decision._tag === "CompleteChildAdmission" || decision._tag === "RepairSubagentStartLink" || decision._tag === "AwaitChildAdmissionResolution" || decision._tag === "MarkUnknown" && snapshot.childReservations.some((reservation) => reservation.status !== "released"))) {
5423
- const claimed = yield* claimFor(submission);
5646
+ const claimed = yield* claimFor(submission, decision);
5424
5647
  if (Option.isNone(claimed)) return "deferred";
5425
5648
  const claim = claimed.value;
5426
5649
  yield* store.materialize(ThreadMaterialization.make({
@@ -5428,7 +5651,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5428
5651
  producerEpoch: claim.producerEpoch
5429
5652
  }));
5430
5653
  const ctx = yield* attemptContextFor(submission.threadId, claim.producerEpoch);
5431
- yield* reconcileExpiredChildren(ctx, submission, claim.ownershipToken);
5654
+ yield* reconcileRetainedChildren(ctx, submission, claim.ownershipToken);
5432
5655
  const open = yield* completeJoinedReleases(submission);
5433
5656
  yield* ledger.releaseOwnership(ReleaseOwnershipRequest.make({
5434
5657
  submissionId: submission.submissionId,
@@ -5443,7 +5666,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5443
5666
  case "MarkUnknown": return yield* markUnknownForRecovery(snapshot, evidence, records, decision);
5444
5667
  case "ApplyUnknownResolutions": return yield* applyUnknownResolutionsForRecovery(snapshot, evidence, records);
5445
5668
  case "AwaitUnknownResolution": return "unknown";
5446
- case "AwaitApprovalDecision": return yield* awaitApprovalForRecovery(snapshot, evidence);
5669
+ case "AwaitApprovalDecision": return yield* awaitApprovalForRecovery(snapshot, evidence, decision);
5447
5670
  case "ResumeSuspended": return yield* resumeSuspendedForRecovery(snapshot);
5448
5671
  case "RevertJoining":
5449
5672
  yield* ledger.revertJoining(RevertJoiningRequest.make({ submissionId: submission.submissionId }));
@@ -5464,7 +5687,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5464
5687
  }
5465
5688
  const host = yield* ledger.lookup(SubmissionLookupById.make({ submissionId: hostSubmissionId }));
5466
5689
  if (Option.isNone(host)) return "deferred";
5467
- const claimed = yield* claimFor(host.value);
5690
+ const claimed = yield* claimFor(host.value, decision);
5468
5691
  if (Option.isNone(claimed)) return "deferred";
5469
5692
  const claim = claimed.value;
5470
5693
  yield* ledger.markJoined(MarkJoinedRequest.make({
@@ -5503,7 +5726,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5503
5726
  return "repaired";
5504
5727
  case "ApplyInput":
5505
5728
  case "RepairInputMarker": {
5506
- const claimed = yield* claimFor(submission);
5729
+ const claimed = yield* claimFor(submission, decision);
5507
5730
  if (Option.isNone(claimed)) return "deferred";
5508
5731
  const claim = claimed.value;
5509
5732
  yield* store.materialize(ThreadMaterialization.make({
@@ -5525,7 +5748,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5525
5748
  case "AppendReservedSettlement": {
5526
5749
  const reservation = snapshot.reservation;
5527
5750
  if (reservation === void 0) return "deferred";
5528
- const claimed = yield* claimFor(submission);
5751
+ const claimed = yield* claimFor(submission, decision);
5529
5752
  if (Option.isNone(claimed)) {
5530
5753
  if (reservation.outcome === "aborted" && snapshot.abortIntent !== void 0 && snapshot.ownership === void 0) {
5531
5754
  const ctx = yield* attemptContextAtTail(submission.threadId);
@@ -5562,7 +5785,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5562
5785
  if (requestedPayload === void 0) return "deferred";
5563
5786
  const admission = yield* establishChildFromRequest(submission, requestedPayload);
5564
5787
  if (admission._tag === "indeterminate") return "deferred";
5565
- const claimed = yield* claimFor(submission);
5788
+ const claimed = yield* claimFor(submission, decision);
5566
5789
  if (Option.isNone(claimed)) return "deferred";
5567
5790
  const claim = claimed.value;
5568
5791
  yield* store.materialize(ThreadMaterialization.make({
@@ -5602,7 +5825,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5602
5825
  return "repaired";
5603
5826
  }
5604
5827
  case "EnsureWaitingForChild": {
5605
- const claimed = yield* claimFor(submission);
5828
+ const claimed = yield* claimFor(submission, decision);
5606
5829
  if (Option.isNone(claimed)) return "deferred";
5607
5830
  const claim = claimed.value;
5608
5831
  const suspension = yield* ledger.suspend(SuspendRequest.make({
@@ -5657,7 +5880,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5657
5880
  if (Option.isSome(childRow)) yield* wake.notify(childRow.value.threadId);
5658
5881
  }
5659
5882
  if (submission.state !== "suspended") {
5660
- const claimed = yield* claimFor(submission);
5883
+ const claimed = yield* claimFor(submission, decision);
5661
5884
  if (Option.isNone(claimed)) return "deferred";
5662
5885
  const claim = claimed.value;
5663
5886
  const suspension = yield* ledger.suspend(SuspendRequest.make({
@@ -5701,7 +5924,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5701
5924
  const evidence = yield* evidenceFor(history.records, submission.submissionId, history.materialized, snapshot.hostSubmissionId);
5702
5925
  const decision = classifyRecovery(snapshot, evidence);
5703
5926
  const disposition = decision._tag === "ApplyInput" && snapshot.submission.state === "ready" ? "deferred" : yield* Effect.scoped(executeRecoveryDecision(snapshot, evidence, decision, history.records, history));
5704
- if (disposition === "repaired") yield* annotateRepair(submission.threadId, submission.submissionId, decision);
5705
5927
  return RecoveryReport.make({
5706
5928
  submissionId: submission.submissionId,
5707
5929
  threadId: submission.threadId,
@@ -5758,10 +5980,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5758
5980
  reason: cause.reason === "storage" || cause.reason === "unavailable" ? "unavailable" : "refused",
5759
5981
  code: `message-${cause.reason}`
5760
5982
  })));
5761
- if (options.definitions.replay !== void 0 && workerAdmission === void 0 && messageAdmission === void 0 && !registeredBindings.some((binding) => binding.agentId === agent.definition.id && (definitionDigestsEqual(binding.digests, options.definitions) || binding.retainedManifests?.some((retained) => definitionDigestsEqual(DefinitionDigests.make({
5762
- ...retained.digests,
5763
- replay: retained.replay
5764
- }), options.definitions))))) {
5983
+ if (options.definitions.replay !== void 0 && workerAdmission === void 0 && messageAdmission === void 0 && !registeredBindings.some((binding) => binding.agentId === agent.definition.id && definitionDigestsEqual(binding.digests, options.definitions))) {
5765
5984
  const retained = yield* ledger.lookup(SubmissionLookupByKey.make({
5766
5985
  threadId: options.threadId,
5767
5986
  principal: options.principal,
@@ -5980,7 +6199,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5980
6199
  const nowMillis = yield* Clock.currentTimeMillis;
5981
6200
  const explainedAt = yield* nowUtc;
5982
6201
  const runId = runIdForSubmission(submission.submissionId);
5983
- const resolvedIds = new Set(snapshot.unknownResolutions.map((intent) => intent.toolCallId));
6202
+ const resolvedIds = new Set(read.records.flatMap(({ record }) => record.payload._tag === "ToolCallSettled" && record.payload.runId === runId ? [record.payload.toolCallId] : []));
5984
6203
  const unknownCalls = [];
5985
6204
  for (const envelope of read.records) {
5986
6205
  const payload = envelope.record.payload;
@@ -6013,6 +6232,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6013
6232
  inputRecorded: evidence.inputRecorded,
6014
6233
  abortRecorded: evidence.abortRecorded,
6015
6234
  openToolCalls: evidence.openToolCalls,
6235
+ pendingOperations: read.records.flatMap(({ record }) => record.payload._tag === "ToolCallPrepared" && record.payload.runId === runId && !resolvedIds.has(record.payload.toolCallId) ? [record.payload] : []),
6016
6236
  openDelegationCalls: evidence.openDelegationCalls,
6017
6237
  approvalsPending: evidence.approvalsPending,
6018
6238
  unknownCalls,
@@ -6027,7 +6247,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6027
6247
  }),
6028
6248
  decision,
6029
6249
  decisionMeaning: recoveryDecisionMeaning(decision._tag),
6030
- disposition: predictRecoveryDisposition(decision, snapshot),
6250
+ disposition: decision._tag === "ApplyUnknownResolutions" && (yield* hasUnsupportedRetry(snapshot, read.records).pipe(Effect.mapError((cause) => RunJournalError.make({
6251
+ message: "Cannot verify the pending retry contract",
6252
+ cause
6253
+ })))) ? "unknown" : predictRecoveryDisposition(decision, snapshot),
6031
6254
  explainedAt
6032
6255
  });
6033
6256
  });
@@ -6105,9 +6328,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6105
6328
  });
6106
6329
  /**
6107
6330
  * Safe re-drive of exactly one Submission's recovery decision (plan §3): classify, execute
6108
- * the ONE repair the classifier names, append the deterministic `RepairAnnotated` audit when
6109
- * it repairs (DUR-013 — retry adds no new record type), and wake the lane. Typed refusals
6110
- * protect the paths that own their own operations: settled work (`NoAction`), lanes blocked
6331
+ * the ONE repair the classifier names, annotate owned repair attempts with their actual
6332
+ * claim epoch (DUR-013), and wake the lane. Typed refusals
6333
+ * protect the paths that own their own operations: settled work (`NoAction`), requests parked
6111
6334
  * on Unknown Outcomes (`resolveUnknown`, DUR-017), and lanes awaiting approval decisions
6112
6335
  * (`resolveApproval`). The mandatory `author`/`reason` audit fields (SEC-011) annotate the
6113
6336
  * structured operator log.
@@ -6132,7 +6355,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6132
6355
  submissionId: command.submissionId,
6133
6356
  refusal: "await-unknown-resolution",
6134
6357
  decisionTag: decision._tag,
6135
- message: "The lane is durably blocked on Unknown Outcomes; resolve them through the authorized resolveUnknown path (DUR-017) instead of retrying."
6358
+ message: "This Submission is parked on Unknown Outcomes; resolve them through the authorized resolveUnknown path (DUR-017) instead of retrying."
6136
6359
  });
6137
6360
  if (decision._tag === "AwaitApprovalDecision") return yield* RetryRefused.make({
6138
6361
  submissionId: command.submissionId,
@@ -6148,7 +6371,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6148
6371
  decision: decision._tag
6149
6372
  }));
6150
6373
  const disposition = yield* Effect.scoped(executeRecoveryDecision(snapshot, evidence, decision, read.records, read));
6151
- if (disposition === "repaired") yield* annotateRepair(submission.threadId, command.submissionId, decision);
6152
6374
  yield* wake.notify(submission.threadId);
6153
6375
  return RecoveryReport.make({
6154
6376
  submissionId: command.submissionId,
@@ -6185,8 +6407,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6185
6407
  case "unknown": {
6186
6408
  blockedOn = "unknown";
6187
6409
  const records = (yield* readAllTolerant(submission.threadId, [submission.submissionId])).records;
6188
- const resolvedIds = new Set(snapshot.unknownResolutions.map((intent) => intent.toolCallId));
6189
6410
  const runId = runIdForSubmission(submission.submissionId);
6411
+ const resolvedIds = new Set(records.flatMap(({ record }) => record.payload._tag === "ToolCallSettled" && record.payload.runId === runId ? [record.payload.toolCallId] : []));
6190
6412
  let earliest;
6191
6413
  for (const envelope of records) {
6192
6414
  const payload = envelope.record.payload;
@@ -6338,19 +6560,16 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6338
6560
  * the next model request, reattaches through the prompt-coverage rule after a crash, and the
6339
6561
  * joined Submissions settle with the host outcome (DUR-002/DUR-016).
6340
6562
  * - `runWorker(agent)` — scan-seeded, wake-driven worker loop over every lane (WP4's host driver),
6341
- * reimplemented over a singleton identity-exact (digest-transparent) Binding resolution: a
6342
- * claimed head belonging to a different Agent never runs against this binding (SUB-023); a
6343
- * parent-linked head refused this way settles with the framework `ChildCompatibilityFailure`.
6563
+ * over a singleton current Binding: a claimed head belonging to a different Agent never
6564
+ * runs against this binding. Missing bindings leave accepted roots and children owed.
6344
6565
  * - `processThreadResolved(threadId)` / `runResolvedWorker` — the
6345
- * S2 multi-binding equivalents over registrations owned by the runtime Layer (spec §11, D7): every claimed
6346
- * head's stored `(agentId, agentDigests)` resolves to the exact registered Binding before any
6347
- * code runs; an unresolvable parent-linked child settles with the Schema-stable framework
6348
- * `ChildCompatibilityFailure` (no application code, the child never executes), and an
6349
- * unresolvable root surfaces the typed refusal after releasing the claim.
6566
+ * equivalents over registrations owned by the runtime Layer: each stable `agentId` selects
6567
+ * one current Binding. Unfinished operation contracts gate handler execution independently
6568
+ * of immutable admission evidence; missing bindings release the claim with a typed refusal.
6350
6569
  * - `runRecovery()` — classify every nonterminal Submission with the pure `classifyRecovery` and
6351
- * execute the repair decisions, appending a `RepairAnnotated` audit record per executed decision
6570
+ * execute repair decisions, annotating owned repair attempts with their granted epochs
6352
6571
  * (DUR-013). Untouched ready input and model-resuming work are reported `deferred` for a
6353
- * worker claim; lanes blocked on
6572
+ * worker claim; Submissions parked on
6354
6573
  * Unknown Outcomes are reported `unknown`. The S2 binding-free Subagent executors (admission
6355
6574
  * completion, start-link repair, waiting restoration, wake replay, canonical join accounting,
6356
6575
  * abort propagation, orphan reservation release) run here; the settlement join itself is
@@ -6385,4 +6604,4 @@ var DurableAgentRuntime = class DurableAgentRuntime extends Context.Service()("@
6385
6604
  //#endregion
6386
6605
  export { RecoveryReport as a, recoveryRepairRecordId as c, DurableRuntimeConfig as i, threadCreatedBatchId as l, DurableAgentRuntime_exports as n, childReservationIdFor as o, DurableApprovalResolver as r, recoveryRepairBatchId as s, DurableAgentRuntime as t, threadCreatedRecordId as u };
6387
6606
 
6388
- //# sourceMappingURL=DurableAgentRuntime-DbUvZHDZ.mjs.map
6607
+ //# sourceMappingURL=DurableAgentRuntime-BE_OjQjz.mjs.map