deepline 0.1.198 → 0.1.200

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 (72) hide show
  1. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +5 -1
  2. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +51 -17
  3. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/README.md +1 -1
  4. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +16 -0
  5. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +40 -9
  6. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +108 -39
  7. package/dist/bundling-sources/sdk/src/http.ts +5 -2
  8. package/dist/bundling-sources/sdk/src/play.ts +1 -1
  9. package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +7 -0
  10. package/dist/bundling-sources/sdk/src/release.ts +2 -2
  11. package/dist/bundling-sources/sdk/src/worker-play-entry.ts +11 -55
  12. package/dist/bundling-sources/shared_libs/play-runtime/adaptive-tool-dispatcher.ts +355 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +172 -73
  14. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1794 -377
  15. package/dist/bundling-sources/shared_libs/play-runtime/ctx-contract.ts +3 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +67 -4
  17. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +161 -39
  18. package/dist/bundling-sources/shared_libs/play-runtime/governor/adaptive-admission.ts +218 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +1 -1
  20. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +115 -25
  21. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +17 -17
  22. package/dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts +22 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +2 -1
  24. package/dist/bundling-sources/shared_libs/play-runtime/live-events.ts +0 -1
  25. package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +265 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +135 -0
  27. package/dist/bundling-sources/shared_libs/play-runtime/play-input.ts +17 -0
  28. package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +0 -3
  29. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +10 -1
  30. package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +9 -15
  31. package/dist/bundling-sources/shared_libs/play-runtime/receipt-completion-sink.ts +262 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/receipt-heartbeat-supervisor.ts +64 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +76 -8
  34. package/dist/bundling-sources/shared_libs/play-runtime/receipt-state-sink.ts +59 -0
  35. package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +3 -2
  36. package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +269 -0
  37. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +2 -3
  38. package/dist/bundling-sources/shared_libs/play-runtime/run-terminal-source.ts +0 -1
  39. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +433 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +227 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +882 -0
  42. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +573 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/bundle.ts +264 -0
  44. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/index.ts +33 -0
  45. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runner-events.ts +35 -0
  46. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +53 -0
  47. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +0 -1
  48. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +459 -103
  49. package/dist/bundling-sources/shared_libs/play-runtime/runtime-constants.ts +12 -0
  50. package/dist/bundling-sources/shared_libs/play-runtime/runtime-infra-mode.ts +23 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-attempt-state-machine.ts +23 -2
  52. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +16 -24
  53. package/dist/bundling-sources/shared_libs/play-runtime/serial-task-queue.ts +26 -0
  54. package/dist/bundling-sources/shared_libs/play-runtime/submit-limits.ts +1 -1
  55. package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +81 -0
  56. package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +54 -9
  57. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +17 -3
  58. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +0 -20
  59. package/dist/bundling-sources/shared_libs/plays/dataset.ts +105 -4
  60. package/dist/bundling-sources/shared_libs/plays/row-identity.ts +4 -11
  61. package/dist/cli/index.js +441 -101
  62. package/dist/cli/index.mjs +441 -101
  63. package/dist/index.d.mts +1 -1
  64. package/dist/index.d.ts +1 -1
  65. package/dist/index.js +8 -8
  66. package/dist/index.mjs +8 -8
  67. package/dist/plays/bundle-play-file.mjs +15 -20
  68. package/package.json +1 -1
  69. package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-diagnosis.ts +0 -321
  70. package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-target.ts +0 -158
  71. package/dist/bundling-sources/shared_libs/temporal/constants.ts +0 -39
  72. package/dist/bundling-sources/shared_libs/temporal/preview-config.ts +0 -150
@@ -5745,7 +5745,11 @@ async function handleCoordinatorWarmup(
5745
5745
  `https://deepline.coordinator.internal/workflow/${encodeURIComponent(runId)}/submit`,
5746
5746
  {
5747
5747
  method: 'POST',
5748
- headers: { 'content-type': 'application/json' },
5748
+ headers: {
5749
+ 'content-type': 'application/json',
5750
+ [COORDINATOR_INTERNAL_TOKEN_HEADER]:
5751
+ env.DEEPLINE_INTERNAL_TOKEN?.trim() ?? '',
5752
+ },
5749
5753
  body: JSON.stringify(params),
5750
5754
  },
5751
5755
  ),
@@ -39,7 +39,7 @@ import {
39
39
  import {
40
40
  STANDARD_PLAY_RUNTIME_LIMIT_LABEL,
41
41
  STANDARD_PLAY_RUNTIME_LIMIT_SECONDS,
42
- } from '../../../shared_libs/temporal/constants';
42
+ } from '../../../shared_libs/play-runtime/runtime-constants';
43
43
  import {
44
44
  createPlayExecutionGovernor,
45
45
  type GovernanceSnapshot,
@@ -194,6 +194,11 @@ import {
194
194
  runtimeLeaseHeartbeatIntervalMs,
195
195
  } from '../../../shared_libs/play-runtime/lease-policy';
196
196
  import { RuntimeSheetRowsBlockedError } from '../../../shared_libs/play-runtime/runtime-sheet-errors';
197
+ import {
198
+ resolveRuntimeMapRowAdmission,
199
+ runtimeMapJsonByteLength,
200
+ } from '../../../shared_libs/play-runtime/map-memory-limits';
201
+ import { createSerialTaskQueue } from '../../../shared_libs/play-runtime/serial-task-queue';
197
202
  // The harness stub forwards leaf calls (validation, runtime-api HTTP) into
198
203
  // the long-lived Play Harness Worker via env.HARNESS. We import the
199
204
  // `setHarnessBinding` setter eagerly so it's available the moment
@@ -4440,10 +4445,14 @@ function createMinimalWorkerCtx(
4440
4445
  );
4441
4446
  const executeWithRuntimeReceipt = async <T>(
4442
4447
  key: string,
4443
- execute: (context: WorkerRuntimeReceiptExecutionContext) => Promise<T> | T,
4448
+ execute: (
4449
+ context: WorkerRuntimeReceiptExecutionContext,
4450
+ wasFailed: boolean,
4451
+ ) => Promise<T> | T,
4444
4452
  options: {
4445
4453
  repairRunningReceiptForSameRun?: boolean;
4446
4454
  reclaimRunning?: boolean;
4455
+ forceFailedRefresh?: boolean;
4447
4456
  repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
4448
4457
  runningReceiptWaitMaxAttempts?: number;
4449
4458
  } = {},
@@ -4454,11 +4463,12 @@ function createMinimalWorkerCtx(
4454
4463
  runId: req.runId,
4455
4464
  key,
4456
4465
  receiptStore,
4457
- execute: async (context) =>
4458
- serializeDurableStepValue(await execute(context)),
4466
+ execute: async (context, wasFailed) =>
4467
+ serializeDurableStepValue(await execute(context, wasFailed)),
4459
4468
  repairRunningReceiptForSameRun:
4460
4469
  options.repairRunningReceiptForSameRun ?? false,
4461
4470
  reclaimRunning: options.reclaimRunning ?? false,
4471
+ forceFailedRefresh: options.forceFailedRefresh ?? false,
4462
4472
  repairRunningReceiptForSameRunAfterWaitTimeout:
4463
4473
  options.repairRunningReceiptForSameRunAfterWaitTimeout ?? false,
4464
4474
  runningReceiptWaitMaxAttempts: options.runningReceiptWaitMaxAttempts,
@@ -5131,7 +5141,32 @@ function createMinimalWorkerCtx(
5131
5141
  // body additionally acquires a global row slot (the Governor's rowMax
5132
5142
  // semaphore) so total in-flight rows across all maps in this isolate stay
5133
5143
  // bounded even when several maps run at once.
5134
- const concurrency = governor.resolveRowConcurrency();
5144
+ const requestedRowConcurrency = governor.resolveRowConcurrency();
5145
+ const largestActiveRowBytes = uniqueRowsToExecuteEntries.reduce(
5146
+ (largest, entry) =>
5147
+ Math.max(
5148
+ largest,
5149
+ runtimeMapJsonByteLength(
5150
+ runtimeCsvExecutionRow(
5151
+ entry.row,
5152
+ pendingRowsByKey.get(entry.rowKey),
5153
+ ),
5154
+ ),
5155
+ ),
5156
+ 0,
5157
+ );
5158
+ const rowAdmission = resolveRuntimeMapRowAdmission({
5159
+ rowCount: rowsToExecute.length,
5160
+ requestedConcurrency: requestedRowConcurrency,
5161
+ largestRowBytes: largestActiveRowBytes,
5162
+ activeRowsBudgetBytes: WORKERS_EDGE_MAP_RESIDENT_ROW_BYTES,
5163
+ });
5164
+ const concurrency = rowAdmission.concurrency;
5165
+ if (rowsToExecute.length > 0 && concurrency === 0) {
5166
+ throw new Error(
5167
+ `RUNTIME_MAP_MEMORY_LIMIT_EXCEEDED:${name} row needs ${rowAdmission.estimatedBytesPerActiveRow} bytes; limit ${WORKERS_EDGE_MAP_RESIDENT_ROW_BYTES}. Reduce or stage blobs.`,
5168
+ );
5169
+ }
5135
5170
  const executedRows: Array<T & Record<string, unknown>> = new Array(
5136
5171
  rowsToExecute.length,
5137
5172
  );
@@ -5191,11 +5226,10 @@ function createMinimalWorkerCtx(
5191
5226
  let pendingPersistRows = 0;
5192
5227
  let pendingPersistBytes = 0;
5193
5228
  let scheduledPersistTimer: ReturnType<typeof setTimeout> | null = null;
5194
- let persistFlushChain: Promise<void> = Promise.resolve();
5195
5229
  let persistFailure: unknown = null;
5196
5230
  let liveUpdateTimer: ReturnType<typeof setTimeout> | null = null;
5197
- let liveFlushChain: Promise<void> = Promise.resolve();
5198
5231
  let liveUpdateFailures = 0;
5232
+ const sheetWriteQueue = createSerialTaskQueue();
5199
5233
 
5200
5234
  const clearScheduledPersistTimer = () => {
5201
5235
  if (scheduledPersistTimer) {
@@ -5326,12 +5360,12 @@ function createMinimalWorkerCtx(
5326
5360
  clearScheduledPersistTimer();
5327
5361
  pendingPersistRows = 0;
5328
5362
  pendingPersistBytes = 0;
5329
- const task = persistFlushChain.then(async () => {
5363
+ const task = sheetWriteQueue.enqueue(async () => {
5330
5364
  const circuitError = runtimePersistenceCircuitError();
5331
5365
  if (circuitError) throw circuitError;
5332
5366
  await persistExecutedRows();
5333
5367
  });
5334
- persistFlushChain = task.catch((error) => {
5368
+ void task.catch((error) => {
5335
5369
  persistFailure ??= error;
5336
5370
  tripRuntimePersistenceLatch(persistenceLatch, error);
5337
5371
  });
@@ -5363,11 +5397,11 @@ function createMinimalWorkerCtx(
5363
5397
  const flushLiveRowUpdates = (): Promise<void> => {
5364
5398
  clearLiveUpdateTimer();
5365
5399
  if (pendingLiveUpdates.length === 0) {
5366
- return liveFlushChain;
5400
+ return sheetWriteQueue.idle();
5367
5401
  }
5368
5402
  const updates = coalesceLiveUpdates(pendingLiveUpdates.splice(0));
5369
5403
  const extraOutputFields = Array.from(generatedOutputFields);
5370
- const task = liveFlushChain.then(async () => {
5404
+ return sheetWriteQueue.enqueue(async () => {
5371
5405
  try {
5372
5406
  await applyLiveMapRowUpdates({
5373
5407
  req,
@@ -5390,8 +5424,6 @@ function createMinimalWorkerCtx(
5390
5424
  }
5391
5425
  }
5392
5426
  });
5393
- liveFlushChain = task.catch(() => undefined);
5394
- return task;
5395
5427
  };
5396
5428
 
5397
5429
  const scheduleLiveRowUpdates = () => {
@@ -5849,9 +5881,8 @@ function createMinimalWorkerCtx(
5849
5881
  });
5850
5882
  try {
5851
5883
  await flushLiveRowUpdates();
5852
- await liveFlushChain;
5853
5884
  await enqueuePersistExecutedRows();
5854
- await persistFlushChain;
5885
+ await sheetWriteQueue.idle();
5855
5886
  if (persistFailure) throw persistFailure;
5856
5887
  recordRunnerPerfTrace({
5857
5888
  req,
@@ -6897,7 +6928,7 @@ function createMinimalWorkerCtx(
6897
6928
  });
6898
6929
  return await executeWithRuntimeReceipt(
6899
6930
  receiptKey,
6900
- async () => {
6931
+ async (receiptContext, wasFailed) => {
6901
6932
  // The Governor owns the play-call lineage: forkChild does the cycle
6902
6933
  // guard, depth/per-parent/playCall/descendant budget charges, and
6903
6934
  // returns the snapshot to thread into the child so budgets accumulate
@@ -6980,7 +7011,9 @@ function createMinimalWorkerCtx(
6980
7011
  options?.timeoutMs == null && !childNeedsWorkflowScheduler,
6981
7012
  body: {
6982
7013
  name: resolvedName,
6983
- childIdempotencyKey: receiptKey,
7014
+ childIdempotencyKey: wasFailed && receiptContext.leaseId
7015
+ ? `${receiptKey}:claim:${receiptContext.leaseId}`
7016
+ : receiptKey,
6984
7017
  input: isRecord(input) ? input : {},
6985
7018
  orgId: req.orgId,
6986
7019
  callbackUrl: req.callbackUrl,
@@ -7259,6 +7292,7 @@ function createMinimalWorkerCtx(
7259
7292
  }
7260
7293
  },
7261
7294
  {
7295
+ forceFailedRefresh: true,
7262
7296
  repairRunningReceiptForSameRunAfterWaitTimeout: true,
7263
7297
  runningReceiptWaitMaxAttempts:
7264
7298
  resolveRuntimeToolReceiptWaitMaxAttempts(input),
@@ -7,7 +7,7 @@ These modules are bundled into every `esm_workers` play artifact. Keep them:
7
7
  - specific to the Cloudflare Worker execution path
8
8
 
9
9
  Do not import broad `shared_libs/play-runtime/*` modules here. Shared runtime
10
- modules are optimized for the Node/Temporal runner and often bring along code
10
+ modules are optimized for the Node runner and often bring along code
11
11
  that is correct but expensive in a per-graphHash Worker isolate.
12
12
 
13
13
  If a helper needs zod, Neon, runtime API transport, validation registries, or
@@ -7,6 +7,7 @@ import {
7
7
  harnessFailRuntimeReceipts,
8
8
  harnessGetRuntimeReceipt,
9
9
  harnessHeartbeatRuntimeReceipts,
10
+ harnessMarkRuntimeReceiptsRunning,
10
11
  harnessReleaseRuntimeReceipt,
11
12
  } from '../../../../sdk/src/plays/harness-stub';
12
13
  import type { PreloadedRuntimeDbSessionInput } from '../../../play-harness-worker/src/rpc-types';
@@ -124,6 +125,21 @@ export function createHarnessWorkerReceiptStore(input: {
124
125
  ...command,
125
126
  });
126
127
  },
128
+ markReceiptsRunning(command) {
129
+ return harnessMarkRuntimeReceiptsRunning({
130
+ executorToken: input.executorToken,
131
+ orgId: input.orgId,
132
+ preloadedDbSessions: input.preloadedDbSessions ?? null,
133
+ userEmail: input.userEmail ?? null,
134
+ runtimeTestFaultHeader: input.runtimeTestFaultHeader ?? null,
135
+ ...leaseTtlField,
136
+ ...command,
137
+ receipts: command.receipts.map((receipt) => ({
138
+ ...runAttemptField,
139
+ ...receipt,
140
+ })),
141
+ });
142
+ },
127
143
  completeReceipt(command) {
128
144
  return harnessCompleteRuntimeReceipt({
129
145
  executorToken: input.executorToken,
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  buildScopedWorkReceiptKey,
3
3
  type WorkReceipt,
4
+ type WorkReceiptBatchMarkRunningCommand,
4
5
  type WorkReceiptClaim,
5
6
  type WorkReceiptCommand,
6
7
  type WorkReceiptFailureKind,
@@ -41,7 +42,14 @@ export type WorkerRuntimeReceiptStore = Pick<
41
42
  | 'releaseReceipt'
42
43
  | 'heartbeatReceipts'
43
44
  >
44
- >;
45
+ > & {
46
+ markReceiptsRunning?: (
47
+ input: WorkReceiptBatchMarkRunningCommand,
48
+ ) => Promise<Array<WorkReceipt | null>>;
49
+ };
50
+
51
+ export type WorkerRuntimeReceiptMarkRunningCommand =
52
+ WorkReceiptBatchMarkRunningCommand;
45
53
 
46
54
  type RuntimeReceiptContext = {
47
55
  orgId?: string | null;
@@ -57,6 +65,11 @@ export type WorkerRuntimeReceiptExecutionContext = {
57
65
  leaseId: string | null;
58
66
  };
59
67
 
68
+ type WorkerRuntimeReceiptExecute<T> = (
69
+ context: WorkerRuntimeReceiptExecutionContext,
70
+ wasFailed: boolean,
71
+ ) => Promise<T> | T;
72
+
60
73
  const WORKER_RECEIPT_DEFAULT_WAIT_MS = 300_000;
61
74
  const WORKER_RECEIPT_WAIT_DELAY_MS = 250;
62
75
  const WORKER_RECEIPT_WAIT_MAX_ATTEMPTS = Math.ceil(
@@ -194,6 +207,21 @@ function buildLeaseLostError(input: {
194
207
  return new RuntimeLeaseLostError(input);
195
208
  }
196
209
 
210
+ function receiptHeartbeatStillOwnsOrSettled(input: {
211
+ receipt: WorkerRuntimeReceipt | null | undefined;
212
+ leaseId: string;
213
+ }): boolean {
214
+ const { receipt } = input;
215
+ if (!receipt) return false;
216
+ if (receipt.status === 'completed' || receipt.status === 'skipped') {
217
+ return true;
218
+ }
219
+ if (receipt.status !== 'running' && receipt.status !== 'queued') {
220
+ return false;
221
+ }
222
+ return receipt.leaseId === input.leaseId;
223
+ }
224
+
197
225
  export async function executeWithWorkerReceiptHeartbeats<T>(input: {
198
226
  playName: string;
199
227
  runId: string;
@@ -244,9 +272,10 @@ export async function executeWithWorkerReceiptHeartbeats<T>(input: {
244
272
  for (let index = 0; index < group.keys.length; index += 1) {
245
273
  const receipt = receipts[index];
246
274
  if (
247
- !receipt ||
248
- receipt.status !== 'running' ||
249
- receipt.leaseId !== group.leaseId
275
+ !receiptHeartbeatStillOwnsOrSettled({
276
+ receipt,
277
+ leaseId: group.leaseId,
278
+ })
250
279
  ) {
251
280
  throw buildLeaseLostError({
252
281
  keys: group.keys,
@@ -316,7 +345,8 @@ async function executeAndPersistReceipt<T>(input: {
316
345
  runId: string;
317
346
  runAttempt?: number | null;
318
347
  leaseId?: string | null;
319
- execute: (context: WorkerRuntimeReceiptExecutionContext) => Promise<T> | T;
348
+ wasFailed?: boolean;
349
+ execute: WorkerRuntimeReceiptExecute<T>;
320
350
  receiptStore: WorkerRuntimeReceiptStore;
321
351
  ownership: 'claimed' | 'reconciled';
322
352
  budgetMeter?: WorkerWorkBudgetMeter;
@@ -334,9 +364,7 @@ async function executeAndPersistReceipt<T>(input: {
334
364
  heartbeatIntervalMs: input.heartbeatIntervalMs,
335
365
  targets: [{ key: input.key, leaseId }],
336
366
  execute: () =>
337
- input.execute({
338
- leaseId,
339
- }),
367
+ input.execute({ leaseId }, input.wasFailed === true),
340
368
  });
341
369
  } catch (error) {
342
370
  if (error instanceof RuntimeLeaseLostError) {
@@ -406,7 +434,7 @@ async function executeAndPersistReceipt<T>(input: {
406
434
 
407
435
  export async function runWorkerRuntimeReceiptBoundary<T>(
408
436
  input: RuntimeReceiptContext & {
409
- execute: (context: WorkerRuntimeReceiptExecutionContext) => Promise<T> | T;
437
+ execute: WorkerRuntimeReceiptExecute<T>;
410
438
  repairRunningReceiptForSameRun?: boolean;
411
439
  repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
412
440
  runningReceiptWaitMaxAttempts?: number;
@@ -414,6 +442,7 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
414
442
  runtimeReceiptHeartbeatIntervalMs?: number;
415
443
  runtimeReceiptLeaseTtlMs?: number;
416
444
  reclaimRunning?: boolean;
445
+ forceFailedRefresh?: boolean;
417
446
  },
418
447
  ): Promise<T> {
419
448
  const key = scopedReceiptKey(input);
@@ -426,6 +455,7 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
426
455
  leaseAware: true,
427
456
  leaseTtlMs: input.runtimeReceiptLeaseTtlMs,
428
457
  ...(input.reclaimRunning === true ? { reclaimRunning: true } : {}),
458
+ ...(input.forceFailedRefresh === true ? { forceFailedRefresh: true } : {}),
429
459
  });
430
460
  if (claimed.disposition === 'reused') {
431
461
  return receiptOutput<T>(claimed.receipt);
@@ -540,6 +570,7 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
540
570
  runId: input.runId,
541
571
  ...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
542
572
  leaseId: claimed.receipt.leaseId ?? null,
573
+ wasFailed: claimed.wasFailed === true,
543
574
  execute: input.execute,
544
575
  receiptStore,
545
576
  ownership: 'claimed',
@@ -15,10 +15,10 @@ import {
15
15
  import {
16
16
  RuntimeReceiptWaitTimeoutError,
17
17
  resolveRuntimeToolReceiptWaitMaxAttempts,
18
+ runtimeReceiptFailureKindForError,
18
19
  waitForCompletedRuntimeReceipt,
19
20
  } from '../../../../shared_libs/play-runtime/durable-receipt-execution';
20
21
  import type { PlayExecutionGovernor } from '../../../../shared_libs/play-runtime/governor/governor';
21
- import { getToolHttpErrorReceiptFailureKind } from '../../../../shared_libs/play-runtime/tool-http-errors';
22
22
  import { ToolExecuteAuthScopeChangedError } from '../../../../shared_libs/play-runtime/tool-execute-retry-policy';
23
23
  import { getPlayRuntimeBatchStrategy } from '../../../../shared_libs/play-runtime/play-runtime-batching-registry';
24
24
  import {
@@ -46,10 +46,7 @@ import {
46
46
  type ToolResultMetadataInput,
47
47
  } from '../../../../shared_libs/play-runtime/tool-result';
48
48
  import { toolExecutionMetadataForOutcome } from '../../../../shared_libs/play-runtime/tool-execution-outcome';
49
- import {
50
- buildScopedWorkReceiptKey,
51
- type WorkReceiptFailureKind,
52
- } from '../../../../shared_libs/play-runtime/work-receipts';
49
+ import { buildScopedWorkReceiptKey } from '../../../../shared_libs/play-runtime/work-receipts';
53
50
  import {
54
51
  canReclaimFailedWorkerToolReceipt,
55
52
  canReclaimTimedOutWorkerToolReceipt,
@@ -213,6 +210,7 @@ type ClaimedWorkerToolBatchRequest = {
213
210
  request: WorkerToolBatchRequest;
214
211
  receiptKey: string | null;
215
212
  receiptLeaseId: string | null;
213
+ receiptStatus: WorkerRuntimeReceipt['status'] | null;
216
214
  providerForce?: boolean;
217
215
  followers: WorkerToolBatchRequest[];
218
216
  };
@@ -281,18 +279,6 @@ async function sleepWorkerMs(ms: number): Promise<void> {
281
279
  await new Promise((resolve) => setTimeout(resolve, ms));
282
280
  }
283
281
 
284
- function workReceiptFailureKindForError(
285
- error: unknown,
286
- ): WorkReceiptFailureKind {
287
- if (
288
- isPlayExecutionSuspendedError(error) ||
289
- isPlayRowExecutionSuspendedError(error)
290
- ) {
291
- return 'repairable';
292
- }
293
- return getToolHttpErrorReceiptFailureKind(error) ?? 'terminal';
294
- }
295
-
296
282
  export class RuntimeReceiptPersistenceError extends Error {
297
283
  readonly receiptKeys: string[];
298
284
  readonly errors: unknown[];
@@ -312,7 +298,7 @@ export class RuntimeReceiptPersistenceError extends Error {
312
298
  }
313
299
 
314
300
  function receiptPersistenceError(input: {
315
- operation: 'complete' | 'fail';
301
+ operation: 'mark-running' | 'complete' | 'fail';
316
302
  receiptKeys: string[];
317
303
  cause: unknown;
318
304
  }): RuntimeReceiptPersistenceError {
@@ -434,7 +420,18 @@ function toolMetadataFallback(toolId: string): ToolResultMetadataInput {
434
420
  return { toolId };
435
421
  }
436
422
 
437
- function workerDurableToolCallCacheKey(input: {
423
+ function durableReceiptInputForTool(
424
+ toolId: string,
425
+ input: Record<string, unknown>,
426
+ ): Record<string, unknown> {
427
+ if (toolId !== 'test_rate_limit') {
428
+ return input;
429
+ }
430
+ const { simulated_delay_ms: _simulatedDelayMs, ...semanticInput } = input;
431
+ return semanticInput;
432
+ }
433
+
434
+ export function workerDurableToolCallCacheKey(input: {
438
435
  req: WorkerToolDispatchRunRequest;
439
436
  toolId: string;
440
437
  requestInput: Record<string, unknown>;
@@ -446,7 +443,7 @@ function workerDurableToolCallCacheKey(input: {
446
443
  orgId: input.req.orgId,
447
444
  playLocalScope: input.req.playName,
448
445
  toolId: input.toolId,
449
- requestInput: input.requestInput,
446
+ requestInput: durableReceiptInputForTool(input.toolId, input.requestInput),
450
447
  authScopeDigest: buildDurableToolCallAuthScopeDigest({
451
448
  orgId: input.req.orgId,
452
449
  toolId: input.toolId,
@@ -928,6 +925,7 @@ export class WorkerToolBatchScheduler {
928
925
  request,
929
926
  receiptKey: input.receiptKey,
930
927
  receiptLeaseId: claim.receipt.leaseId ?? null,
928
+ receiptStatus: claim.receipt.status,
931
929
  providerForce: true,
932
930
  followers,
933
931
  },
@@ -995,6 +993,7 @@ export class WorkerToolBatchScheduler {
995
993
  request,
996
994
  receiptKey: input.receiptKey,
997
995
  receiptLeaseId: claim.receipt.leaseId ?? null,
996
+ receiptStatus: claim.receipt.status,
998
997
  providerForce: true,
999
998
  followers,
1000
999
  },
@@ -1136,6 +1135,7 @@ export class WorkerToolBatchScheduler {
1136
1135
  request,
1137
1136
  receiptKey: null,
1138
1137
  receiptLeaseId: null,
1138
+ receiptStatus: null,
1139
1139
  followers: [],
1140
1140
  })),
1141
1141
  deferredClaimedRequests: [],
@@ -1159,6 +1159,7 @@ export class WorkerToolBatchScheduler {
1159
1159
  request,
1160
1160
  receiptKey: null,
1161
1161
  receiptLeaseId: null,
1162
+ receiptStatus: null,
1162
1163
  followers: [],
1163
1164
  });
1164
1165
  }
@@ -1277,6 +1278,7 @@ export class WorkerToolBatchScheduler {
1277
1278
  request,
1278
1279
  receiptKey,
1279
1280
  receiptLeaseId: claim.receipt.leaseId ?? null,
1281
+ receiptStatus: claim.receipt.status,
1280
1282
  providerForce: groupState.forceDurableRefresh,
1281
1283
  followers,
1282
1284
  });
@@ -1340,6 +1342,77 @@ export class WorkerToolBatchScheduler {
1340
1342
  throw completedResult.error;
1341
1343
  }
1342
1344
 
1345
+ private async markAdmittedToolRequestsRunning(
1346
+ claimedRequests: ClaimedWorkerToolBatchRequest[],
1347
+ ): Promise<void> {
1348
+ const queued = claimedRequests.filter(
1349
+ (claimed) =>
1350
+ claimed.receiptKey !== null && claimed.receiptStatus !== 'running',
1351
+ );
1352
+ if (queued.length === 0) return;
1353
+
1354
+ const markReceiptsRunning = this.options.receiptStore?.markReceiptsRunning;
1355
+ const receiptKeys = queued.map((claimed) => claimed.receiptKey!);
1356
+ if (!markReceiptsRunning) {
1357
+ throw receiptPersistenceError({
1358
+ operation: 'mark-running',
1359
+ receiptKeys,
1360
+ cause: new Error(
1361
+ 'Queued runtime receipts require a fenced mark-running store operation.',
1362
+ ),
1363
+ });
1364
+ }
1365
+
1366
+ const runId = this.options.req.runId;
1367
+ const runAttempt = this.options.req.runAttempt ?? 0;
1368
+ this.options.budgetMeter?.count('receipt');
1369
+ let marked: Array<WorkerRuntimeReceipt | null>;
1370
+ try {
1371
+ marked = await markReceiptsRunning({
1372
+ playName: this.options.req.playName,
1373
+ leaseTtlMs: this.options.runtimeReceiptLeaseTtlMs,
1374
+ receipts: queued.map((claimed) => ({
1375
+ runId,
1376
+ runAttempt,
1377
+ key: claimed.receiptKey!,
1378
+ leaseId: claimed.receiptLeaseId,
1379
+ })),
1380
+ });
1381
+ } catch (error) {
1382
+ throw receiptPersistenceError({
1383
+ operation: 'mark-running',
1384
+ receiptKeys,
1385
+ cause: error,
1386
+ });
1387
+ }
1388
+
1389
+ for (let index = 0; index < queued.length; index += 1) {
1390
+ const claimed = queued[index]!;
1391
+ const receipt = marked[index];
1392
+ const leaseId = claimed.receiptLeaseId?.trim() || null;
1393
+ const ownerRunId = receipt?.leaseOwnerRunId ?? receipt?.runId ?? null;
1394
+ if (
1395
+ !receipt ||
1396
+ receipt.key !== claimed.receiptKey ||
1397
+ receipt.status !== 'running' ||
1398
+ !leaseId ||
1399
+ receipt.leaseId !== leaseId ||
1400
+ ownerRunId !== runId ||
1401
+ receipt.leaseOwnerAttempt !== runAttempt
1402
+ ) {
1403
+ throw new RuntimeLeaseLostError({
1404
+ keys: [claimed.receiptKey!],
1405
+ runId,
1406
+ leaseId: leaseId ?? 'missing',
1407
+ });
1408
+ }
1409
+ }
1410
+
1411
+ for (const claimed of queued) {
1412
+ claimed.receiptStatus = 'running';
1413
+ }
1414
+ }
1415
+
1343
1416
  private completedDurableToolRequestResult(input: {
1344
1417
  claimed: ClaimedWorkerToolBatchRequest;
1345
1418
  ownerResult: unknown;
@@ -1380,14 +1453,8 @@ export class WorkerToolBatchScheduler {
1380
1453
  };
1381
1454
  }
1382
1455
  return {
1383
- ok: false,
1384
- error: receiptPersistenceError({
1385
- operation: 'complete',
1386
- receiptKeys: [claimed.receiptKey ?? claimed.request.cacheKey],
1387
- cause: new Error(
1388
- `Durable tool call ${claimed.receiptKey ?? claimed.request.cacheKey} lost receipt ownership before completion.`,
1389
- ),
1390
- }),
1456
+ ok: true,
1457
+ value: ownerResult,
1391
1458
  };
1392
1459
  }
1393
1460
 
@@ -1803,7 +1870,7 @@ export class WorkerToolBatchScheduler {
1803
1870
  key: claimed.receiptKey,
1804
1871
  leaseId: claimed.receiptLeaseId,
1805
1872
  error: error instanceof Error ? error.message : String(error),
1806
- failureKind: workReceiptFailureKindForError(error),
1873
+ failureKind: runtimeReceiptFailureKindForError(error),
1807
1874
  });
1808
1875
  } catch (receiptError) {
1809
1876
  if (this.options.persistenceLatch) {
@@ -1900,7 +1967,7 @@ export class WorkerToolBatchScheduler {
1900
1967
  key: claimed.receiptKey!,
1901
1968
  leaseId: claimed.receiptLeaseId,
1902
1969
  error: error instanceof Error ? error.message : String(error),
1903
- failureKind: workReceiptFailureKindForError(error),
1970
+ failureKind: runtimeReceiptFailureKindForError(error),
1904
1971
  })),
1905
1972
  });
1906
1973
  } catch (receiptError) {
@@ -1991,17 +2058,18 @@ export class WorkerToolBatchScheduler {
1991
2058
  );
1992
2059
  return;
1993
2060
  }
1994
- this.options.budgetMeter?.count('provider');
1995
- this.options.budgetMeter?.count(
1996
- 'egress',
1997
- Math.max(
1998
- 0,
1999
- WORKER_PLATFORM_SUBREQUESTS_PER_UNBATCHED_TOOL_CALL - 1,
2000
- ),
2001
- );
2002
2061
  try {
2003
2062
  let result: unknown;
2004
2063
  try {
2064
+ await this.markAdmittedToolRequestsRunning([claimed]);
2065
+ this.options.budgetMeter?.count('provider');
2066
+ this.options.budgetMeter?.count(
2067
+ 'egress',
2068
+ Math.max(
2069
+ 0,
2070
+ WORKER_PLATFORM_SUBREQUESTS_PER_UNBATCHED_TOOL_CALL - 1,
2071
+ ),
2072
+ );
2005
2073
  const durableReceiptKey = claimed.receiptKey
2006
2074
  ? durableCallReceiptKeyForClaimed({
2007
2075
  req: this.options.req,
@@ -2176,8 +2244,9 @@ export class WorkerToolBatchScheduler {
2176
2244
  signal: this.options.abortSignal,
2177
2245
  },
2178
2246
  );
2179
- this.options.budgetMeter?.count('provider');
2180
2247
  try {
2248
+ await this.markAdmittedToolRequestsRunning(batch.memberRequests);
2249
+ this.options.budgetMeter?.count('provider');
2181
2250
  const receiptKeys = batch.memberRequests.map((member) =>
2182
2251
  durableCallReceiptKeyForClaimed({
2183
2252
  req: this.options.req,
@@ -9,7 +9,7 @@
9
9
  * - **Max attempts**: `1 + maxRetries` (default: 4 total attempts)
10
10
  * - **Backoff**: exponential — 1s, 2s, 4s, 8s, ... capped at 30s
11
11
  * - **Retryable**: network errors, timeouts, HTTP 429 (rate limit)
12
- * - **Not retryable**: HTTP 401/403 (auth errors), other 4xx/5xx API errors
12
+ * - **Not retryable**: HTTP 401/403 (auth errors), other 4xx API errors
13
13
  *
14
14
  * ## Localhost failover
15
15
  *
@@ -504,7 +504,10 @@ export class HttpClient {
504
504
  * @param path - API path (e.g. `"/api/v2/tools"`)
505
505
  */
506
506
  async get<T = unknown>(path: string): Promise<T> {
507
- return this.request<T>(path, { method: 'GET' });
507
+ return this.request<T>(path, {
508
+ method: 'GET',
509
+ retryApiErrors: true,
510
+ });
508
511
  }
509
512
 
510
513
  async *streamSse<TEvent extends LiveEventEnvelope = LiveEventEnvelope>(
@@ -936,7 +936,7 @@ export interface DeeplinePlayRuntimeContext {
936
936
  *
937
937
  * // Poll status
938
938
  * const status = await job.status();
939
- * console.log(status.temporalStatus); // 'RUNNING'
939
+ * console.log(status.status); // 'running'
940
940
  *
941
941
  * // Stream logs until completion
942
942
  * const finalStatus = await job.tail({
@@ -37,6 +37,7 @@ import type {
37
37
  FailRuntimeReceiptInput,
38
38
  FailRuntimeReceiptsInput,
39
39
  HeartbeatRuntimeReceiptsInput,
40
+ MarkRuntimeReceiptsRunningInput,
40
41
  ReleaseRuntimeReceiptInput,
41
42
  RuntimeApiCallInput,
42
43
  RuntimeApiCallResult,
@@ -157,6 +158,12 @@ export async function harnessGetRuntimeReceipt(
157
158
  return requireBinding().getRuntimeReceipt(input);
158
159
  }
159
160
 
161
+ export async function harnessMarkRuntimeReceiptsRunning(
162
+ input: MarkRuntimeReceiptsRunningInput,
163
+ ): Promise<Array<WorkReceipt | null>> {
164
+ return requireBinding().markRuntimeReceiptsRunning(input);
165
+ }
166
+
160
167
  export async function harnessCompleteRuntimeReceipt(
161
168
  input: CompleteRuntimeReceiptInput,
162
169
  ): Promise<WorkReceipt | null> {