effect-agent 0.1.0-beta.157 → 0.1.0-beta.159

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 (109) hide show
  1. package/dist/{Admin-BbTZHSLu.d.mts → Admin-DWDYlvvZ.d.mts} +15 -19
  2. package/dist/{Agent-BCyrGaWg.d.mts → Agent-ZfnNCQXs.d.mts} +2 -2
  3. package/dist/{AgentPolicy-CnXWWwdg.d.mts → AgentPolicy-B4oH8TbG.d.mts} +13 -1
  4. package/dist/{AgentRuntime-GQACxY_m.mjs → AgentRuntime-CVMHysXm.mjs} +52 -12
  5. package/dist/AgentRuntime-CVMHysXm.mjs.map +1 -0
  6. package/dist/{AgentRuntime-BPlAn5nA.d.mts → AgentRuntime-D1MmB_0z.d.mts} +10 -10
  7. package/dist/{ContextCompactor-CPlStZRc.d.mts → ContextCompactor-BoKQOjr5.d.mts} +2 -2
  8. package/dist/{Digest-CNHImGy0.d.mts → Digest-COAt3Orl.d.mts} +2 -2
  9. package/dist/{DurableAgentRuntime-BAQvZK1e.mjs → DurableAgentRuntime-C7A5guvq.mjs} +60 -30
  10. package/dist/DurableAgentRuntime-C7A5guvq.mjs.map +1 -0
  11. package/dist/{EventSource-CETtQTiH.d.mts → EventSource-BfNZBG4A.d.mts} +3 -3
  12. package/dist/{Messaging-Dkg87ebg.d.mts → Messaging-7VXQnWI3.d.mts} +2 -2
  13. package/dist/{Messaging-B1zumorg.d.mts → Messaging-CICd_CSi.d.mts} +4 -4
  14. package/dist/{MessagingHost-B4jqlJmx.d.mts → MessagingHost-DXwWnrMh.d.mts} +4 -4
  15. package/dist/{Output-WL7JexCm.d.mts → Output-CY7NZByV.d.mts} +2 -2
  16. package/dist/{PreparedInputAdmission-Swp9t800.d.mts → PreparedInputAdmission-VStpn8u2.d.mts} +4 -4
  17. package/dist/{Records-BxGWRs-t.d.mts → Records-Bm4Qt70a.d.mts} +3 -3
  18. package/dist/{RunEventSink-DsbcGXog.d.mts → RunEventSink-9CUdI9Ar.d.mts} +2 -2
  19. package/dist/{RunOptions-GjDlAuKh.d.mts → RunOptions-BHmBa05n.d.mts} +6 -6
  20. package/dist/{Schedule-B94iS2X3.d.mts → Schedule-Bi_XVKLF.d.mts} +2 -2
  21. package/dist/{Subagent-CLaMeZKu.d.mts → Subagent-D59om8SR.d.mts} +11 -13
  22. package/dist/{Subagent-JjcidJ7q.mjs → Subagent-DH6G120f.mjs} +3 -6
  23. package/dist/Subagent-DH6G120f.mjs.map +1 -0
  24. package/dist/{SubagentContract-DdT7Cqe6.d.mts → SubagentContract-tyN9v-Jv.d.mts} +3 -7
  25. package/dist/{SubagentHost--HBuY8fF.d.mts → SubagentHost-VTyf_wEF.d.mts} +6 -6
  26. package/dist/{Subscription-DaMCppa_.d.mts → Subscription-Bx1ZRRVS.d.mts} +6 -6
  27. package/dist/{SubscriptionInput-B0ZtrQoY.d.mts → SubscriptionInput-B5k5vb1S.d.mts} +3 -3
  28. package/dist/{Worker-2r2zGDkh.d.mts → Worker-XEBYxRQv.d.mts} +3 -3
  29. package/dist/capabilities/Commands.d.mts +1 -1
  30. package/dist/capabilities/ContextTools.d.mts +1 -135
  31. package/dist/capabilities/ContextTools.mjs +1 -40
  32. package/dist/capabilities/ContextTools.mjs.map +1 -1
  33. package/dist/capabilities/Messaging.d.mts +2 -2
  34. package/dist/capabilities/RunHooks.d.mts +1 -1
  35. package/dist/capabilities/Subagent.d.mts +4 -4
  36. package/dist/capabilities/Subagent.mjs +2 -2
  37. package/dist/capabilities/SubagentReservations.d.mts +1 -1
  38. package/dist/core/Agent.d.mts +1 -1
  39. package/dist/core/AgentPolicy.d.mts +1 -1
  40. package/dist/core/AgentPolicy.mjs +6 -0
  41. package/dist/core/AgentPolicy.mjs.map +1 -1
  42. package/dist/core/SubagentContract.d.mts +2 -2
  43. package/dist/core/SubagentContract.mjs +1 -7
  44. package/dist/core/SubagentContract.mjs.map +1 -1
  45. package/dist/core/Worker.d.mts +2 -2
  46. package/dist/durable/ActivityStore.d.mts +1 -1
  47. package/dist/durable/Admin.d.mts +1 -1
  48. package/dist/durable/AgentRegistration.d.mts +1 -1
  49. package/dist/durable/Certification.d.mts +1 -1
  50. package/dist/durable/CommittedActivity.d.mts +3 -3
  51. package/dist/durable/Digest.d.mts +1 -1
  52. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  53. package/dist/durable/DurableAgentRuntime.mjs +1 -1
  54. package/dist/durable/EventSource.d.mts +1 -1
  55. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  56. package/dist/durable/LifecyclePublication.d.mts +1 -1
  57. package/dist/durable/MessageDelivery.d.mts +26 -12
  58. package/dist/durable/MessageDelivery.mjs +49 -17
  59. package/dist/durable/MessageDelivery.mjs.map +1 -1
  60. package/dist/durable/MessageDeliveryStoreConformance.d.mts +7 -7
  61. package/dist/durable/PersistentHistory.d.mts +1 -1
  62. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  63. package/dist/durable/Records.d.mts +1 -1
  64. package/dist/durable/Recovery.d.mts +1 -1
  65. package/dist/durable/RunJournal.d.mts +1 -1
  66. package/dist/durable/Schedule.d.mts +1 -1
  67. package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
  68. package/dist/durable/ScheduleTransition.d.mts +1 -1
  69. package/dist/durable/Scheduling.d.mts +3 -3
  70. package/dist/durable/SubmissionLedger.d.mts +1 -1
  71. package/dist/durable/SubmissionLedgerConformance.d.mts +2 -2
  72. package/dist/durable/SubmissionStatus.d.mts +1 -1
  73. package/dist/durable/Subscription.d.mts +1 -1
  74. package/dist/durable/SubscriptionInput.d.mts +1 -1
  75. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  76. package/dist/durable/SubscriptionTools.d.mts +4 -4
  77. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  78. package/dist/durable/Subscriptions.d.mts +6 -6
  79. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  80. package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
  81. package/dist/durable/ThreadInvariants.d.mts +2 -2
  82. package/dist/durable/ThreadProjection.d.mts +1 -1
  83. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  84. package/dist/durable/ThreadStore.d.mts +1 -1
  85. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  86. package/dist/durable/WorkerAdmission.d.mts +4 -4
  87. package/dist/durable/WorkerHost.d.mts +6 -6
  88. package/dist/engine/AgentRuntime.d.mts +1 -1
  89. package/dist/engine/AgentRuntime.mjs +1 -1
  90. package/dist/engine/Compaction.d.mts +1 -1
  91. package/dist/engine/ContextCompactor.d.mts +1 -1
  92. package/dist/engine/MessagingHost.d.mts +1 -1
  93. package/dist/engine/Output.d.mts +1 -1
  94. package/dist/engine/RunEventSink.d.mts +1 -1
  95. package/dist/engine/RunOptions.d.mts +1 -1
  96. package/dist/engine/SubagentHost.d.mts +1 -1
  97. package/dist/index.d.mts +21 -21
  98. package/dist/index.mjs +3 -3
  99. package/package.json +1 -1
  100. package/src/capabilities/ContextTools.ts +0 -55
  101. package/src/capabilities/Subagent.ts +0 -3
  102. package/src/core/AgentPolicy.ts +6 -0
  103. package/src/core/SubagentContract.ts +0 -7
  104. package/src/durable/DurableAgentRuntime.ts +100 -121
  105. package/src/durable/MessageDelivery.ts +64 -20
  106. package/src/engine/internal/agent-runtime.ts +109 -30
  107. package/dist/AgentRuntime-GQACxY_m.mjs.map +0 -1
  108. package/dist/DurableAgentRuntime-BAQvZK1e.mjs.map +0 -1
  109. package/dist/Subagent-JjcidJ7q.mjs.map +0 -1
@@ -67,6 +67,12 @@ const AgentPolicyFields = Schema.Struct({
67
67
  runStatus: Schema.Literals(["appended", "off"]),
68
68
  /** Restart a disposable model call on joined input, at most twice per Run. Default off. */
69
69
  restartOnJoinedInput: Schema.optionalKey(Schema.Boolean),
70
+ /**
71
+ * Retries per model call after a retryable `AiError` or a 429/5xx stream error part,
72
+ * before any content. Provider-defined Tools must be `Tool.Readonly`. Exponential
73
+ * backoff honors longer `retryAfter` delays and counts toward `maxDuration`. Default 0.
74
+ */
75
+ modelRetries: Schema.optionalKey(NonNegativeInt),
70
76
  compaction: CompactionPolicy,
71
77
  }).check(
72
78
  Schema.makeFilter((policy) =>
@@ -77,13 +77,6 @@ export type SubagentBudgetReservation = typeof SubagentBudgetReservation.Type;
77
77
  export const DelegationDepth = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1));
78
78
  export type DelegationDepth = typeof DelegationDepth.Type;
79
79
 
80
- /** @deprecated Names are application-owned; use DelegationTool metadata for classification. */
81
- export const delegationToolPrefix = "delegate_";
82
-
83
- /** @deprecated Checks a legacy spelling only, never execution, nesting or recovery semantics. */
84
- export const isDelegationToolName = (toolName: string): boolean =>
85
- toolName.startsWith(delegationToolPrefix);
86
-
87
80
  /** Marks a Tool implementing the idempotent Subagent establishment protocol. */
88
81
  export const DelegationTool = Context.Reference<boolean>("@effect-agent/core/DelegationTool", {
89
82
  defaultValue: () => false,
@@ -167,6 +167,7 @@ import { makeMessagingRuntime } from "./internal/messaging-host.ts";
167
167
  import * as ThreadInitialization from "./internal/thread-initialization.ts";
168
168
  import { makeWorkerRuntime, WorkerInputControl } from "./internal/worker-host.ts";
169
169
  import { WorkerRuntime } from "./internal/worker-runtime.ts";
170
+ import { MessageDeliveryStore } from "./MessageDelivery.ts";
170
171
  import {
171
172
  OperationAuthorizationRequest,
172
173
  OperationAuthorizer,
@@ -1266,6 +1267,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
1266
1267
  const ledger = yield* SubmissionLedger;
1267
1268
  const submissionScheduling = yield* SubmissionScheduling;
1268
1269
  const store = yield* ThreadStore;
1270
+ const deliveries = yield* Effect.serviceOption(MessageDeliveryStore);
1269
1271
 
1270
1272
  // Disposable: canonical tail and owner identify the exact projection, never ownership.
1271
1273
  // Retain only the most recent view; persisted checkpoints still govern cold recovery.
@@ -3112,7 +3114,75 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
3112
3114
  */
3113
3115
  const notifyParentOfChildSettlement = Effect.fn(
3114
3116
  "DurableAgentRuntime.notifyParentOfChildSettlement",
3115
- )(function* (submission: SubmissionSnapshot): Effect.fn.Return<void, LedgerError> {
3117
+ )(function* (
3118
+ submission: SubmissionSnapshot,
3119
+ record: RecordEnvelope,
3120
+ ): Effect.fn.Return<void, LedgerError> {
3121
+ const admission = submission.messageAdmission;
3122
+ const worker = submission.workerAdmission;
3123
+
3124
+ const key =
3125
+ admission === undefined
3126
+ ? worker === undefined
3127
+ ? undefined
3128
+ : {
3129
+ ownerThreadId: worker.origin.source.threadId,
3130
+ messageId: worker.messageId,
3131
+ }
3132
+ : Schema.is(MessageAdmission)(admission)
3133
+ ? admission.message
3134
+ : {
3135
+ ownerThreadId:
3136
+ admission._tag === "WorkerCompletion"
3137
+ ? admission.report.worker.threadId
3138
+ : admission.worker.threadId,
3139
+ messageId: submission.idempotencyKey,
3140
+ };
3141
+
3142
+ // Commit the destination's exact canonical acknowledgement at the source before
3143
+ // making the ledger terminal. A failed/lost response is replayed during recovery.
3144
+ if (key !== undefined && Option.isSome(deliveries)) {
3145
+ const settled = yield* settlementPayloadFromRecord(record, submission.submissionId);
3146
+
3147
+ yield* deliveries.value
3148
+ .change(key, {
3149
+ _tag: "Complete",
3150
+ nowMillis: yield* Clock.currentTimeMillis,
3151
+ admissionKey: submission.idempotencyKey,
3152
+ inputDigest: submission.inputDigest,
3153
+ receipt: Receipt.make({
3154
+ threadId: submission.threadId,
3155
+ submissionId: submission.submissionId,
3156
+ receiptId: submission.receiptId,
3157
+ queueSequence: submission.queueSequence,
3158
+ }),
3159
+ settlement: Settlement.make({
3160
+ submissionId: settled.submissionId,
3161
+ receiptId: settled.receiptId,
3162
+ settlementId: settled.settlementId,
3163
+ outcome: settled.outcome,
3164
+ settledAt: record.createdAt,
3165
+ ...(settled.outcome === "failed" ? { failure: settled.result } : {}),
3166
+ ...(settled.usageSummary === undefined ? {} : { usageSummary: settled.usageSummary }),
3167
+ ...(settled.runDisposition === undefined
3168
+ ? {}
3169
+ : { runDisposition: settled.runDisposition }),
3170
+ }),
3171
+ })
3172
+ .pipe(
3173
+ Effect.catchCause((cause) =>
3174
+ Effect.failCause(
3175
+ Cause.map(cause, (error) =>
3176
+ LedgerError.make({
3177
+ operation: "delivery-completion",
3178
+ message: "Destination acknowledgement remains pending",
3179
+ cause: error,
3180
+ }),
3181
+ ),
3182
+ ),
3183
+ ),
3184
+ );
3185
+ }
3116
3186
  if (submission.workerAdmission?.origin.reporting?.mode === "standard")
3117
3187
  yield* updateRuntime.repair(submission.threadId).pipe(
3118
3188
  Effect.mapError(() =>
@@ -3227,14 +3297,14 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
3227
3297
  Effect.asVoid,
3228
3298
  );
3229
3299
  yield* hit("terminalize:after-canonical-append");
3230
- yield* notifyParentOfChildSettlement(submission);
3300
+ yield* notifyParentOfChildSettlement(submission, record);
3231
3301
 
3232
3302
  const settlement = yield* ledger.finalizeSettlement(
3233
3303
  SettlementFinalization.make({ submissionId, settlementId }),
3234
3304
  );
3235
3305
 
3236
3306
  yield* wake.notify(submission.threadId);
3237
- yield* notifyParentOfChildSettlement(submission);
3307
+ yield* notifyParentOfChildSettlement(submission, record);
3238
3308
 
3239
3309
  return settlement;
3240
3310
  });
@@ -3349,7 +3419,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
3349
3419
  Effect.asVoid,
3350
3420
  );
3351
3421
  yield* hit("terminalize:after-canonical-append");
3352
- yield* notifyParentOfChildSettlement(submission);
3422
+ yield* notifyParentOfChildSettlement(submission, reserved.record);
3353
3423
 
3354
3424
  const settlement = yield* ledger.finalizeSettlement(
3355
3425
  SettlementFinalization.make({ submissionId, settlementId }),
@@ -3358,7 +3428,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
3358
3428
  const canonicalSettlement = yield* settlementPayloadFromRecord(reserved.record, submissionId);
3359
3429
 
3360
3430
  yield* wake.notify(submission.threadId);
3361
- yield* notifyParentOfChildSettlement(submission);
3431
+ yield* notifyParentOfChildSettlement(submission, reserved.record);
3362
3432
  yield* settleJoinedSubmissions(ctx, canonicalSettlement);
3363
3433
 
3364
3434
  return materializeSettlement(settlement, reserved.record);
@@ -3385,7 +3455,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
3385
3455
  );
3386
3456
  yield* hit("terminalize:after-canonical-append");
3387
3457
  }
3388
- yield* notifyParentOfChildSettlement(submission);
3458
+ yield* notifyParentOfChildSettlement(submission, reservation.record);
3389
3459
 
3390
3460
  const settlement = yield* ledger.finalizeSettlement(
3391
3461
  SettlementFinalization.make({
@@ -3400,7 +3470,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
3400
3470
  );
3401
3471
 
3402
3472
  yield* wake.notify(submission.threadId);
3403
- yield* notifyParentOfChildSettlement(submission);
3473
+ yield* notifyParentOfChildSettlement(submission, reservation.record);
3404
3474
  yield* settleJoinedSubmissions(ctx, canonicalSettlement);
3405
3475
 
3406
3476
  return materializeSettlement(settlement, reservation.record);
@@ -3413,7 +3483,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
3413
3483
  ): Effect.fn.Return<Settlement, LedgerError | SettlementConflict> {
3414
3484
  const canonicalSettlement = yield* settlementPayloadFromRecord(record, submission.submissionId);
3415
3485
 
3416
- yield* notifyParentOfChildSettlement(submission);
3486
+ yield* notifyParentOfChildSettlement(submission, record);
3417
3487
 
3418
3488
  const settlement = yield* ledger.finalizeSettlement(
3419
3489
  SettlementFinalization.make({
@@ -3427,7 +3497,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
3427
3497
  );
3428
3498
 
3429
3499
  yield* wake.notify(submission.threadId);
3430
- yield* notifyParentOfChildSettlement(submission);
3500
+ yield* notifyParentOfChildSettlement(submission, record);
3431
3501
 
3432
3502
  return materializeSettlement(settlement, snapshot.reservation?.record);
3433
3503
  });
@@ -4594,6 +4664,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
4594
4664
  journalMetadata,
4595
4665
  );
4596
4666
 
4667
+ // Do not retain the metadata snapshot across model or Tool waits, including cache hits.
4668
+ // The projected prompt owns its needed context.
4669
+ journalMetadata = undefined;
4670
+
4597
4671
  projectedJournal = {
4598
4672
  threadId: ctx.threadId,
4599
4673
  through: canonicalThrough,
@@ -8113,14 +8187,21 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8113
8187
  const retainControl = controlRecords([submissionId]);
8114
8188
 
8115
8189
  const collectControl = (record: CanonicalRecordEnvelope): boolean => {
8116
- // Compaction payloads may carry large summaries or handoffs. Keep their metadata
8117
- // scoped to ordinary projection, never retained across this Attempt's model waits.
8118
- if (record.record.payload._tag === "CompactionCreated") journalMetadata = undefined;
8119
- else journalMetadata?.add(record);
8190
+ journalMetadata?.add(record);
8120
8191
 
8121
8192
  return retainControl(record);
8122
8193
  };
8123
8194
 
8195
+ const takeJournalMetadata = (): JournalMetadata | undefined => {
8196
+ const metadata = journalMetadata?.snapshot();
8197
+
8198
+ // Reuse the validated prefix once, then release compaction payloads before model
8199
+ // waits. An immediate resume falls back to a fresh canonical metadata scan.
8200
+ if (metadata !== undefined && metadata.compactions.length > 0) journalMetadata = undefined;
8201
+
8202
+ return metadata;
8203
+ };
8204
+
8124
8205
  let records: ReadonlyArray<CanonicalRecordEnvelope> = yield* Stream.runCollect(
8125
8206
  initialView.canonical.pipe(Stream.filter(collectControl)),
8126
8207
  );
@@ -8492,7 +8573,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8492
8573
  canonical,
8493
8574
  tail.tailSequence,
8494
8575
  initialView.seed,
8495
- journalMetadata?.snapshot(),
8576
+ takeJournalMetadata(),
8496
8577
  lineage,
8497
8578
  approvalDecisionIntents,
8498
8579
  currentContracts,
@@ -11077,55 +11158,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
11077
11158
  return ObligationReport.make({ thresholds, entries, generatedAt });
11078
11159
  });
11079
11160
 
11080
- const runWorkerImpl = <
11081
- InputSchema extends Schema.Top,
11082
- OutputSchema extends Schema.Top,
11083
- Instructions,
11084
- Tools extends Record<string, Tool.Any>,
11085
- Provider,
11086
- ModelProvides,
11087
- ModelRequires,
11088
- InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
11089
- InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
11090
- RunDispositionValue extends
11091
- | RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
11092
- | undefined = undefined,
11093
- InputPromptValue extends InputPromptSource<InputSchema["Type"], unknown, unknown> | undefined =
11094
- undefined,
11095
- UpdatesSchema extends Schema.Top | undefined = undefined,
11096
- >(
11097
- agent: RuntimeBinding<
11098
- InputSchema,
11099
- OutputSchema,
11100
- Instructions,
11101
- Tools,
11102
- Provider,
11103
- ModelProvides,
11104
- ModelRequires,
11105
- InstructionError,
11106
- InstructionRequirements,
11107
- RunDispositionValue,
11108
- InputPromptValue,
11109
- UpdatesSchema
11110
- >,
11111
- ) =>
11112
- Effect.gen(function* () {
11113
- // Wake subscriptions may drop hints, so a ledger scan seeds the worklist (persistence §14).
11114
- const nonterminal = yield* Stream.runCollect(ledger.scanNonterminal);
11115
- const seen = new Set<ThreadId>();
11116
-
11117
- for (const submission of nonterminal) {
11118
- if (seen.has(submission.threadId)) continue;
11119
- seen.add(submission.threadId);
11120
- yield* processThreadImpl(agent, submission.threadId);
11121
- }
11122
- yield* Stream.runForEach(wake.wakes, (threadId) => processThreadImpl(agent, threadId));
11123
- });
11124
-
11125
11161
  const runResolvedWorkerImpl = Effect.gen(function* () {
11126
- // The multi-binding worker (plan §1.7): every claimed head resolves its exact stored
11127
- // Binding from the explicit registration array, so one worker pool serves parent and child
11128
- // lanes (spec §12's smallest-pool wakeup proof runs over this loop).
11162
+ // Each claimed head selects a current Binding by stable Agent ID and optional host routing.
11163
+ // Original per-operation replay contracts still govern unfinished handlers, while one worker
11164
+ // pool serves both parent and child lanes.
11129
11165
  const nonterminal = yield* Stream.runCollect(ledger.scanNonterminal);
11130
11166
  const seen = new Set<ThreadId>();
11131
11167
 
@@ -11219,7 +11255,6 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
11219
11255
  processThread: processThreadImpl,
11220
11256
  processThreadResolved: processThreadResolvedImpl,
11221
11257
  processThreadHead: processThreadHeadImpl,
11222
- runWorker: runWorkerImpl,
11223
11258
  runResolvedWorker: runResolvedWorkerImpl,
11224
11259
  runRecovery,
11225
11260
  recoverSubmission,
@@ -11261,11 +11296,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
11261
11296
  * seam (Joining/Joined, plan §2.5): the queued input becomes canonical (`input:{sid}`) before
11262
11297
  * the next model request, reattaches through the prompt-coverage rule after a crash, and the
11263
11298
  * joined Submissions settle with the host outcome (DUR-002/DUR-016).
11264
- * - `runWorker(agent)` — scan-seeded, wake-driven worker loop over every lane (WP4's host driver),
11265
- * over a singleton current Binding: a claimed head belonging to a different Agent never
11266
- * runs against this binding. Missing bindings leave accepted roots and children owed.
11267
- * - `processThreadResolved(threadId)` / `runResolvedWorker` — the
11268
- * equivalents over registrations owned by the runtime Layer: each stable `agentId` selects
11299
+ * - `processThreadResolved(threadId)` / `runResolvedWorker` — drain or continuously process
11300
+ * lanes using registrations owned by the runtime Layer: each stable `agentId` selects
11269
11301
  * one current Binding. Unfinished operation contracts gate handler execution independently
11270
11302
  * of immutable admission evidence; missing bindings release the claim with a typed refusal.
11271
11303
  * - `runRecovery()` — classify every nonterminal Submission with the pure `classifyRecovery` and
@@ -11468,59 +11500,6 @@ export class DurableAgentRuntime extends Context.Service<
11468
11500
  threadId: ThreadId,
11469
11501
  options?: { readonly yieldAfter?: DateTime.Utc },
11470
11502
  ) => Effect.Effect<Option.Option<Settlement>, DurableWorkerFailure | DurableBindingFailure>;
11471
- readonly runWorker: <
11472
- InputSchema extends Schema.Top,
11473
- OutputSchema extends Schema.Top,
11474
- Instructions,
11475
- Tools extends Record<string, Tool.Any>,
11476
- Provider,
11477
- ModelProvides,
11478
- ModelRequires,
11479
- InstructionError = InstructionErrorOf<Instructions, InputSchema["Type"]>,
11480
- InstructionRequirements = InstructionRequirementsOf<Instructions, InputSchema["Type"]>,
11481
- RunDispositionValue extends
11482
- | RunDispositionDeclaration<OutputSchema["Type"], Schema.Top>
11483
- | undefined = undefined,
11484
- InputPromptValue extends
11485
- | InputPromptSource<InputSchema["Type"], unknown, unknown>
11486
- | undefined = undefined,
11487
- UpdatesSchema extends Schema.Top | undefined = undefined,
11488
- >(
11489
- agent: RuntimeBinding<
11490
- InputSchema,
11491
- OutputSchema,
11492
- Instructions,
11493
- Tools,
11494
- Provider,
11495
- ModelProvides,
11496
- ModelRequires,
11497
- InstructionError,
11498
- InstructionRequirements,
11499
- RunDispositionValue,
11500
- InputPromptValue,
11501
- UpdatesSchema
11502
- >,
11503
- ) => Effect.Effect<
11504
- void,
11505
- DurableWorkerFailure | DurableBindingFailure,
11506
- DurableWorkerRequirements<
11507
- RuntimeBinding<
11508
- InputSchema,
11509
- OutputSchema,
11510
- Instructions,
11511
- Tools,
11512
- Provider,
11513
- ModelProvides,
11514
- ModelRequires,
11515
- InstructionError,
11516
- InstructionRequirements,
11517
- RunDispositionValue,
11518
- InputPromptValue,
11519
- UpdatesSchema
11520
- >,
11521
- InstructionRequirements
11522
- >
11523
- >;
11524
11503
  readonly runResolvedWorker: Effect.Effect<void, DurableWorkerFailure | DurableBindingFailure>;
11525
11504
  /**
11526
11505
  * Recover each pending Thread independently. History/child failures, defects and the
@@ -14,6 +14,8 @@ import { PreparedInput } from "./Subscription.ts";
14
14
 
15
15
  const Positive = Schema.Int.check(Schema.isGreaterThan(0));
16
16
  const BoundedName = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256));
17
+ const receiptsEqual = Schema.toEquivalence(Receipt);
18
+ const settlementsEqual = Schema.toEquivalence(Settlement);
17
19
 
18
20
  /** An owner is always a source Thread. Destination admission has its own frozen key. */
19
21
  export const MessageDeliveryKey = Schema.Struct({
@@ -66,7 +68,12 @@ export const defaultMessageDeliveryStoreLimits: MessageDeliveryStoreLimits = {
66
68
  maxRetainedUpdatesPerOwner: 256,
67
69
  };
68
70
 
69
- const ParkReason = Schema.Literals(["exhausted", "deadline", "status-unavailable"]);
71
+ const ParkReason = Schema.Literals([
72
+ "exhausted",
73
+ "deadline",
74
+ "status-unavailable",
75
+ "awaiting-settlement",
76
+ ]);
70
77
 
71
78
  export const MessageDeliveryRecord = Schema.Struct({
72
79
  schemaVersion: Schema.Literal(1),
@@ -152,7 +159,20 @@ export const MessageDeliveryFailpoint = Context.Reference<{
152
159
 
153
160
  const fence = { expectedVersion: Positive, nowMillis: ScheduleInstant };
154
161
 
162
+ /** Terminal destination evidence, independent of an expiring source transport claim. */
163
+ export const MessageDeliveryCompletion = Schema.Struct({
164
+ receipt: Receipt,
165
+ settlement: Settlement,
166
+ admissionKey: IdempotencyKey,
167
+ inputDigest: Digest,
168
+ });
169
+
155
170
  export const MessageDeliveryChange = Schema.Union([
171
+ Schema.Struct({
172
+ _tag: Schema.Literal("Complete"),
173
+ nowMillis: ScheduleInstant,
174
+ ...MessageDeliveryCompletion.fields,
175
+ }),
156
176
  Schema.Struct({ _tag: Schema.Literal("Claim"), ...fence }),
157
177
  Schema.Struct({ _tag: Schema.Literal("Accept"), ...fence, receipt: Receipt }),
158
178
  Schema.Struct({ _tag: Schema.Literal("Process"), ...fence, settlement: Settlement }),
@@ -424,6 +444,34 @@ export const applyMessageDeliveryChange = (
424
444
  ): Result.Result<MessageDeliveryRecord, MessageDeliveryError> => {
425
445
  const conflict = () => Result.fail(error("conflict", change._tag));
426
446
 
447
+ if (change._tag === "Complete") {
448
+ if (
449
+ change.admissionKey !== record.envelope.admissionKey ||
450
+ change.inputDigest !== record.envelope.inputDigest ||
451
+ change.receipt.threadId !== record.envelope.threadId ||
452
+ change.settlement.submissionId !== change.receipt.submissionId ||
453
+ change.settlement.receiptId !== change.receipt.receiptId ||
454
+ (record.receipt !== null && !receiptsEqual(record.receipt, change.receipt))
455
+ )
456
+ return Result.fail(error("corrupt", "completion-identity"));
457
+ if (record.status === "refused") return conflict();
458
+ if (record.status === "processed")
459
+ return record.settlement !== null && settlementsEqual(record.settlement, change.settlement)
460
+ ? Result.succeed(record)
461
+ : Result.fail(error("corrupt", "completion-identity"));
462
+
463
+ return Result.succeed({
464
+ ...record,
465
+ version: record.version + 1,
466
+ status: "processed",
467
+ receipt: change.receipt,
468
+ settlement: change.settlement,
469
+ leaseUntilMillis: null,
470
+ parkReason: null,
471
+ retry: { ...record.retry, parked: false },
472
+ });
473
+ }
474
+
427
475
  if (record.version !== change.expectedVersion) return conflict();
428
476
  if (record.status === "processed" || record.status === "refused") return conflict();
429
477
  const now = change.nowMillis;
@@ -540,21 +588,8 @@ export const applyMessageDeliveryChange = (
540
588
  }
541
589
  if (change._tag === "ObservePending") {
542
590
  if (record.status !== "accepted") return conflict();
543
- if (now >= record.deadlineAtMillis) return Result.succeed(parked("deadline"));
544
591
 
545
- return Result.succeed({
546
- ...base,
547
- leaseUntilMillis: null,
548
- retry: {
549
- ...record.retry,
550
- automaticAttempts: 0,
551
- lastFailure: null,
552
- nextAttemptAtMillis: Math.min(
553
- now + record.policy.settlementPollMillis,
554
- record.deadlineAtMillis,
555
- ),
556
- },
557
- });
592
+ return Result.succeed(parked("awaiting-settlement"));
558
593
  }
559
594
  if (change._tag === "Refuse") {
560
595
  if (record.status !== "pending") return conflict();
@@ -700,9 +735,10 @@ export class MessageDeliveryDriver extends Context.Service<
700
735
  // Digest computation cannot extend this claim's admission authority.
701
736
  if (timeout <= 0) return claimed;
702
737
 
703
- type WithoutFence<T> = T extends MessageDeliveryChange
704
- ? Omit<T, "expectedVersion" | "nowMillis">
705
- : never;
738
+ type WithoutFence<T> =
739
+ T extends Exclude<MessageDeliveryChange, { _tag: "Complete" }>
740
+ ? Omit<T, "expectedVersion" | "nowMillis">
741
+ : never;
706
742
 
707
743
  const commit = (change: WithoutFence<MessageDeliveryChange>) =>
708
744
  Clock.currentTimeMillis.pipe(
@@ -722,8 +758,16 @@ export class MessageDeliveryDriver extends Context.Service<
722
758
  ).pipe(Effect.mapError((cause) => error("corrupt", "admission", cause)));
723
759
 
724
760
  yield* failpoint.hit("message-delivery:admission:after");
725
- if (outcome._tag === "Receipt")
726
- return yield* commit({ _tag: "Accept", receipt: outcome.receipt });
761
+ if (outcome._tag === "Receipt") {
762
+ const accepted = yield* commit({ _tag: "Accept", receipt: outcome.receipt });
763
+
764
+ return yield* store.change(key, {
765
+ _tag: "Park",
766
+ reason: "awaiting-settlement",
767
+ expectedVersion: accepted.version,
768
+ nowMillis: yield* Clock.currentTimeMillis,
769
+ });
770
+ }
727
771
  if (outcome._tag === "Refused")
728
772
  return yield* commit({
729
773
  _tag: "Refuse",