deepline 0.1.167 → 0.1.169

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 (30) hide show
  1. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +317 -26
  2. package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +99 -6
  3. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +219 -73
  4. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +119 -33
  5. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +2 -0
  6. package/dist/bundling-sources/apps/play-runner-workers/src/workflow-instance-create.ts +3 -0
  7. package/dist/bundling-sources/sdk/src/client.ts +29 -1
  8. package/dist/bundling-sources/sdk/src/release.ts +2 -2
  9. package/dist/bundling-sources/sdk/src/types.ts +3 -0
  10. package/dist/bundling-sources/shared_libs/play-data-plane/column-names.ts +50 -8
  11. package/dist/bundling-sources/shared_libs/play-data-plane/sheet-contract.ts +40 -1
  12. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +1 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +3 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +2 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +1 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +2 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +1 -0
  19. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +202 -45
  20. package/dist/cli/index.js +70 -113
  21. package/dist/cli/index.mjs +70 -113
  22. package/dist/{compiler-manifest-VhtM9n24.d.mts → compiler-manifest-OwORQ07f.d.mts} +1 -0
  23. package/dist/{compiler-manifest-VhtM9n24.d.ts → compiler-manifest-OwORQ07f.d.ts} +1 -0
  24. package/dist/index.d.mts +5 -1
  25. package/dist/index.d.ts +5 -1
  26. package/dist/index.js +26 -5
  27. package/dist/index.mjs +26 -5
  28. package/dist/plays/bundle-play-file.d.mts +2 -2
  29. package/dist/plays/bundle-play-file.d.ts +2 -2
  30. package/package.json +1 -1
@@ -263,6 +263,7 @@ type RunRequest = {
263
263
  callbackUrl: string;
264
264
  executorToken: string;
265
265
  baseUrl: string;
266
+ integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
266
267
  orgId: string;
267
268
  playName: string;
268
269
  graphHash?: string | null;
@@ -325,6 +326,14 @@ function getStringField(value: unknown, key: string): string | null {
325
326
  return typeof field === 'string' && field.trim() ? field : null;
326
327
  }
327
328
 
329
+ function normalizeIntegrationMode(
330
+ value: unknown,
331
+ ): 'live' | 'eval_stub' | 'fixture' | null {
332
+ return value === 'live' || value === 'eval_stub' || value === 'fixture'
333
+ ? value
334
+ : null;
335
+ }
336
+
328
337
  function getObjectField(
329
338
  value: unknown,
330
339
  key: string,
@@ -1472,6 +1481,9 @@ async function callToolDirect(
1472
1481
  body: JSON.stringify({
1473
1482
  payload: input,
1474
1483
  metadata: { parent_run_id: req.runId },
1484
+ ...(req.integrationMode
1485
+ ? { integration_mode: req.integrationMode }
1486
+ : {}),
1475
1487
  }),
1476
1488
  },
1477
1489
  {
@@ -1650,13 +1662,44 @@ const WORKER_TOOL_BATCH_GRACE_MS = 250;
1650
1662
  const MAP_EXECUTION_HEARTBEAT_INTERVAL_MS = 5_000;
1651
1663
  const MAP_INCREMENTAL_PERSIST_CHUNK_ROWS = 100;
1652
1664
  const MAP_INCREMENTAL_PERSIST_CHUNK_BYTES = 1 * 1024 * 1024;
1653
- const MAP_INCREMENTAL_PERSIST_INTERVAL_MS = 100;
1665
+ const MAP_INCREMENTAL_PERSIST_INTERVAL_MS = 1_000;
1666
+ const MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS = 1_000;
1654
1667
  /**
1655
1668
  * Bounded number of per-row failure samples carried in chunk summaries and the
1656
1669
  * map's terminal partial-failure log. Every failed row is persisted with its
1657
1670
  * full error in the runtime sheet; the samples just keep run logs readable.
1658
1671
  */
1659
1672
  const MAP_ROW_FAILURE_SAMPLE_LIMIT = 3;
1673
+
1674
+ class RuntimeReceiptPersistenceError extends Error {
1675
+ readonly errors: unknown[];
1676
+
1677
+ constructor(message: string, errors: unknown[] = []) {
1678
+ super(message);
1679
+ this.name = 'RuntimeReceiptPersistenceError';
1680
+ this.errors = errors;
1681
+ }
1682
+ }
1683
+
1684
+ function isRuntimeReceiptPersistenceError(error: unknown): boolean {
1685
+ if (!error || typeof error !== 'object') return false;
1686
+ if (error instanceof RuntimeReceiptPersistenceError) return true;
1687
+ if (error instanceof Error && error.name === 'RuntimeReceiptPersistenceError') {
1688
+ return true;
1689
+ }
1690
+ const nestedErrors = (error as { errors?: unknown }).errors;
1691
+ return (
1692
+ Array.isArray(nestedErrors) &&
1693
+ nestedErrors.some(isRuntimeReceiptPersistenceError)
1694
+ );
1695
+ }
1696
+
1697
+ function isRunFatalWorkerRowError(error: unknown): boolean {
1698
+ return (
1699
+ isRowIsolationExemptError(error) || isRuntimeReceiptPersistenceError(error)
1700
+ );
1701
+ }
1702
+
1660
1703
  // Fallback batch-chunk parallelism when a tool declares no provider rate hints.
1661
1704
  // Matches the prior hardcoded `Math.min(4, ...)` so undeclared providers keep
1662
1705
  // their previous batching behavior; declared providers tighten via the
@@ -1982,6 +2025,7 @@ class WorkerToolBatchScheduler {
1982
2025
  !canReclaimTimedOutWorkerToolReceipt({
1983
2026
  ownerRunId: input.runningReceipt.runId,
1984
2027
  currentRunId: this.req.runId,
2028
+ updatedAt: input.runningReceipt.updatedAt,
1985
2029
  })
1986
2030
  ) {
1987
2031
  this.rejectRawRequests(input.group, waitError);
@@ -2100,9 +2144,9 @@ class WorkerToolBatchScheduler {
2100
2144
  await this.failDurableToolRequest(claimed, error);
2101
2145
  return error;
2102
2146
  } catch (receiptError) {
2103
- return new AggregateError(
2104
- [error, receiptError],
2147
+ return new RuntimeReceiptPersistenceError(
2105
2148
  'Tool call failed and durable receipt could not be marked failed',
2149
+ [error, receiptError],
2106
2150
  );
2107
2151
  }
2108
2152
  }
@@ -2459,25 +2503,38 @@ class WorkerToolBatchScheduler {
2459
2503
  signal: this.abortSignal,
2460
2504
  });
2461
2505
  try {
2462
- const result = await executeToolWithLifecycle(
2463
- this.req,
2464
- { id: request.id, toolId, input: request.input },
2465
- request.workflowStep,
2466
- this.callbacks,
2467
- (retryAfterMs) => this.reportBackpressure(toolId, retryAfterMs),
2468
- () => this.governor.chargeBudget('retry'),
2469
- toolContract?.retrySafeTransientHttp === true,
2470
- this.abortSignal,
2471
- this.runtimeDeadlineMs,
2472
- );
2506
+ let result: unknown;
2507
+ try {
2508
+ result = await executeToolWithLifecycle(
2509
+ this.req,
2510
+ { id: request.id, toolId, input: request.input },
2511
+ request.workflowStep,
2512
+ this.callbacks,
2513
+ (retryAfterMs) =>
2514
+ this.reportBackpressure(toolId, retryAfterMs),
2515
+ () => this.governor.chargeBudget('retry'),
2516
+ toolContract?.retrySafeTransientHttp === true,
2517
+ this.abortSignal,
2518
+ this.runtimeDeadlineMs,
2519
+ );
2520
+ } catch (error) {
2521
+ this.rejectRequests(
2522
+ claimed,
2523
+ await this.failureForRejectedToolRequest(claimed, error),
2524
+ );
2525
+ return;
2526
+ }
2473
2527
  this.settleRequests(
2474
2528
  claimed,
2475
2529
  await this.completeDurableToolRequest(claimed, result),
2476
2530
  );
2477
- } catch (error) {
2531
+ } catch (receiptError) {
2478
2532
  this.rejectRequests(
2479
2533
  claimed,
2480
- await this.failureForRejectedToolRequest(claimed, error),
2534
+ new RuntimeReceiptPersistenceError(
2535
+ 'Tool call succeeded but durable receipt could not be marked completed',
2536
+ [receiptError],
2537
+ ),
2481
2538
  );
2482
2539
  } finally {
2483
2540
  slot.release();
@@ -2651,9 +2708,9 @@ async function executeBatchedWorkerToolGroup(input: {
2651
2708
  try {
2652
2709
  await input.failRequests(entry.request.memberRequests, entry.error);
2653
2710
  } catch (receiptError) {
2654
- rejection = new AggregateError(
2655
- [entry.error, receiptError],
2711
+ rejection = new RuntimeReceiptPersistenceError(
2656
2712
  'Tool call failed and durable receipts could not be marked failed',
2713
+ [entry.error, receiptError],
2657
2714
  );
2658
2715
  }
2659
2716
  for (const claimed of entry.request.memberRequests) {
@@ -2668,16 +2725,28 @@ async function executeBatchedWorkerToolGroup(input: {
2668
2725
  batchResult != null
2669
2726
  ? entry.request.splitResults(batchResult)
2670
2727
  : entry.request.memberRequests.map(() => null);
2671
- const completedResults = await input.completeRequests(
2672
- entry.request.memberRequests.map((claimed, index) => ({
2673
- claimed,
2674
- result: wrapWorkerToolResult(
2675
- claimed.request.toolId,
2676
- splitResults[index] ?? null,
2677
- toolMetadataFallback(claimed.request.toolId),
2678
- ),
2679
- })),
2680
- );
2728
+ let completedResults: unknown[];
2729
+ try {
2730
+ completedResults = await input.completeRequests(
2731
+ entry.request.memberRequests.map((claimed, index) => ({
2732
+ claimed,
2733
+ result: wrapWorkerToolResult(
2734
+ claimed.request.toolId,
2735
+ splitResults[index] ?? null,
2736
+ toolMetadataFallback(claimed.request.toolId),
2737
+ ),
2738
+ })),
2739
+ );
2740
+ } catch (receiptError) {
2741
+ const rejection = new RuntimeReceiptPersistenceError(
2742
+ 'Tool call succeeded but durable receipts could not be marked completed',
2743
+ [receiptError],
2744
+ );
2745
+ for (const claimed of entry.request.memberRequests) {
2746
+ input.rejectRequest(claimed, rejection);
2747
+ }
2748
+ continue;
2749
+ }
2681
2750
  for (let index = 0; index < completedResults.length; index += 1) {
2682
2751
  const claimed = entry.request.memberRequests[index]!;
2683
2752
  const request = claimed.request;
@@ -2690,9 +2759,9 @@ async function executeBatchedWorkerToolGroup(input: {
2690
2759
  try {
2691
2760
  await input.failRequests(input.requests, error);
2692
2761
  } catch (receiptError) {
2693
- rejection = new AggregateError(
2694
- [error, receiptError],
2762
+ rejection = new RuntimeReceiptPersistenceError(
2695
2763
  'Tool call failed and durable receipts could not be marked failed',
2764
+ [error, receiptError],
2696
2765
  );
2697
2766
  }
2698
2767
  for (const claimed of input.requests) {
@@ -2728,6 +2797,7 @@ type WorkerMapChunkSummary<T extends Record<string, unknown>> = {
2728
2797
  stepCellsSkipped: number;
2729
2798
  outputDatasetId: string;
2730
2799
  hash: string;
2800
+ fatalError?: string;
2731
2801
  preview: T[];
2732
2802
  cachedRows?: T[];
2733
2803
  };
@@ -3816,6 +3886,15 @@ async function persistCompletedMapRows(input: {
3816
3886
  rows,
3817
3887
  outputFields,
3818
3888
  });
3889
+ console.warn('[play-runner.persist_completed_map_rows.start]', {
3890
+ runId: req.runId,
3891
+ playName: req.playName,
3892
+ tableNamespace,
3893
+ rows: rows.length,
3894
+ outputFields,
3895
+ extraOutputFields: input.extraOutputFields ?? [],
3896
+ contractColumnCount: sheetContract.columns.length,
3897
+ });
3819
3898
  const persistRequest = {
3820
3899
  ...sessionScope,
3821
3900
  tableNamespace,
@@ -3830,6 +3909,8 @@ async function persistCompletedMapRows(input: {
3830
3909
  .filter((key): key is string => Boolean(key)),
3831
3910
  ),
3832
3911
  ];
3912
+ const expectedVisibleRows =
3913
+ expectedKeys.length > 0 ? expectedKeys.length : rows.length;
3833
3914
  const readVisibleRowCount = async () => {
3834
3915
  if (expectedKeys.length > 0) {
3835
3916
  const result = await harnessReadSheetDatasetRowKeys({
@@ -3852,12 +3933,24 @@ async function persistCompletedMapRows(input: {
3852
3933
  return result.rows.length;
3853
3934
  };
3854
3935
  const result = await harnessPersistCompletedSheetRows(persistRequest);
3936
+ console.warn('[play-runner.persist_completed_map_rows.result]', {
3937
+ runId: req.runId,
3938
+ playName: req.playName,
3939
+ tableNamespace,
3940
+ rows: rows.length,
3941
+ rowsWritten: result.rowsWritten,
3942
+ expectedVisibleRows,
3943
+ expectedKeyCount: expectedKeys.length,
3944
+ });
3855
3945
  let visibleRows = -1;
3856
3946
  let retryWritten: number | null = null;
3857
3947
  let retryVisible: number | null = null;
3858
- if (rows.length <= MAP_INCREMENTAL_PERSIST_CHUNK_ROWS) {
3948
+ if (
3949
+ rows.length <= MAP_INCREMENTAL_PERSIST_CHUNK_ROWS ||
3950
+ result.rowsWritten !== rows.length
3951
+ ) {
3859
3952
  visibleRows = await readVisibleRowCount();
3860
- if (visibleRows < rows.length) {
3953
+ if (visibleRows < expectedVisibleRows) {
3861
3954
  await harnessStartSheetDataset({
3862
3955
  ...sessionScope,
3863
3956
  tableNamespace,
@@ -3869,7 +3962,7 @@ async function persistCompletedMapRows(input: {
3869
3962
  const retry = await harnessPersistCompletedSheetRows(persistRequest);
3870
3963
  retryWritten = retry.rowsWritten;
3871
3964
  retryVisible = await readVisibleRowCount();
3872
- if (retryVisible >= rows.length) {
3965
+ if (retryVisible >= expectedVisibleRows) {
3873
3966
  return {
3874
3967
  rows: rows.length,
3875
3968
  written: result.rowsWritten,
@@ -3879,13 +3972,13 @@ async function persistCompletedMapRows(input: {
3879
3972
  };
3880
3973
  }
3881
3974
  throw new Error(
3882
- `Runtime sheet persistence mismatch for ${tableNamespace}: wrote ${result.rowsWritten}/${rows.length}; visible ${visibleRows}; retry wrote ${retryWritten}/${rows.length}; retry visible ${retryVisible}; run ${req.runId}.`,
3975
+ `Runtime sheet persistence mismatch for ${tableNamespace}: wrote ${result.rowsWritten}/${rows.length}; visible ${visibleRows}/${expectedVisibleRows}; retry wrote ${retryWritten}/${rows.length}; retry visible ${retryVisible}/${expectedVisibleRows}; run ${req.runId}.`,
3883
3976
  );
3884
3977
  }
3885
3978
  }
3886
- if (result.rowsWritten !== rows.length) {
3979
+ if (result.rowsWritten !== rows.length && visibleRows < expectedVisibleRows) {
3887
3980
  throw new Error(
3888
- `Runtime sheet persistence mismatch for ${tableNamespace}: wrote ${result.rowsWritten}/${rows.length}; run ${req.runId}.`,
3981
+ `Runtime sheet persistence mismatch for ${tableNamespace}: wrote ${result.rowsWritten}/${rows.length}; visible ${visibleRows}/${expectedVisibleRows}; run ${req.runId}.`,
3889
3982
  );
3890
3983
  }
3891
3984
  return {
@@ -4940,7 +5033,7 @@ function createMinimalWorkerCtx(
4940
5033
  let persistFailure: unknown = null;
4941
5034
  let scheduledLiveUpdateTimer: ReturnType<typeof setTimeout> | null = null;
4942
5035
  let liveUpdateFlushChain: Promise<void> = Promise.resolve();
4943
- let liveUpdateFailure: unknown = null;
5036
+ let liveUpdateFailureCount = 0;
4944
5037
 
4945
5038
  const clearScheduledPersistTimer = () => {
4946
5039
  if (scheduledPersistTimer) {
@@ -5078,25 +5171,35 @@ function createMinimalWorkerCtx(
5078
5171
  const updates = pendingLiveRowUpdates.splice(0);
5079
5172
  const extraOutputFields = Array.from(generatedOutputFields);
5080
5173
  const task = liveUpdateFlushChain.then(async () => {
5081
- if (liveUpdateFailure) throw liveUpdateFailure;
5082
- await applyLiveMapRowUpdates({
5083
- req,
5084
- tableNamespace: name,
5085
- outputFields,
5086
- extraOutputFields,
5087
- updates,
5088
- });
5089
- });
5090
- liveUpdateFlushChain = task.catch((error) => {
5091
- liveUpdateFailure ??= error;
5174
+ try {
5175
+ await applyLiveMapRowUpdates({
5176
+ req,
5177
+ tableNamespace: name,
5178
+ outputFields,
5179
+ extraOutputFields,
5180
+ updates,
5181
+ });
5182
+ } catch (error) {
5183
+ liveUpdateFailureCount += 1;
5184
+ if (liveUpdateFailureCount <= MAP_ROW_FAILURE_SAMPLE_LIMIT) {
5185
+ emitEvent({
5186
+ type: 'log',
5187
+ level: 'warn',
5188
+ message:
5189
+ `Live row update flush failed for ctx.dataset("${name}") ` +
5190
+ `(non-fatal; terminal rows still persist): ${formatWorkerRowFailureMessage(error)}`,
5191
+ ts: nowMs(),
5192
+ });
5193
+ }
5194
+ }
5092
5195
  });
5196
+ liveUpdateFlushChain = task.catch(() => undefined);
5093
5197
  return task;
5094
5198
  };
5095
5199
 
5096
5200
  const scheduleLiveRowUpdates = () => {
5097
- if (liveUpdateFailure) return;
5098
5201
  if (
5099
- pendingLiveRowUpdates.length >= MAP_INCREMENTAL_PERSIST_CHUNK_ROWS
5202
+ pendingLiveRowUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS
5100
5203
  ) {
5101
5204
  void flushLiveRowUpdates().catch(() => undefined);
5102
5205
  return;
@@ -5111,14 +5214,12 @@ function createMinimalWorkerCtx(
5111
5214
  const enqueueLiveRowUpdate = (
5112
5215
  update: Omit<PlayRowUpdate, 'rowId'> & { runId?: string },
5113
5216
  ): Promise<void> => {
5114
- if (liveUpdateFailure) {
5115
- return Promise.reject(liveUpdateFailure);
5116
- }
5117
5217
  pendingLiveRowUpdates.push(update);
5118
5218
  if (
5119
- pendingLiveRowUpdates.length >= MAP_INCREMENTAL_PERSIST_CHUNK_ROWS
5219
+ pendingLiveRowUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS
5120
5220
  ) {
5121
- return flushLiveRowUpdates();
5221
+ void flushLiveRowUpdates().catch(() => undefined);
5222
+ return Promise.resolve();
5122
5223
  }
5123
5224
  scheduleLiveRowUpdates();
5124
5225
  return Promise.resolve();
@@ -5301,7 +5402,7 @@ function createMinimalWorkerCtx(
5301
5402
  } catch (rowError) {
5302
5403
  // Abort/budget errors stay run-fatal and leave no partial
5303
5404
  // state: rethrow immediately without recording the row.
5304
- if (isRowIsolationExemptError(rowError)) {
5405
+ if (isRunFatalWorkerRowError(rowError)) {
5305
5406
  throw rowError;
5306
5407
  }
5307
5408
  const message = formatWorkerRowFailureMessage(rowError);
@@ -5401,6 +5502,43 @@ function createMinimalWorkerCtx(
5401
5502
  concurrency,
5402
5503
  },
5403
5504
  });
5505
+ const buildRowFailureSamples = () =>
5506
+ failedRowEntries
5507
+ .map((failure, executedIndex) =>
5508
+ failure
5509
+ ? {
5510
+ rowKey: uniqueRowsToExecuteEntries[executedIndex]!.rowKey,
5511
+ error: failure.error,
5512
+ }
5513
+ : null,
5514
+ )
5515
+ .filter(
5516
+ (sample): sample is { rowKey: string; error: string } =>
5517
+ sample !== null,
5518
+ )
5519
+ .slice(0, MAP_ROW_FAILURE_SAMPLE_LIMIT);
5520
+ const fatalMapChunkSummary = async (
5521
+ error: unknown,
5522
+ ): Promise<WorkerMapChunkSummary<T & Record<string, unknown>>> => ({
5523
+ chunkIndex,
5524
+ rangeStart: baseOffset + chunkStart,
5525
+ rangeEnd: baseOffset + chunkStart,
5526
+ rowsRead: chunkRows.length,
5527
+ rowsWritten: 0,
5528
+ rowsExecuted: rowsToExecute.length,
5529
+ rowsCached: 0,
5530
+ rowsDuplicateReused: duplicateInputReuseCount,
5531
+ rowsInserted,
5532
+ rowsSkipped,
5533
+ rowsFailed: failedExecutedRows,
5534
+ rowFailureSamples: buildRowFailureSamples(),
5535
+ stepCellsCompleted,
5536
+ stepCellsSkipped,
5537
+ outputDatasetId: `map:${name}`,
5538
+ hash: await hashJson([]),
5539
+ fatalError: formatWorkerRowFailureMessage(error),
5540
+ preview: [],
5541
+ });
5404
5542
  const persistRowsStartedAt = nowMs();
5405
5543
  recordRunnerPerfTrace({
5406
5544
  req,
@@ -5415,7 +5553,6 @@ function createMinimalWorkerCtx(
5415
5553
  try {
5416
5554
  await flushLiveRowUpdates();
5417
5555
  await liveUpdateFlushChain;
5418
- if (liveUpdateFailure) throw liveUpdateFailure;
5419
5556
  await enqueuePersistExecutedRows();
5420
5557
  await persistFlushChain;
5421
5558
  if (persistFailure) throw persistFailure;
@@ -5441,13 +5578,16 @@ function createMinimalWorkerCtx(
5441
5578
  error: error instanceof Error ? error.message : String(error),
5442
5579
  },
5443
5580
  });
5444
- throw error;
5581
+ return await fatalMapChunkSummary(error);
5445
5582
  }
5446
5583
  const rejectedWorker = workerResults.find(
5447
5584
  (result): result is PromiseRejectedResult =>
5448
5585
  result.status === 'rejected',
5449
5586
  );
5450
5587
  if (rejectedWorker) {
5588
+ if (isRuntimeReceiptPersistenceError(rejectedWorker.reason)) {
5589
+ return await fatalMapChunkSummary(rejectedWorker.reason);
5590
+ }
5451
5591
  throw rejectedWorker.reason;
5452
5592
  }
5453
5593
  const resultByKey = new Map<string, T & Record<string, unknown>>();
@@ -5502,20 +5642,7 @@ function createMinimalWorkerCtx(
5502
5642
  0,
5503
5643
  executedRows.length - failedExecutedRows,
5504
5644
  );
5505
- const rowFailureSamples = failedRowEntries
5506
- .map((failure, executedIndex) =>
5507
- failure
5508
- ? {
5509
- rowKey: uniqueRowsToExecuteEntries[executedIndex]!.rowKey,
5510
- error: failure.error,
5511
- }
5512
- : null,
5513
- )
5514
- .filter(
5515
- (sample): sample is { rowKey: string; error: string } =>
5516
- sample !== null,
5517
- )
5518
- .slice(0, MAP_ROW_FAILURE_SAMPLE_LIMIT);
5645
+ const rowFailureSamples = buildRowFailureSamples();
5519
5646
  const publicOut = out.map((row) => publicCsvOutputRow(row));
5520
5647
  const keyedOut = outEntries.map(({ key, inputIndex, row }) => ({
5521
5648
  ...row,
@@ -5764,6 +5891,15 @@ function createMinimalWorkerCtx(
5764
5891
  continue;
5765
5892
  }
5766
5893
  const chunkResult = await runChunkStep(chunkRows, chunkStart, chunkIndex);
5894
+ if (chunkResult.fatalError) {
5895
+ throw new Error(
5896
+ `ctx.dataset("${name}") stopped after a runtime persistence failure ` +
5897
+ `outside the retryable chunk step. Provider calls already executed for ` +
5898
+ `${chunkResult.rowsExecuted} row(s), so the chunk was not retried. ` +
5899
+ `Fix the runtime persistence cause and re-run to resume. ` +
5900
+ `First error: ${chunkResult.fatalError}`,
5901
+ );
5902
+ }
5767
5903
  totalRowsWritten += chunkResult.rowsWritten;
5768
5904
  totalRowsExecuted += chunkResult.rowsExecuted;
5769
5905
  totalRowsCached += chunkResult.rowsCached;
@@ -6282,6 +6418,7 @@ function createMinimalWorkerCtx(
6282
6418
  orgId: req.orgId,
6283
6419
  callbackBaseUrl: req.callbackUrl,
6284
6420
  baseUrl: req.baseUrl,
6421
+ integrationMode: req.integrationMode ?? null,
6285
6422
  parentExecutorToken: req.executorToken,
6286
6423
  userEmail: req.userEmail ?? '',
6287
6424
  profile: 'workers_edge',
@@ -7660,11 +7797,18 @@ function extractMaxCreditsPerRun(contractSnapshot: unknown): number | null {
7660
7797
  : null;
7661
7798
  }
7662
7799
 
7800
+ function shouldSkipWorkerComputeBilling(req: RunRequest): boolean {
7801
+ return req.integrationMode === 'fixture' || req.integrationMode === 'eval_stub';
7802
+ }
7803
+
7663
7804
  async function finalizeWorkerComputeBilling(input: {
7664
7805
  req: RunRequest;
7665
7806
  success: boolean;
7666
7807
  actionEstimate: number;
7667
7808
  }): Promise<void> {
7809
+ if (shouldSkipWorkerComputeBilling(input.req)) {
7810
+ return;
7811
+ }
7668
7812
  const maxCreditsPerRun = extractMaxCreditsPerRun(input.req.contractSnapshot);
7669
7813
  await postRuntimeApi(input.req.baseUrl, input.req.executorToken, {
7670
7814
  action: 'compute_billing_finalize',
@@ -7720,6 +7864,7 @@ function runRequestFromWorkflowParams(
7720
7864
  callbackUrl: String(params.baseUrl ?? ''),
7721
7865
  executorToken: String(params.executorToken ?? ''),
7722
7866
  baseUrl: String(params.baseUrl ?? ''),
7867
+ integrationMode: normalizeIntegrationMode(params.integrationMode),
7723
7868
  orgId: String(params.orgId ?? ''),
7724
7869
  playName: String(params.playName ?? ''),
7725
7870
  userEmail: typeof params.userEmail === 'string' ? params.userEmail : null,
@@ -8059,7 +8204,7 @@ export class TenantWorkflow extends WorkflowEntrypoint<
8059
8204
  // somewhere inside executeRunRequest. If it doesn't appear, the
8060
8205
  // throw is in the framework wrapper between the loader and run().
8061
8206
  console.log(
8062
- `${runPrefix} TenantWorkflow.run entered baseUrl=${req.baseUrl}`,
8207
+ `${runPrefix} TenantWorkflow.run entered baseUrl=${req.baseUrl} integrationMode=${req.integrationMode ?? 'default'}`,
8063
8208
  );
8064
8209
  captureCoordinatorBinding(this.env);
8065
8210
  captureRuntimeApiBinding(this.env);
@@ -8073,6 +8218,7 @@ export class TenantWorkflow extends WorkflowEntrypoint<
8073
8218
  ms: 0,
8074
8219
  extra: {
8075
8220
  hasWorkflowStep: true,
8221
+ integrationMode: req.integrationMode ?? null,
8076
8222
  },
8077
8223
  });
8078
8224
  // Fire the one-time wiring probe (deduplicated across runs in the