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
@@ -14,9 +14,10 @@ import {
14
14
 
15
15
  export const CACHE_ENABLED_SIMPLE_MAP_CHUNK_SIZE = 10_000;
16
16
  export { TOOL_CALLING_MAP_CHUNK_SIZE };
17
- // Cloudflare preview Workers enforce the 50-subrequest invocation limit. Leave
18
- // headroom for coordinator/storage calls around row-level unbatched tool RPCs.
19
- export const UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET = 40;
17
+ // Paid Cloudflare Workers support a much higher configured subrequest limit.
18
+ // Keep a large buffer for coordinator/storage calls around row-level unbatched
19
+ // tool RPCs, but avoid pathological one-row chunks for provider waterfalls.
20
+ export const UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET = 500;
20
21
  // Fresh unbatched tool calls use one RUNTIME_API integration execute RPC and
21
22
  // one HARNESS durable-receipt completion RPC. Batch-cap rows by both.
22
23
  export const SUBREQUESTS_PER_UNBATCHED_TOOL_CALL = 2;
@@ -46,6 +47,10 @@ type ChunkSizingPlan = {
46
47
 
47
48
  type MapToolStats = [totalToolCount: number, unbatchedToolCount: number];
48
49
 
50
+ function fieldRoot(field: string | null | undefined): string {
51
+ return String(field ?? '').split('.', 1)[0] ?? '';
52
+ }
53
+
49
54
  function declarationBelongsToMapOutput(
50
55
  declaration: { field?: string | null },
51
56
  outputFields: readonly string[],
@@ -76,39 +81,124 @@ function countToolSubsteps(
76
81
  let unbatchedToolCount = 0;
77
82
 
78
83
  for (const substep of substeps) {
79
- if (substep.type === 'tool') {
84
+ const [nestedTotal, nestedUnbatched] = countToolSubstep(substep);
85
+ totalToolCount += nestedTotal;
86
+ unbatchedToolCount += nestedUnbatched;
87
+ }
88
+
89
+ return [totalToolCount, unbatchedToolCount];
90
+ }
91
+
92
+ function countToolSubstep(substep: PlayStaticSubstep): MapToolStats {
93
+ if (substep.type === 'tool') {
94
+ return [1, isUnbatchedTool(substep.toolId) ? 1 : 0];
95
+ }
96
+
97
+ if (substep.type === 'waterfall') {
98
+ let totalToolCount = 0;
99
+ let unbatchedToolCount = 0;
100
+ for (const step of substep.steps ?? []) {
101
+ if (!step.toolId) continue;
80
102
  totalToolCount += 1;
81
- if (isUnbatchedTool(substep.toolId)) {
103
+ if (isUnbatchedTool(step.toolId)) {
82
104
  unbatchedToolCount += 1;
83
105
  }
84
- continue;
85
106
  }
107
+ return [totalToolCount, unbatchedToolCount];
108
+ }
86
109
 
87
- if (substep.type === 'waterfall') {
88
- for (const step of substep.steps ?? []) {
89
- if (!step.toolId) continue;
90
- totalToolCount += 1;
91
- if (isUnbatchedTool(step.toolId)) {
92
- unbatchedToolCount += 1;
93
- }
94
- }
95
- continue;
96
- }
110
+ if (substep.type === 'step_suite') {
111
+ return countStepSuiteTools(substep);
112
+ }
97
113
 
98
- if (
99
- substep.type === 'step_suite' ||
100
- substep.type === 'control_flow' ||
101
- substep.type === 'dataset'
102
- ) {
103
- const [nestedTotal, nestedUnbatched] = countToolSubsteps(
104
- substep.steps ?? [],
114
+ if (substep.type === 'control_flow') {
115
+ if (substep.kind === 'conditional' && substep.branches?.length) {
116
+ const branchStats = substep.branches.map((branch) =>
117
+ countToolSubsteps(branch.steps),
118
+ );
119
+ return branchStats.reduce<MapToolStats>(
120
+ ([totalMax, unbatchedMax], [total, unbatched]) => [
121
+ Math.max(totalMax, total),
122
+ Math.max(unbatchedMax, unbatched),
123
+ ],
124
+ [0, 0],
105
125
  );
106
- totalToolCount += nestedTotal;
107
- unbatchedToolCount += nestedUnbatched;
108
126
  }
127
+ return countToolSubsteps(substep.steps);
109
128
  }
110
129
 
111
- return [totalToolCount, unbatchedToolCount];
130
+ if (substep.type === 'dataset') {
131
+ return countToolSubsteps(substep.steps ?? []);
132
+ }
133
+
134
+ return [0, 0];
135
+ }
136
+
137
+ function countStepSuiteTools(
138
+ substep: Extract<PlayStaticSubstep, { type: 'step_suite' }>,
139
+ ): MapToolStats {
140
+ const childStats = substep.steps.map((step) => ({
141
+ step,
142
+ stats: countToolSubstep(step),
143
+ }));
144
+ const conditionalChildren = childStats.filter(
145
+ ({ step }) =>
146
+ step.conditional ||
147
+ (step.type === 'control_flow' && step.kind === 'conditional'),
148
+ );
149
+ if (
150
+ conditionalChildren.length === 0 ||
151
+ conditionalChildren.length < childStats.length / 2
152
+ ) {
153
+ return childStats.reduce<MapToolStats>(
154
+ ([totalSum, unbatchedSum], { stats: [total, unbatched] }) => [
155
+ totalSum + total,
156
+ unbatchedSum + unbatched,
157
+ ],
158
+ [0, 0],
159
+ );
160
+ }
161
+
162
+ const suiteRoot = fieldRoot(substep.field);
163
+ const sameRoot = childStats.every(({ step }) => {
164
+ if (!('field' in step)) return true;
165
+ return fieldRoot(step.field) === suiteRoot;
166
+ });
167
+ if (!sameRoot) {
168
+ return childStats.reduce<MapToolStats>(
169
+ ([totalSum, unbatchedSum], { stats: [total, unbatched] }) => [
170
+ totalSum + total,
171
+ unbatchedSum + unbatched,
172
+ ],
173
+ [0, 0],
174
+ );
175
+ }
176
+
177
+ const unconditionalStats = childStats
178
+ .filter(
179
+ ({ step }) =>
180
+ !step.conditional &&
181
+ !(step.type === 'control_flow' && step.kind === 'conditional'),
182
+ )
183
+ .reduce<MapToolStats>(
184
+ ([totalSum, unbatchedSum], { stats: [total, unbatched] }) => [
185
+ totalSum + total,
186
+ unbatchedSum + unbatched,
187
+ ],
188
+ [0, 0],
189
+ );
190
+ const conditionalStats = conditionalChildren.reduce<MapToolStats>(
191
+ ([totalSum, unbatchedSum], { stats: [total, unbatched] }) => [
192
+ totalSum + total,
193
+ unbatchedSum + unbatched,
194
+ ],
195
+ [0, 0],
196
+ );
197
+
198
+ return [
199
+ unconditionalStats[0] + conditionalStats[0],
200
+ unconditionalStats[1] + conditionalStats[1],
201
+ ];
112
202
  }
113
203
 
114
204
  function countProducerTools(
@@ -125,13 +215,9 @@ function countProducerTools(
125
215
  }
126
216
  }
127
217
  if (producer.substep.type === 'waterfall') {
128
- for (const step of producer.substep.steps ?? []) {
129
- if (!step.toolId) continue;
130
- totalToolCount += 1;
131
- if (isUnbatchedTool(step.toolId)) {
132
- unbatchedToolCount += 1;
133
- }
134
- }
218
+ const [nestedTotal, nestedUnbatched] = countToolSubstep(producer.substep);
219
+ totalToolCount += nestedTotal;
220
+ unbatchedToolCount += nestedUnbatched;
135
221
  }
136
222
  if (producer.steps?.length) {
137
223
  const [nestedTotal, nestedUnbatched] = countProducerTools(producer.steps);
@@ -22,6 +22,8 @@ export type WorkerToolReceiptGroupPlan<TRequest> = {
22
22
  export function canReclaimTimedOutWorkerToolReceipt(input: {
23
23
  ownerRunId?: string | null;
24
24
  currentRunId: string;
25
+ updatedAt?: string | null;
26
+ nowMs?: number;
25
27
  }): boolean {
26
28
  const currentRunId = input.currentRunId.trim();
27
29
  return Boolean(currentRunId);
@@ -19,11 +19,13 @@ export async function createOrAttachWorkflowInstance<
19
19
  >(input: {
20
20
  create: () => Promise<T>;
21
21
  getExisting: () => Promise<T>;
22
+ onCreateFailure?: (error: unknown) => Promise<void>;
22
23
  }): Promise<WorkflowInstanceCreateResult<T>> {
23
24
  try {
24
25
  return { instance: await input.create(), startMode: 'created' };
25
26
  } catch (error) {
26
27
  if (!isWorkflowInstanceAlreadyExistsError(error)) {
28
+ await input.onCreateFailure?.(error);
27
29
  throw error;
28
30
  }
29
31
  try {
@@ -32,6 +34,7 @@ export async function createOrAttachWorkflowInstance<
32
34
  startMode: 'reattached_existing',
33
35
  };
34
36
  } catch (attachError) {
37
+ await input.onCreateFailure?.(attachError);
35
38
  const message =
36
39
  attachError instanceof Error
37
40
  ? attachError.message
@@ -91,6 +91,23 @@ const REGISTER_PLAY_ARTIFACTS_COMPILE_CONCURRENCY = 3;
91
91
  const REGISTER_PLAY_ARTIFACTS_MAX_BATCH_COUNT = 3;
92
92
  const REGISTER_PLAY_ARTIFACTS_MAX_BATCH_BYTES = 2_500_000;
93
93
 
94
+ function normalizePlayRunIntegrationMode(
95
+ value: unknown,
96
+ ): 'live' | 'eval_stub' | 'fixture' | undefined {
97
+ if (value === 'live' || value === 'eval_stub' || value === 'fixture') {
98
+ return value;
99
+ }
100
+ return undefined;
101
+ }
102
+
103
+ function resolvePlayRunIntegrationMode(
104
+ request: StartPlayRunRequest,
105
+ ): 'live' | 'eval_stub' | 'fixture' | undefined {
106
+ return normalizePlayRunIntegrationMode(
107
+ request.integrationMode ?? process.env.DEEPLINE_EVAL_INTEGRATION_MODE,
108
+ );
109
+ }
110
+
94
111
  function sleep(ms: number): Promise<void> {
95
112
  return new Promise((resolve) => setTimeout(resolve, ms));
96
113
  }
@@ -1238,6 +1255,7 @@ export class DeeplineClient {
1238
1255
  * ```
1239
1256
  */
1240
1257
  async startPlayRun(request: StartPlayRunRequest): Promise<PlayRunStart> {
1258
+ const integrationMode = resolvePlayRunIntegrationMode(request);
1241
1259
  const response = await this.http.post<Record<string, unknown>>(
1242
1260
  '/api/v2/plays/run',
1243
1261
  {
@@ -1279,6 +1297,7 @@ export class DeeplineClient {
1279
1297
  // defaults to workers_edge; tests and runtime probes that want a
1280
1298
  // different profile pass `request.profile` explicitly.
1281
1299
  ...(request.profile ? { profile: request.profile } : {}),
1300
+ ...(integrationMode ? { integrationMode } : {}),
1282
1301
  },
1283
1302
  );
1284
1303
  return normalizePlayRunStart(response);
@@ -1299,6 +1318,7 @@ export class DeeplineClient {
1299
1318
  request: StartPlayRunRequest,
1300
1319
  options?: { signal?: AbortSignal },
1301
1320
  ): AsyncGenerator<PlayLiveEvent> {
1321
+ const integrationMode = resolvePlayRunIntegrationMode(request);
1302
1322
  const body = {
1303
1323
  ...(request.name ? { name: request.name } : {}),
1304
1324
  ...(request.revisionId ? { revisionId: request.revisionId } : {}),
@@ -1335,6 +1355,7 @@ export class DeeplineClient {
1335
1355
  ? { waitForCompletionMs: request.waitForCompletionMs }
1336
1356
  : {}),
1337
1357
  ...(request.profile ? { profile: request.profile } : {}),
1358
+ ...(integrationMode ? { integrationMode } : {}),
1338
1359
  };
1339
1360
  for await (const event of this.http.streamSse<PlayLiveEvent>(
1340
1361
  '/api/v2/plays/run?stream=true',
@@ -1538,8 +1559,15 @@ export class DeeplineClient {
1538
1559
  sourceFiles?: Record<string, string>;
1539
1560
  description?: string;
1540
1561
  artifact: Record<string, unknown>;
1562
+ integrationMode?: 'live' | 'eval_stub' | 'fixture';
1541
1563
  }): Promise<PlayCheckResult> {
1542
- return this.http.post('/api/v2/plays/check', input);
1564
+ const integrationMode = normalizePlayRunIntegrationMode(
1565
+ input.integrationMode ?? process.env.DEEPLINE_EVAL_INTEGRATION_MODE,
1566
+ );
1567
+ return this.http.post('/api/v2/plays/check', {
1568
+ ...input,
1569
+ ...(integrationMode ? { integrationMode } : {}),
1570
+ });
1543
1571
  }
1544
1572
 
1545
1573
  /**
@@ -104,10 +104,10 @@ export const SDK_RELEASE = {
104
104
  // 0.1.111 ships dataset-native tool list getters and result row datasets.
105
105
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
106
106
  // fields shipped in 0.1.153.
107
- version: '0.1.167',
107
+ version: '0.1.169',
108
108
  apiContract: '2026-06-dataset-handle-results-hard-cutover',
109
109
  supportPolicy: {
110
- latest: '0.1.167',
110
+ latest: '0.1.169',
111
111
  minimumSupported: '0.1.53',
112
112
  deprecatedBelow: '0.1.53',
113
113
  commandMinimumSupported: [
@@ -919,6 +919,7 @@ export interface PlayRunStart {
919
919
  export interface PlayCheckResult {
920
920
  valid: boolean;
921
921
  errors: string[];
922
+ warnings?: string[];
922
923
  staticPipeline?: Record<string, unknown> | null;
923
924
  toolGetterHints?: PlayCheckToolGetterHint[];
924
925
  artifactHash?: string | null;
@@ -1005,6 +1006,8 @@ export interface StartPlayRunRequest {
1005
1006
  * should leave this unset.
1006
1007
  */
1007
1008
  profile?: string;
1009
+ /** Optional per-run provider execution mode for eval/smoke runs. */
1010
+ integrationMode?: 'live' | 'eval_stub' | 'fixture';
1008
1011
  }
1009
1012
 
1010
1013
  /**
@@ -1,11 +1,7 @@
1
- /**
2
- * SQL-safe physical column name for a play-authored logical field id.
3
- *
4
- * Runtime Sheet adapters must use this when they create or discover physical
5
- * JSONB columns so logical names such as `_metadata` and `person.email` do not
6
- * drift between writer and reader paths.
7
- */
8
- export function sqlSafePlayColumnName(id: string): string {
1
+ const POSTGRES_IDENTIFIER_MAX_BYTES = 63;
2
+ const HASH_SUFFIX_LENGTH = 10;
3
+
4
+ function normalizePlayColumnName(id: string): string {
9
5
  const normalized = id
10
6
  .trim()
11
7
  .replace(/\.+/g, '__')
@@ -15,3 +11,49 @@ export function sqlSafePlayColumnName(id: string): string {
15
11
  const safe = normalized || 'column';
16
12
  return /^[A-Za-z_]/.test(safe) ? safe : `c_${safe}`;
17
13
  }
14
+
15
+ function stableHexSuffix(value: string): string {
16
+ let primary = 0x811c9dc5;
17
+ let secondary = 0x811c9dc5 ^ value.length;
18
+ for (let index = 0; index < value.length; index += 1) {
19
+ const code = value.charCodeAt(index);
20
+ primary = Math.imul(primary ^ code, 0x01000193) >>> 0;
21
+ secondary = Math.imul(secondary ^ (code + index), 0x01000193) >>> 0;
22
+ }
23
+ return `${primary.toString(16).padStart(8, '0')}${secondary
24
+ .toString(16)
25
+ .padStart(8, '0')}`.slice(0, HASH_SUFFIX_LENGTH);
26
+ }
27
+
28
+ /**
29
+ * SQL-safe physical column name for a play-authored logical field id.
30
+ *
31
+ * Runtime Sheet adapters must use this when they create or discover physical
32
+ * JSONB columns so logical names such as `_metadata` and `person.email` do not
33
+ * drift between writer and reader paths.
34
+ */
35
+ export function sqlSafePlayColumnName(id: string): string {
36
+ const prefixed = normalizePlayColumnName(id);
37
+ if (prefixed.length <= POSTGRES_IDENTIFIER_MAX_BYTES) {
38
+ return prefixed;
39
+ }
40
+
41
+ const hash = stableHexSuffix(prefixed);
42
+ const maxPrefixLength =
43
+ POSTGRES_IDENTIFIER_MAX_BYTES - HASH_SUFFIX_LENGTH - 1;
44
+ const prefix = prefixed
45
+ .slice(0, maxPrefixLength)
46
+ .replace(/_+$/g, '')
47
+ .slice(0, maxPrefixLength);
48
+ return `${prefix}_${hash}`;
49
+ }
50
+
51
+ export function legacyPostgresTruncatedPlayColumnName(
52
+ id: string,
53
+ ): string | null {
54
+ const prefixed = normalizePlayColumnName(id);
55
+ if (prefixed.length <= POSTGRES_IDENTIFIER_MAX_BYTES) {
56
+ return null;
57
+ }
58
+ return prefixed.slice(0, POSTGRES_IDENTIFIER_MAX_BYTES);
59
+ }
@@ -1,11 +1,19 @@
1
1
  import type { PlaySheetContract } from '../plays/static-pipeline';
2
- import { sqlSafePlayColumnName } from './column-names';
2
+ import {
3
+ legacyPostgresTruncatedPlayColumnName,
4
+ sqlSafePlayColumnName,
5
+ } from './column-names';
3
6
 
4
7
  export type PhysicalSheetColumnProjection = {
5
8
  sqlName: string;
6
9
  fieldName: string;
7
10
  };
8
11
 
12
+ export type LegacyPhysicalSheetColumnBackfill = {
13
+ sqlName: string;
14
+ legacySqlName: string;
15
+ };
16
+
9
17
  const RUNTIME_SHEET_SYSTEM_FIELDS = new Set([
10
18
  '_key',
11
19
  '_status',
@@ -58,6 +66,37 @@ export function physicalSheetColumnNames(
58
66
  );
59
67
  }
60
68
 
69
+ export function legacyPhysicalSheetColumnBackfills(
70
+ sheetContract: PlaySheetContract | null | undefined,
71
+ ): LegacyPhysicalSheetColumnBackfill[] {
72
+ if (!sheetContract) {
73
+ return [];
74
+ }
75
+ const seen = new Set<string>();
76
+ const backfills: LegacyPhysicalSheetColumnBackfill[] = [];
77
+ for (const column of sheetContract.columns) {
78
+ const sqlName = column.sqlName.trim();
79
+ if (!sqlName || sqlName.startsWith('_')) {
80
+ continue;
81
+ }
82
+ const candidates = [
83
+ typeof column.field === 'string' ? column.field : null,
84
+ column.id,
85
+ ];
86
+ for (const candidate of candidates) {
87
+ if (!candidate) continue;
88
+ const legacySqlName = legacyPostgresTruncatedPlayColumnName(candidate);
89
+ const key = legacySqlName ? `${sqlName}:${legacySqlName}` : '';
90
+ if (!legacySqlName || legacySqlName === sqlName || seen.has(key)) {
91
+ continue;
92
+ }
93
+ seen.add(key);
94
+ backfills.push({ sqlName, legacySqlName });
95
+ }
96
+ }
97
+ return backfills;
98
+ }
99
+
61
100
  export function physicalSheetColumnFieldLookup(
62
101
  sheetContract: PlaySheetContract | null | undefined,
63
102
  ): Map<string, string> {
@@ -230,6 +230,7 @@ type RuntimeApiRequest =
230
230
  export type WorkerRuntimeApiContext = {
231
231
  baseUrl: string;
232
232
  executorToken: string;
233
+ integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
233
234
  vercelProtectionBypassToken?: string | null;
234
235
  fetch?: typeof fetch;
235
236
  };
@@ -4850,6 +4850,9 @@ export class PlayContextImpl {
4850
4850
  body: JSON.stringify({
4851
4851
  payload: input,
4852
4852
  metadata: { parent_run_id: this.#options.runId },
4853
+ ...(this.#options.integrationMode
4854
+ ? { integration_mode: this.#options.integrationMode }
4855
+ : {}),
4853
4856
  }),
4854
4857
  });
4855
4858
  } catch (error) {
@@ -388,6 +388,8 @@ export interface ContextOptions {
388
388
  executorToken?: string;
389
389
  baseUrl?: string;
390
390
  vercelProtectionBypassToken?: string | null;
391
+ /** Optional per-run integration execution mode for provider calls. */
392
+ integrationMode?: 'live' | 'eval_stub' | 'fixture';
391
393
  orgId?: string;
392
394
  userEmail?: string;
393
395
  playName?: string;
@@ -14,6 +14,7 @@ export interface PlayRunnerContextConfig {
14
14
  executorToken?: string;
15
15
  baseUrl?: string;
16
16
  vercelProtectionBypassToken?: string | null;
17
+ integrationMode?: 'live' | 'eval_stub' | 'fixture';
17
18
  orgId?: string;
18
19
  workflowId?: string;
19
20
  playId?: string;
@@ -83,6 +83,7 @@ type RuntimeApiContext = {
83
83
  playName?: string | null;
84
84
  runId?: string | null;
85
85
  userEmail?: string | null;
86
+ integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
86
87
  dbSessionStrategy?: 'preloaded' | 'sandbox_public_key' | null;
87
88
  preloadedDbSessions?: PreloadedRuntimeDbSession[] | null;
88
89
  vercelProtectionBypassToken?: string | null;
@@ -1512,6 +1513,7 @@ function mapRuntimeWorkReceiptRow(raw: Record<string, unknown>): WorkReceipt {
1512
1513
  output: raw.output == null ? null : raw.output,
1513
1514
  error: raw.error == null ? null : String(raw.error),
1514
1515
  runId: raw.run_id == null ? null : String(raw.run_id),
1516
+ updatedAt: raw.updated_at == null ? null : String(raw.updated_at),
1515
1517
  };
1516
1518
  }
1517
1519
 
@@ -124,6 +124,8 @@ export type PlaySchedulerSubmitInput = {
124
124
  dynamicWorkerCode?: string | null;
125
125
  executorToken: string;
126
126
  baseUrl: string;
127
+ /** Optional per-run provider execution mode. Defaults to server policy when omitted. */
128
+ integrationMode?: 'live' | 'eval_stub' | 'fixture';
127
129
  orgId: string;
128
130
  userEmail: string;
129
131
  userId?: string | null;
@@ -11,6 +11,7 @@ export type WorkReceipt = {
11
11
  output?: unknown;
12
12
  error?: string | null;
13
13
  runId?: string | null;
14
+ updatedAt?: string | null;
14
15
  };
15
16
 
16
17
  export type WorkReceiptCommand = {