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

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 (116) 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-GQACxY_m.mjs} +21 -5
  6. package/dist/AgentRuntime-GQACxY_m.mjs.map +1 -0
  7. package/dist/{Digest-CaJ2RIsF.d.mts → Digest-CNHImGy0.d.mts} +2 -2
  8. package/dist/{DurableAgentRuntime-BQzYkFVy.mjs → DurableAgentRuntime-BAQvZK1e.mjs} +30 -18
  9. package/dist/DurableAgentRuntime-BAQvZK1e.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-Dld4W1BY.d.mts → Subagent-CLaMeZKu.d.mts} +6 -6
  19. package/dist/{Subagent-CmFtZaeI.mjs → Subagent-JjcidJ7q.mjs} +2 -2
  20. package/dist/{Subagent-CmFtZaeI.mjs.map → Subagent-JjcidJ7q.mjs.map} +1 -1
  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 +4 -4
  60. package/dist/durable/MessageDeliveryStoreConformance.d.mts +3 -3
  61. package/dist/durable/PersistentHistory.d.mts +2 -2
  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/SqlMemoryStore.d.mts +715 -5
  71. package/dist/durable/SqlMemoryStore.mjs +313 -123
  72. package/dist/durable/SqlMemoryStore.mjs.map +1 -1
  73. package/dist/durable/SubmissionLedger.d.mts +1 -1
  74. package/dist/durable/SubmissionLedgerConformance.d.mts +2 -2
  75. package/dist/durable/SubmissionStatus.d.mts +1 -1
  76. package/dist/durable/Subscription.d.mts +1 -1
  77. package/dist/durable/SubscriptionInput.d.mts +1 -1
  78. package/dist/durable/SubscriptionInput.mjs +1 -1
  79. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  80. package/dist/durable/SubscriptionTools.d.mts +4 -4
  81. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  82. package/dist/durable/Subscriptions.d.mts +6 -6
  83. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  84. package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
  85. package/dist/durable/ThreadInvariants.d.mts +2 -2
  86. package/dist/durable/ThreadProjection.d.mts +1 -1
  87. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  88. package/dist/durable/ThreadStore.d.mts +1 -1
  89. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  90. package/dist/durable/WorkerAdmission.d.mts +2 -2
  91. package/dist/durable/WorkerAdmission.mjs +1 -1
  92. package/dist/durable/WorkerHost.d.mts +2 -2
  93. package/dist/engine/AgentRuntime.d.mts +1 -1
  94. package/dist/engine/AgentRuntime.mjs +1 -1
  95. package/dist/engine/RunEventSink.d.mts +1 -1
  96. package/dist/engine/RunOptions.d.mts +1 -1
  97. package/dist/engine/RunOptions.mjs +1 -0
  98. package/dist/engine/RunOptions.mjs.map +1 -1
  99. package/dist/engine/ThreadHistory.d.mts +1 -1
  100. package/dist/index.d.mts +15 -15
  101. package/dist/index.mjs +5 -5
  102. package/package.json +1 -1
  103. package/src/capabilities/ToolDiscovery.ts +24 -10
  104. package/src/capabilities/WebSearch.ts +15 -0
  105. package/src/core/Agent.ts +16 -2
  106. package/src/core/Usage.ts +41 -0
  107. package/src/durable/DurableAgentRuntime.ts +40 -12
  108. package/src/durable/LifecyclePublication.ts +15 -2
  109. package/src/durable/SqlMemoryStore.ts +462 -188
  110. package/src/durable/internal/agent-registration.ts +11 -0
  111. package/src/engine/RunOptions.ts +3 -0
  112. package/src/engine/internal/agent-runtime.ts +68 -5
  113. package/dist/Agent-1DMSO_Fl.mjs.map +0 -1
  114. package/dist/AgentRuntime-IF_1GoUf.mjs.map +0 -1
  115. package/dist/DurableAgentRuntime-BQzYkFVy.mjs.map +0 -1
  116. package/dist/agent-registration-U3kmV7nW.mjs.map +0 -1
package/src/core/Agent.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  type Model,
7
7
  type Prompt,
8
8
  Tool,
9
- type Toolkit,
9
+ Toolkit,
10
10
  } from "effect/unstable/ai";
11
11
 
12
12
  import type { AgentInputError, AgentOutputError, AgentRunDispositionError } from "./AgentError.ts";
@@ -569,9 +569,23 @@ export function make(
569
569
  completionNames.add(declaration.tool);
570
570
  }
571
571
 
572
+ const tools = Object.values(options.toolkit.tools);
573
+
574
+ const readonlyTools = tools.map((tool) =>
575
+ Tool.isProviderDefined(tool) &&
576
+ !tool.requiresHandler &&
577
+ ["web_search", "web_search_preview", "file_search"].includes(tool.providerName)
578
+ ? tool.annotate(Tool.Readonly, true)
579
+ : tool,
580
+ );
581
+
582
+ const toolkit = readonlyTools.some((tool, index) => tool !== tools[index])
583
+ ? Toolkit.make(...readonlyTools)
584
+ : options.toolkit;
585
+
572
586
  return Object.freeze({
573
587
  ...options,
574
- toolkit: withUpdateTool(options.toolkit, options.updates),
588
+ toolkit: withUpdateTool(toolkit, options.updates),
575
589
  policy: AgentPolicy.resolve(options.policy),
576
590
  policyOverrides: Object.freeze({ ...options.policy }),
577
591
  toolExposure:
package/src/core/Usage.ts CHANGED
@@ -68,6 +68,8 @@ export class ModelCallUsage extends Schema.Class<ModelCallUsage>(
68
68
  pricingStatus: Schema.optionalKey(Schema.Literals(["estimated", "unknown"])),
69
69
  inputTokens: InputTokenUsage,
70
70
  outputTokens: OutputTokenUsage,
71
+ /** Observed hosted web searches; excludes OpenAI page/find actions. Absent in legacy records. */
72
+ webSearchCalls: Schema.optionalKey(Schema.Natural),
71
73
  costMicrousd: Schema.Natural,
72
74
  }) {}
73
75
 
@@ -80,6 +82,8 @@ export class RunTotals extends Schema.Class<RunTotals>("@effect-agent/core/RunTo
80
82
  modelCalls: Schema.Natural,
81
83
  inputTokens: Schema.Natural,
82
84
  outputTokens: Schema.Natural,
85
+ /** Observed hosted web searches; excludes OpenAI page/find actions. Absent in legacy records. */
86
+ webSearchCalls: Schema.optionalKey(Schema.Natural),
83
87
  costMicrousd: Schema.Natural,
84
88
  usageStatus: Schema.optionalKey(UsageCompleteness),
85
89
  pricingStatus: Schema.optionalKey(UsageCompleteness),
@@ -104,6 +108,7 @@ export class ChildRunUsage extends Schema.Class<ChildRunUsage>("@effect-agent/co
104
108
  export const emptyRunTotals = (): RunTotals =>
105
109
  RunTotals.make({
106
110
  modelCalls: 0,
111
+ webSearchCalls: 0,
107
112
  inputTokens: 0,
108
113
  outputTokens: 0,
109
114
  costMicrousd: 0,
@@ -133,6 +138,8 @@ export class ModelUsageGroup extends Schema.Class<ModelUsageGroup>(
133
138
  modelCalls: Schema.Natural.check(Schema.isGreaterThan(0)),
134
139
  inputTokens: InputTokenUsage,
135
140
  outputTokens: OutputTokenUsage,
141
+ /** Observed hosted web searches; excludes OpenAI page/find actions. Absent in legacy records. */
142
+ webSearchCalls: Schema.optionalKey(Schema.Natural),
136
143
  costMicrousd: Schema.Natural,
137
144
  }) {}
138
145
 
@@ -140,6 +147,8 @@ const RunUsageSummaryFields = Schema.Struct({
140
147
  modelCalls: Schema.Natural,
141
148
  inputTokens: InputTokenUsage,
142
149
  outputTokens: OutputTokenUsage,
150
+ /** Observed hosted web searches; excludes OpenAI page/find actions. Absent in legacy records. */
151
+ webSearchCalls: Schema.optionalKey(Schema.Natural),
143
152
  costMicrousd: Schema.Natural,
144
153
  byModel: Schema.Array(ModelUsageGroup),
145
154
  /** Coverage of recorded calls only; interruptions can make the Run less complete. */
@@ -162,6 +171,11 @@ const RunUsageSummaryFields = Schema.Struct({
162
171
 
163
172
  return (
164
173
  new Set(identities).size === identities.length &&
174
+ (summary.webSearchCalls === undefined ||
175
+ hasAdditiveTotal(
176
+ summary.webSearchCalls,
177
+ summary.byModel.map((group) => group.webSearchCalls ?? 0),
178
+ )) &&
165
179
  hasAdditiveTotal(
166
180
  summary.modelCalls,
167
181
  summary.byModel.map((group) => group.modelCalls),
@@ -219,6 +233,7 @@ interface MutableUsageGroup {
219
233
  readonly serviceTier?: string | undefined;
220
234
  readonly pricingVersion?: string | undefined;
221
235
  modelCalls: number;
236
+ webSearchCalls: number | undefined;
222
237
  inputTokens: {
223
238
  total: number;
224
239
  uncached: number;
@@ -267,6 +282,7 @@ export const sumRunTotals = Effect.fn("sumRunTotals")(function* (
267
282
  contributions: ReadonlyArray<RunTotals>,
268
283
  ): Effect.fn.Return<RunTotals, UsageAggregationError> {
269
284
  let modelCalls = 0;
285
+ let webSearchCalls: number | undefined = 0;
270
286
  let inputTokens = 0;
271
287
  let outputTokens = 0;
272
288
  let costMicrousd = 0;
@@ -282,6 +298,13 @@ export const sumRunTotals = Effect.fn("sumRunTotals")(function* (
282
298
  );
283
299
 
284
300
  modelCalls = yield* checkedAdd("modelCalls", modelCalls, value.modelCalls);
301
+ if (value.webSearchCalls === undefined && value.modelCalls > 0) webSearchCalls = undefined;
302
+ else if (webSearchCalls !== undefined)
303
+ webSearchCalls = yield* checkedAdd(
304
+ "webSearchCalls",
305
+ webSearchCalls,
306
+ value.webSearchCalls ?? 0,
307
+ );
285
308
  inputTokens = yield* checkedAdd("inputTokens", inputTokens, value.inputTokens);
286
309
  outputTokens = yield* checkedAdd("outputTokens", outputTokens, value.outputTokens);
287
310
  costMicrousd = yield* checkedAdd("costMicrousd", costMicrousd, value.costMicrousd);
@@ -316,6 +339,7 @@ export const sumRunTotals = Effect.fn("sumRunTotals")(function* (
316
339
 
317
340
  return RunTotals.make({
318
341
  modelCalls,
342
+ ...(webSearchCalls === undefined ? {} : { webSearchCalls }),
319
343
  inputTokens,
320
344
  outputTokens,
321
345
  costMicrousd,
@@ -329,6 +353,7 @@ export const sumRunTotals = Effect.fn("sumRunTotals")(function* (
329
353
  export const runTotalsFromSummary = (summary: RunUsageSummary): RunTotals =>
330
354
  RunTotals.make({
331
355
  modelCalls: summary.modelCalls,
356
+ ...(summary.webSearchCalls === undefined ? {} : { webSearchCalls: summary.webSearchCalls }),
332
357
  inputTokens: summary.inputTokens.total,
333
358
  outputTokens: summary.outputTokens.total,
334
359
  costMicrousd: summary.costMicrousd,
@@ -363,6 +388,10 @@ export const summarizeModelUsage = Effect.fn("summarizeModelUsage")(function* (
363
388
  const outputTokens = initial === undefined ? emptyOutputTokens() : { ...initial.outputTokens };
364
389
  const groups = new Map<string, MutableUsageGroup>();
365
390
  let modelCalls = initial?.modelCalls ?? 0;
391
+
392
+ let webSearchCalls =
393
+ initial === undefined || initial.modelCalls === 0 ? 0 : initial.webSearchCalls;
394
+
366
395
  let costMicrousd = initial?.costMicrousd ?? 0;
367
396
  const hasSeedCalls = modelCalls > 0;
368
397
 
@@ -387,6 +416,7 @@ export const summarizeModelUsage = Effect.fn("summarizeModelUsage")(function* (
387
416
 
388
417
  groups.set(key, {
389
418
  ...group,
419
+ webSearchCalls: group.webSearchCalls,
390
420
  inputTokens: { ...group.inputTokens },
391
421
  outputTokens: { ...group.outputTokens },
392
422
  });
@@ -398,6 +428,10 @@ export const summarizeModelUsage = Effect.fn("summarizeModelUsage")(function* (
398
428
  allPricingUnknown &&= call.pricingStatus !== "estimated";
399
429
  allPricingComplete &&= call.pricingStatus === "estimated";
400
430
  modelCalls = yield* checkedAdd("modelCalls", modelCalls, 1);
431
+ webSearchCalls =
432
+ webSearchCalls === undefined || call.webSearchCalls === undefined
433
+ ? undefined
434
+ : yield* checkedAdd("webSearchCalls", webSearchCalls, call.webSearchCalls);
401
435
  inputTokens.total = yield* checkedAdd(
402
436
  "inputTokens.total",
403
437
  inputTokens.total,
@@ -453,6 +487,7 @@ export const summarizeModelUsage = Effect.fn("summarizeModelUsage")(function* (
453
487
  ...(call.serviceTier === undefined ? {} : { serviceTier: call.serviceTier }),
454
488
  ...(call.pricingVersion === undefined ? {} : { pricingVersion: call.pricingVersion }),
455
489
  modelCalls: 0,
490
+ webSearchCalls: 0,
456
491
  inputTokens: emptyInputTokens(),
457
492
  outputTokens: emptyOutputTokens(),
458
493
  costMicrousd: 0,
@@ -460,6 +495,10 @@ export const summarizeModelUsage = Effect.fn("summarizeModelUsage")(function* (
460
495
  groups.set(key, group);
461
496
  }
462
497
  group.modelCalls = yield* checkedAdd("byModel.modelCalls", group.modelCalls, 1);
498
+ group.webSearchCalls =
499
+ group.webSearchCalls === undefined || call.webSearchCalls === undefined
500
+ ? undefined
501
+ : yield* checkedAdd("byModel.webSearchCalls", group.webSearchCalls, call.webSearchCalls);
463
502
  group.inputTokens.total = yield* checkedAdd(
464
503
  "byModel.inputTokens.total",
465
504
  group.inputTokens.total,
@@ -504,6 +543,7 @@ export const summarizeModelUsage = Effect.fn("summarizeModelUsage")(function* (
504
543
 
505
544
  return RunUsageSummary.make({
506
545
  modelCalls,
546
+ ...(webSearchCalls === undefined ? {} : { webSearchCalls }),
507
547
  inputTokens: InputTokenUsage.make(inputTokens),
508
548
  outputTokens: OutputTokenUsage.make(outputTokens),
509
549
  costMicrousd,
@@ -520,6 +560,7 @@ export const summarizeModelUsage = Effect.fn("summarizeModelUsage")(function* (
520
560
  ...(group.serviceTier === undefined ? {} : { serviceTier: group.serviceTier }),
521
561
  ...(group.pricingVersion === undefined ? {} : { pricingVersion: group.pricingVersion }),
522
562
  modelCalls: group.modelCalls,
563
+ ...(group.webSearchCalls === undefined ? {} : { webSearchCalls: group.webSearchCalls }),
523
564
  inputTokens: InputTokenUsage.make(group.inputTokens),
524
565
  outputTokens: OutputTokenUsage.make(group.outputTokens),
525
566
  costMicrousd: group.costMicrousd,
@@ -4559,6 +4559,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
4559
4559
  ctx: AttemptAppendContext,
4560
4560
  submission: SubmissionSnapshot,
4561
4561
  tokenRef: Ref.Ref<OwnershipToken>,
4562
+ renewAtRef: Ref.Ref<number>,
4562
4563
  records: ReadonlyArray<CanonicalRecordEnvelope>,
4563
4564
  canonical: Stream.Stream<CanonicalRecordEnvelope, ThreadStoreError | ThreadNotMaterialized>,
4564
4565
  canonicalThrough: CanonicalSequence,
@@ -5258,6 +5259,9 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
5258
5259
  );
5259
5260
 
5260
5261
  return RunUsageSummary.make({
5262
+ ...(legacyCalls === 0 && detailed.webSearchCalls !== undefined
5263
+ ? { webSearchCalls: detailed.webSearchCalls }
5264
+ : {}),
5261
5265
  modelCalls,
5262
5266
  inputTokens: InputTokenUsage.make({
5263
5267
  total: inputTokens,
@@ -7798,18 +7802,33 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
7798
7802
  // Liveness only: the lease keeps the claim visible; correctness stays with the epoch fence.
7799
7803
  // OwnershipLost ends the race and interrupts the Run fiber cleanly.
7800
7804
  const renewal = halt(
7801
- Effect.repeat(
7802
- Effect.gen(function* () {
7803
- const ownershipToken = yield* Ref.get(tokenRef);
7805
+ Effect.gen(function* () {
7806
+ // Binding selection and preparation consume the acquired lease too. Carry the next
7807
+ // deadline across model continuations; an overdue first renewal must run immediately.
7808
+ const renewAt = yield* Ref.get(renewAtRef);
7809
+ const now = yield* Clock.currentTimeMillis;
7804
7810
 
7805
- const renewal = yield* ledger.renewOwnership(
7806
- RenewOwnershipRequest.make({ submissionId, ownershipToken }),
7807
- );
7811
+ yield* Effect.sleep(Math.max(0, renewAt - now));
7812
+ yield* Effect.repeat(
7813
+ Effect.gen(function* () {
7814
+ const ownershipToken = yield* Ref.get(tokenRef);
7815
+ const renewingAt = yield* Clock.currentTimeMillis;
7816
+
7817
+ const renewal = yield* ledger.renewOwnership(
7818
+ RenewOwnershipRequest.make({ submissionId, ownershipToken }),
7819
+ );
7808
7820
 
7809
- yield* Ref.set(tokenRef, renewal.ownershipToken);
7810
- }).pipe(Effect.uninterruptible),
7811
- { schedule: Schedule.spaced(config.leaseRenewalInterval) },
7812
- ).pipe(Effect.andThen(Effect.never)),
7821
+ yield* Ref.set(tokenRef, renewal.ownershipToken);
7822
+ yield* Ref.set(
7823
+ renewAtRef,
7824
+ renewingAt + Duration.toMillis(config.leaseRenewalInterval),
7825
+ );
7826
+ }).pipe(Effect.uninterruptible),
7827
+ { schedule: Schedule.spaced(config.leaseRenewalInterval) },
7828
+ );
7829
+
7830
+ return yield* Effect.never;
7831
+ }),
7813
7832
  );
7814
7833
 
7815
7834
  const execution = Effect.raceFirst(consume, Effect.raceFirst(abortWatcher, renewal));
@@ -8031,6 +8050,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8031
8050
  threadId: ThreadId,
8032
8051
  claim: Claim,
8033
8052
  tokenRef: Ref.Ref<OwnershipToken>,
8053
+ renewAtRef: Ref.Ref<number>,
8034
8054
  resumeAfterRetention: () => void,
8035
8055
  onHandoff: (nextSubmissionId: SubmissionId) => void,
8036
8056
  yieldAfter?: DateTime.Utc,
@@ -8467,6 +8487,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8467
8487
  ctx,
8468
8488
  submission,
8469
8489
  tokenRef,
8490
+ renewAtRef,
8470
8491
  currentRecords,
8471
8492
  canonical,
8472
8493
  tail.tailSequence,
@@ -8647,6 +8668,8 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8647
8668
  threadId: ThreadId,
8648
8669
  handoff?: ClaimHandoff,
8649
8670
  ) {
8671
+ const acquiringAt = yield* Clock.currentTimeMillis;
8672
+
8650
8673
  const claimed = yield* ledger.claim(
8651
8674
  ClaimRequest.make({
8652
8675
  threadId,
@@ -8659,6 +8682,10 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8659
8682
  const claim = claimed.value;
8660
8683
  const tokenRef = yield* Ref.make(claim.ownershipToken);
8661
8684
 
8685
+ const renewAtRef = yield* Ref.make(
8686
+ acquiringAt + Duration.toMillis(config.leaseRenewalInterval),
8687
+ );
8688
+
8662
8689
  yield* Effect.addFinalizer(() =>
8663
8690
  Ref.get(tokenRef).pipe(
8664
8691
  Effect.flatMap((ownershipToken) =>
@@ -8681,7 +8708,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8681
8708
  message: "The submission adapter did not honor the requested handoff",
8682
8709
  });
8683
8710
 
8684
- return Option.some({ claim, tokenRef });
8711
+ return Option.some({ claim, tokenRef, renewAtRef });
8685
8712
  }, Effect.uninterruptible);
8686
8713
 
8687
8714
  const processThreadHead = (
@@ -8701,7 +8728,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8701
8728
  const claimed = yield* acquireClaim(threadId, handoff);
8702
8729
 
8703
8730
  if (Option.isNone(claimed)) return Option.none();
8704
- const { claim, tokenRef } = claimed.value;
8731
+ const { claim, tokenRef, renewAtRef } = claimed.value;
8705
8732
 
8706
8733
  const attributes = {
8707
8734
  threadId,
@@ -8781,6 +8808,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
8781
8808
  attemptThreadId,
8782
8809
  attemptClaim,
8783
8810
  tokenRef,
8811
+ renewAtRef,
8784
8812
  () => {
8785
8813
  resumeAfterRetention = true;
8786
8814
  },
@@ -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