deepline 0.3.128 → 0.3.129

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 (128) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +34 -9
  2. package/dist/bundling-sources/sdk/src/http.ts +22 -0
  3. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  4. package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +22 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +265 -2
  6. package/dist/bundling-sources/shared_libs/play-runtime/bounded-batch-function.ts +682 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/child-execution-strategy.ts +21 -5
  8. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5390 -1732
  9. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +107 -1
  10. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +7 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +229 -7
  12. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +25 -4
  13. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-capacity.ts +71 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +1 -1
  15. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +4 -1
  16. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +694 -28
  17. package/dist/bundling-sources/shared_libs/play-runtime/egress-bandwidth-probe.ts +167 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/execute-server-timing.ts +103 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-receipt-output-reference.ts +34 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-response-size.ts +27 -0
  21. package/dist/bundling-sources/shared_libs/play-runtime/gateway-postgres-admission.ts +66 -1
  22. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +425 -71
  23. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +210 -32
  24. package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +27 -13
  25. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +19 -6
  26. package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +61 -2
  27. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +33 -1
  28. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +42 -1
  29. package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +11 -11
  30. package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +49 -16
  31. package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +1653 -98
  32. package/dist/bundling-sources/shared_libs/play-runtime/memory-ledger.ts +807 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/memory-pressure-signal.ts +138 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +85 -3
  35. package/dist/bundling-sources/shared_libs/play-runtime/persistence-latch.ts +60 -2
  36. package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +46 -1
  37. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +2 -0
  38. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +1 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +1 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +1 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +70 -27
  42. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +56 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/adaptive-provider-capacity-controller.ts +192 -0
  44. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/epoch-aimd-rate-controller.ts +194 -0
  45. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/fixture-capacity-simulator.ts +220 -0
  46. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/gradient2-concurrency-controller.ts +214 -0
  47. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-capacity-policy.ts +43 -0
  48. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-concurrency-policy.ts +30 -0
  49. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-rate-policy.ts +95 -0
  50. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-throttle-classifier.ts +40 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +1535 -11
  52. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +49 -2
  53. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +6 -1
  54. package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +55 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/egress-probe.ts +221 -0
  56. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +339 -29
  57. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +28 -1
  58. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +192 -165
  59. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +6 -0
  60. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +90 -5
  61. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +281 -6
  62. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +27 -1
  63. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +19 -0
  64. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +108 -0
  65. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +40 -0
  66. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +11 -5
  67. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +30 -0
  68. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-compression.ts +276 -0
  69. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +723 -115
  70. package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +98 -2
  71. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +3 -2
  72. package/dist/bundling-sources/shared_libs/play-runtime/runtime-environment.ts +41 -4
  73. package/dist/bundling-sources/shared_libs/play-runtime/runtime-flow.ts +448 -0
  74. package/dist/bundling-sources/shared_libs/play-runtime/runtime-map-row-feed.ts +215 -0
  75. package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +11 -0
  76. package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +161 -14
  77. package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-value.ts +184 -0
  78. package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +607 -121
  79. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-placement-policy.ts +4 -0
  80. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-writer.ts +21 -0
  81. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +15 -2
  82. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +71 -14
  83. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +2 -2
  84. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +288 -119
  85. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -6
  86. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +28 -3
  87. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/canonical-integration-operation-store.ts +179 -0
  88. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +80 -2
  89. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +31 -2
  90. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-store-contract.ts +104 -0
  91. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-reference.ts +47 -0
  92. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-retirement.ts +193 -0
  93. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-storage.ts +420 -0
  94. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +41 -0
  95. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +134 -3
  96. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +1657 -95
  97. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +885 -73
  98. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-completion-coalescer.ts +452 -0
  99. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +601 -42
  100. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +1 -0
  101. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +371 -0
  102. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +86 -7
  103. package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +153 -22
  104. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/index.ts +1 -0
  105. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/receipt-cohort.ts +188 -38
  106. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +28 -13
  107. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +44 -1
  108. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +56 -4
  109. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +2 -0
  110. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +16 -10
  111. package/dist/bundling-sources/shared_libs/plays/dataset.ts +4 -1
  112. package/dist/cli/index.js +250 -38
  113. package/dist/cli/index.mjs +250 -38
  114. package/dist/{compiler-manifest-DfSnbCTW.d.mts → compiler-manifest-BIqRyj5m.d.mts} +1 -1
  115. package/dist/{compiler-manifest-DfSnbCTW.d.ts → compiler-manifest-BIqRyj5m.d.ts} +1 -1
  116. package/dist/index.d.mts +4 -2
  117. package/dist/index.d.ts +4 -2
  118. package/dist/index.js +231 -35
  119. package/dist/index.mjs +231 -35
  120. package/dist/install-integrity.json +31 -2
  121. package/dist/plays/bundle-play-file.d.mts +2 -2
  122. package/dist/plays/bundle-play-file.d.ts +2 -2
  123. package/dist/plays/bundle-play-file.mjs +65 -1
  124. package/dist/release.d.mts +1 -1
  125. package/dist/release.d.ts +1 -1
  126. package/dist/release.js +1 -1
  127. package/dist/release.mjs +1 -1
  128. package/package.json +1 -1
@@ -41,6 +41,8 @@ import {
41
41
  runtimeLimitExceededFailure,
42
42
  } from '../run-failure';
43
43
  import { createPlayContext } from '../context';
44
+ import type { RuntimeApiResponseMeta } from '../app-runtime-api';
45
+ import { onMemoryPressure } from '../memory-pressure-signal';
44
46
  import {
45
47
  shouldEmitProgressSidecarEvent,
46
48
  shouldEmitStdoutEvent,
@@ -58,6 +60,7 @@ import {
58
60
  createRunnerProgressPusher,
59
61
  type RunnerProgressPusher,
60
62
  } from './runtime-progress-push';
63
+ import { collectStartupEgressProbeLines } from './egress-probe';
61
64
  import {
62
65
  pushRunnerTerminal,
63
66
  runnerTerminalResultForTransport,
@@ -92,6 +95,12 @@ import {
92
95
  } from '../dataset-lifecycle-log';
93
96
  import {
94
97
  assertCustomerPlayReturnWithinLimit,
98
+ RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES,
99
+ RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_ITEMS,
100
+ RUNTIME_RECEIPT_COMPLETION_MAX_IN_FLIGHT_BATCHES,
101
+ RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_BYTES,
102
+ RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_ITEMS,
103
+ RUNTIME_RECEIPT_CONTROL_MAX_IN_FLIGHT_BATCHES,
95
104
  RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES,
96
105
  } from '../output-size-limits';
97
106
  import {
@@ -100,7 +109,10 @@ import {
100
109
  readRuntimeSheetDatasetRowKeys,
101
110
  readRuntimeSheetDatasetRows,
102
111
  releaseRuntimeWorkReceipts,
112
+ RuntimeSheetAttemptSupersededError,
103
113
  startRuntimeSheetDataset,
114
+ supersededRuntimeMapRows,
115
+ type RuntimeMapRowsWriteResult,
104
116
  } from '../runtime-api';
105
117
  import { installPgRuntimePoolDriver } from '../runtime-pg-driver-pg';
106
118
  import { createRuntimeToolAuthScopeDigestResolver } from '../auth-scope-resolver';
@@ -141,6 +153,32 @@ function loadSafeOutboundFetch(): Promise<SafeOutboundFetchModule> {
141
153
  return safeOutboundFetchModule;
142
154
  }
143
155
 
156
+ type ReceiptGatewayResponseObserverTarget = Pick<
157
+ ReturnType<typeof createPlayContext>,
158
+ 'observeRuntimeGatewayResponse'
159
+ >;
160
+
161
+ /**
162
+ * The receipt adapter is created before the Play Context because the Context
163
+ * needs its durable handlers. Keep the response observer late-bound so those
164
+ * completion requests still contribute their gateway/transport split to the
165
+ * Context's terminal runtime summary.
166
+ */
167
+ export function createReceiptGatewayResponseObserver(): {
168
+ attach: (target: ReceiptGatewayResponseObserverTarget) => void;
169
+ onResponseMeta: (meta: RuntimeApiResponseMeta) => void;
170
+ } {
171
+ let target: ReceiptGatewayResponseObserverTarget | null = null;
172
+ return {
173
+ attach: (nextTarget) => {
174
+ target = nextTarget;
175
+ },
176
+ onResponseMeta: (meta) => {
177
+ target?.observeRuntimeGatewayResponse(meta);
178
+ },
179
+ };
180
+ }
181
+
144
182
  async function safeNodeOutboundFetch(
145
183
  input: string | URL,
146
184
  init?: RequestInit,
@@ -250,6 +288,36 @@ const RUNTIME_RECEIPT_GATEWAY_REQUEST_TIMEOUT_MS =
250
288
  RUNTIME_CAPACITY_POLICY.receiptGateway.requestTimeoutMs;
251
289
  const RETRY_SAFE_EVENT_WAIT_ARM_TOOLS = new Set(['test_wait_for_event']);
252
290
 
291
+ /**
292
+ * The Context already bounds receipt completion to sixteen flights. The
293
+ * adapter is the same receipt writer at the gateway boundary, so leaving its
294
+ * default single flight serializes those independent batches again after they
295
+ * have left the Context. Keep the two writers on the same bounded lanes and
296
+ * make the adapter's retained command envelope explicit.
297
+ */
298
+ export function playRunnerReceiptStoreAdapterOptions() {
299
+ return {
300
+ maxBatchSize: RUNTIME_RECEIPT_GATEWAY_BATCH_SIZE,
301
+ maxFlushMs: RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_WAIT_MS,
302
+ maxInFlightBatches:
303
+ RUNTIME_RECEIPT_COMPLETION_MAX_IN_FLIGHT_BATCHES +
304
+ RUNTIME_RECEIPT_CONTROL_MAX_IN_FLIGHT_BATCHES,
305
+ maxBufferedItems:
306
+ RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_ITEMS +
307
+ RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_ITEMS,
308
+ maxBufferedBytes:
309
+ RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES +
310
+ RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_BYTES,
311
+ maxCompletionInFlightBatches:
312
+ RUNTIME_RECEIPT_COMPLETION_MAX_IN_FLIGHT_BATCHES,
313
+ maxControlInFlightBatches: RUNTIME_RECEIPT_CONTROL_MAX_IN_FLIGHT_BATCHES,
314
+ maxCompletionBufferedItems: RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_ITEMS,
315
+ maxCompletionBufferedBytes: RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES,
316
+ maxControlBufferedItems: RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_ITEMS,
317
+ maxControlBufferedBytes: RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_BYTES,
318
+ };
319
+ }
320
+
253
321
  // The one receipt backend: one Play Durability Store Adapter bound to the
254
322
  // co-located receipt gateway. There is no host-injected or app-runtime path.
255
323
  type RuntimeStepReceiptHandlers = RuntimeReceiptStoreHandlers;
@@ -296,8 +364,9 @@ export function assertRuntimeSheetAttemptPersistedTerminalRows(input: {
296
364
  fencedKeys?: string[];
297
365
  }): void {
298
366
  // Kept as a temporary source-compatible seam for callers compiled against
299
- // the lease model. A partial update now means a newer scheduled write won;
300
- // it is an observable stale drop, not a run-fatal ownership error.
367
+ // the lease model. A partial update is classified from the terminal-write
368
+ // result (`assertRuntimeSheetTerminalWriteNotSuperseded`), never from this
369
+ // count.
301
370
  void input;
302
371
  }
303
372
 
@@ -320,6 +389,99 @@ export function deterministicRuntimeSheetAttemptId(input: {
320
389
  return `sheet-attempt:${digest}`;
321
390
  }
322
391
 
392
+ /** The sheet's admission version for one table; a value, not a wire marker. */
393
+ type SheetWriteVersionValue = number;
394
+
395
+ export type RunnerRuntimeSheetAttempt = {
396
+ attemptId: string;
397
+ attemptOwnerRunId: string;
398
+ attemptExpiresAt: string;
399
+ attemptSeq: number;
400
+ writeVersion?: SheetWriteVersionValue;
401
+ };
402
+
403
+ /**
404
+ * The sheet attempt record to keep after a map start admitted one source page
405
+ * of a table. A runtime-sheet-backed map admits its source one page at a time
406
+ * while earlier pages' rows are still executing, and every terminal write is
407
+ * fenced by the write version the runner sends. If each page minted its own
408
+ * version, every completion flushed after the next page's admission would
409
+ * carry a version the earlier rows were never admitted under and be rejected
410
+ * as stale (the 2026-09-18 20,000-row incident: 4,350 rows executed, 45
411
+ * persisted). So the first admission's write version is requested again for
412
+ * every later page (see `runtimeSheetStartWriteVersion`) and must come back
413
+ * unchanged; anything else means the sheet no longer belongs to this runner.
414
+ */
415
+ export function runtimeSheetAttemptForMapStart(input: {
416
+ tableNamespace: string;
417
+ runId: string;
418
+ existing: RunnerRuntimeSheetAttempt | undefined;
419
+ mapStartResult: {
420
+ attemptId: string;
421
+ attemptOwnerRunId: string;
422
+ attemptExpiresAt: string;
423
+ attemptSeq?: number | null;
424
+ writeVersion?: SheetWriteVersionValue | null;
425
+ };
426
+ runnerRunAttempt: number;
427
+ }): RunnerRuntimeSheetAttempt {
428
+ const requested = input.existing?.writeVersion;
429
+ const returned = input.mapStartResult.writeVersion ?? undefined;
430
+ if (requested != null && returned !== requested) {
431
+ throw new Error(
432
+ `Runtime sheet start for table "${input.tableNamespace}" (run ${input.runId}) ` +
433
+ `was asked to keep write version ${requested} for a later source page but ` +
434
+ `returned ${returned ?? 'none'}; the runner cannot persist rows under two ` +
435
+ `write versions, so this map cannot continue.`,
436
+ );
437
+ }
438
+ return {
439
+ attemptId: input.mapStartResult.attemptId,
440
+ attemptOwnerRunId: input.mapStartResult.attemptOwnerRunId,
441
+ attemptExpiresAt: input.mapStartResult.attemptExpiresAt,
442
+ attemptSeq: input.mapStartResult.attemptSeq ?? input.runnerRunAttempt,
443
+ ...(returned != null ? { writeVersion: returned } : {}),
444
+ };
445
+ }
446
+
447
+ /** The write version a later source page of the same table must be admitted under. */
448
+ export function runtimeSheetStartWriteVersion(
449
+ existing: RunnerRuntimeSheetAttempt | undefined,
450
+ ): { writeVersion: SheetWriteVersionValue } | Record<string, never> {
451
+ return existing?.writeVersion != null
452
+ ? { writeVersion: existing.writeVersion }
453
+ : {};
454
+ }
455
+
456
+ /**
457
+ * Terminal writes rejected as stale for any reason other than a verified
458
+ * same-run settlement or an unversioned already-terminal row mean the sheet
459
+ * rows belong to a different write version than this attempt's: a newer
460
+ * admission (rerun, resumed attempt, concurrent run) owns them. Throw so the
461
+ * map fails and the persistence latch trips, instead of logging a perf line
462
+ * and spending provider calls on rows that can never be persisted.
463
+ */
464
+ export function assertRuntimeSheetTerminalWriteNotSuperseded(input: {
465
+ tableNamespace: string;
466
+ runId: string;
467
+ writeVersion: SheetWriteVersionValue | null;
468
+ submittedRows: number;
469
+ result: Pick<RuntimeMapRowsWriteResult, 'staleKeys' | 'staleRows'>;
470
+ recoveredStaleKeys: Iterable<string>;
471
+ }): void {
472
+ const supersededRows = supersededRuntimeMapRows(input.result, {
473
+ exceptKeys: input.recoveredStaleKeys,
474
+ });
475
+ if (supersededRows.length === 0) return;
476
+ throw new RuntimeSheetAttemptSupersededError({
477
+ tableNamespace: input.tableNamespace,
478
+ runId: input.runId,
479
+ writeVersionValue: input.writeVersion,
480
+ submittedRows: input.submittedRows,
481
+ supersededRows,
482
+ });
483
+ }
484
+
323
485
  async function vercelBypassHeaders(
324
486
  config: PlayRunnerExecutionConfig,
325
487
  ): Promise<Record<string, string>> {
@@ -882,6 +1044,13 @@ async function createStreamingCsvDataset(input: {
882
1044
  export interface ExecutePlayRunnerOptions {
883
1045
  emitEvent?: (event: PlayRunnerEvent) => void;
884
1046
  signal?: AbortSignal;
1047
+ /** Stops future row admission while already admitted rows can persist. */
1048
+ rowAdmissionSignal?: AbortSignal;
1049
+ /** Runner-local terminal state made available to the deadline drain. */
1050
+ onRuntimeReady?: (runtime: {
1051
+ log: (line: string) => void;
1052
+ timeoutResult: (runtimeLimitSeconds: number) => PlayRunnerResult;
1053
+ }) => void;
885
1054
  }
886
1055
 
887
1056
  function emitRunnerEvent(
@@ -1646,6 +1815,18 @@ async function releaseRuntimeLeasesOnAppRunnerSettlement(input: {
1646
1815
  }
1647
1816
  }
1648
1817
 
1818
+ function terminalLogsWithPhaseSummary(ctx: {
1819
+ getLogs(): string[];
1820
+ getRuntimeResourceSnapshot(): unknown;
1821
+ getLogDiagnostics(): unknown;
1822
+ }): string[] {
1823
+ return [
1824
+ ...ctx.getLogs(),
1825
+ `[runtime.logs] ${JSON.stringify(ctx.getLogDiagnostics())}`,
1826
+ `[runtime.phase_summary] ${JSON.stringify(ctx.getRuntimeResourceSnapshot())}`,
1827
+ ];
1828
+ }
1829
+
1649
1830
  async function run(
1650
1831
  config: PlayRunnerExecutionConfig,
1651
1832
  options?: ExecutePlayRunnerOptions,
@@ -1690,6 +1871,7 @@ async function run(
1690
1871
  vercelProtectionBypassToken: config.context.vercelProtectionBypassToken,
1691
1872
  runtimeTestFaultHeader: config.context.runtimeTestFaultHeader ?? null,
1692
1873
  };
1874
+ const receiptGatewayResponseObserver = createReceiptGatewayResponseObserver();
1693
1875
  const receiptGatewayBaseUrl = config.context.receiptGatewayBaseUrl?.trim();
1694
1876
  // Receipt state has exactly two backends: handlers provided by the host
1695
1877
  // process (inline executor) or the co-located receipt gateway named by the
@@ -1701,6 +1883,7 @@ async function run(
1701
1883
  baseUrl: receiptGatewayBaseUrl,
1702
1884
  boundary: 'receipt_gateway' as const,
1703
1885
  requestTimeoutMs: RUNTIME_RECEIPT_GATEWAY_REQUEST_TIMEOUT_MS,
1886
+ onResponseMeta: receiptGatewayResponseObserver.onResponseMeta,
1704
1887
  }
1705
1888
  : null;
1706
1889
  const canUseRuntimeStepReceipts = playRunnerRuntimeReceiptsEnabled(
@@ -1718,8 +1901,7 @@ async function run(
1718
1901
  config.context.runId ??
1719
1902
  config.context.workflowId ??
1720
1903
  'anonymous-run',
1721
- maxBatchSize: RUNTIME_RECEIPT_GATEWAY_BATCH_SIZE,
1722
- maxFlushMs: RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_WAIT_MS,
1904
+ ...playRunnerReceiptStoreAdapterOptions(),
1723
1905
  onRetryTelemetry: (line) => emitSystemLog(line, options),
1724
1906
  })
1725
1907
  : null;
@@ -1836,6 +2018,7 @@ async function run(
1836
2018
  });
1837
2019
  const ctx = createPlayContext({
1838
2020
  artifactRuntimeContract,
2021
+ rowAdmissionSignal: options?.rowAdmissionSignal,
1839
2022
  executorToken: config.context.executorToken,
1840
2023
  baseUrl: config.context.baseUrl,
1841
2024
  // Runtime control stays on baseUrl (the receipt gateway); provider calls
@@ -2164,6 +2347,15 @@ async function run(
2164
2347
  `Missing sheet contract for play "${playName}" and table namespace "${tableNamespace}".`,
2165
2348
  );
2166
2349
  }
2350
+ const attemptKey = runtimeSheetAttemptKey({
2351
+ playName,
2352
+ runId,
2353
+ tableNamespace,
2354
+ });
2355
+ // A later source page of a table this runner already admitted keeps the
2356
+ // first page's write version: rows of earlier pages are still executing
2357
+ // and their terminal writes are fenced by it.
2358
+ const existingAttempt = runtimeSheetAttempts.get(attemptKey);
2167
2359
  const mapStartResult = await retryRuntimeSheetPersistence({
2168
2360
  description: `runtime map start table=${tableNamespace}`,
2169
2361
  log: (line) => emitWorkerLog(line, options),
@@ -2205,6 +2397,7 @@ async function run(
2205
2397
  }),
2206
2398
  attemptOwnerRunId: runId,
2207
2399
  attemptSeq: runnerRunAttempt,
2400
+ ...runtimeSheetStartWriteVersion(existingAttempt),
2208
2401
  ...(playRunnerContextRuntimeSheetForce(config.context)
2209
2402
  ? { force: true }
2210
2403
  : {}),
@@ -2274,14 +2467,20 @@ async function run(
2274
2467
  mapStartResult.attemptExpiresAt
2275
2468
  ) {
2276
2469
  runtimeSheetAttempts.set(
2277
- runtimeSheetAttemptKey({ playName, runId, tableNamespace }),
2278
- {
2279
- attemptId: mapStartResult.attemptId,
2280
- attemptOwnerRunId: mapStartResult.attemptOwnerRunId,
2281
- attemptExpiresAt: mapStartResult.attemptExpiresAt,
2282
- attemptSeq: mapStartResult.attemptSeq ?? runnerRunAttempt,
2283
- writeVersion: mapStartResult.writeVersion,
2284
- },
2470
+ attemptKey,
2471
+ runtimeSheetAttemptForMapStart({
2472
+ tableNamespace,
2473
+ runId,
2474
+ existing: existingAttempt,
2475
+ mapStartResult: {
2476
+ attemptId: mapStartResult.attemptId,
2477
+ attemptOwnerRunId: mapStartResult.attemptOwnerRunId,
2478
+ attemptExpiresAt: mapStartResult.attemptExpiresAt,
2479
+ attemptSeq: mapStartResult.attemptSeq,
2480
+ writeVersion: mapStartResult.writeVersion,
2481
+ },
2482
+ runnerRunAttempt,
2483
+ }),
2285
2484
  );
2286
2485
  }
2287
2486
  emitWorkerLog(
@@ -2487,6 +2686,22 @@ async function run(
2487
2686
  `[info] dropped ${staleKeys.size} stale runtime sheet write(s) for table=${input.tableNamespace}; ${recoveredStaleKeys.length} same-run terminal write(s) were accepted idempotently`,
2488
2687
  options,
2489
2688
  );
2689
+ try {
2690
+ assertRuntimeSheetTerminalWriteNotSuperseded({
2691
+ tableNamespace: input.tableNamespace,
2692
+ runId,
2693
+ writeVersion: attempt?.writeVersion ?? null,
2694
+ submittedRows: input.rows.length,
2695
+ result,
2696
+ recoveredStaleKeys,
2697
+ });
2698
+ } catch (error) {
2699
+ emitWorkerLog(
2700
+ `[error] ${error instanceof Error ? error.message : String(error)}`,
2701
+ options,
2702
+ );
2703
+ throw error;
2704
+ }
2490
2705
  }
2491
2706
  const failedRows = input.rows.filter(
2492
2707
  (row) =>
@@ -2511,7 +2726,9 @@ async function run(
2511
2726
  failedRows: 0,
2512
2727
  };
2513
2728
  // Only exact-version accepted rows belong to this run's lifecycle
2514
- // settlement. Stale rows belong to the newer writer and stay excluded.
2729
+ // settlement. The stale keys that reach this point were all recovered as
2730
+ // same-run terminal settlements, counted when they first committed, so
2731
+ // they stay excluded here.
2515
2732
  settlement.persistedRows += committedTerminalRows;
2516
2733
  settlement.succeededRows += succeededRows;
2517
2734
  settlement.failedRows += failedRows;
@@ -2527,6 +2744,7 @@ async function run(
2527
2744
  };
2528
2745
  },
2529
2746
  });
2747
+ receiptGatewayResponseObserver.attach(ctx);
2530
2748
 
2531
2749
  const csvSourceCache = new Map<string, ReplayableCsvSource>();
2532
2750
  const csvDatasetCache = new Map<
@@ -2666,6 +2884,53 @@ async function run(
2666
2884
  `[perf] context setup elapsed_ms=${Date.now() - contextSetupStartedAt}`,
2667
2885
  options,
2668
2886
  );
2887
+ for (const line of await collectStartupEgressProbeLines({
2888
+ // A local/direct executor can use baseUrl without a receipt gateway.
2889
+ // Probe only the explicit data-plane hop.
2890
+ receiptGatewayBaseUrl: config.context.receiptGatewayBaseUrl ?? null,
2891
+ executionRelayBaseUrl: config.context.executionGatewayBaseUrl ?? null,
2892
+ executorToken: config.context.executorToken ?? null,
2893
+ })) {
2894
+ ctx.logRunnerDiagnostic(line);
2895
+ }
2896
+ const logRecovery = () => {
2897
+ const runId = config.context.runId ?? config.context.workflowId;
2898
+ for (const saved of [...runtimeMapDatasetSettlements.values()].slice(
2899
+ 0,
2900
+ 16,
2901
+ )) {
2902
+ ctx.logRunnerDiagnostic(
2903
+ formatDatasetAvailabilityLog({
2904
+ runId: saved.runId,
2905
+ dataset: saved,
2906
+ persistedRows: saved.persistedRows,
2907
+ }),
2908
+ );
2909
+ }
2910
+ if (runId) {
2911
+ ctx.logRunnerDiagnostic(
2912
+ `Run stopped before returning. Inspect saved datasets: deepline runs get ${runId} --json. Export saved data before rerunning unfinished work.`,
2913
+ );
2914
+ }
2915
+ };
2916
+ options?.onRuntimeReady?.({
2917
+ log: (line) => ctx.logRunnerDiagnostic(line),
2918
+ timeoutResult: (runtimeLimitSeconds) => {
2919
+ logRecovery();
2920
+ return {
2921
+ status: 'failed',
2922
+ error: runtimeLimitExceededCause(runtimeLimitSeconds),
2923
+ errors: [runtimeLimitExceededFailure(runtimeLimitSeconds)],
2924
+ logs: terminalLogsWithPhaseSummary(ctx),
2925
+ stats: ctx.getStats(),
2926
+ steps: ctx.getSteps(),
2927
+ tableNamespace: null,
2928
+ totalRows,
2929
+ inserted,
2930
+ skipped,
2931
+ };
2932
+ },
2933
+ });
2669
2934
 
2670
2935
  const playInput: PlayRunInputPayload =
2671
2936
  runtimeInputAlias && !Array.isArray(runtimeInput)
@@ -2683,6 +2948,7 @@ async function run(
2683
2948
  runtimeInput: playInput,
2684
2949
  options,
2685
2950
  });
2951
+ ctx.assertRowAdmissionOpen();
2686
2952
  // A durable ctx boundary deliberately unwinds execution. Customer code can
2687
2953
  // catch that error, so the context keeps a sticky handoff that remains
2688
2954
  // authoritative until this invocation returns it to the scheduler.
@@ -2850,7 +3116,7 @@ async function run(
2850
3116
  reason: 'completed',
2851
3117
  });
2852
3118
 
2853
- const logs = ctx.getLogs();
3119
+ const logs = terminalLogsWithPhaseSummary(ctx);
2854
3120
  const runtimeDiagnostics = Array.from(
2855
3121
  new Map(
2856
3122
  logs
@@ -2888,9 +3154,13 @@ async function run(
2888
3154
  // sticky handoff is still authoritative until this runner returns it to
2889
3155
  // the scheduler. Resolve that precedence before classifying the terminal
2890
3156
  // result so a replacement error cannot turn a suspended run into failed.
2891
- let error: unknown = caughtError;
3157
+ let error: unknown = options?.rowAdmissionSignal?.aborted
3158
+ ? options.rowAdmissionSignal.reason
3159
+ : caughtError;
2892
3160
  try {
2893
- ctx.assertNoPendingDurableSuspension();
3161
+ if (!options?.rowAdmissionSignal?.aborted) {
3162
+ ctx.assertNoPendingDurableSuspension();
3163
+ }
2894
3164
  } catch (suspension) {
2895
3165
  error = suspension;
2896
3166
  }
@@ -2921,7 +3191,7 @@ async function run(
2921
3191
  return {
2922
3192
  status: 'suspended',
2923
3193
  suspension: error.suspension,
2924
- logs: ctx.getLogs(),
3194
+ logs: terminalLogsWithPhaseSummary(ctx),
2925
3195
  stats: ctx.getStats(),
2926
3196
  steps: ctx.getSteps(),
2927
3197
  checkpoint: ctx.getCheckpoint(),
@@ -2949,11 +3219,12 @@ async function run(
2949
3219
 
2950
3220
  ensureSourceMapSupport();
2951
3221
  const failure = normalizePlayRunFailure(error);
3222
+ logRecovery();
2952
3223
  return {
2953
3224
  status: 'failed',
2954
3225
  error: formatRunnerFailure(error),
2955
3226
  errors: [failure],
2956
- logs: ctx.getLogs(),
3227
+ logs: terminalLogsWithPhaseSummary(ctx),
2957
3228
  stats: ctx.getStats(),
2958
3229
  steps: ctx.getSteps(),
2959
3230
  checkpoint: ctx.getCheckpoint(),
@@ -3220,6 +3491,10 @@ async function main() {
3220
3491
  // is reachable instead of relying only on the detached ceiling salvage.
3221
3492
  runnerTerminalPushContext = terminalPushContextFromConfig(parsedConfig);
3222
3493
  const executionController = new AbortController();
3494
+ const rowAdmissionController = new AbortController();
3495
+ let runtimeState:
3496
+ | Parameters<NonNullable<ExecutePlayRunnerOptions['onRuntimeReady']>>[0]
3497
+ | undefined;
3223
3498
  const heartbeat = startRunnerPushHeartbeatFromConfig(parsedConfig, () => {
3224
3499
  executionController.abort(
3225
3500
  new Error('Play runner stopped by the runtime heartbeat channel.'),
@@ -3238,14 +3513,31 @@ async function main() {
3238
3513
  runnerProgressPusher = startRunnerProgressPusherFromConfig(config);
3239
3514
  const runtimeDeadline = startConfiguredPlayRunnerRuntimeDeadline({
3240
3515
  config,
3516
+ onDrain: (runtimeLimitSeconds) => {
3517
+ runtimeState?.log(
3518
+ `[runtime.log_transport] ${JSON.stringify(runnerProgressPusher?.snapshot() ?? null)}`,
3519
+ );
3520
+ runtimeState?.log(
3521
+ `Runtime limit approaching (${runtimeLimitSeconds}s configured); stopping new rows and saving started work for up to 4 seconds.`,
3522
+ );
3523
+ rowAdmissionController.abort(
3524
+ new Error(runtimeLimitExceededCause(runtimeLimitSeconds)),
3525
+ );
3526
+ },
3241
3527
  onDeadline: async (runtimeLimitSeconds) => {
3528
+ runtimeState?.log(
3529
+ `[runtime.log_transport] ${JSON.stringify(runnerProgressPusher?.snapshot() ?? null)}`,
3530
+ );
3242
3531
  executionController.abort(
3243
3532
  new Error(
3244
3533
  `Play runner reached its ${runtimeLimitSeconds}-second runtime limit.`,
3245
3534
  ),
3246
3535
  );
3247
3536
  heartbeat?.stop();
3248
- await terminateRunnerForRuntimeDeadline(runtimeLimitSeconds);
3537
+ await terminateRunnerForRuntimeDeadline(
3538
+ runtimeLimitSeconds,
3539
+ runtimeState?.timeoutResult(runtimeLimitSeconds),
3540
+ );
3249
3541
  },
3250
3542
  onError: (error) => {
3251
3543
  console.error('[play-runner.runtime-deadline] terminal_failed', {
@@ -3263,6 +3555,10 @@ async function main() {
3263
3555
  try {
3264
3556
  result = await executePlayRunner(config, {
3265
3557
  signal: executionController.signal,
3558
+ rowAdmissionSignal: rowAdmissionController.signal,
3559
+ onRuntimeReady: (runtime) => {
3560
+ runtimeState = runtime;
3561
+ },
3266
3562
  });
3267
3563
  } catch (error) {
3268
3564
  executionFailed = true;
@@ -3586,7 +3882,7 @@ function terminateRunnerForHeartbeatStop(
3586
3882
  ? stop.terminal === 'cancelled'
3587
3883
  ? 'Play run was cancelled; sandbox runner self-terminated on the receipt gateway heartbeat cancellation channel.'
3588
3884
  : 'Play run already failed in the scheduler; sandbox runner self-terminated on the receipt gateway heartbeat cancellation channel.'
3589
- : `Sandbox runner could not reach the receipt gateway for ${stop.reason === 'unreachable' ? stop.sinceMs : 0}ms (past the lease window); self-terminating — the parked attempt's ceiling timeout salvages this result from the captured output. ${formatRunnerGatewayTransportSummary(stop.reason === 'unreachable' ? stop.gatewayTransport : undefined)}`;
3885
+ : `RUNTIME_CONTROL_PLANE_UNAVAILABLE: Sandbox runner could not reach the receipt gateway for ${stop.reason === 'unreachable' ? stop.sinceMs : 0}ms (past the lease window); self-terminating — the parked attempt's ceiling timeout salvages this result from the captured output. ${formatRunnerGatewayTransportSummary(stop.reason === 'unreachable' ? stop.gatewayTransport : undefined)}`;
3590
3886
  const failedResult = runnerTerminalResultForTransport({
3591
3887
  status: 'failed',
3592
3888
  error,
@@ -3606,17 +3902,20 @@ function terminateRunnerForHeartbeatStop(
3606
3902
 
3607
3903
  async function terminateRunnerForRuntimeDeadline(
3608
3904
  runtimeLimitSeconds: number,
3905
+ snapshot?: PlayRunnerResult,
3609
3906
  ): Promise<never> {
3610
3907
  const error = runtimeLimitExceededCause(runtimeLimitSeconds);
3611
- const failedResult = runnerTerminalResultForTransport({
3612
- status: 'failed',
3613
- error,
3614
- errors: [runtimeLimitExceededFailure(runtimeLimitSeconds, error)],
3615
- logs: [],
3616
- stats: {},
3617
- steps: [],
3618
- tableNamespace: null,
3619
- });
3908
+ const failedResult = runnerTerminalResultForTransport(
3909
+ snapshot ?? {
3910
+ status: 'failed',
3911
+ error,
3912
+ errors: [runtimeLimitExceededFailure(runtimeLimitSeconds, error)],
3913
+ logs: [],
3914
+ stats: {},
3915
+ steps: [],
3916
+ tableNamespace: null,
3917
+ },
3918
+ );
3620
3919
  // Stop customer code immediately. The structured stdout event is captured by
3621
3920
  // the Daytona shell wrapper, whose crash epilogue owns the bounded gateway
3622
3921
  // push during the outer grace period. Awaiting network/progress work here
@@ -3630,6 +3929,17 @@ async function terminateRunnerForRuntimeDeadline(
3630
3929
  );
3631
3930
  }
3632
3931
 
3932
+ // The external watchdog samples the sandbox cgroup and raises SIGUSR2 at its
3933
+ // pressure threshold. Register at module load: Node would otherwise terminate
3934
+ // before map admission can react and release retained result bodies.
3935
+ onMemoryPressure((sample) => {
3936
+ emitWorkerLog(
3937
+ `[memory] pressure signal received usage=${sample.usageBytes ?? 'unknown'} limit=${sample.limitBytes ?? 'unknown'}`,
3938
+ );
3939
+ // The shared sandbox argv enables explicit collection for this recovery path.
3940
+ (globalThis as { gc?: () => void }).gc?.();
3941
+ });
3942
+
3633
3943
  export function runPlayRunnerMain(): void {
3634
3944
  main().catch(async (error) => {
3635
3945
  ensureSourceMapSupport();
@@ -7,6 +7,9 @@ import {
7
7
  export interface PlayRunnerRuntimeDeadlineOptions {
8
8
  limitMs: number;
9
9
  onDeadline: () => void | Promise<void>;
10
+ /** Stop new work while leaving a bounded window for durable writes. */
11
+ onDrain?: () => void;
12
+ drainLeadMs?: number;
10
13
  onError?: (error: unknown) => void;
11
14
  setTimeoutImpl?: (callback: () => void, delayMs: number) => unknown;
12
15
  clearTimeoutImpl?: (handle: unknown) => void;
@@ -22,6 +25,7 @@ export interface PlayRunnerRuntimeDeadlineHandle {
22
25
  export function startConfiguredPlayRunnerRuntimeDeadline(input: {
23
26
  config: Pick<PlayRunnerExecutionConfig, 'context'>;
24
27
  onDeadline: (runtimeLimitSeconds: number) => void | Promise<void>;
28
+ onDrain?: (runtimeLimitSeconds: number) => void;
25
29
  onError?: (error: unknown) => void;
26
30
  setTimeoutImpl?: (callback: () => void, delayMs: number) => unknown;
27
31
  clearTimeoutImpl?: (handle: unknown) => void;
@@ -33,7 +37,13 @@ export function startConfiguredPlayRunnerRuntimeDeadline(input: {
33
37
  );
34
38
  const runtimeLimitSeconds = runtimeLimits.timeoutSeconds;
35
39
  return startPlayRunnerRuntimeDeadline({
36
- limitMs: runtimeLimitSeconds * 1_000,
40
+ // Reserve one second for captured terminal stdout before the independent
41
+ // watchdog's hard deadline. Legacy callers keep their exact deadline.
42
+ limitMs: runtimeLimitSeconds * 1_000 - (input.onDrain ? 1_000 : 0),
43
+ onDrain: input.onDrain
44
+ ? () => input.onDrain!(runtimeLimitSeconds)
45
+ : undefined,
46
+ drainLeadMs: 4_000,
37
47
  onDeadline: () => input.onDeadline(runtimeLimitSeconds),
38
48
  onError: input.onError,
39
49
  setTimeoutImpl: input.setTimeoutImpl,
@@ -62,6 +72,22 @@ export function startPlayRunnerRuntimeDeadline(
62
72
  const deadlineSettled = new Promise<void>((resolve) => {
63
73
  settleDeadline = resolve;
64
74
  });
75
+ const drainHandle = options.onDrain
76
+ ? setTimeoutImpl(
77
+ () => {
78
+ if (fired || completionClaimed) return;
79
+ try {
80
+ options.onDrain?.();
81
+ } catch (error) {
82
+ options.onError?.(error);
83
+ }
84
+ },
85
+ Math.max(
86
+ 1,
87
+ limitMs - Math.min(options.drainLeadMs ?? 4_000, limitMs / 2),
88
+ ),
89
+ )
90
+ : null;
65
91
  const handle = setTimeoutImpl(() => {
66
92
  if (fired || completionClaimed) return;
67
93
  fired = true;
@@ -77,6 +103,7 @@ export function startPlayRunnerRuntimeDeadline(
77
103
  if (fired || completionClaimed) return false;
78
104
  completionClaimed = true;
79
105
  clearTimeoutImpl(handle);
106
+ if (drainHandle !== null) clearTimeoutImpl(drainHandle);
80
107
  return true;
81
108
  },
82
109
  deadlineSettled,