effect-agent 0.1.0-beta.96 → 0.1.0-beta.98

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 (142) 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-BGwGUIT-.mjs → DurableAgentRuntime-gF4YiEj2.mjs} +485 -262
  5. package/dist/DurableAgentRuntime-gF4YiEj2.mjs.map +1 -0
  6. package/dist/{DurableFailpoint-DQJet828.d.mts → DurableFailpoint-mx3-nsxU.d.mts} +3 -3
  7. package/dist/{EventSource-tCO_dAff.d.mts → EventSource-BZ73J0s5.d.mts} +3 -3
  8. package/dist/{PreparedInputAdmission-CFSddiT8.d.mts → PreparedInputAdmission-BkeI-Xvr.d.mts} +4 -4
  9. package/dist/{Records-CMByd1eD.d.mts → Records-BIiRLqlz.d.mts} +93 -101
  10. package/dist/{Records-BEz5IFUu.mjs → Records-DR1xtbXY.mjs} +36 -7
  11. package/dist/Records-DR1xtbXY.mjs.map +1 -0
  12. package/dist/{RunJournal-B_fxe57c.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-BxCfnYr0.d.mts → Schedule-BNFlluAl.d.mts} +2 -2
  16. package/dist/{Scheduling-BW6BD_TP.mjs → Scheduling-HFH0pST1.mjs} +2 -2
  17. package/dist/{Scheduling-BW6BD_TP.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-CrTIQKZR.d.mts → Subscription-BCQ3L9Zp.d.mts} +3 -3
  22. package/dist/{SubscriptionInput-BVOeFF1L.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-BTa5vmQF.mjs → agent-registration-cXpB8cBU.mjs} +40 -95
  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 -7
  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 -4
  124. package/src/durable/DurableAgentRuntime.ts +936 -462
  125. package/src/durable/DurableFailpoint.ts +2 -0
  126. package/src/durable/Records.ts +31 -4
  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 +77 -188
  134. package/src/durable/internal/messaging-host.ts +1 -6
  135. package/src/durable/internal/worker-host.ts +88 -116
  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-BGwGUIT-.mjs.map +0 -1
  140. package/dist/Records-BEz5IFUu.mjs.map +0 -1
  141. package/dist/RunJournal-B_fxe57c.mjs.map +0 -1
  142. package/dist/agent-registration-BTa5vmQF.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-BEz5IFUu.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, d as resolveWorkerBinding, l as makeLegacyWorkerBinding, o as compileRegistrations, r as BindingUnavailable, s as definitionDigestsEqual, u as resolveDefinitionBinding } from "./agent-registration-BTa5vmQF.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-B_fxe57c.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";
@@ -272,7 +273,7 @@ const makeMessagingRuntime = Effect.fn("MessagingHost.make")(function* (options)
272
273
  const log = yield* read(threadId);
273
274
  const created = log.records[0]?.record.payload;
274
275
  if (created?._tag !== "ThreadCreated") return yield* failure$1("context", "not-found");
275
- if (deps.bindings.filter((binding) => binding.agentId === created.agentId && bindingSupports(binding, created.definitions)).length !== 1) return yield* failure$1("context", "binding-mismatch");
276
+ if (deps.bindings.filter((binding) => binding.agentId === created.agentId).length !== 1) return yield* failure$1("context", "binding-mismatch");
276
277
  return {
277
278
  log,
278
279
  address: {
@@ -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) || !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,11 +677,15 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
676
677
  ...report.destination === void 0 ? {} : { destinationDelegationId: report.destination.delegationId }
677
678
  };
678
679
  });
680
+ const currentBinding = (agentId) => {
681
+ const matches = deps.bindings.filter((entry) => entry.agentId === agentId);
682
+ return matches.length === 1 ? matches[0] : void 0;
683
+ };
679
684
  const sourceAuthority = Effect.fn("WorkerHost.sourceAuthority")(function* (threadId, submissionId) {
680
685
  const current = yield* read(threadId, "start");
681
686
  const created = current.records[0]?.record.payload;
682
687
  if (created?._tag !== "ThreadCreated") return yield* failure("start", "not-found");
683
- const binding = deps.bindings.find((entry) => entry.agentId === created.agentId && bindingSupports(entry, created.definitions));
688
+ const binding = currentBinding(created.agentId);
684
689
  const worker = current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
685
690
  const attached = current.records.find(({ record }) => record.payload._tag === "SubagentLineageRecorded")?.record.payload;
686
691
  const nested = worker?._tag === "WorkerOriginRecorded" || attached?._tag === "SubagentLineageRecorded";
@@ -693,9 +698,11 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
693
698
  if (worker?._tag === "WorkerOriginRecorded" && (submission.value.workerAdmission === void 0 || !sameOrigin(submission.value.workerAdmission.origin, worker.origin))) return yield* failure("start", "denied");
694
699
  if (attached?._tag === "SubagentLineageRecorded" && (submission.value.parentLinkage?.parentSubmissionId !== attached.parentSubmissionId || submission.value.parentLinkage.parentToolCallId !== attached.parentLink.parentToolCallId)) return yield* failure("start", "denied");
695
700
  }
701
+ const ownerBinding = ownerSubmission === void 0 ? void 0 : currentBinding(ownerSubmission.agentId);
702
+ const selectedBinding = ownerBinding ?? binding;
696
703
  if (worker?._tag === "WorkerOriginRecorded") return {
697
704
  current,
698
- binding,
705
+ binding: selectedBinding,
699
706
  submission: ownerSubmission,
700
707
  policyOverride: Option.none(),
701
708
  policy: worker.origin.policy,
@@ -708,7 +715,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
708
715
  if (attached.policy === void 0 || attached.budget === void 0 || attached.grant === void 0) return yield* failure("start", "denied");
709
716
  return {
710
717
  current,
711
- binding,
718
+ binding: selectedBinding,
712
719
  submission: ownerSubmission,
713
720
  policyOverride: Option.none(),
714
721
  policy: attached.policy,
@@ -718,8 +725,6 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
718
725
  depth: attached.parentLink.depth
719
726
  };
720
727
  }
721
- const ownerBinding = ownerSubmission === void 0 ? void 0 : deps.bindings.find((entry) => entry.agentId === ownerSubmission.agentId && bindingSupports(entry, ownerSubmission.agentDigests));
722
- const selectedBinding = ownerBinding ?? binding;
723
728
  const changedRootAgent = ownerSubmission !== void 0 && ownerSubmission.agentId !== created.agentId;
724
729
  if (selectedBinding === void 0 || changedRootAgent && ownerBinding === void 0) return yield* failure("start", "declaration-unavailable");
725
730
  const selected = yield* deps.policyResolver.resolveSource({
@@ -729,7 +734,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
729
734
  ...ownerSubmission === void 0 ? {} : { submission: ownerSubmission }
730
735
  });
731
736
  if (Option.isSome(selected) && ownerSubmission !== void 0 && ownerBinding === void 0) return yield* failure("start", "declaration-unavailable");
732
- const effectiveBinding = Option.isSome(selected) || changedRootAgent ? selectedBinding : binding;
737
+ const effectiveBinding = Option.isSome(selected) || changedRootAgent || binding === void 0 ? selectedBinding : binding;
733
738
  if (effectiveBinding === void 0) return yield* failure("start", "declaration-unavailable");
734
739
  const policy = Option.isNone(selected) ? effectiveBinding.definition.policy : yield* decode(AgentPolicy, selected.value, "start");
735
740
  return {
@@ -802,7 +807,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
802
807
  }
803
808
  return yield* failure("start", "storage");
804
809
  });
805
- const reservation = Effect.fn("WorkerHost.reserveInput")(function* (admission, inputDigest, input, principal) {
810
+ const reservation = Effect.fn("WorkerHost.reserveInput")(function* (admission, inputDigest, input, principal, retainedFrameworkInput) {
806
811
  const origin = admission.origin;
807
812
  const id = `worker-input:${admission.messageId}`;
808
813
  for (let attempt = 0; attempt < 16; attempt++) {
@@ -823,9 +828,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
823
828
  if (prior === void 0 && admission.messageId !== origin.firstMessageId) return yield* failure("start", "not-found");
824
829
  const caps = origin.budget.caps;
825
830
  const sourceBinding = source.binding;
826
- const targetBinding = deps.bindings.find((entry) => entry.agentId === origin.worker.targetAgentId && bindingSupports(entry, origin.targetDigests));
831
+ const targetBinding = currentBinding(origin.worker.targetAgentId);
827
832
  if (sourceBinding === void 0 || targetBinding === void 0) return yield* failure("start", "declaration-unavailable");
828
- 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")));
829
834
  const targetRequest = {
830
835
  definition: targetBinding.definition,
831
836
  definitions: targetBinding.digests,
@@ -922,7 +927,9 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
922
927
  });
923
928
  yield* authorizeBudget(admission.origin, options.principal);
924
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");
925
- 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);
926
933
  return admission;
927
934
  });
928
935
  const prepareUpdate = Effect.fn("WorkerHost.prepareUpdate")(function* (update, submission, messageId) {
@@ -930,23 +937,22 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
930
937
  const intent = origin?.reporting;
931
938
  if (origin === void 0 || intent?.mode !== "standard") return void 0;
932
939
  if (update.threadId !== submission.threadId || update.agentId !== origin.worker.targetAgentId) return yield* failure("followUp", "worker-mismatch");
933
- const sourceBinding = deps.bindings.find((entry) => entry.agentId === origin.source.agentId && bindingSupports(entry, intent.sourceDigests));
934
- const targetBinding = deps.bindings.find((entry) => entry.agentId === origin.worker.targetAgentId && bindingSupports(entry, origin.targetDigests));
935
- const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
936
- 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");
937
940
  const sourceHistory = yield* read(origin.source.threadId, "followUp");
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)) return yield* failure("followUp", "declaration-unavailable");
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);
947
+ const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
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");
942
949
  const recorded = source.current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
943
950
  const receivingOrigin = recorded?._tag === "WorkerOriginRecorded" ? recorded.origin : void 0;
944
951
  if (source.depth !== 0 && receivingOrigin === void 0) return yield* failure("followUp", "denied");
945
952
  const now = yield* Clock.currentTimeMillis;
946
953
  const deadlineAtMillis = Math.min(origin.expiresAtMillis, receivingOrigin?.expiresAtMillis ?? Infinity);
947
954
  if (now >= deadlineAtMillis) return yield* failure("followUp", "denied");
948
- const sourceSubmission = source.submission;
949
- 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;
950
956
  let workerAdmission;
951
957
  let principal = submission.principal;
952
958
  if (receivingOrigin !== void 0) {
@@ -1029,11 +1035,6 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1029
1035
  messageId,
1030
1036
  reason
1031
1037
  });
1032
- const sourceBinding = deps.bindings.find((entry) => entry.agentId === origin.source.agentId && bindingSupports(entry, intent.sourceDigests));
1033
- const targetBinding = deps.bindings.find((entry) => entry.agentId === origin.worker.targetAgentId && bindingSupports(entry, origin.targetDigests));
1034
- const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
1035
- const descriptor = reports[0];
1036
- 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");
1037
1038
  const selected = yield* deps.ledger.lookup(SubmissionLookupById.make({ submissionId: host.submissionId })).pipe(Effect.mapError(storageFailure("inspect")));
1038
1039
  if (Option.isNone(selected) || selected.value.threadId !== submission.threadId || selected.value.receiptId !== host.receiptId || selected.value.workerAdmission === void 0 || !sameOrigin(selected.value.workerAdmission.origin, origin)) return yield* failure("inspect", "corrupt");
1039
1040
  const hostSubmission = selected.value;
@@ -1042,7 +1043,12 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1042
1043
  const firstInput = requests(sourceHistory.records).find((row) => row.admission.messageId === origin.firstMessageId);
1043
1044
  if (firstInput === void 0 || !sameOrigin(firstInput.admission.origin, origin)) return yield* failure("inspect", "corrupt");
1044
1045
  const source = yield* sourceAuthority(origin.source.threadId, firstInput.admission.sourceSubmissionId);
1045
- if (source.binding === void 0 || !bindingSupports(source.binding, intent.sourceDigests)) return refused("declaration-unavailable");
1046
+ if (source.binding === void 0) return refused("declaration-unavailable");
1047
+ const sourceBinding = currentBinding(origin.source.agentId);
1048
+ const targetBinding = currentBinding(origin.worker.targetAgentId);
1049
+ const reports = sourceBinding?.reporting?.filter((entry) => entry.delegationId === origin.worker.delegationId) ?? [];
1050
+ const descriptor = reports[0];
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");
1046
1052
  const sourceOriginRecord = source.current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
1047
1053
  const sourceOrigin = sourceOriginRecord?._tag === "WorkerOriginRecorded" ? sourceOriginRecord.origin : void 0;
1048
1054
  if (source.depth !== 0 && sourceOrigin === void 0) return refused("destination");
@@ -1087,7 +1093,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1087
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");
1088
1094
  } else if (projection.value.message !== void 0) return refused("input");
1089
1095
  const reportInput = descriptor.mode === "standard" ? source.submission?.inputPayload : projection.value.encodedInput;
1090
- 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);
1091
1097
  if (Option.isNone(validated)) return refused("input");
1092
1098
  const input = validated.value;
1093
1099
  const inputDigest = yield* withCrypto(digestJson(input)).pipe(Effect.mapError(storageFailure("inspect")));
@@ -1236,12 +1242,12 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1236
1242
  };
1237
1243
  });
1238
1244
  const findOrigin = Effect.fn("WorkerHost.findOrigin")(function* (worker, target, operation) {
1239
- const resolved = yield* binding(target, operation);
1245
+ yield* binding(target, operation);
1240
1246
  if (worker.targetAgentId !== target.id) return yield* failure(operation, "worker-mismatch");
1241
1247
  const source = yield* read(context.source.threadId, operation);
1242
1248
  const origin = requests(source.records).find((row) => row.admission.origin.worker.threadId === worker.threadId)?.admission.origin;
1243
1249
  if (origin === void 0) return yield* failure(operation, "not-found");
1244
- if (origin.worker.delegationId !== worker.delegationId || !bindingSupports(resolved, origin.targetDigests)) return yield* failure(operation, "worker-mismatch");
1250
+ if (origin.worker.delegationId !== worker.delegationId) return yield* failure(operation, "worker-mismatch");
1245
1251
  return origin;
1246
1252
  });
1247
1253
  const receiptInput = Effect.fn("WorkerHost.receiptInput")(function* (request, operation) {
@@ -1418,7 +1424,7 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1418
1424
  threadId: Schema.decodeSync(ThreadId)(messageId)
1419
1425
  },
1420
1426
  source: existing?.envelope.workerAdmission?.origin.source ?? context.source,
1421
- targetDigests: resolved.digests,
1427
+ targetDigests: previousOrigin?.targetDigests ?? resolved.digests,
1422
1428
  policy: request.policy,
1423
1429
  budget: request.budget,
1424
1430
  ...request.budgetScope === void 0 ? {} : { budgetScope: request.budgetScope },
@@ -1570,12 +1576,12 @@ const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (options) {
1570
1576
  const current = yield* read(sourceThreadId, "context");
1571
1577
  const created = current.records[0]?.record.payload;
1572
1578
  if (created?._tag !== "ThreadCreated") return yield* failure("context", "not-found");
1573
- const resolved = deps.bindings.find((entry) => entry.agentId === created.agentId && bindingSupports(entry, created.definitions));
1579
+ const selected = request.sourceSubmissionId === void 0 ? void 0 : yield* sourceAuthority(sourceThreadId, request.sourceSubmissionId);
1580
+ const resolved = selected === void 0 ? currentBinding(created.agentId) : selected.binding;
1574
1581
  if (resolved === void 0) return yield* failure("context", "declaration-unavailable");
1575
1582
  const origin = current.records.find(({ record }) => record.payload._tag === "WorkerOriginRecorded")?.record.payload;
1576
1583
  const attached = current.records.find(({ record }) => record.payload._tag === "SubagentLineageRecorded")?.record.payload;
1577
1584
  const retained = origin?._tag === "WorkerOriginRecorded" ? origin.origin : attached?._tag === "SubagentLineageRecorded" ? attached : void 0;
1578
- const selected = request.sourceSubmissionId === void 0 ? void 0 : yield* sourceAuthority(sourceThreadId, request.sourceSubmissionId);
1579
1585
  return facet({
1580
1586
  source: {
1581
1587
  _tag: "programmatic",
@@ -1729,7 +1735,7 @@ var RecoveryReport = class extends Schema.Class("@effect-agent/thread/RecoveryRe
1729
1735
  decision: RecoveryDecision,
1730
1736
  /**
1731
1737
  * `repaired` = executed; `deferred` = a claiming worker must finish it; `none` = settled;
1732
- * `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
1733
1739
  * DUR-017 resolution path — the settlement obligation stays visible while no worker permit
1734
1740
  * is consumed (durability §16).
1735
1741
  */
@@ -1866,18 +1872,6 @@ const declaredToolCalls = Effect.fn("DurableAgentRuntime.declaredToolCalls")((me
1866
1872
  })));
1867
1873
  /** Application calls alone drive durable preparation, settlement, and batch resume. */
1868
1874
  const declaredApplicationCalls = Effect.fn("DurableAgentRuntime.declaredApplicationCalls")((messages) => declaredToolCalls(messages).pipe(Effect.map(({ application }) => application)));
1869
- /** Rebuild the exact JSON text that the engine decoded from one terminal no-Tool response. */
1870
- const terminalAssistantText = Effect.fn("DurableAgentRuntime.terminalAssistantText")((messages) => decodePrompt(messages).pipe(Effect.mapError((cause) => RunJournalError.make({
1871
- message: "ModelResponseRecorded messages are not Schema-encoded Prompt messages",
1872
- cause
1873
- })), Effect.map((prompt) => {
1874
- let text = "";
1875
- for (const message of prompt.content) {
1876
- if (message.role !== "assistant") continue;
1877
- for (const part of message.content) if (part.type === "text") text += part.text;
1878
- }
1879
- return text;
1880
- })));
1881
1875
  const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
1882
1876
  const registeredBindings = [...bindings];
1883
1877
  const ledger = yield* SubmissionLedger;
@@ -1899,6 +1893,34 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
1899
1893
  const operationAuthorizer = yield* OperationAuthorizer;
1900
1894
  const toolFailureObserver = yield* CurrentToolFailureObserver;
1901
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 === definition ? 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
+ };
1902
1924
  const hit = (location) => failpoint.hit(location);
1903
1925
  const makeEnvelope = (recordId, payload) => Effect.map(nowUtc, (createdAt) => RecordEnvelope.make({
1904
1926
  recordId,
@@ -1979,7 +2001,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
1979
2001
  return yield* Stream.runCollect(view.canonical.pipe(Stream.filter(controlRecords(submissionIds))));
1980
2002
  });
1981
2003
  /** A checkpoint is useful only for its explicitly retained submissions and exact definitions. */
1982
- const recoveryView = Effect.fn("DurableAgentRuntime.recoveryView")(function* (threadId, throughSequence, submissionIds) {
2004
+ const recoveryView = Effect.fn("DurableAgentRuntime.recoveryView")(function* (threadId, throughSequence, submissionIds, journalOwner) {
1983
2005
  const full = { canonical: canonicalRange(threadId, throughSequence) };
1984
2006
  if (store.recoveryCheckpoints === void 0) return full;
1985
2007
  const loaded = yield* store.recoveryCheckpoints.load(LoadCheckpointRequest.make({
@@ -1992,7 +2014,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
1992
2014
  const decoded = yield* Schema.decodeUnknownEffect(RecoveryCheckpointContents)(checkpoint.state).pipe(Effect.option);
1993
2015
  if (Option.isNone(decoded)) return full;
1994
2016
  const { state, digest } = decoded.value;
1995
- 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;
1996
2018
  const encoded = yield* Schema.encodeEffect(RecoveryCheckpointState)(state).pipe(Effect.mapError((cause) => ThreadStoreError.make({
1997
2019
  operation: "decode recovery checkpoint",
1998
2020
  message: "Invalid recovery state",
@@ -2119,6 +2141,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2119
2141
  const preparedTurns = /* @__PURE__ */ new Set();
2120
2142
  const settledIds = /* @__PURE__ */ new Set();
2121
2143
  const resolvedIds = /* @__PURE__ */ new Set();
2144
+ const unknownIds = /* @__PURE__ */ new Set();
2122
2145
  const requested = [];
2123
2146
  const decidedIds = /* @__PURE__ */ new Set();
2124
2147
  let lastResponse;
@@ -2159,8 +2182,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2159
2182
  case "ToolCallSettled":
2160
2183
  if (payload.runId === runId) settledIds.add(payload.toolCallId);
2161
2184
  break;
2185
+ case "ToolCallUnknown":
2186
+ if (payload.runId === runId) unknownIds.add(payload.toolCallId);
2187
+ break;
2162
2188
  case "ToolCallResolved":
2163
- 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);
2164
2190
  break;
2165
2191
  case "ToolApprovalRequested":
2166
2192
  if (payload.runId !== runId) break;
@@ -2241,8 +2267,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2241
2267
  }));
2242
2268
  }
2243
2269
  const isPreparedWorker = (call) => preparedKinds.get(toolCallPreparedRecordId(runId, call.turn, call.toolCallId)) === "orchestration";
2244
- const openWorkerCalls = allOpenCalls.filter(isPreparedWorker);
2245
- 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));
2246
2272
  let declaredPendingBatch;
2247
2273
  if (lastResponse !== void 0 && !preparedTurns.has(lastResponse.turn)) {
2248
2274
  const declared = yield* declaredApplicationCalls(lastResponse.messages);
@@ -2267,6 +2293,25 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2267
2293
  ...hostSettlementOutcome === void 0 ? {} : { hostSettlementOutcome }
2268
2294
  });
2269
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
+ });
2270
2315
  /**
2271
2316
  * The declared-but-unsettled Tool batch of the Run's LAST committed Turn (§2.4). Keyed on
2272
2317
  * SETTLED coverage deliberately: a call closed by a never-started/safe-retry `ToolCallResolved`
@@ -2285,12 +2330,15 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2285
2330
  turn: payload.turn,
2286
2331
  messages: payload.messages,
2287
2332
  toolParameterRejections: payload.toolParameterRejections,
2333
+ toolOperations: payload.toolOperations,
2288
2334
  ...payload.toolExposure === void 0 ? {} : { toolExposure: payload.toolExposure }
2289
2335
  };
2290
2336
  }
2291
2337
  continue;
2292
2338
  }
2293
2339
  if (payload._tag === "ToolCallSettled" && payload.runId === runId) settledByCallId.set(payload.toolCallId, {
2340
+ recordId: envelope.record.recordId,
2341
+ toolName: payload.toolName,
2294
2342
  result: payload.result,
2295
2343
  isFailure: payload.isFailure,
2296
2344
  ...payload.budgetRejected === true ? { budgetRejected: true } : {},
@@ -2301,6 +2349,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2301
2349
  const declared = yield* declaredToolCalls(lastResponse.messages);
2302
2350
  if (declared.application.length === 0) return void 0;
2303
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
+ }
2304
2364
  for (const part of declared.providerResults) {
2305
2365
  const result = yield* decodePersisted(part.result).pipe(Effect.mapError((cause) => RunJournalError.make({
2306
2366
  message: `Invalid canonical provider result ${part.id}`,
@@ -2315,10 +2375,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2315
2375
  for (const call of calls) {
2316
2376
  const recorded = settledByCallId.get(call.id);
2317
2377
  if (call.providerExecuted === true && recorded === void 0) return yield* RunJournalError.make({ message: `Pending Turn lacks canonical provider result for ${call.id}` });
2318
- if (recorded !== void 0) settled.push({
2319
- id: call.id,
2320
- ...recorded
2321
- });
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
+ }
2322
2386
  }
2323
2387
  const completionCall = declared.application[0];
2324
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);
@@ -2332,6 +2396,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2332
2396
  responseRecordId: modelResponseRecordId(runId, lastResponse.turn),
2333
2397
  messages: lastResponse.messages,
2334
2398
  toolParameterRejections: lastResponse.toolParameterRejections,
2399
+ toolOperations: lastResponse.toolOperations,
2335
2400
  ...lastResponse.toolExposure === void 0 ? {} : { toolExposure: lastResponse.toolExposure }
2336
2401
  };
2337
2402
  });
@@ -2392,9 +2457,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2392
2457
  };
2393
2458
  });
2394
2459
  /**
2395
- * Append context at the Thread's CURRENT fence, without a claim. Used only where no live
2396
- * owner can exist (an `unknown` head is never claimable, WP2 claim rule): record-identity
2397
- * 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.
2398
2463
  */
2399
2464
  const attemptContextAtTail = Effect.fn("DurableAgentRuntime.attemptContextAtTail")(function* (threadId) {
2400
2465
  const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
@@ -2538,16 +2603,28 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2538
2603
  * `Uncertain` collects for Unknown marking, and a reconciler FAILURE is no proof at all (the
2539
2604
  * call stays open and blocked without being marked). Nothing here ever executes a handler.
2540
2605
  */
2541
- 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) {
2542
2607
  const submissionId = submission.submissionId;
2543
2608
  const runId = runIdForSubmission(submissionId);
2609
+ yield* pendingToolBatchFor(records, runId);
2610
+ const operations = operationsFor(records, runId);
2544
2611
  const preparedByCallId = /* @__PURE__ */ new Map();
2545
- for (const envelope of records) {
2546
- const payload = envelope.record.payload;
2547
- 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
+ }
2548
2618
  }
2549
- const intents = /* @__PURE__ */ new Map();
2550
- 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]));
2551
2628
  const review = {
2552
2629
  uncertain: [],
2553
2630
  unproven: [],
@@ -2556,7 +2633,39 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2556
2633
  };
2557
2634
  let recovered = 0;
2558
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);
2559
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
+ });
2560
2669
  if (intent !== void 0) switch (intent.resolution._tag) {
2561
2670
  case "AbortSubmission": continue;
2562
2671
  case "CompletedWithResult":
@@ -2566,33 +2675,29 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2566
2675
  isFailure: intent.resolution.isFailure
2567
2676
  },
2568
2677
  resolution: intent.resolution.isFailure ? "failed-with-error" : "completed-with-result",
2569
- author: intent.author,
2570
- reason: intent.reason
2678
+ author,
2679
+ reason
2571
2680
  });
2572
2681
  recovered += 1;
2573
2682
  continue;
2574
2683
  case "NeverHappened":
2575
- yield* appendClosedCall(ctx, submissionId, knownIds, call, {
2576
- resolution: "never-started",
2577
- author: intent.author,
2578
- reason: intent.reason
2579
- });
2580
- review.retryable.push(call);
2684
+ yield* neverStarted;
2581
2685
  continue;
2582
2686
  case "SafeToRetry":
2583
- yield* appendClosedCall(ctx, submissionId, knownIds, call, {
2584
- resolution: "safe-retry",
2585
- author: intent.author,
2586
- reason: intent.reason
2587
- });
2588
- 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);
2589
2690
  continue;
2590
2691
  }
2591
- 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") {
2592
2698
  review.retryable.push(call);
2593
2699
  continue;
2594
2700
  }
2595
- const prepared = preparedByCallId.get(call.toolCallId);
2596
2701
  if (prepared === void 0) {
2597
2702
  review.unproven.push(call);
2598
2703
  continue;
@@ -2605,7 +2710,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2605
2710
  toolCallId: prepared.toolCallId,
2606
2711
  toolName: prepared.toolName,
2607
2712
  parameters: prepared.parameters,
2608
- 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 }
2609
2717
  })).pipe(Effect.map(Option.some), Effect.catchTag("ToolReconcilerError", () => Effect.succeed(Option.none())));
2610
2718
  if (Option.isNone(reconciled)) {
2611
2719
  review.unproven.push(call);
@@ -2620,14 +2728,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
2620
2728
  isFailure: decision.isFailure
2621
2729
  },
2622
2730
  resolution: decision.isFailure ? "failed-with-error" : "completed-with-result",
2623
- author: RECONCILER_AUTHOR,
2624
- reason: "A registered reconciliation policy recovered the external outcome"
2731
+ author,
2732
+ reason
2625
2733
  });
2626
2734
  recovered += 1;
2627
2735
  continue;
2628
2736
  case "NeverStarted":
2737
+ yield* neverStarted;
2738
+ continue;
2629
2739
  case "SafeToRetry":
2630
- 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);
2631
2743
  continue;
2632
2744
  case "Uncertain":
2633
2745
  review.uncertain.push(call);
@@ -3168,11 +3280,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3168
3280
  * Join a settled child after parent abort or expiry without running application handlers.
3169
3281
  * The canonical child outcome stays intact; the parent result records why it cannot continue.
3170
3282
  */
3171
- 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) {
3172
3284
  const runId = runIdForSubmission(parent.submissionId);
3173
3285
  const request = subagent.requested.get(toolCallId);
3174
3286
  if (request === void 0) return yield* LedgerError.make({
3175
- operation: "joinSettledChildForClosedParent",
3287
+ operation: "joinSettledChildWithoutHandler",
3176
3288
  message: `Tool Call ${toolCallId} has an attached child but no canonical SubagentRequested record`
3177
3289
  });
3178
3290
  const joinedRecordId = subagentJoinedRecordId(runId, toolCallId);
@@ -3182,16 +3294,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3182
3294
  else {
3183
3295
  const verification = yield* verifySettledChild(parent, request, childSubmissionId);
3184
3296
  if (verification._tag === "mismatch") return yield* LedgerError.make({
3185
- operation: "joinSettledChildForClosedParent",
3297
+ operation: "joinSettledChildWithoutHandler",
3186
3298
  message: `Child Settlement verification failed for Tool Call ${toolCallId}: ${verification.message}`
3187
3299
  });
3188
3300
  const verified = verification.value;
3189
- 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
+ } : {
3190
3307
  errorTag: reason === "aborted" ? "SubagentParentAborted" : "SubagentParentDurationExceeded",
3191
3308
  message: boundedText(`The parent Submission ${reason === "aborted" ? "aborted" : "exceeded its duration"}; attached child ${childSubmissionId} settled ${verified.outcome}`)
3192
3309
  }).pipe(Effect.orDie);
3193
3310
  finalAccounting = yield* decodePersisted({
3194
- basis: reason === "aborted" ? "aborted-conservative" : "duration-conservative",
3311
+ basis: `${reason}-conservative`,
3195
3312
  allocation: reservation.allocation
3196
3313
  }).pipe(Effect.orDie);
3197
3314
  const childRunId = runIdForSubmission(childSubmissionId);
@@ -3262,25 +3379,51 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3262
3379
  return open;
3263
3380
  });
3264
3381
  /** Release unused reservations or repair and join existing children, never admit new work. */
3265
- const reconcileExpiredChildren = Effect.fn("DurableAgentRuntime.reconcileExpiredChildren")(function* (ctx, parent, ownershipToken) {
3382
+ const reconcileRetainedChildren = Effect.fn("DurableAgentRuntime.reconcileRetainedChildren")(function* (ctx, parent, ownershipToken, unavailableCalls) {
3266
3383
  const snapshot = yield* ledger.loadRecoverySnapshot(RecoverySnapshotRequest.make({ submissionId: parent.submissionId }));
3267
3384
  const records = yield* readControl(parent.threadId, [parent.submissionId]);
3268
3385
  const runId = runIdForSubmission(parent.submissionId);
3269
3386
  const pending = yield* pendingToolBatchFor(records, runId);
3270
3387
  const knownIds = knownRecordIdsOf(records);
3271
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
+ }
3272
3400
  for (const reservation of snapshot.childReservations) {
3273
3401
  const toolCallId = reservation.parentToolCallId;
3274
- 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
+ }
3275
3417
  let started = subagent.started.get(toolCallId);
3276
3418
  const request = subagent.requested.get(toolCallId);
3277
3419
  if (started === void 0 && request === void 0 && reservation.childSubmissionId === void 0) {
3278
3420
  yield* releaseOrphanReservation(reservation.reservationId);
3421
+ notExecuted.add(toolCallId);
3279
3422
  continue;
3280
3423
  }
3281
3424
  const call = pending?.calls.find((candidate) => candidate.id === toolCallId);
3282
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({
3283
- operation: "reconcileExpiredChildren",
3426
+ operation: "reconcileRetainedChildren",
3284
3427
  message: `Tool Call ${toolCallId} has inconsistent canonical child request evidence`
3285
3428
  });
3286
3429
  if (started === void 0) {
@@ -3289,17 +3432,26 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3289
3432
  principal: decodePrincipalSync(request.childPrincipal),
3290
3433
  idempotencyKey: decodeIdempotencyKeySync(request.childIdempotencyKey)
3291
3434
  }));
3292
- if (resolution._tag === "Indeterminate") continue;
3435
+ if (resolution._tag === "Indeterminate") {
3436
+ indeterminate.add(toolCallId);
3437
+ continue;
3438
+ }
3293
3439
  if (resolution._tag === "NotAdmitted") {
3294
3440
  if (reservation.childSubmissionId !== void 0) return yield* LedgerError.make({
3295
- operation: "reconcileExpiredChildren",
3441
+ operation: "reconcileRetainedChildren",
3296
3442
  message: `Tool Call ${toolCallId} has an attachment but no admitted child`
3297
3443
  });
3298
- 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);
3299
3453
  continue;
3300
3454
  }
3301
- const admission = yield* establishChildFromRequest(parent, request, false);
3302
- if (admission._tag === "indeterminate") continue;
3303
3455
  started = SubagentStarted.make({
3304
3456
  runId,
3305
3457
  toolCallId,
@@ -3321,7 +3473,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3321
3473
  }
3322
3474
  const child = yield* ledger.lookup(SubmissionLookupById.make({ submissionId: started.childSubmissionId }));
3323
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({
3324
- operation: "reconcileExpiredChildren",
3476
+ operation: "reconcileRetainedChildren",
3325
3477
  message: `Tool Call ${call.id} has inconsistent canonical child attachment evidence`
3326
3478
  });
3327
3479
  yield* ledger.attachChildToReservation(AttachChildToReservationRequest.make({
@@ -3329,9 +3481,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3329
3481
  ownershipToken,
3330
3482
  childSubmissionId: started.childSubmissionId
3331
3483
  })).pipe(Effect.catchTag("ChildReservationConflict", conflictToLedgerError("attachChildToReservation")));
3332
- if (child.value.state !== "settled") continue;
3333
- 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");
3334
3493
  }
3494
+ return {
3495
+ notExecuted,
3496
+ waiting,
3497
+ indeterminate
3498
+ };
3335
3499
  });
3336
3500
  /**
3337
3501
  * Request-abort-and-join over every attached child of an aborting parent (spec §13.1,
@@ -3385,7 +3549,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3385
3549
  message: `Attached child ${childSubmissionId} is unknown to the ledger`
3386
3550
  });
3387
3551
  if (child.value.state === "settled") {
3388
- yield* joinSettledChildForClosedParent(ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, "aborted");
3552
+ yield* joinSettledChildWithoutHandler(ctx, parent, knownIds, subagent, reservation, toolCallId, childSubmissionId, "aborted");
3389
3553
  continue;
3390
3554
  }
3391
3555
  yield* ledger.requestAbort(AbortCommand.make({
@@ -3613,14 +3777,20 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3613
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" });
3614
3778
  const pending = yield* pendingToolBatchFor(records, runId, agent.definition.completionFromTools?.map((declaration) => declaration.tool));
3615
3779
  const resumeProjection = pending === void 0 ? journal : yield* projectRunJournalStream(withoutPendingBatch(canonical, pending, runId), runId, void 0, journalSeed);
3616
- const tools = agent.definition.toolkit.tools;
3617
- for (const envelope of records) {
3618
- const payload = envelope.record.payload;
3619
- if (payload._tag !== "ToolCallPrepared" || payload.runId !== runId) continue;
3620
- const tool = tools[payload.toolName];
3621
- const kind = tool === void 0 ? "ordinary" : getToolExecutionKind(tool.annotations);
3622
- if (tool === void 0 || (payload.executionKind ?? "ordinary") !== kind) return yield* RunJournalError.make({ message: `Prepared Tool ${payload.toolCallId} classification conflicts with the resolved binding` });
3623
- }
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;
3624
3794
  const knownIds = knownRecordIdsOf(records);
3625
3795
  const stepOutputs = /* @__PURE__ */ new Map();
3626
3796
  for (const envelope of records) {
@@ -3740,51 +3910,12 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3740
3910
  const response = [...records].reverse().find((envelope) => envelope.record.payload._tag === "ModelResponseRecorded" && envelope.record.payload.runId === runId)?.record.payload;
3741
3911
  if (response?._tag !== "ModelResponseRecorded") return yield* RunJournalError.make({ message: `Run ${runId} has a terminal completion marker without a response` });
3742
3912
  const calls = (yield* declaredToolCalls(response.messages)).application;
3743
- let expectedOutput;
3744
3913
  if (calls.length > 0) {
3745
- const completion = agent.definition.completion;
3746
3914
  const call = calls[0];
3747
- const actionCompletion = agent.definition.completionFromTools?.find((declaration) => declaration.tool === call?.name);
3748
- 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;
3749
- 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` });
3750
- 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({
3751
- message: `Run ${runId} completion Tool output failed recovery projection`,
3752
- cause
3753
- })));
3754
- if (Option.isNone(projected)) return yield* RunJournalError.make({ message: `Run ${runId} completion Tool declined its recorded terminal output` });
3755
- expectedOutput = projected.value;
3756
- } else {
3757
- const responseText = yield* terminalAssistantText(response.messages);
3758
- expectedOutput = yield* decodeFinalOutput(agent, responseText).pipe(Effect.mapError((cause) => RunJournalError.make({
3759
- message: `Run ${runId} final response output failed recovery decoding`,
3760
- cause
3761
- })));
3762
- }
3763
- const boundedExpectedOutput = yield* decodePersisted(expectedOutput.encoded).pipe(Effect.mapError((cause) => RunJournalError.make({
3764
- message: `Run ${runId} reconstructed output exceeds canonical persistence bounds`,
3765
- cause
3766
- })));
3767
- const [expectedOutputDigest, recordedOutputDigest] = yield* Effect.all([withCrypto(digestJson(boundedExpectedOutput)), withCrypto(digestJson(recordedCompletion.output))]).pipe(Effect.mapError((cause) => RunJournalError.make({
3768
- message: `Run ${runId} completion output comparison failed`,
3769
- cause
3770
- })));
3771
- if (expectedOutputDigest !== recordedOutputDigest) return yield* RunJournalError.make({ message: `Run ${runId} terminal completion output disagrees with its canonical response or Tool result` });
3772
- const expectedRunDisposition = recordedCompletion.finishReason === "budget-exhausted" ? void 0 : yield* encodeRunDisposition(agent, expectedOutput.decoded).pipe(Effect.mapError((cause) => RunJournalError.make({
3773
- message: `Run ${runId} run disposition failed recovery projection`,
3774
- cause
3775
- })));
3776
- if (expectedRunDisposition === void 0 !== (recordedCompletion.runDisposition === void 0)) return yield* RunJournalError.make({ message: `Run ${runId} terminal run disposition disagrees with its reconstructed output` });
3777
- if (expectedRunDisposition !== void 0 && recordedCompletion.runDisposition !== void 0) {
3778
- const boundedExpectedRunDisposition = yield* decodePersisted(expectedRunDisposition).pipe(Effect.mapError((cause) => RunJournalError.make({
3779
- message: `Run ${runId} reconstructed run disposition exceeds canonical persistence bounds`,
3780
- cause
3781
- })));
3782
- const [expectedRunDispositionDigest, recordedRunDispositionDigest] = yield* Effect.all([withCrypto(digestJson(boundedExpectedRunDisposition)), withCrypto(digestJson(recordedCompletion.runDisposition))]).pipe(Effect.mapError((cause) => RunJournalError.make({
3783
- message: `Run ${runId} run disposition comparison failed`,
3784
- cause
3785
- })));
3786
- 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` });
3787
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" });
3788
3919
  return {
3789
3920
  _tag: "completed",
3790
3921
  result: recordedCompletion.output,
@@ -3822,7 +3953,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3822
3953
  cause
3823
3954
  })));
3824
3955
  const firstAssistant = pendingMessages.content.findIndex((message) => message.role === "assistant");
3825
- 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"));
3826
3957
  if (firstAssistant >= 0) resumeResponseMessages = Prompt.fromMessages(pendingMessages.content.slice(firstAssistant));
3827
3958
  }
3828
3959
  let currentToolTurn = pending === void 0 ? void 0 : {
@@ -3859,7 +3990,24 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3859
3990
  ...state,
3860
3991
  history
3861
3992
  });
3862
- 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)]) }))) };
3863
4011
  const externalContext = runContextPreparation.hook;
3864
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({
3865
4013
  ...request,
@@ -3904,6 +4052,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3904
4052
  const createdAt = yield* nowUtc;
3905
4053
  const batch = yield* withCrypto(turnResponseBatch({
3906
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
+ })),
3907
4062
  toolParameterRejections: commit.toolParameterRejections,
3908
4063
  runId,
3909
4064
  turn: canonicalTurn,
@@ -3942,7 +4097,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
3942
4097
  toolName: call.toolName,
3943
4098
  parameters,
3944
4099
  parametersDigest,
3945
- executionKind: call.executionKind
4100
+ executionKind: call.executionKind,
4101
+ executionClass: call.executionClass,
4102
+ replay: currentContracts[call.toolName]
3946
4103
  })));
3947
4104
  }
3948
4105
  const head = preparedRecords[0];
@@ -4010,14 +4167,16 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4010
4167
  }
4011
4168
  const coverable = sourceBoundaries.filter((boundary) => commit.kind !== "summarize" || ownerFirstSequence === void 0 || boundary.sequence < ownerFirstSequence);
4012
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);
4013
4172
  let matchingPrefix = 0;
4014
4173
  let requiredThrough = 0;
4015
- const comparisonLimit = Math.min(sourceJournal.prompt.content.length, commit.through);
4174
+ const comparisonLimit = Math.min(comparisonView.messages.length, commit.through);
4016
4175
  for (let index = 0; index < commit.through; index += 1) {
4017
4176
  const message = commit.source.content[index];
4018
4177
  if (message !== void 0 && (commit.kind === "clear-tool-results" ? message.role === "tool" : message.role !== "system")) requiredThrough = index + 1;
4019
4178
  if (index >= comparisonLimit || index !== matchingPrefix || message === void 0) continue;
4020
- const canonicalMessage = sourceJournal.prompt.content[index];
4179
+ const canonicalMessage = comparisonView.messages[index];
4021
4180
  if (canonicalMessage === void 0) continue;
4022
4181
  const encode = (entry) => Schema.encodeEffect(Prompt.Message)(entry).pipe(Effect.mapError((cause) => CompactionError.make({
4023
4182
  message: "Could not encode the canonical compaction prefix",
@@ -4033,7 +4192,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4033
4192
  if (candidate === void 0) continue;
4034
4193
  const following = coverable[index + 1];
4035
4194
  if (following !== void 0 && following.tag !== "ModelResponseRecorded") continue;
4036
- const length = candidate.promptLength;
4195
+ const length = comparisonView.prefixLength(candidate.promptLength);
4037
4196
  if (length === 0 || length > commit.through) continue;
4038
4197
  if (length < requiredThrough || length > matchingPrefix) continue;
4039
4198
  lastCovered = candidate;
@@ -4576,6 +4735,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4576
4735
  history: pending === void 0 ? journal.historyBefore : resumeProjection.historyBefore,
4577
4736
  onHistory,
4578
4737
  input,
4738
+ ...journal.committedTurns > 0 || Schema.is(FrameworkMessage)(submission.messageAdmission) ? { retainedInput: submission.inputPayload } : {},
4579
4739
  ...Schema.is(FrameworkMessage)(submission.messageAdmission) ? { frameworkMessage: submission.messageAdmission } : {},
4580
4740
  approval,
4581
4741
  toolAuthorization,
@@ -4591,12 +4751,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4591
4751
  ...submission.workerAdmission === void 0 ? {} : { toolCallAllowance: submission.workerAdmission.origin.toolCallAllowance },
4592
4752
  ...submission.parentLinkage !== void 0 && childLineage?._tag === "SubagentLineageRecorded" ? { toolCallAllowance: childLineage.toolCallAllowance } : {},
4593
4753
  ...pending === void 0 ? {} : { resume: {
4754
+ ...settledCompletion === void 0 ? {} : { settledCompletion },
4594
4755
  turn: pending.turn,
4595
4756
  turnId: pending.turnId,
4596
4757
  calls: pending.calls,
4597
4758
  toolParameterRejections: pending.toolParameterRejections,
4598
4759
  settled: pending.settled,
4599
- ...pending.toolExposure === void 0 ? {} : { toolExposure: pending.toolExposure },
4760
+ toolExposure: pending.toolExposure ?? Snapshot.make({ exposedToolNames: [...new Set(pending.calls.map((call) => call.name))] }),
4600
4761
  ...resumeLeadingMessages === void 0 ? {} : { leadingMessages: resumeLeadingMessages },
4601
4762
  ...resumeResponseMessages === void 0 ? {} : { responseMessages: resumeResponseMessages }
4602
4763
  } },
@@ -4618,7 +4779,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4618
4779
  },
4619
4780
  ...journal.contextWindowId === void 0 ? {} : { initialContextWindowId: journal.contextWindowId },
4620
4781
  ...journal.protectedContext === void 0 ? {} : { protectedContext: journal.protectedContext },
4621
- ...journal.pendingContextToolCallId === void 0 ? {} : { pendingContextToolCallId: journal.pendingContextToolCallId }
4782
+ ...pendingContextToolCallId === void 0 ? {} : { pendingContextToolCallId }
4622
4783
  };
4623
4784
  const commitPendingTurn = Effect.gen(function* () {
4624
4785
  const state = yield* Ref.get(stateRef);
@@ -4664,7 +4825,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4664
4825
  const completion = agent.definition.completion;
4665
4826
  const completionPart = resultParts[0];
4666
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;
4667
- const batch = yield* turnResultsBatch({
4828
+ const batch = yield* withCrypto(turnResultsBatch({
4668
4829
  toolSelections: stagedToolSelections,
4669
4830
  budgetRejectedCalls,
4670
4831
  runId,
@@ -4675,7 +4836,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4675
4836
  deploymentId: config.deploymentId,
4676
4837
  createdAt,
4677
4838
  ...runCompletion === void 0 ? {} : { runCompletion }
4678
- });
4839
+ }));
4679
4840
  let pendingBatch = batch;
4680
4841
  if (settledCompletionPart !== void 0) {
4681
4842
  const completionRecord = batch.records.find((record) => record.payload._tag === "RunCompleted");
@@ -4946,7 +5107,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
4946
5107
  const ctx = yield* attemptContextFor(threadId, claim.producerEpoch);
4947
5108
  let controlThrough = (yield* store.inspectTail(ThreadTailRequest.make({ threadId }))).tailSequence;
4948
5109
  const initialThrough = controlThrough;
4949
- const initialView = yield* recoveryView(threadId, initialThrough, [submissionId]);
5110
+ const initialView = yield* recoveryView(threadId, initialThrough, [submissionId], runIdForSubmission(submissionId));
4950
5111
  let journalMetadata = initialView.seed === void 0 ? makeJournalMetadata(runIdForSubmission(submissionId)) : void 0;
4951
5112
  const retainControl = controlRecords([submissionId]);
4952
5113
  const collectControl = (record) => {
@@ -5005,7 +5166,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5005
5166
  };
5006
5167
  let expiredChildObligation = false;
5007
5168
  if ((yield* Clock.currentTimeMillis) >= DateTime.toEpochMillis(runTiming.deadline)) {
5008
- yield* reconcileExpiredChildren(ctx, submission, yield* Ref.get(tokenRef));
5169
+ yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef));
5009
5170
  expiredChildObligation = yield* completeJoinedReleases(submission);
5010
5171
  }
5011
5172
  const ordinaryCallsResolved = Effect.gen(function* () {
@@ -5014,10 +5175,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5014
5175
  const reconciliationEvidence = yield* evidenceFor(reconciliationRecords, submissionId, true, reconciliationSnapshot.hostSubmissionId);
5015
5176
  if (reconciliationEvidence.openToolCalls.length === 0) return true;
5016
5177
  for (const envelope of reconciliationRecords) knownIds.add(envelope.record.recordId);
5017
- const tools = agent.definition.toolkit.tools;
5018
- const review = yield* reconcileOpenCalls(ctx, submission, reconciliationSnapshot, reconciliationRecords, reconciliationEvidence.openToolCalls, knownIds, (toolName) => {
5019
- const tool = tools[toolName];
5020
- 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)
5021
5181
  });
5022
5182
  if (review.uncertain.length > 0 || review.unproven.length > 0) {
5023
5183
  if (review.uncertain.length > 0) yield* markCallsUnknown(ctx, submissionId, knownIds, review.uncertain, "An ordinary Tool call may have executed without a canonical outcome");
@@ -5031,6 +5191,74 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5031
5191
  return true;
5032
5192
  });
5033
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
+ }
5034
5262
  const lineage = {
5035
5263
  attemptId: claim.attemptId,
5036
5264
  supersededEpoch,
@@ -5099,7 +5327,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5099
5327
  }));
5100
5328
  yield* hit("run:after-duration-append");
5101
5329
  }
5102
- yield* reconcileExpiredChildren(ctx, submission, yield* Ref.get(tokenRef));
5330
+ yield* reconcileRetainedChildren(ctx, submission, yield* Ref.get(tokenRef));
5103
5331
  }
5104
5332
  if (yield* completeJoinedReleases(submission)) {
5105
5333
  if (outcome._tag === "failed") {
@@ -5168,24 +5396,24 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5168
5396
  const resolution = yield* resolve(submission).pipe(Effect.map((binding) => ({
5169
5397
  _tag: "resolved",
5170
5398
  binding
5171
- })), Effect.catchTags({
5172
- BindingUnavailable: (failure) => Effect.succeed({
5173
- _tag: "refused",
5174
- failure
5175
- }),
5176
- BindingDigestMismatch: (failure) => Effect.succeed({
5177
- _tag: "refused",
5178
- failure
5179
- })
5180
- }));
5399
+ })), Effect.catchTags({ BindingUnavailable: (failure) => Effect.succeed({
5400
+ _tag: "refused",
5401
+ failure
5402
+ }) }));
5181
5403
  if (resolution._tag === "refused") return yield* resolution.failure;
5182
5404
  return yield* resolution.binding.attempt((agent, attemptThreadId, attemptClaim) => runAttempt(agent, attemptThreadId, attemptClaim, tokenRef, options?.yieldAfter), threadId, claim);
5183
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)))));
5184
5407
  const drainThread = (resolve, threadId) => Effect.gen(function* () {
5185
5408
  const settlements = [];
5186
5409
  while (true) {
5410
+ const before = yield* eligibleThreadHead(threadId);
5187
5411
  const settlement = yield* processThreadHead(resolve, threadId);
5188
- 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
+ }
5189
5417
  settlements.push(settlement.value);
5190
5418
  }
5191
5419
  });
@@ -5196,25 +5424,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5196
5424
  message: `This worker is bound to Agent ${binding.agentId}; the claimed head belongs to Agent ${submission.agentId} (SUB-023)`
5197
5425
  })), threadId);
5198
5426
  });
5199
- const resolveRetainedBinding = Effect.fn("DurableAgentRuntime.resolveRetainedBinding")(function* (submission) {
5200
- const exact = registeredBindings.find((binding) => binding.agentId === submission.agentId && definitionDigestsEqual(binding.digests, submission.agentDigests));
5201
- if (exact !== void 0) return exact;
5202
- const candidates = registeredBindings.filter((binding) => binding.agentId === submission.agentId);
5203
- const candidate = candidates[0];
5204
- if (candidate === void 0 || candidates.length !== 1) return yield* resolveWorkerBinding(registeredBindings, submission.agentId, submission.agentDigests);
5205
- if (bindingSupports(candidate, submission.agentDigests)) return candidate;
5206
- const records = yield* Effect.gen(function* () {
5207
- const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId: submission.threadId }));
5208
- return yield* Stream.runCollect(canonicalRange(submission.threadId, tail.tailSequence).pipe(Stream.filter(controlRecords([submission.submissionId]))));
5209
- }).pipe(Effect.catchTag("ThreadNotMaterialized", () => Effect.succeed([])));
5210
- const completionTools = candidate === void 0 ? [] : [...candidate.definition.completion === void 0 ? [] : [candidate.definition.completion.tool], ...candidate.definition.completionFromTools?.map((entry) => entry.tool) ?? []];
5211
- const pending = yield* pendingToolBatchFor(records, runIdForSubmission(submission.submissionId), completionTools);
5212
- return yield* resolveWorkerBinding(registeredBindings, submission.agentId, submission.agentDigests, pending?.calls.filter((call) => call.providerExecuted !== true || completionTools.includes(call.name)).map((call) => call.name) ?? []);
5213
- });
5214
- const processThreadResolvedImpl = (threadId) => drainThread(resolveRetainedBinding, threadId);
5215
- const processThreadHeadImpl = (threadId, options) => processThreadHead(resolveRetainedBinding, threadId, options);
5216
- const claimFor = Effect.fn("DurableAgentRuntime.claimFor")(function* (submission) {
5217
- 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);
5218
5432
  if (Option.isNone(head) || head.value.submissionId !== submission.submissionId) return Option.none();
5219
5433
  const claimed = yield* acquireClaim(submission.threadId);
5220
5434
  if (Option.isNone(claimed)) return Option.none();
@@ -5225,35 +5439,40 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5225
5439
  })).pipe(Effect.catchTag("OwnershipLost", () => Effect.void));
5226
5440
  return Option.none();
5227
5441
  }
5442
+ yield* annotateRepair(submission.threadId, decision, claimed.value.claim.producerEpoch);
5228
5443
  return Option.some(claimed.value.claim);
5229
5444
  });
5230
5445
  /**
5231
- * DUR-013 audit: one deterministic `RepairAnnotated` record per executed (submission, decision)
5232
- * pair. Conflicts mean the annotation is already canonical (or a newer epoch owns the lane), so
5233
- * 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.
5234
5449
  */
5235
- 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;
5236
5457
  const encodedDecision = yield* Schema.encodeEffect(RecoveryDecision)(decision).pipe(Effect.orDie);
5237
5458
  const details = yield* Schema.decodeEffect(PersistedJson)(encodedDecision).pipe(Effect.orDie);
5238
- const envelope = yield* makeEnvelope(recoveryRepairRecordId(submissionId, decision._tag), RepairAnnotated.make({
5459
+ const envelope = yield* makeEnvelope(recoveryRepairRecordId(decision.submissionId, decision._tag), RepairAnnotated.make({
5239
5460
  reason: `recovery:${decision._tag}`,
5240
5461
  details
5241
5462
  }));
5242
- const tail = yield* store.inspectTail(ThreadTailRequest.make({ threadId }));
5243
5463
  yield* store.append(FencedAppendRequest.make({
5244
5464
  threadId,
5245
5465
  batch: CanonicalBatch.make({
5246
- batchId: recoveryRepairBatchId(submissionId, decision._tag),
5466
+ batchId: recoveryRepairBatchId(decision.submissionId, decision._tag),
5247
5467
  producerId: config.producerId,
5248
5468
  records: [envelope]
5249
5469
  }),
5250
5470
  expectedTailSequence: tail.tailSequence,
5251
5471
  expectedTailDigest: tail.tailDigest,
5252
- producerEpoch: tail.producerEpoch
5472
+ producerEpoch
5253
5473
  }));
5254
- yield* wake.notify(threadId);
5255
5474
  }).pipe(Effect.ignore);
5256
- const settleAbortedForRecovery = Effect.fn("DurableAgentRuntime.settleAbortedForRecovery")(function* (snapshot, evidence, records, _decision) {
5475
+ const settleAbortedForRecovery = Effect.fn("DurableAgentRuntime.settleAbortedForRecovery")(function* (snapshot, evidence, records, decision) {
5257
5476
  const intent = snapshot.abortIntent;
5258
5477
  if (intent === void 0) return "deferred";
5259
5478
  const submission = snapshot.submission;
@@ -5280,7 +5499,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5280
5499
  }
5281
5500
  }
5282
5501
  }
5283
- const claimed = yield* claimFor(submission);
5502
+ const claimed = yield* claimFor(submission, decision);
5284
5503
  if (Option.isNone(claimed)) {
5285
5504
  if (submission.state === "ready" && snapshot.ownership === void 0) return yield* Effect.gen(function* () {
5286
5505
  yield* materializeAtLeast(submission.threadId, ZERO_EPOCH);
@@ -5308,15 +5527,15 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5308
5527
  return "repaired";
5309
5528
  });
5310
5529
  /**
5311
- * Execute the reconcile-then-mark flow for a `MarkUnknown` decision (plan §2.2). The recovery
5312
- * pass has no Agent Binding, so no execution-class annotation is visible here — every call
5313
- * without a durable intent or reconciler proof stays fail-closed uncertain. A lane whose open
5314
- * calls all closed reports `repaired`; provably-retryable calls defer to a worker's batch
5315
- * 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.
5316
5535
  */
5317
5536
  const markUnknownForRecovery = Effect.fn("DurableAgentRuntime.markUnknownForRecovery")(function* (snapshot, evidence, records, decision) {
5318
5537
  const submission = snapshot.submission;
5319
- const claimed = yield* claimFor(submission);
5538
+ const claimed = yield* claimFor(submission, decision);
5320
5539
  if (Option.isNone(claimed)) return "deferred";
5321
5540
  const claim = claimed.value;
5322
5541
  yield* store.materialize(ThreadMaterialization.make({
@@ -5326,7 +5545,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5326
5545
  const ctx = yield* attemptContextFor(submission.threadId, claim.producerEpoch);
5327
5546
  const knownIds = knownRecordIdsOf(records);
5328
5547
  const markableIds = new Set(decision.openToolCallIds);
5329
- 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));
5330
5549
  let disposition;
5331
5550
  if (review.uncertain.length > 0) {
5332
5551
  yield* markCallsUnknown(ctx, submission.submissionId, knownIds, review.uncertain, decision.reason);
@@ -5339,17 +5558,25 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5339
5558
  })).pipe(Effect.catchTag("OwnershipLost", () => Effect.void));
5340
5559
  return disposition;
5341
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
+ });
5342
5573
  /**
5343
- * Apply covering DUR-017 resolution intents canonically and wake the lane. The `unknown` head
5344
- * is never claimable, so the appends run unfenced at the current tail (no live owner can
5345
- * exist); the wake itself is the ledger's idempotent re-check on intent replay (WP2), so a
5346
- * 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.
5347
5576
  */
5348
- 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";
5349
5579
  const submission = snapshot.submission;
5350
- const ctx = yield* attemptContextAtTail(submission.threadId);
5351
- const knownIds = knownRecordIdsOf(records);
5352
- if (!(yield* reconcileOpenCalls(ctx, submission, snapshot, records, evidence.openToolCalls, knownIds, () => void 0).pipe(Effect.as(true), Effect.catchTag("FenceRejected", () => Effect.succeed(false))))) return "deferred";
5353
5580
  for (const intent of snapshot.unknownResolutions) yield* ledger.recordUnknownResolution(UnknownResolutionCommand.make({
5354
5581
  submissionId: intent.submissionId,
5355
5582
  toolCallId: intent.toolCallId,
@@ -5374,14 +5601,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5374
5601
  * permit, and stays `deferred`. Decisions that raced ahead of the repair leave the lane to a
5375
5602
  * worker's batch resume (`deferred`).
5376
5603
  */
5377
- const awaitApprovalForRecovery = Effect.fn("DurableAgentRuntime.awaitApprovalForRecovery")(function* (snapshot, evidence) {
5604
+ const awaitApprovalForRecovery = Effect.fn("DurableAgentRuntime.awaitApprovalForRecovery")(function* (snapshot, evidence, decision) {
5378
5605
  const submission = snapshot.submission;
5379
5606
  if (submission.state === "suspended") return "deferred";
5380
5607
  const decided = new Set(snapshot.approvalDecisions.map((decision) => decision.toolCallId));
5381
5608
  const undecided = evidence.approvalsPending.filter((pending) => !decided.has(pending.toolCallId));
5382
5609
  const first = undecided[0];
5383
5610
  if (first === void 0) return "deferred";
5384
- const claimed = yield* claimFor(submission);
5611
+ const claimed = yield* claimFor(submission, decision);
5385
5612
  if (Option.isNone(claimed)) return "deferred";
5386
5613
  const claim = claimed.value;
5387
5614
  const outcome = yield* ledger.suspend(SuspendRequest.make({
@@ -5416,7 +5643,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5416
5643
  const runId = runIdForSubmission(submission.submissionId);
5417
5644
  const start = yield* canonicalRunStartFromRecords(records, runId);
5418
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"))) {
5419
- const claimed = yield* claimFor(submission);
5646
+ const claimed = yield* claimFor(submission, decision);
5420
5647
  if (Option.isNone(claimed)) return "deferred";
5421
5648
  const claim = claimed.value;
5422
5649
  yield* store.materialize(ThreadMaterialization.make({
@@ -5424,7 +5651,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5424
5651
  producerEpoch: claim.producerEpoch
5425
5652
  }));
5426
5653
  const ctx = yield* attemptContextFor(submission.threadId, claim.producerEpoch);
5427
- yield* reconcileExpiredChildren(ctx, submission, claim.ownershipToken);
5654
+ yield* reconcileRetainedChildren(ctx, submission, claim.ownershipToken);
5428
5655
  const open = yield* completeJoinedReleases(submission);
5429
5656
  yield* ledger.releaseOwnership(ReleaseOwnershipRequest.make({
5430
5657
  submissionId: submission.submissionId,
@@ -5439,7 +5666,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5439
5666
  case "MarkUnknown": return yield* markUnknownForRecovery(snapshot, evidence, records, decision);
5440
5667
  case "ApplyUnknownResolutions": return yield* applyUnknownResolutionsForRecovery(snapshot, evidence, records);
5441
5668
  case "AwaitUnknownResolution": return "unknown";
5442
- case "AwaitApprovalDecision": return yield* awaitApprovalForRecovery(snapshot, evidence);
5669
+ case "AwaitApprovalDecision": return yield* awaitApprovalForRecovery(snapshot, evidence, decision);
5443
5670
  case "ResumeSuspended": return yield* resumeSuspendedForRecovery(snapshot);
5444
5671
  case "RevertJoining":
5445
5672
  yield* ledger.revertJoining(RevertJoiningRequest.make({ submissionId: submission.submissionId }));
@@ -5460,7 +5687,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5460
5687
  }
5461
5688
  const host = yield* ledger.lookup(SubmissionLookupById.make({ submissionId: hostSubmissionId }));
5462
5689
  if (Option.isNone(host)) return "deferred";
5463
- const claimed = yield* claimFor(host.value);
5690
+ const claimed = yield* claimFor(host.value, decision);
5464
5691
  if (Option.isNone(claimed)) return "deferred";
5465
5692
  const claim = claimed.value;
5466
5693
  yield* ledger.markJoined(MarkJoinedRequest.make({
@@ -5499,7 +5726,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5499
5726
  return "repaired";
5500
5727
  case "ApplyInput":
5501
5728
  case "RepairInputMarker": {
5502
- const claimed = yield* claimFor(submission);
5729
+ const claimed = yield* claimFor(submission, decision);
5503
5730
  if (Option.isNone(claimed)) return "deferred";
5504
5731
  const claim = claimed.value;
5505
5732
  yield* store.materialize(ThreadMaterialization.make({
@@ -5521,7 +5748,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5521
5748
  case "AppendReservedSettlement": {
5522
5749
  const reservation = snapshot.reservation;
5523
5750
  if (reservation === void 0) return "deferred";
5524
- const claimed = yield* claimFor(submission);
5751
+ const claimed = yield* claimFor(submission, decision);
5525
5752
  if (Option.isNone(claimed)) {
5526
5753
  if (reservation.outcome === "aborted" && snapshot.abortIntent !== void 0 && snapshot.ownership === void 0) {
5527
5754
  const ctx = yield* attemptContextAtTail(submission.threadId);
@@ -5558,7 +5785,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5558
5785
  if (requestedPayload === void 0) return "deferred";
5559
5786
  const admission = yield* establishChildFromRequest(submission, requestedPayload);
5560
5787
  if (admission._tag === "indeterminate") return "deferred";
5561
- const claimed = yield* claimFor(submission);
5788
+ const claimed = yield* claimFor(submission, decision);
5562
5789
  if (Option.isNone(claimed)) return "deferred";
5563
5790
  const claim = claimed.value;
5564
5791
  yield* store.materialize(ThreadMaterialization.make({
@@ -5598,7 +5825,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5598
5825
  return "repaired";
5599
5826
  }
5600
5827
  case "EnsureWaitingForChild": {
5601
- const claimed = yield* claimFor(submission);
5828
+ const claimed = yield* claimFor(submission, decision);
5602
5829
  if (Option.isNone(claimed)) return "deferred";
5603
5830
  const claim = claimed.value;
5604
5831
  const suspension = yield* ledger.suspend(SuspendRequest.make({
@@ -5653,7 +5880,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5653
5880
  if (Option.isSome(childRow)) yield* wake.notify(childRow.value.threadId);
5654
5881
  }
5655
5882
  if (submission.state !== "suspended") {
5656
- const claimed = yield* claimFor(submission);
5883
+ const claimed = yield* claimFor(submission, decision);
5657
5884
  if (Option.isNone(claimed)) return "deferred";
5658
5885
  const claim = claimed.value;
5659
5886
  const suspension = yield* ledger.suspend(SuspendRequest.make({
@@ -5697,7 +5924,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5697
5924
  const evidence = yield* evidenceFor(history.records, submission.submissionId, history.materialized, snapshot.hostSubmissionId);
5698
5925
  const decision = classifyRecovery(snapshot, evidence);
5699
5926
  const disposition = decision._tag === "ApplyInput" && snapshot.submission.state === "ready" ? "deferred" : yield* Effect.scoped(executeRecoveryDecision(snapshot, evidence, decision, history.records, history));
5700
- if (disposition === "repaired") yield* annotateRepair(submission.threadId, submission.submissionId, decision);
5701
5927
  return RecoveryReport.make({
5702
5928
  submissionId: submission.submissionId,
5703
5929
  threadId: submission.threadId,
@@ -5754,10 +5980,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5754
5980
  reason: cause.reason === "storage" || cause.reason === "unavailable" ? "unavailable" : "refused",
5755
5981
  code: `message-${cause.reason}`
5756
5982
  })));
5757
- 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({
5758
- ...retained.digests,
5759
- replay: retained.replay
5760
- }), 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))) {
5761
5984
  const retained = yield* ledger.lookup(SubmissionLookupByKey.make({
5762
5985
  threadId: options.threadId,
5763
5986
  principal: options.principal,
@@ -5976,7 +6199,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
5976
6199
  const nowMillis = yield* Clock.currentTimeMillis;
5977
6200
  const explainedAt = yield* nowUtc;
5978
6201
  const runId = runIdForSubmission(submission.submissionId);
5979
- 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] : []));
5980
6203
  const unknownCalls = [];
5981
6204
  for (const envelope of read.records) {
5982
6205
  const payload = envelope.record.payload;
@@ -6009,6 +6232,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6009
6232
  inputRecorded: evidence.inputRecorded,
6010
6233
  abortRecorded: evidence.abortRecorded,
6011
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] : []),
6012
6236
  openDelegationCalls: evidence.openDelegationCalls,
6013
6237
  approvalsPending: evidence.approvalsPending,
6014
6238
  unknownCalls,
@@ -6023,7 +6247,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6023
6247
  }),
6024
6248
  decision,
6025
6249
  decisionMeaning: recoveryDecisionMeaning(decision._tag),
6026
- 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),
6027
6254
  explainedAt
6028
6255
  });
6029
6256
  });
@@ -6101,9 +6328,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6101
6328
  });
6102
6329
  /**
6103
6330
  * Safe re-drive of exactly one Submission's recovery decision (plan §3): classify, execute
6104
- * the ONE repair the classifier names, append the deterministic `RepairAnnotated` audit when
6105
- * it repairs (DUR-013 — retry adds no new record type), and wake the lane. Typed refusals
6106
- * 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
6107
6334
  * on Unknown Outcomes (`resolveUnknown`, DUR-017), and lanes awaiting approval decisions
6108
6335
  * (`resolveApproval`). The mandatory `author`/`reason` audit fields (SEC-011) annotate the
6109
6336
  * structured operator log.
@@ -6128,7 +6355,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6128
6355
  submissionId: command.submissionId,
6129
6356
  refusal: "await-unknown-resolution",
6130
6357
  decisionTag: decision._tag,
6131
- 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."
6132
6359
  });
6133
6360
  if (decision._tag === "AwaitApprovalDecision") return yield* RetryRefused.make({
6134
6361
  submissionId: command.submissionId,
@@ -6144,7 +6371,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6144
6371
  decision: decision._tag
6145
6372
  }));
6146
6373
  const disposition = yield* Effect.scoped(executeRecoveryDecision(snapshot, evidence, decision, read.records, read));
6147
- if (disposition === "repaired") yield* annotateRepair(submission.threadId, command.submissionId, decision);
6148
6374
  yield* wake.notify(submission.threadId);
6149
6375
  return RecoveryReport.make({
6150
6376
  submissionId: command.submissionId,
@@ -6181,8 +6407,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6181
6407
  case "unknown": {
6182
6408
  blockedOn = "unknown";
6183
6409
  const records = (yield* readAllTolerant(submission.threadId, [submission.submissionId])).records;
6184
- const resolvedIds = new Set(snapshot.unknownResolutions.map((intent) => intent.toolCallId));
6185
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] : []));
6186
6412
  let earliest;
6187
6413
  for (const envelope of records) {
6188
6414
  const payload = envelope.record.payload;
@@ -6334,19 +6560,16 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (bindings) {
6334
6560
  * the next model request, reattaches through the prompt-coverage rule after a crash, and the
6335
6561
  * joined Submissions settle with the host outcome (DUR-002/DUR-016).
6336
6562
  * - `runWorker(agent)` — scan-seeded, wake-driven worker loop over every lane (WP4's host driver),
6337
- * reimplemented over a singleton identity-exact (digest-transparent) Binding resolution: a
6338
- * claimed head belonging to a different Agent never runs against this binding (SUB-023); a
6339
- * 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.
6340
6565
  * - `processThreadResolved(threadId)` / `runResolvedWorker` — the
6341
- * S2 multi-binding equivalents over registrations owned by the runtime Layer (spec §11, D7): every claimed
6342
- * head's stored `(agentId, agentDigests)` resolves to the exact registered Binding before any
6343
- * code runs; an unresolvable parent-linked child settles with the Schema-stable framework
6344
- * `ChildCompatibilityFailure` (no application code, the child never executes), and an
6345
- * 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.
6346
6569
  * - `runRecovery()` — classify every nonterminal Submission with the pure `classifyRecovery` and
6347
- * execute the repair decisions, appending a `RepairAnnotated` audit record per executed decision
6570
+ * execute repair decisions, annotating owned repair attempts with their granted epochs
6348
6571
  * (DUR-013). Untouched ready input and model-resuming work are reported `deferred` for a
6349
- * worker claim; lanes blocked on
6572
+ * worker claim; Submissions parked on
6350
6573
  * Unknown Outcomes are reported `unknown`. The S2 binding-free Subagent executors (admission
6351
6574
  * completion, start-link repair, waiting restoration, wake replay, canonical join accounting,
6352
6575
  * abort propagation, orphan reservation release) run here; the settlement join itself is
@@ -6381,4 +6604,4 @@ var DurableAgentRuntime = class DurableAgentRuntime extends Context.Service()("@
6381
6604
  //#endregion
6382
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 };
6383
6606
 
6384
- //# sourceMappingURL=DurableAgentRuntime-BGwGUIT-.mjs.map
6607
+ //# sourceMappingURL=DurableAgentRuntime-gF4YiEj2.mjs.map