deepline 0.1.182 → 0.1.184

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 (69) hide show
  1. package/dist/bundling-sources/apps/play-runner-workers/src/child-play-await.ts +203 -6
  2. package/dist/bundling-sources/apps/play-runner-workers/src/child-play-submit.ts +8 -1
  3. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +639 -109
  4. package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +3 -2
  5. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +1752 -1899
  6. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +116 -0
  7. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +542 -78
  8. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +292 -11
  9. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/run-work-dispatcher.ts +519 -0
  10. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +2381 -0
  11. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +18 -5
  12. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/work-budget.ts +130 -0
  13. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/worker-platform-budget.ts +39 -0
  14. package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +2 -0
  15. package/dist/bundling-sources/sdk/src/client.ts +41 -0
  16. package/dist/bundling-sources/sdk/src/http.ts +23 -8
  17. package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +56 -3
  18. package/dist/bundling-sources/sdk/src/release.ts +2 -2
  19. package/dist/bundling-sources/sdk/src/types.ts +2 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +133 -10
  21. package/dist/bundling-sources/shared_libs/play-runtime/auth-scope-resolver.ts +92 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +4 -4
  23. package/dist/bundling-sources/shared_libs/play-runtime/batching-types.ts +8 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/child-run-id.ts +101 -0
  25. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1612 -287
  26. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +105 -6
  27. package/dist/bundling-sources/shared_libs/play-runtime/dedup-backend.ts +0 -0
  28. package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +1 -0
  29. package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +4 -0
  30. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +116 -16
  31. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +186 -20
  32. package/dist/bundling-sources/shared_libs/play-runtime/dynamic-worker-version.ts +2 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/execution-ledger-store.ts +133 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +245 -0
  35. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -5
  36. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +14 -0
  37. package/dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts +52 -0
  38. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +41 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +3 -2
  40. package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +124 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +6 -2
  42. package/dist/bundling-sources/shared_libs/play-runtime/row-isolation.ts +48 -2
  43. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +158 -15
  44. package/dist/bundling-sources/shared_libs/play-runtime/run-terminal-source.ts +45 -0
  45. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +9 -0
  46. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-paths.ts +21 -0
  47. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +2038 -262
  48. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +42 -0
  49. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-attempt-state-machine.ts +183 -0
  50. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-errors.ts +88 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +8 -1
  52. package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +103 -0
  53. package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +19 -1
  54. package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +343 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +43 -0
  56. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +11 -0
  57. package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +437 -0
  58. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +83 -6
  59. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +28 -11
  60. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +38 -6
  61. package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +24 -17
  62. package/dist/cli/index.js +76 -17
  63. package/dist/cli/index.mjs +76 -17
  64. package/dist/index.d.mts +2 -0
  65. package/dist/index.d.ts +2 -0
  66. package/dist/index.js +63 -9
  67. package/dist/index.mjs +63 -9
  68. package/dist/plays/bundle-play-file.mjs +20 -6
  69. package/package.json +1 -1
@@ -3,10 +3,20 @@ import {
3
3
  type WorkReceipt,
4
4
  type WorkReceiptClaim,
5
5
  type WorkReceiptCommand,
6
+ type WorkReceiptFailureKind,
6
7
  type WorkReceiptGetCommand,
7
8
  type WorkReceiptStatus,
8
- type WorkReceiptStore,
9
9
  } from '../../../../shared_libs/play-runtime/work-receipts';
10
+ import type { ExecutionLedgerStore } from '../../../../shared_libs/play-runtime/execution-ledger-store';
11
+ import {
12
+ PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
13
+ runtimeLeaseHeartbeatIntervalMs,
14
+ } from '../../../../shared_libs/play-runtime/lease-policy';
15
+ import {
16
+ isPlayExecutionSuspendedError,
17
+ isPlayRowExecutionSuspendedError,
18
+ } from '../../../../shared_libs/play-runtime/suspension';
19
+ import type { WorkerWorkBudgetMeter } from './work-budget';
10
20
 
11
21
  export type RuntimeReceiptStatus = WorkReceiptStatus;
12
22
 
@@ -17,14 +27,34 @@ export type WorkerRuntimeReceiptGetCommand = WorkReceiptGetCommand;
17
27
 
18
28
  export type WorkerRuntimeReceiptClaim = WorkReceiptClaim;
19
29
 
20
- export type WorkerRuntimeReceiptStore = WorkReceiptStore;
30
+ export type WorkerRuntimeReceiptStore = Pick<
31
+ ExecutionLedgerStore,
32
+ 'claimReceipt' | 'completeReceipt' | 'failReceipt'
33
+ > &
34
+ Partial<
35
+ Pick<
36
+ ExecutionLedgerStore,
37
+ | 'getReceipt'
38
+ | 'claimReceipts'
39
+ | 'completeReceipts'
40
+ | 'failReceipts'
41
+ | 'releaseReceipt'
42
+ | 'heartbeatReceipts'
43
+ >
44
+ >;
21
45
 
22
46
  type RuntimeReceiptContext = {
23
47
  orgId?: string | null;
24
48
  playName: string;
25
49
  runId: string;
50
+ runAttempt?: number | null;
26
51
  key: string;
27
52
  receiptStore: WorkerRuntimeReceiptStore;
53
+ budgetMeter?: WorkerWorkBudgetMeter;
54
+ };
55
+
56
+ export type WorkerRuntimeReceiptExecutionContext = {
57
+ leaseId: string | null;
28
58
  };
29
59
 
30
60
  const WORKER_RECEIPT_DEFAULT_WAIT_MS = 300_000;
@@ -32,6 +62,9 @@ const WORKER_RECEIPT_WAIT_DELAY_MS = 250;
32
62
  const WORKER_RECEIPT_WAIT_MAX_ATTEMPTS = Math.ceil(
33
63
  WORKER_RECEIPT_DEFAULT_WAIT_MS / WORKER_RECEIPT_WAIT_DELAY_MS,
34
64
  );
65
+ const WORKER_RECEIPT_HEARTBEAT_INTERVAL_MS = runtimeLeaseHeartbeatIntervalMs(
66
+ PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
67
+ );
35
68
 
36
69
  class RuntimeReceiptWaitTimeoutError extends Error {
37
70
  constructor(key: string) {
@@ -40,6 +73,32 @@ class RuntimeReceiptWaitTimeoutError extends Error {
40
73
  }
41
74
  }
42
75
 
76
+ export class RuntimeLeaseLostError extends Error {
77
+ readonly keys: string[];
78
+ readonly runId: string;
79
+ readonly leaseId: string;
80
+
81
+ constructor(input: {
82
+ keys: string[];
83
+ runId: string;
84
+ leaseId: string;
85
+ cause?: unknown;
86
+ }) {
87
+ const keySummary =
88
+ input.keys.length === 1
89
+ ? input.keys[0]!
90
+ : `${input.keys.length} receipts`;
91
+ super(
92
+ `Runtime receipt ${keySummary} lost lease ${input.leaseId} before execution finished.`,
93
+ { cause: input.cause },
94
+ );
95
+ this.name = 'RuntimeLeaseLostError';
96
+ this.keys = input.keys;
97
+ this.runId = input.runId;
98
+ this.leaseId = input.leaseId;
99
+ }
100
+ }
101
+
43
102
  function scopedReceiptKey(input: {
44
103
  orgId?: string | null;
45
104
  playName: string;
@@ -60,6 +119,15 @@ function errorMessage(error: unknown): string {
60
119
  return error instanceof Error ? error.message : String(error);
61
120
  }
62
121
 
122
+ function runtimeReceiptFailureKindForError(
123
+ error: unknown,
124
+ ): WorkReceiptFailureKind {
125
+ return isPlayExecutionSuspendedError(error) ||
126
+ isPlayRowExecutionSuspendedError(error)
127
+ ? 'repairable'
128
+ : 'terminal';
129
+ }
130
+
63
131
  async function waitForReusableReceipt<T>(input: {
64
132
  key: string;
65
133
  playName: string;
@@ -94,6 +162,141 @@ async function waitForReusableReceipt<T>(input: {
94
162
  throw new RuntimeReceiptWaitTimeoutError(input.key);
95
163
  }
96
164
 
165
+ type WorkerRuntimeReceiptHeartbeatTarget = {
166
+ key: string | null | undefined;
167
+ leaseId: string | null | undefined;
168
+ };
169
+
170
+ function heartbeatGroups(
171
+ targets: WorkerRuntimeReceiptHeartbeatTarget[],
172
+ ): Array<{ leaseId: string; keys: string[] }> {
173
+ const byLeaseId = new Map<string, string[]>();
174
+ for (const target of targets) {
175
+ const key = target.key?.trim();
176
+ const leaseId = target.leaseId?.trim();
177
+ if (!key || !leaseId) continue;
178
+ const keys = byLeaseId.get(leaseId);
179
+ if (keys) {
180
+ keys.push(key);
181
+ } else {
182
+ byLeaseId.set(leaseId, [key]);
183
+ }
184
+ }
185
+ return Array.from(byLeaseId, ([leaseId, keys]) => ({ leaseId, keys }));
186
+ }
187
+
188
+ function buildLeaseLostError(input: {
189
+ keys: string[];
190
+ runId: string;
191
+ leaseId: string;
192
+ cause?: unknown;
193
+ }): RuntimeLeaseLostError {
194
+ return new RuntimeLeaseLostError(input);
195
+ }
196
+
197
+ export async function executeWithWorkerReceiptHeartbeats<T>(input: {
198
+ playName: string;
199
+ runId: string;
200
+ runAttempt?: number | null;
201
+ receiptStore?: WorkerRuntimeReceiptStore | null;
202
+ budgetMeter?: WorkerWorkBudgetMeter;
203
+ targets: WorkerRuntimeReceiptHeartbeatTarget[];
204
+ heartbeatIntervalMs?: number;
205
+ execute: () => Promise<T> | T;
206
+ }): Promise<T> {
207
+ const groups = heartbeatGroups(input.targets);
208
+ const heartbeatReceipts = input.receiptStore?.heartbeatReceipts;
209
+ if (groups.length === 0 || !heartbeatReceipts) {
210
+ return await input.execute();
211
+ }
212
+
213
+ const heartbeatIntervalMs =
214
+ input.heartbeatIntervalMs ?? WORKER_RECEIPT_HEARTBEAT_INTERVAL_MS;
215
+ if (!Number.isFinite(heartbeatIntervalMs) || heartbeatIntervalMs <= 0) {
216
+ throw new Error('Runtime receipt heartbeat interval must be positive.');
217
+ }
218
+
219
+ let stopped = false;
220
+ let timeout: ReturnType<typeof setTimeout> | null = null;
221
+ let rejectLeaseLost!: (error: RuntimeLeaseLostError) => void;
222
+ const leaseLost = new Promise<never>((_, reject) => {
223
+ rejectLeaseLost = reject;
224
+ });
225
+
226
+ const stop = () => {
227
+ stopped = true;
228
+ if (timeout !== null) {
229
+ clearTimeout(timeout);
230
+ timeout = null;
231
+ }
232
+ };
233
+
234
+ const heartbeatOnce = async () => {
235
+ for (const group of groups) {
236
+ input.budgetMeter?.count('heartbeat');
237
+ const receipts = await heartbeatReceipts({
238
+ playName: input.playName,
239
+ runId: input.runId,
240
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
241
+ leaseId: group.leaseId,
242
+ keys: group.keys,
243
+ });
244
+ for (let index = 0; index < group.keys.length; index += 1) {
245
+ const receipt = receipts[index];
246
+ if (
247
+ !receipt ||
248
+ receipt.status !== 'running' ||
249
+ receipt.leaseId !== group.leaseId
250
+ ) {
251
+ throw buildLeaseLostError({
252
+ keys: group.keys,
253
+ runId: input.runId,
254
+ leaseId: group.leaseId,
255
+ });
256
+ }
257
+ }
258
+ }
259
+ };
260
+
261
+ const schedule = () => {
262
+ timeout = setTimeout(() => {
263
+ void (async () => {
264
+ try {
265
+ await heartbeatOnce();
266
+ } catch (error) {
267
+ stop();
268
+ if (error instanceof RuntimeLeaseLostError) {
269
+ rejectLeaseLost(error);
270
+ } else {
271
+ rejectLeaseLost(
272
+ buildLeaseLostError({
273
+ keys: groups.flatMap((group) => group.keys),
274
+ runId: input.runId,
275
+ leaseId: groups[0]?.leaseId ?? 'unknown',
276
+ cause: error,
277
+ }),
278
+ );
279
+ }
280
+ return;
281
+ }
282
+ if (!stopped) {
283
+ schedule();
284
+ }
285
+ })();
286
+ }, heartbeatIntervalMs);
287
+ };
288
+
289
+ await heartbeatOnce();
290
+ schedule();
291
+ const execution = Promise.resolve().then(input.execute);
292
+ execution.catch(() => {});
293
+ try {
294
+ return await Promise.race([execution, leaseLost]);
295
+ } finally {
296
+ stop();
297
+ }
298
+ }
299
+
97
300
  function shouldRepairSameRunRunningReceipt(input: {
98
301
  repairRunningReceiptForSameRun?: boolean;
99
302
  currentRunId: string;
@@ -111,19 +314,65 @@ async function executeAndPersistReceipt<T>(input: {
111
314
  key: string;
112
315
  playName: string;
113
316
  runId: string;
114
- execute: () => Promise<T> | T;
317
+ runAttempt?: number | null;
318
+ leaseId?: string | null;
319
+ execute: (context: WorkerRuntimeReceiptExecutionContext) => Promise<T> | T;
115
320
  receiptStore: WorkerRuntimeReceiptStore;
116
321
  ownership: 'claimed' | 'reconciled';
322
+ budgetMeter?: WorkerWorkBudgetMeter;
323
+ heartbeatIntervalMs?: number;
117
324
  }): Promise<T> {
118
325
  let output: T;
119
326
  try {
120
- output = await input.execute();
327
+ const leaseId = input.leaseId?.trim() || null;
328
+ output = await executeWithWorkerReceiptHeartbeats({
329
+ playName: input.playName,
330
+ runId: input.runId,
331
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
332
+ receiptStore: input.receiptStore,
333
+ budgetMeter: input.budgetMeter,
334
+ heartbeatIntervalMs: input.heartbeatIntervalMs,
335
+ targets: [{ key: input.key, leaseId }],
336
+ execute: () =>
337
+ input.execute({
338
+ leaseId,
339
+ }),
340
+ });
121
341
  } catch (error) {
342
+ if (error instanceof RuntimeLeaseLostError) {
343
+ throw error;
344
+ }
345
+ if (
346
+ isPlayExecutionSuspendedError(error) ||
347
+ isPlayRowExecutionSuspendedError(error)
348
+ ) {
349
+ if (!input.receiptStore.releaseReceipt) {
350
+ throw new Error(
351
+ `Runtime receipt ${input.key} suspended but the receipt store cannot release ownership: ${errorMessage(error)}`,
352
+ );
353
+ }
354
+ const released = await input.receiptStore.releaseReceipt({
355
+ playName: input.playName,
356
+ runId: input.runId,
357
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
358
+ key: input.key,
359
+ ...(input.leaseId ? { leaseId: input.leaseId } : {}),
360
+ });
361
+ if (!released || released.status !== 'pending') {
362
+ throw new Error(
363
+ `Runtime receipt ${input.key} suspended but failed to release ownership: ${errorMessage(error)}`,
364
+ );
365
+ }
366
+ throw error;
367
+ }
122
368
  const failed = await input.receiptStore.failReceipt({
123
369
  playName: input.playName,
124
370
  runId: input.runId,
371
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
125
372
  key: input.key,
373
+ ...(input.leaseId ? { leaseId: input.leaseId } : {}),
126
374
  error: errorMessage(error),
375
+ failureKind: runtimeReceiptFailureKindForError(error),
127
376
  });
128
377
  if (!failed) {
129
378
  throw new Error(
@@ -136,16 +385,20 @@ async function executeAndPersistReceipt<T>(input: {
136
385
  const completed = await input.receiptStore.completeReceipt({
137
386
  playName: input.playName,
138
387
  runId: input.runId,
388
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
139
389
  key: input.key,
390
+ ...(input.leaseId ? { leaseId: input.leaseId } : {}),
140
391
  output,
141
392
  });
142
- if (!completed) {
143
- return output;
144
- }
145
393
  if (
146
- (completed.status === 'completed' || completed.status === 'skipped') &&
147
- completed.output !== undefined
394
+ !completed ||
395
+ (completed.status !== 'completed' && completed.status !== 'skipped')
148
396
  ) {
397
+ throw new Error(
398
+ `Runtime receipt ${input.key} lost receipt ownership before completion.`,
399
+ );
400
+ }
401
+ if (completed.output !== undefined) {
149
402
  return receiptOutput<T>(completed);
150
403
  }
151
404
  return output;
@@ -153,11 +406,13 @@ async function executeAndPersistReceipt<T>(input: {
153
406
 
154
407
  export async function runWorkerRuntimeReceiptBoundary<T>(
155
408
  input: RuntimeReceiptContext & {
156
- execute: () => Promise<T> | T;
409
+ execute: (context: WorkerRuntimeReceiptExecutionContext) => Promise<T> | T;
157
410
  repairRunningReceiptForSameRun?: boolean;
158
411
  repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
159
412
  runningReceiptWaitMaxAttempts?: number;
160
413
  runningReceiptWaitDelayMs?: number;
414
+ runtimeReceiptHeartbeatIntervalMs?: number;
415
+ runtimeReceiptLeaseTtlMs?: number;
161
416
  reclaimRunning?: boolean;
162
417
  },
163
418
  ): Promise<T> {
@@ -166,13 +421,19 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
166
421
  const claimed = await receiptStore.claimReceipt({
167
422
  playName: input.playName,
168
423
  runId: input.runId,
424
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
169
425
  key,
426
+ leaseAware: true,
427
+ leaseTtlMs: input.runtimeReceiptLeaseTtlMs,
170
428
  ...(input.reclaimRunning === true ? { reclaimRunning: true } : {}),
171
429
  });
172
430
  if (claimed.disposition === 'reused') {
173
431
  return receiptOutput<T>(claimed.receipt);
174
432
  }
175
- if (claimed.disposition === 'running') {
433
+ if (
434
+ claimed.disposition === 'running' ||
435
+ claimed.disposition === 'blocked_active_lease'
436
+ ) {
176
437
  if (
177
438
  shouldRepairSameRunRunningReceipt({
178
439
  repairRunningReceiptForSameRun: input.repairRunningReceiptForSameRun,
@@ -183,7 +444,10 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
183
444
  const repaired = await receiptStore.claimReceipt({
184
445
  playName: input.playName,
185
446
  runId: input.runId,
447
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
186
448
  key,
449
+ leaseAware: true,
450
+ leaseTtlMs: input.runtimeReceiptLeaseTtlMs,
187
451
  reclaimRunning: true,
188
452
  });
189
453
  if (repaired.disposition === 'reused') {
@@ -199,9 +463,13 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
199
463
  key,
200
464
  playName: input.playName,
201
465
  runId: input.runId,
466
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
467
+ leaseId: repaired.receipt.leaseId ?? null,
202
468
  execute: input.execute,
203
469
  receiptStore,
204
470
  ownership: 'claimed',
471
+ budgetMeter: input.budgetMeter,
472
+ heartbeatIntervalMs: input.runtimeReceiptHeartbeatIntervalMs,
205
473
  });
206
474
  }
207
475
  }
@@ -226,7 +494,10 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
226
494
  const repaired = await receiptStore.claimReceipt({
227
495
  playName: input.playName,
228
496
  runId: input.runId,
497
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
229
498
  key,
499
+ leaseAware: true,
500
+ leaseTtlMs: input.runtimeReceiptLeaseTtlMs,
230
501
  reclaimRunning: true,
231
502
  });
232
503
  if (repaired.disposition === 'reused') {
@@ -242,9 +513,15 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
242
513
  key,
243
514
  playName: input.playName,
244
515
  runId: input.runId,
516
+ ...(input.runAttempt != null
517
+ ? { runAttempt: input.runAttempt }
518
+ : {}),
519
+ leaseId: repaired.receipt.leaseId ?? null,
245
520
  execute: input.execute,
246
521
  receiptStore,
247
522
  ownership: 'claimed',
523
+ budgetMeter: input.budgetMeter,
524
+ heartbeatIntervalMs: input.runtimeReceiptHeartbeatIntervalMs,
248
525
  });
249
526
  }
250
527
  }
@@ -261,8 +538,12 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
261
538
  key,
262
539
  playName: input.playName,
263
540
  runId: input.runId,
541
+ ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
542
+ leaseId: claimed.receipt.leaseId ?? null,
264
543
  execute: input.execute,
265
544
  receiptStore,
266
545
  ownership: 'claimed',
546
+ budgetMeter: input.budgetMeter,
547
+ heartbeatIntervalMs: input.runtimeReceiptHeartbeatIntervalMs,
267
548
  });
268
549
  }