effect-agent 0.1.0-beta.156 → 0.1.0-beta.158

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 (115) hide show
  1. package/dist/{Admin-BjmupLN3.d.mts → Admin-BbTZHSLu.d.mts} +23 -11
  2. package/dist/{Agent-1DMSO_Fl.mjs → Agent-BMATXYsI.mjs} +9 -2
  3. package/dist/Agent-BMATXYsI.mjs.map +1 -0
  4. package/dist/{AgentRuntime-BAjT5gXl.d.mts → AgentRuntime-BPlAn5nA.d.mts} +6 -6
  5. package/dist/{AgentRuntime-IF_1GoUf.mjs → AgentRuntime-D4eoBpIW.mjs} +32 -10
  6. package/dist/AgentRuntime-D4eoBpIW.mjs.map +1 -0
  7. package/dist/{Digest-CaJ2RIsF.d.mts → Digest-CNHImGy0.d.mts} +2 -2
  8. package/dist/{DurableAgentRuntime-BQzYkFVy.mjs → DurableAgentRuntime-eImXoGqD.mjs} +78 -27
  9. package/dist/DurableAgentRuntime-eImXoGqD.mjs.map +1 -0
  10. package/dist/{EventSource-C8CKwQy5.d.mts → EventSource-CETtQTiH.d.mts} +3 -3
  11. package/dist/InMemory.d.mts +1 -1
  12. package/dist/{PreparedInputAdmission-CU1PzUVD.d.mts → PreparedInputAdmission-Swp9t800.d.mts} +4 -4
  13. package/dist/{Records-BrEpibum.d.mts → Records-BxGWRs-t.d.mts} +2 -2
  14. package/dist/{RunEvent-DV_llBcq.d.mts → RunEvent-D0aepYwN.d.mts} +2 -2
  15. package/dist/{RunEventSink-D5aVhISG.d.mts → RunEventSink-DsbcGXog.d.mts} +2 -2
  16. package/dist/{RunOptions-CkRYnlJa.d.mts → RunOptions-GjDlAuKh.d.mts} +5 -2
  17. package/dist/{Schedule-B_z7PMg-.d.mts → Schedule-B94iS2X3.d.mts} +2 -2
  18. package/dist/{Subagent-CmFtZaeI.mjs → Subagent-211Oz2IN.mjs} +2 -2
  19. package/dist/{Subagent-CmFtZaeI.mjs.map → Subagent-211Oz2IN.mjs.map} +1 -1
  20. package/dist/{Subagent-Dld4W1BY.d.mts → Subagent-CLaMeZKu.d.mts} +6 -6
  21. package/dist/{Subscription-BomcQ9_W.d.mts → Subscription-DaMCppa_.d.mts} +4 -4
  22. package/dist/{SubscriptionInput-gpZSR9vB.d.mts → SubscriptionInput-B0ZtrQoY.d.mts} +3 -3
  23. package/dist/{ThreadHistory-ZEu_ja6L.d.mts → ThreadHistory-FD-o4TsR.d.mts} +2 -2
  24. package/dist/{Usage-Dx0aknfk.d.mts → Usage-BNjPR7Zr.d.mts} +9 -1
  25. package/dist/{WorkerAdmission-CNbm4o_x.mjs → WorkerAdmission-N9hZV85Q.mjs} +2 -2
  26. package/dist/{WorkerAdmission-CNbm4o_x.mjs.map → WorkerAdmission-N9hZV85Q.mjs.map} +1 -1
  27. package/dist/{agent-registration-U3kmV7nW.mjs → agent-registration-BIOzAej4.mjs} +10 -1
  28. package/dist/agent-registration-BIOzAej4.mjs.map +1 -0
  29. package/dist/capabilities/Commands.d.mts +1 -1
  30. package/dist/capabilities/Redaction.d.mts +1 -1
  31. package/dist/capabilities/RunHooks.d.mts +1 -1
  32. package/dist/capabilities/Subagent.d.mts +1 -1
  33. package/dist/capabilities/Subagent.mjs +1 -1
  34. package/dist/capabilities/ToolDiscovery.d.mts +5 -0
  35. package/dist/capabilities/ToolDiscovery.mjs +18 -9
  36. package/dist/capabilities/ToolDiscovery.mjs.map +1 -1
  37. package/dist/capabilities/WebSearch.d.mts +5 -1
  38. package/dist/capabilities/WebSearch.mjs +9 -1
  39. package/dist/capabilities/WebSearch.mjs.map +1 -1
  40. package/dist/core/Agent.mjs +1 -1
  41. package/dist/core/RunEvent.d.mts +1 -1
  42. package/dist/core/Usage.d.mts +1 -1
  43. package/dist/core/Usage.mjs +22 -1
  44. package/dist/core/Usage.mjs.map +1 -1
  45. package/dist/durable/ActivityStore.d.mts +1 -1
  46. package/dist/durable/Admin.d.mts +1 -1
  47. package/dist/durable/AgentRegistration.d.mts +1 -1
  48. package/dist/durable/AgentRegistration.mjs +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 +2 -2
  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/LifecyclePublication.mjs +2 -2
  58. package/dist/durable/LifecyclePublication.mjs.map +1 -1
  59. package/dist/durable/MessageDelivery.d.mts +24 -10
  60. package/dist/durable/MessageDelivery.mjs +49 -17
  61. package/dist/durable/MessageDelivery.mjs.map +1 -1
  62. package/dist/durable/MessageDeliveryStoreConformance.d.mts +5 -5
  63. package/dist/durable/PersistentHistory.d.mts +2 -2
  64. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  65. package/dist/durable/Records.d.mts +1 -1
  66. package/dist/durable/Recovery.d.mts +1 -1
  67. package/dist/durable/RunJournal.d.mts +1 -1
  68. package/dist/durable/Schedule.d.mts +1 -1
  69. package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
  70. package/dist/durable/ScheduleTransition.d.mts +1 -1
  71. package/dist/durable/Scheduling.d.mts +3 -3
  72. package/dist/durable/SubmissionLedger.d.mts +1 -1
  73. package/dist/durable/SubmissionLedgerConformance.d.mts +2 -2
  74. package/dist/durable/SubmissionStatus.d.mts +1 -1
  75. package/dist/durable/Subscription.d.mts +1 -1
  76. package/dist/durable/SubscriptionInput.d.mts +1 -1
  77. package/dist/durable/SubscriptionInput.mjs +1 -1
  78. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  79. package/dist/durable/SubscriptionTools.d.mts +4 -4
  80. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  81. package/dist/durable/Subscriptions.d.mts +6 -6
  82. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  83. package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
  84. package/dist/durable/ThreadInvariants.d.mts +2 -2
  85. package/dist/durable/ThreadProjection.d.mts +1 -1
  86. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  87. package/dist/durable/ThreadStore.d.mts +1 -1
  88. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  89. package/dist/durable/WorkerAdmission.d.mts +2 -2
  90. package/dist/durable/WorkerAdmission.mjs +1 -1
  91. package/dist/durable/WorkerHost.d.mts +2 -2
  92. package/dist/engine/AgentRuntime.d.mts +1 -1
  93. package/dist/engine/AgentRuntime.mjs +1 -1
  94. package/dist/engine/RunEventSink.d.mts +1 -1
  95. package/dist/engine/RunOptions.d.mts +1 -1
  96. package/dist/engine/RunOptions.mjs +1 -0
  97. package/dist/engine/RunOptions.mjs.map +1 -1
  98. package/dist/engine/ThreadHistory.d.mts +1 -1
  99. package/dist/index.d.mts +15 -15
  100. package/dist/index.mjs +5 -5
  101. package/package.json +1 -1
  102. package/src/capabilities/ToolDiscovery.ts +24 -10
  103. package/src/capabilities/WebSearch.ts +15 -0
  104. package/src/core/Agent.ts +16 -2
  105. package/src/core/Usage.ts +41 -0
  106. package/src/durable/DurableAgentRuntime.ts +119 -21
  107. package/src/durable/LifecyclePublication.ts +15 -2
  108. package/src/durable/MessageDelivery.ts +64 -20
  109. package/src/durable/internal/agent-registration.ts +11 -0
  110. package/src/engine/RunOptions.ts +3 -0
  111. package/src/engine/internal/agent-runtime.ts +91 -20
  112. package/dist/Agent-1DMSO_Fl.mjs.map +0 -1
  113. package/dist/AgentRuntime-IF_1GoUf.mjs.map +0 -1
  114. package/dist/DurableAgentRuntime-BQzYkFVy.mjs.map +0 -1
  115. package/dist/agent-registration-U3kmV7nW.mjs.map +0 -1
@@ -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
  });
@@ -4559,6 +4629,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
4559
4629
  ctx: AttemptAppendContext,
4560
4630
  submission: SubmissionSnapshot,
4561
4631
  tokenRef: Ref.Ref<OwnershipToken>,
4632
+ renewAtRef: Ref.Ref<number>,
4562
4633
  records: ReadonlyArray<CanonicalRecordEnvelope>,
4563
4634
  canonical: Stream.Stream<CanonicalRecordEnvelope, ThreadStoreError | ThreadNotMaterialized>,
4564
4635
  canonicalThrough: CanonicalSequence,
@@ -5258,6 +5329,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
5258
5329
  );
5259
5330
 
5260
5331
  return RunUsageSummary.make({
5332
+ ...(legacyCalls === 0 && detailed.webSearchCalls !== undefined
5333
+ ? { webSearchCalls: detailed.webSearchCalls }
5334
+ : {}),
5261
5335
  modelCalls,
5262
5336
  inputTokens: InputTokenUsage.make({
5263
5337
  total: inputTokens,
@@ -7798,18 +7872,33 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
7798
7872
  // Liveness only: the lease keeps the claim visible; correctness stays with the epoch fence.
7799
7873
  // OwnershipLost ends the race and interrupts the Run fiber cleanly.
7800
7874
  const renewal = halt(
7801
- Effect.repeat(
7802
- Effect.gen(function* () {
7803
- const ownershipToken = yield* Ref.get(tokenRef);
7875
+ Effect.gen(function* () {
7876
+ // Binding selection and preparation consume the acquired lease too. Carry the next
7877
+ // deadline across model continuations; an overdue first renewal must run immediately.
7878
+ const renewAt = yield* Ref.get(renewAtRef);
7879
+ const now = yield* Clock.currentTimeMillis;
7804
7880
 
7805
- const renewal = yield* ledger.renewOwnership(
7806
- RenewOwnershipRequest.make({ submissionId, ownershipToken }),
7807
- );
7881
+ yield* Effect.sleep(Math.max(0, renewAt - now));
7882
+ yield* Effect.repeat(
7883
+ Effect.gen(function* () {
7884
+ const ownershipToken = yield* Ref.get(tokenRef);
7885
+ const renewingAt = yield* Clock.currentTimeMillis;
7808
7886
 
7809
- yield* Ref.set(tokenRef, renewal.ownershipToken);
7810
- }).pipe(Effect.uninterruptible),
7811
- { schedule: Schedule.spaced(config.leaseRenewalInterval) },
7812
- ).pipe(Effect.andThen(Effect.never)),
7887
+ const renewal = yield* ledger.renewOwnership(
7888
+ RenewOwnershipRequest.make({ submissionId, ownershipToken }),
7889
+ );
7890
+
7891
+ yield* Ref.set(tokenRef, renewal.ownershipToken);
7892
+ yield* Ref.set(
7893
+ renewAtRef,
7894
+ renewingAt + Duration.toMillis(config.leaseRenewalInterval),
7895
+ );
7896
+ }).pipe(Effect.uninterruptible),
7897
+ { schedule: Schedule.spaced(config.leaseRenewalInterval) },
7898
+ );
7899
+
7900
+ return yield* Effect.never;
7901
+ }),
7813
7902
  );
7814
7903
 
7815
7904
  const execution = Effect.raceFirst(consume, Effect.raceFirst(abortWatcher, renewal));
@@ -8031,6 +8120,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8031
8120
  threadId: ThreadId,
8032
8121
  claim: Claim,
8033
8122
  tokenRef: Ref.Ref<OwnershipToken>,
8123
+ renewAtRef: Ref.Ref<number>,
8034
8124
  resumeAfterRetention: () => void,
8035
8125
  onHandoff: (nextSubmissionId: SubmissionId) => void,
8036
8126
  yieldAfter?: DateTime.Utc,
@@ -8467,6 +8557,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8467
8557
  ctx,
8468
8558
  submission,
8469
8559
  tokenRef,
8560
+ renewAtRef,
8470
8561
  currentRecords,
8471
8562
  canonical,
8472
8563
  tail.tailSequence,
@@ -8647,6 +8738,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8647
8738
  threadId: ThreadId,
8648
8739
  handoff?: ClaimHandoff,
8649
8740
  ) {
8741
+ const acquiringAt = yield* Clock.currentTimeMillis;
8742
+
8650
8743
  const claimed = yield* ledger.claim(
8651
8744
  ClaimRequest.make({
8652
8745
  threadId,
@@ -8659,6 +8752,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8659
8752
  const claim = claimed.value;
8660
8753
  const tokenRef = yield* Ref.make(claim.ownershipToken);
8661
8754
 
8755
+ const renewAtRef = yield* Ref.make(
8756
+ acquiringAt + Duration.toMillis(config.leaseRenewalInterval),
8757
+ );
8758
+
8662
8759
  yield* Effect.addFinalizer(() =>
8663
8760
  Ref.get(tokenRef).pipe(
8664
8761
  Effect.flatMap((ownershipToken) =>
@@ -8681,7 +8778,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8681
8778
  message: "The submission adapter did not honor the requested handoff",
8682
8779
  });
8683
8780
 
8684
- return Option.some({ claim, tokenRef });
8781
+ return Option.some({ claim, tokenRef, renewAtRef });
8685
8782
  }, Effect.uninterruptible);
8686
8783
 
8687
8784
  const processThreadHead = (
@@ -8701,7 +8798,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8701
8798
  const claimed = yield* acquireClaim(threadId, handoff);
8702
8799
 
8703
8800
  if (Option.isNone(claimed)) return Option.none();
8704
- const { claim, tokenRef } = claimed.value;
8801
+ const { claim, tokenRef, renewAtRef } = claimed.value;
8705
8802
 
8706
8803
  const attributes = {
8707
8804
  threadId,
@@ -8781,6 +8878,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8781
8878
  attemptThreadId,
8782
8879
  attemptClaim,
8783
8880
  tokenRef,
8881
+ renewAtRef,
8784
8882
  () => {
8785
8883
  resumeAfterRetention = true;
8786
8884
  },
@@ -146,10 +146,13 @@ export type LifecyclePublicationBatch = typeof LifecyclePublicationBatch.Type;
146
146
 
147
147
  /** Native recovery state, independent of Attempts. Hosts serialize drains per storage owner. */
148
148
  export interface LifecyclePublicationStorage {
149
- /** Select up to `limit` due owners, with a bounded pending prefix for each selected owner. */
149
+ /** Select up to `limit` due owners, with a bounded pending prefix for each selected owner.
150
+ * `retainedOnly` excludes unmaterialized source facts, for a precommitted start prefix.
151
+ */
150
152
  readonly pending: (
151
153
  nowMillis: number,
152
154
  limit: number,
155
+ options?: { readonly retainedOnly?: boolean },
153
156
  ) => Effect.Effect<ReadonlyArray<LifecyclePublicationBatch>, LifecyclePublicationError>;
154
157
  /** Atomically acknowledge the exact batch, retaining its identities and fingerprints. */
155
158
  readonly acknowledge: (
@@ -166,6 +169,15 @@ export interface LifecyclePublicationStorage {
166
169
  timeoutMillis: number,
167
170
  ) => Effect.Effect<boolean, LifecyclePublicationError>;
168
171
  readonly pendingDeadline: Effect.Effect<Option.Option<number>, LifecyclePublicationError>;
172
+ /**
173
+ * Deadline for `pending(..., { retainedOnly: true })`, excluding unmaterialized source facts.
174
+ * Source-backed stores provide this when `pendingDeadline` also includes source intent.
175
+ * Otherwise `pendingDeadline` already describes retained work.
176
+ */
177
+ readonly retainedPendingDeadline?: Effect.Effect<
178
+ Option.Option<number>,
179
+ LifecyclePublicationError
180
+ >;
169
181
  /** Explicit operator retry after repairing a parked owner's destination. */
170
182
  readonly retryParked: (
171
183
  ownerThreadId: ThreadId,
@@ -259,9 +271,10 @@ export const drainLifecyclePublications = Effect.fn("LifecyclePublication.drain"
259
271
  storage: LifecyclePublicationStorage,
260
272
  timeoutMillis = 10_000,
261
273
  limit = 4,
274
+ options?: { readonly retainedOnly?: boolean },
262
275
  ) {
263
276
  const handler = yield* LifecyclePublicationHandler;
264
- const pending = yield* storage.pending(yield* Clock.currentTimeMillis, limit);
277
+ const pending = yield* storage.pending(yield* Clock.currentTimeMillis, limit, options);
265
278
 
266
279
  let failures: Cause.Cause<LifecyclePublicationError> = Cause.empty;
267
280
 
@@ -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",
@@ -111,6 +111,17 @@ export const toolReplayContracts = Effect.fn("AgentRegistration.toolReplayContra
111
111
  name: tool.name,
112
112
  contract: {
113
113
  version,
114
+ ...(Tool.isProviderDefined(tool)
115
+ ? {
116
+ provider: {
117
+ id: tool.id,
118
+ providerName: tool.providerName,
119
+ args: Schema.decodeUnknownSync(Schema.Json)(tool.args ?? null),
120
+ requiresHandler: tool.requiresHandler,
121
+ },
122
+ readonly: Context.get(tool.annotations, Tool.Readonly),
123
+ }
124
+ : {}),
114
125
  parameters: jsonSchema(tool.parametersSchema),
115
126
  success: jsonSchema(tool.successSchema),
116
127
  failure: jsonSchema(tool.failureSchema),
@@ -348,6 +348,8 @@ export type RunCostEstimateValue = number | RunCostEstimate;
348
348
 
349
349
  /** Model identity presented beside the legacy raw-usage estimator argument. */
350
350
  export interface RunCostEstimateRequest {
351
+ /** Observed hosted web search calls, separately billed from tokens. */
352
+ readonly webSearchCalls?: number | undefined;
351
353
  readonly provider: string;
352
354
  /** Configured binding identity; only response.model identifies the returned model. */
353
355
  readonly model: string;
@@ -816,6 +818,7 @@ export type RunTurnResumeSettledCall = typeof RunTurnResumeSettledCallSchema.Typ
816
818
  export const RunResumeUsageSchema = Schema.Struct({
817
819
  ...RunPolicyUsage.fields,
818
820
  modelCalls: Schema.Natural,
821
+ webSearchCalls: Schema.optionalKey(Schema.Natural),
819
822
  inputTokens: Schema.Natural,
820
823
  outputTokens: Schema.Natural,
821
824
  lastInputTokens: Schema.Natural,