deepline 0.1.171 → 0.1.173

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.
package/README.md CHANGED
@@ -195,7 +195,7 @@ Full sequence from zero to hitting + surviving rate limits via v2 plays.
195
195
 
196
196
  ```bash
197
197
  # Production
198
- bash <(curl -sS https://code.deepline.com/api/v2/sdk/install)
198
+ bash <(curl -fsSL https://deepline.com/api/v2/sdk/install)
199
199
 
200
200
  # Local dev (no install needed)
201
201
  bun run dev # start server
@@ -1719,7 +1719,10 @@ class RuntimeReceiptPersistenceError extends Error {
1719
1719
  function isRuntimeReceiptPersistenceError(error: unknown): boolean {
1720
1720
  if (!error || typeof error !== 'object') return false;
1721
1721
  if (error instanceof RuntimeReceiptPersistenceError) return true;
1722
- if (error instanceof Error && error.name === 'RuntimeReceiptPersistenceError') {
1722
+ if (
1723
+ error instanceof Error &&
1724
+ error.name === 'RuntimeReceiptPersistenceError'
1725
+ ) {
1723
1726
  return true;
1724
1727
  }
1725
1728
  const nestedErrors = (error as { errors?: unknown }).errors;
@@ -2559,8 +2562,7 @@ class WorkerToolBatchScheduler {
2559
2562
  { id: request.id, toolId, input: request.input },
2560
2563
  request.workflowStep,
2561
2564
  this.callbacks,
2562
- (retryAfterMs) =>
2563
- this.reportBackpressure(toolId, retryAfterMs),
2565
+ (retryAfterMs) => this.reportBackpressure(toolId, retryAfterMs),
2564
2566
  () => this.governor.chargeBudget('retry'),
2565
2567
  toolContract?.retrySafeTransientHttp === true,
2566
2568
  this.abortSignal,
@@ -4559,18 +4561,15 @@ function createMinimalWorkerCtx(
4559
4561
  if (!workflowStep) {
4560
4562
  return await executeWithRuntimeReceipt(name, execute);
4561
4563
  }
4562
- return await executeWithRuntimeReceipt(
4563
- name,
4564
- async () => {
4565
- const serialized = await (
4566
- workflowStep.do as unknown as (
4567
- name: string,
4568
- callback: () => Promise<unknown>,
4569
- ) => Promise<unknown>
4570
- )(name, async () => serializeDurableStepValue(await execute()));
4571
- return deserializeDurableStepValue(serialized) as T;
4572
- },
4573
- );
4564
+ return await executeWithRuntimeReceipt(name, async () => {
4565
+ const serialized = await (
4566
+ workflowStep.do as unknown as (
4567
+ name: string,
4568
+ callback: () => Promise<unknown>,
4569
+ ) => Promise<unknown>
4570
+ )(name, async () => serializeDurableStepValue(await execute()));
4571
+ return deserializeDurableStepValue(serialized) as T;
4572
+ });
4574
4573
  };
4575
4574
  const nextCtxStepReceiptKey = (
4576
4575
  name: string,
@@ -4653,7 +4652,6 @@ function createMinimalWorkerCtx(
4653
4652
  nodeId: mapNodeId,
4654
4653
  progress: {
4655
4654
  artifactTableNamespace: name,
4656
- failed: 0,
4657
4655
  ...progress,
4658
4656
  updatedAt: progress.updatedAt ?? nowMs(),
4659
4657
  },
@@ -5037,6 +5035,7 @@ function createMinimalWorkerCtx(
5037
5035
  {
5038
5036
  completed,
5039
5037
  total: progressTotalRows,
5038
+ failed: failedExecutedRows,
5040
5039
  startedAt: mapStartedAt,
5041
5040
  message: formatMapProgressMessage(completed, progressTotalRows),
5042
5041
  },
@@ -5081,7 +5080,7 @@ function createMinimalWorkerCtx(
5081
5080
  let stepCellsCompleted = 0;
5082
5081
  let stepCellsSkipped = 0;
5083
5082
  const generatedOutputFields = new Set<string>();
5084
- const pendingLiveRowUpdates: Array<
5083
+ const pendingLiveUpdates: Array<
5085
5084
  Omit<PlayRowUpdate, 'rowId'> & { runId?: string }
5086
5085
  > = [];
5087
5086
  const persistedExecutedIndexes = new Set<number>();
@@ -5091,9 +5090,9 @@ function createMinimalWorkerCtx(
5091
5090
  let scheduledPersistTimer: ReturnType<typeof setTimeout> | null = null;
5092
5091
  let persistFlushChain: Promise<void> = Promise.resolve();
5093
5092
  let persistFailure: unknown = null;
5094
- let scheduledLiveUpdateTimer: ReturnType<typeof setTimeout> | null = null;
5095
- let liveUpdateFlushChain: Promise<void> = Promise.resolve();
5096
- let liveUpdateFailureCount = 0;
5093
+ let liveUpdateTimer: ReturnType<typeof setTimeout> | null = null;
5094
+ let liveFlushChain: Promise<void> = Promise.resolve();
5095
+ let liveUpdateFailures = 0;
5097
5096
 
5098
5097
  const clearScheduledPersistTimer = () => {
5099
5098
  if (scheduledPersistTimer) {
@@ -5102,10 +5101,10 @@ function createMinimalWorkerCtx(
5102
5101
  }
5103
5102
  };
5104
5103
 
5105
- const clearScheduledLiveUpdateTimer = () => {
5106
- if (scheduledLiveUpdateTimer) {
5107
- clearTimeout(scheduledLiveUpdateTimer);
5108
- scheduledLiveUpdateTimer = null;
5104
+ const clearLiveUpdateTimer = () => {
5105
+ if (liveUpdateTimer) {
5106
+ clearTimeout(liveUpdateTimer);
5107
+ liveUpdateTimer = null;
5109
5108
  }
5110
5109
  };
5111
5110
 
@@ -5223,14 +5222,36 @@ function createMinimalWorkerCtx(
5223
5222
  return task;
5224
5223
  };
5225
5224
 
5225
+ const coalesceLiveUpdates = (
5226
+ updates: Array<Omit<PlayRowUpdate, 'rowId'> & { runId?: string }>,
5227
+ ): Array<Omit<PlayRowUpdate, 'rowId'> & { runId?: string }> => {
5228
+ const mergedByKey = new Map<
5229
+ string,
5230
+ Omit<PlayRowUpdate, 'rowId'> & { runId?: string }
5231
+ >();
5232
+ for (const update of updates) {
5233
+ const existing = mergedByKey.get(update.key);
5234
+ mergedByKey.set(update.key, {
5235
+ ...existing,
5236
+ ...update,
5237
+ dataPatch: { ...existing?.dataPatch, ...update.dataPatch },
5238
+ cellMetaPatch: {
5239
+ ...existing?.cellMetaPatch,
5240
+ ...update.cellMetaPatch,
5241
+ },
5242
+ });
5243
+ }
5244
+ return [...mergedByKey.values()];
5245
+ };
5246
+
5226
5247
  const flushLiveRowUpdates = (): Promise<void> => {
5227
- clearScheduledLiveUpdateTimer();
5228
- if (pendingLiveRowUpdates.length === 0) {
5229
- return liveUpdateFlushChain;
5248
+ clearLiveUpdateTimer();
5249
+ if (pendingLiveUpdates.length === 0) {
5250
+ return liveFlushChain;
5230
5251
  }
5231
- const updates = pendingLiveRowUpdates.splice(0);
5252
+ const updates = coalesceLiveUpdates(pendingLiveUpdates.splice(0));
5232
5253
  const extraOutputFields = Array.from(generatedOutputFields);
5233
- const task = liveUpdateFlushChain.then(async () => {
5254
+ const task = liveFlushChain.then(async () => {
5234
5255
  try {
5235
5256
  await applyLiveMapRowUpdates({
5236
5257
  req,
@@ -5240,33 +5261,29 @@ function createMinimalWorkerCtx(
5240
5261
  updates,
5241
5262
  });
5242
5263
  } catch (error) {
5243
- liveUpdateFailureCount += 1;
5244
- if (liveUpdateFailureCount <= MAP_ROW_FAILURE_SAMPLE_LIMIT) {
5264
+ liveUpdateFailures += 1;
5265
+ if (liveUpdateFailures <= MAP_ROW_FAILURE_SAMPLE_LIMIT) {
5245
5266
  emitEvent({
5246
5267
  type: 'log',
5247
5268
  level: 'warn',
5248
- message:
5249
- `Live row update flush failed for ctx.dataset("${name}") ` +
5250
- `(non-fatal; terminal rows still persist): ${formatWorkerRowFailureMessage(error)}`,
5269
+ message: formatWorkerRowFailureMessage(error),
5251
5270
  ts: nowMs(),
5252
5271
  });
5253
5272
  }
5254
5273
  }
5255
5274
  });
5256
- liveUpdateFlushChain = task.catch(() => undefined);
5275
+ liveFlushChain = task.catch(() => undefined);
5257
5276
  return task;
5258
5277
  };
5259
5278
 
5260
5279
  const scheduleLiveRowUpdates = () => {
5261
- if (
5262
- pendingLiveRowUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS
5263
- ) {
5280
+ if (pendingLiveUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS) {
5264
5281
  void flushLiveRowUpdates().catch(() => undefined);
5265
5282
  return;
5266
5283
  }
5267
- if (scheduledLiveUpdateTimer) return;
5268
- scheduledLiveUpdateTimer = setTimeout(() => {
5269
- scheduledLiveUpdateTimer = null;
5284
+ if (liveUpdateTimer) return;
5285
+ liveUpdateTimer = setTimeout(() => {
5286
+ liveUpdateTimer = null;
5270
5287
  void flushLiveRowUpdates().catch(() => undefined);
5271
5288
  }, MAP_INCREMENTAL_PERSIST_INTERVAL_MS);
5272
5289
  };
@@ -5274,10 +5291,8 @@ function createMinimalWorkerCtx(
5274
5291
  const enqueueLiveRowUpdate = (
5275
5292
  update: Omit<PlayRowUpdate, 'rowId'> & { runId?: string },
5276
5293
  ): Promise<void> => {
5277
- pendingLiveRowUpdates.push(update);
5278
- if (
5279
- pendingLiveRowUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS
5280
- ) {
5294
+ pendingLiveUpdates.push(update);
5295
+ if (pendingLiveUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS) {
5281
5296
  void flushLiveRowUpdates().catch(() => undefined);
5282
5297
  return Promise.resolve();
5283
5298
  }
@@ -5402,10 +5417,6 @@ function createMinimalWorkerCtx(
5402
5417
  else stepCellsCompleted += 1;
5403
5418
  await enqueueLiveRowUpdate({
5404
5419
  key: entry.rowKey,
5405
- tableNamespace: name,
5406
- runId: req.runId,
5407
- status: 'running',
5408
- stage: stepOutput.stepId,
5409
5420
  dataPatch: {
5410
5421
  [stepOutput.columnName]: stepOutput.value,
5411
5422
  },
@@ -5436,6 +5447,12 @@ function createMinimalWorkerCtx(
5436
5447
  completedAt: nowMs(),
5437
5448
  };
5438
5449
  }
5450
+ if (fieldEntries.length > 1) {
5451
+ await enqueueLiveRowUpdate({
5452
+ key: entry.rowKey,
5453
+ dataPatch: { [key]: resolved.value },
5454
+ });
5455
+ }
5439
5456
  activeField = null;
5440
5457
  }
5441
5458
  for (const stepOutput of stepProgramOutputs) {
@@ -5612,7 +5629,7 @@ function createMinimalWorkerCtx(
5612
5629
  });
5613
5630
  try {
5614
5631
  await flushLiveRowUpdates();
5615
- await liveUpdateFlushChain;
5632
+ await liveFlushChain;
5616
5633
  await enqueuePersistExecutedRows();
5617
5634
  await persistFlushChain;
5618
5635
  if (persistFailure) throw persistFailure;
@@ -7864,7 +7881,9 @@ function extractMaxCreditsPerRun(contractSnapshot: unknown): number | null {
7864
7881
  }
7865
7882
 
7866
7883
  function shouldSkipWorkerComputeBilling(req: RunRequest): boolean {
7867
- return req.integrationMode === 'fixture' || req.integrationMode === 'eval_stub';
7884
+ return (
7885
+ req.integrationMode === 'fixture' || req.integrationMode === 'eval_stub'
7886
+ );
7868
7887
  }
7869
7888
 
7870
7889
  async function finalizeWorkerComputeBilling(input: {
@@ -16,12 +16,13 @@ export const CACHE_ENABLED_SIMPLE_MAP_CHUNK_SIZE = 10_000;
16
16
  export { TOOL_CALLING_MAP_CHUNK_SIZE };
17
17
  // Paid Cloudflare Workers support a much higher configured subrequest limit.
18
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.
19
+ // tool RPCs. Very dense waterfalls may still need one-row chunks to stay under
20
+ // the per-invocation subrequest budget.
20
21
  export const UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET = 200;
21
- // Fresh unbatched tool calls use one RUNTIME_API integration execute RPC and
22
- // one HARNESS durable-receipt completion RPC. Batch-cap rows by both.
23
- export const SUBREQUESTS_PER_UNBATCHED_TOOL_CALL = 2;
24
-
22
+ // Fresh unbatched tool calls use runtime API, receipt, and coordinator/rate
23
+ // callbacks on the Worker no-batch path. Batch-cap rows by the shared
24
+ // per-call budget rather than a synthetic-tool special case.
25
+ export const SUBREQUESTS_PER_UNBATCHED_TOOL_CALL = 5;
25
26
  export type WorkerMapChunkPlanInput = {
26
27
  mapName: string;
27
28
  rowCountHint: number | null;
@@ -45,7 +46,7 @@ type ChunkSizingPlan = {
45
46
  softWorkflowStepBudget?: number | null;
46
47
  };
47
48
 
48
- type MapToolStats = [totalToolCount: number, unbatchedToolCount: number];
49
+ type MapToolStats = [totalToolCount: number, unbatchedSubrequestCount: number];
49
50
 
50
51
  function fieldRoot(field: string | null | undefined): string {
51
52
  return String(field ?? '').split('.', 1)[0] ?? '';
@@ -70,41 +71,42 @@ function declarationBelongsToMapOutput(
70
71
  );
71
72
  }
72
73
 
73
- function isUnbatchedTool(toolId: string): boolean {
74
- return getPlayRuntimeBatchStrategy(toolId) === null;
74
+ function countTool(toolId: string): MapToolStats {
75
+ if (getPlayRuntimeBatchStrategy(toolId) !== null) return [1, 0];
76
+ return [1, SUBREQUESTS_PER_UNBATCHED_TOOL_CALL];
77
+ }
78
+
79
+ function addStats(
80
+ [total, subrequests]: MapToolStats,
81
+ stats: MapToolStats,
82
+ ): MapToolStats {
83
+ return [total + stats[0], subrequests + stats[1]];
75
84
  }
76
85
 
77
86
  function countToolSubsteps(
78
87
  substeps: readonly PlayStaticSubstep[],
79
88
  ): MapToolStats {
80
- let totalToolCount = 0;
81
- let unbatchedToolCount = 0;
89
+ let stats: MapToolStats = [0, 0];
82
90
 
83
91
  for (const substep of substeps) {
84
- const [nestedTotal, nestedUnbatched] = countToolSubstep(substep);
85
- totalToolCount += nestedTotal;
86
- unbatchedToolCount += nestedUnbatched;
92
+ stats = addStats(stats, countToolSubstep(substep));
87
93
  }
88
94
 
89
- return [totalToolCount, unbatchedToolCount];
95
+ return stats;
90
96
  }
91
97
 
92
98
  function countToolSubstep(substep: PlayStaticSubstep): MapToolStats {
93
99
  if (substep.type === 'tool') {
94
- return [1, isUnbatchedTool(substep.toolId) ? 1 : 0];
100
+ return countTool(substep.toolId);
95
101
  }
96
102
 
97
103
  if (substep.type === 'waterfall') {
98
- let totalToolCount = 0;
99
- let unbatchedToolCount = 0;
104
+ let stats: MapToolStats = [0, 0];
100
105
  for (const step of substep.steps ?? []) {
101
106
  if (!step.toolId) continue;
102
- totalToolCount += 1;
103
- if (isUnbatchedTool(step.toolId)) {
104
- unbatchedToolCount += 1;
105
- }
107
+ stats = addStats(stats, countTool(step.toolId));
106
108
  }
107
- return [totalToolCount, unbatchedToolCount];
109
+ return stats;
108
110
  }
109
111
 
110
112
  if (substep.type === 'step_suite') {
@@ -117,9 +119,9 @@ function countToolSubstep(substep: PlayStaticSubstep): MapToolStats {
117
119
  countToolSubsteps(branch.steps),
118
120
  );
119
121
  return branchStats.reduce<MapToolStats>(
120
- ([totalMax, unbatchedMax], [total, unbatched]) => [
122
+ ([totalMax, subrequestsMax], [total, subrequests]) => [
121
123
  Math.max(totalMax, total),
122
- Math.max(unbatchedMax, unbatched),
124
+ Math.max(subrequestsMax, subrequests),
123
125
  ],
124
126
  [0, 0],
125
127
  );
@@ -151,9 +153,9 @@ function countStepSuiteTools(
151
153
  conditionalChildren.length < childStats.length / 2
152
154
  ) {
153
155
  return childStats.reduce<MapToolStats>(
154
- ([totalSum, unbatchedSum], { stats: [total, unbatched] }) => [
156
+ ([totalSum, subrequestsSum], { stats: [total, subrequests] }) => [
155
157
  totalSum + total,
156
- unbatchedSum + unbatched,
158
+ subrequestsSum + subrequests,
157
159
  ],
158
160
  [0, 0],
159
161
  );
@@ -166,9 +168,9 @@ function countStepSuiteTools(
166
168
  });
167
169
  if (!sameRoot) {
168
170
  return childStats.reduce<MapToolStats>(
169
- ([totalSum, unbatchedSum], { stats: [total, unbatched] }) => [
171
+ ([totalSum, subrequestsSum], { stats: [total, subrequests] }) => [
170
172
  totalSum + total,
171
- unbatchedSum + unbatched,
173
+ subrequestsSum + subrequests,
172
174
  ],
173
175
  [0, 0],
174
176
  );
@@ -181,52 +183,41 @@ function countStepSuiteTools(
181
183
  !(step.type === 'control_flow' && step.kind === 'conditional'),
182
184
  )
183
185
  .reduce<MapToolStats>(
184
- ([totalSum, unbatchedSum], { stats: [total, unbatched] }) => [
186
+ ([totalSum, subrequestsSum], { stats: [total, subrequests] }) => [
185
187
  totalSum + total,
186
- unbatchedSum + unbatched,
188
+ subrequestsSum + subrequests,
187
189
  ],
188
190
  [0, 0],
189
191
  );
190
192
  const conditionalStats = conditionalChildren.reduce<MapToolStats>(
191
- ([totalSum, unbatchedSum], { stats: [total, unbatched] }) => [
193
+ ([totalSum, subrequestsSum], { stats: [total, subrequests] }) => [
192
194
  totalSum + total,
193
- unbatchedSum + unbatched,
195
+ subrequestsSum + subrequests,
194
196
  ],
195
197
  [0, 0],
196
198
  );
197
199
 
198
- return [
199
- unconditionalStats[0] + conditionalStats[0],
200
- unconditionalStats[1] + conditionalStats[1],
201
- ];
200
+ return addStats(unconditionalStats, conditionalStats);
202
201
  }
203
202
 
204
203
  function countProducerTools(
205
204
  producers: readonly PlayStaticColumnProducer[],
206
205
  ): MapToolStats {
207
- let totalToolCount = 0;
208
- let unbatchedToolCount = 0;
206
+ let stats: MapToolStats = [0, 0];
209
207
 
210
208
  for (const producer of producers) {
211
209
  if (producer.kind === 'tool' && producer.toolId) {
212
- totalToolCount += 1;
213
- if (isUnbatchedTool(producer.toolId)) {
214
- unbatchedToolCount += 1;
215
- }
210
+ stats = addStats(stats, countTool(producer.toolId));
216
211
  }
217
212
  if (producer.substep.type === 'waterfall') {
218
- const [nestedTotal, nestedUnbatched] = countToolSubstep(producer.substep);
219
- totalToolCount += nestedTotal;
220
- unbatchedToolCount += nestedUnbatched;
213
+ stats = addStats(stats, countToolSubstep(producer.substep));
221
214
  }
222
215
  if (producer.steps?.length) {
223
- const [nestedTotal, nestedUnbatched] = countProducerTools(producer.steps);
224
- totalToolCount += nestedTotal;
225
- unbatchedToolCount += nestedUnbatched;
216
+ stats = addStats(stats, countProducerTools(producer.steps));
226
217
  }
227
218
  }
228
219
 
229
- return [totalToolCount, unbatchedToolCount];
220
+ return stats;
230
221
  }
231
222
 
232
223
  function datasetBelongsToPlanMap(
@@ -278,8 +269,7 @@ function countFallbackDeclarationTools(input: {
278
269
  outputFields: readonly string[];
279
270
  externalStepFields?: readonly string[];
280
271
  }): MapToolStats {
281
- let totalToolCount = 0;
282
- let unbatchedToolCount = 0;
272
+ let stats: MapToolStats = [0, 0];
283
273
 
284
274
  for (const declaration of input.declarations) {
285
275
  if (
@@ -291,23 +281,19 @@ function countFallbackDeclarationTools(input: {
291
281
  ) {
292
282
  continue;
293
283
  }
294
- totalToolCount += 1;
295
- if (isUnbatchedTool(declaration.toolId)) {
296
- unbatchedToolCount += 1;
297
- }
284
+ stats = addStats(stats, countTool(declaration.toolId));
298
285
  }
299
286
 
300
- return [totalToolCount, unbatchedToolCount];
287
+ return stats;
301
288
  }
302
289
 
303
290
  function countDeclarationTools(
304
291
  declarations: readonly { toolId: string; field?: string | null }[],
305
292
  ): MapToolStats {
306
- return [
307
- declarations.length,
308
- declarations.filter((declaration) => isUnbatchedTool(declaration.toolId))
309
- .length,
310
- ];
293
+ return declarations.reduce<MapToolStats>(
294
+ (stats, declaration) => addStats(stats, countTool(declaration.toolId)),
295
+ [0, 0],
296
+ );
311
297
  }
312
298
 
313
299
  function countNestedExecutionSteps(
@@ -409,12 +395,13 @@ export function chooseWorkerMapRowsPerChunk(
409
395
  }
410
396
 
411
397
  if (mapToolStats[1] > 0) {
412
- const unbatchedToolCallsPerRow =
398
+ const unbatchedSubrequestsPerRow =
413
399
  staticMapToolStats !== null
414
400
  ? mapToolStats[1]
415
- : Math.max(planMap?.stepsPerChunk ?? 1, mapToolStats[1]);
416
- const unbatchedSubrequestsPerRow =
417
- unbatchedToolCallsPerRow * SUBREQUESTS_PER_UNBATCHED_TOOL_CALL;
401
+ : Math.max(
402
+ (planMap?.stepsPerChunk ?? 1) * SUBREQUESTS_PER_UNBATCHED_TOOL_CALL,
403
+ mapToolStats[1],
404
+ );
418
405
  const unbatchedToolRowsPerChunk = Math.max(
419
406
  1,
420
407
  Math.floor(
@@ -572,7 +572,16 @@ function normalizePlayRunStart(raw: Record<string, unknown>): PlayRunStart {
572
572
  ? raw.package
573
573
  : null;
574
574
  if (!runPackage) {
575
- return raw as unknown as PlayRunStart;
575
+ const workflowId =
576
+ typeof raw.workflowId === 'string' && raw.workflowId
577
+ ? raw.workflowId
578
+ : typeof raw.runId === 'string'
579
+ ? raw.runId
580
+ : '';
581
+ return {
582
+ ...(raw as unknown as Omit<PlayRunStart, 'workflowId'>),
583
+ workflowId,
584
+ };
576
585
  }
577
586
  const status =
578
587
  typeof runPackage.run.status === 'string'
@@ -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.171',
107
+ version: '0.1.173',
108
108
  apiContract: '2026-06-dataset-handle-results-hard-cutover',
109
109
  supportPolicy: {
110
- latest: '0.1.171',
110
+ latest: '0.1.173',
111
111
  minimumSupported: '0.1.53',
112
112
  deprecatedBelow: '0.1.53',
113
113
  commandMinimumSupported: [
@@ -114,6 +114,9 @@ const RUNTIME_DB_SESSION_ROW_LIMIT_FLOOR = DIRECT_POSTGRES_BATCH_SIZE;
114
114
  const APPEND_KEY_SUFFIX_LENGTH = 12;
115
115
  const RUNTIME_DB_SESSION_TTL_SECONDS = 10 * 60;
116
116
  const RUNTIME_SHEET_ENSURE_CACHE_TTL_MS = 10 * 60_000;
117
+ const RUNTIME_SHEET_READY_AFTER_ENSURE_RETRY_DELAYS_MS = [
118
+ 100, 250, 500, 1_000, 2_000,
119
+ ] as const;
117
120
  const RUNTIME_DB_SESSION_RENEWAL_WINDOW_MS = 60_000;
118
121
  const RUNTIME_API_RETRY_DELAYS_MS = [
119
122
  250, 500, 1_000, 2_000, 4_000, 8_000, 8_000,
@@ -529,6 +532,41 @@ async function isRuntimeSheetSchemaReady(
529
532
  }
530
533
  }
531
534
 
535
+ async function waitForRuntimeSheetSchemaReadyAfterEnsure(
536
+ session: RuntimePostgresSession,
537
+ input: {
538
+ sheetContract: PlaySheetContract;
539
+ timings?: RuntimeSheetTiming[];
540
+ },
541
+ ): Promise<boolean> {
542
+ for (
543
+ let attempt = 0;
544
+ attempt <= RUNTIME_SHEET_READY_AFTER_ENSURE_RETRY_DELAYS_MS.length;
545
+ attempt += 1
546
+ ) {
547
+ const recheckStartedAt = Date.now();
548
+ const ready = await isRuntimeSheetSchemaReady(session, {
549
+ sheetContract: input.sheetContract,
550
+ });
551
+ input.timings?.push({
552
+ phase: 'schema_check_after_preloaded_session_ensure',
553
+ ms: Date.now() - recheckStartedAt,
554
+ ready,
555
+ retried: attempt > 0,
556
+ });
557
+ if (ready) {
558
+ return true;
559
+ }
560
+ const delay =
561
+ RUNTIME_SHEET_READY_AFTER_ENSURE_RETRY_DELAYS_MS[attempt] ?? null;
562
+ if (delay === null) {
563
+ return false;
564
+ }
565
+ await sleep(delay);
566
+ }
567
+ return false;
568
+ }
569
+
532
570
  async function ensureRuntimeSheetForPreloadedSession(
533
571
  context: RuntimeApiContext & { playName: string },
534
572
  input: {
@@ -597,38 +635,35 @@ async function ensureRuntimeSheetForPreloadedSession(
597
635
  return;
598
636
  }
599
637
  const ensureStartedAt = Date.now();
600
- const promise = ensureRuntimeSheet(context, {
601
- playName: context.playName,
602
- tableNamespace: input.tableNamespace,
603
- sheetContract: input.sheetContract,
604
- });
605
- runtimeSheetEnsureCache.set(cacheKey, {
606
- expiresAt: now + RUNTIME_SHEET_ENSURE_CACHE_TTL_MS,
607
- promise,
608
- });
609
- try {
610
- await promise;
611
- input.timings?.push({
612
- phase: 'ensure_sheet_for_preloaded_session',
613
- ms: Date.now() - ensureStartedAt,
614
- });
615
- const recheckStartedAt = Date.now();
616
- const readyAfterEnsure = await isRuntimeSheetSchemaReady(input.session, {
638
+ const promise = (async () => {
639
+ await ensureRuntimeSheet(context, {
640
+ playName: context.playName,
641
+ tableNamespace: input.tableNamespace,
617
642
  sheetContract: input.sheetContract,
618
643
  });
619
644
  input.timings?.push({
620
- phase: 'schema_check_after_preloaded_session_ensure',
621
- ms: Date.now() - recheckStartedAt,
622
- ready: readyAfterEnsure,
645
+ phase: 'ensure_sheet_for_preloaded_session',
646
+ ms: Date.now() - ensureStartedAt,
623
647
  });
648
+ const readyAfterEnsure = await waitForRuntimeSheetSchemaReadyAfterEnsure(
649
+ input.session,
650
+ {
651
+ sheetContract: input.sheetContract,
652
+ timings: input.timings,
653
+ },
654
+ );
624
655
  if (!readyAfterEnsure) {
625
- if (runtimeSheetEnsureCache.get(cacheKey)?.promise === promise) {
626
- runtimeSheetEnsureCache.delete(cacheKey);
627
- }
628
656
  throw new Error(
629
657
  `Runtime sheet schema for ctx.dataset("${input.tableNamespace}") is still not ready after ensure_sheet.`,
630
658
  );
631
659
  }
660
+ })();
661
+ runtimeSheetEnsureCache.set(cacheKey, {
662
+ expiresAt: now + RUNTIME_SHEET_ENSURE_CACHE_TTL_MS,
663
+ promise,
664
+ });
665
+ try {
666
+ await promise;
632
667
  } catch (error) {
633
668
  if (runtimeSheetEnsureCache.get(cacheKey)?.promise === promise) {
634
669
  runtimeSheetEnsureCache.delete(cacheKey);
package/dist/cli/index.js CHANGED
@@ -622,10 +622,10 @@ var SDK_RELEASE = {
622
622
  // 0.1.111 ships dataset-native tool list getters and result row datasets.
623
623
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
624
624
  // fields shipped in 0.1.153.
625
- version: "0.1.171",
625
+ version: "0.1.173",
626
626
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
627
627
  supportPolicy: {
628
- latest: "0.1.171",
628
+ latest: "0.1.173",
629
629
  minimumSupported: "0.1.53",
630
630
  deprecatedBelow: "0.1.53",
631
631
  commandMinimumSupported: [
@@ -2212,7 +2212,11 @@ function normalizePlayStatus(raw) {
2212
2212
  function normalizePlayRunStart(raw) {
2213
2213
  const runPackage = isPlayRunPackage(raw) ? raw : isPlayRunPackage(raw.package) ? raw.package : null;
2214
2214
  if (!runPackage) {
2215
- return raw;
2215
+ const workflowId = typeof raw.workflowId === "string" && raw.workflowId ? raw.workflowId : typeof raw.runId === "string" ? raw.runId : "";
2216
+ return {
2217
+ ...raw,
2218
+ workflowId
2219
+ };
2216
2220
  }
2217
2221
  const status = typeof runPackage.run.status === "string" ? runPackage.run.status : "running";
2218
2222
  return {
@@ -4389,10 +4393,50 @@ end run
4389
4393
  `;
4390
4394
  return runAppleScript(script, [targetUrl, host], runner);
4391
4395
  }
4396
+ function hasOnlyHeadlessMacosBrowserInstances(appName, runner = defaultBrowserCommandRunner) {
4397
+ try {
4398
+ const output2 = runner.execFileSync("ps", ["-axo", "args="], {
4399
+ encoding: "utf-8",
4400
+ stdio: ["ignore", "pipe", "ignore"]
4401
+ });
4402
+ const executableSuffix = `/Contents/MacOS/${appName}`;
4403
+ const lines = String(output2).split(/\r?\n/).map((line) => line.trim()).filter((line) => {
4404
+ const executableIndex = line.indexOf(executableSuffix);
4405
+ if (executableIndex < 0 || !line.includes(".app/Contents/MacOS/")) {
4406
+ return false;
4407
+ }
4408
+ const rest = line.slice(executableIndex + executableSuffix.length);
4409
+ return rest === "" || /^\s+-/.test(rest);
4410
+ });
4411
+ if (lines.length === 0) return false;
4412
+ return lines.every(
4413
+ (line) => line.includes("--headless") || line.includes("--no-startup-window")
4414
+ );
4415
+ } catch {
4416
+ return false;
4417
+ }
4418
+ }
4419
+ function openNewVisibleMacosBrowserInstance(appName, targetUrl, runner = defaultBrowserCommandRunner) {
4420
+ try {
4421
+ runner.execFileSync(
4422
+ "open",
4423
+ ["-na", appName, "--args", "--new-window", targetUrl],
4424
+ {
4425
+ stdio: "ignore"
4426
+ }
4427
+ );
4428
+ return true;
4429
+ } catch {
4430
+ return false;
4431
+ }
4432
+ }
4392
4433
  function openUrlMacos(targetUrl, allowFocus, runner = defaultBrowserCommandRunner) {
4393
4434
  const defaultBundleId = readDefaultMacBrowserBundleId(runner);
4394
4435
  const appName = defaultBundleId ? browserAppNameFromBundleId(defaultBundleId) : "";
4395
4436
  const strategy = browserStrategyForBundleId(defaultBundleId);
4437
+ if (allowFocus && appName && strategy === "chromium" && hasOnlyHeadlessMacosBrowserInstances(appName, runner) && openNewVisibleMacosBrowserInstance(appName, targetUrl, runner)) {
4438
+ return true;
4439
+ }
4396
4440
  if (appName && strategy === "chromium" && retargetChromiumMacos(appName, targetUrl, allowFocus, runner)) {
4397
4441
  return true;
4398
4442
  }
@@ -607,10 +607,10 @@ var SDK_RELEASE = {
607
607
  // 0.1.111 ships dataset-native tool list getters and result row datasets.
608
608
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
609
609
  // fields shipped in 0.1.153.
610
- version: "0.1.171",
610
+ version: "0.1.173",
611
611
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
612
612
  supportPolicy: {
613
- latest: "0.1.171",
613
+ latest: "0.1.173",
614
614
  minimumSupported: "0.1.53",
615
615
  deprecatedBelow: "0.1.53",
616
616
  commandMinimumSupported: [
@@ -2197,7 +2197,11 @@ function normalizePlayStatus(raw) {
2197
2197
  function normalizePlayRunStart(raw) {
2198
2198
  const runPackage = isPlayRunPackage(raw) ? raw : isPlayRunPackage(raw.package) ? raw.package : null;
2199
2199
  if (!runPackage) {
2200
- return raw;
2200
+ const workflowId = typeof raw.workflowId === "string" && raw.workflowId ? raw.workflowId : typeof raw.runId === "string" ? raw.runId : "";
2201
+ return {
2202
+ ...raw,
2203
+ workflowId
2204
+ };
2201
2205
  }
2202
2206
  const status = typeof runPackage.run.status === "string" ? runPackage.run.status : "running";
2203
2207
  return {
@@ -4386,10 +4390,50 @@ end run
4386
4390
  `;
4387
4391
  return runAppleScript(script, [targetUrl, host], runner);
4388
4392
  }
4393
+ function hasOnlyHeadlessMacosBrowserInstances(appName, runner = defaultBrowserCommandRunner) {
4394
+ try {
4395
+ const output2 = runner.execFileSync("ps", ["-axo", "args="], {
4396
+ encoding: "utf-8",
4397
+ stdio: ["ignore", "pipe", "ignore"]
4398
+ });
4399
+ const executableSuffix = `/Contents/MacOS/${appName}`;
4400
+ const lines = String(output2).split(/\r?\n/).map((line) => line.trim()).filter((line) => {
4401
+ const executableIndex = line.indexOf(executableSuffix);
4402
+ if (executableIndex < 0 || !line.includes(".app/Contents/MacOS/")) {
4403
+ return false;
4404
+ }
4405
+ const rest = line.slice(executableIndex + executableSuffix.length);
4406
+ return rest === "" || /^\s+-/.test(rest);
4407
+ });
4408
+ if (lines.length === 0) return false;
4409
+ return lines.every(
4410
+ (line) => line.includes("--headless") || line.includes("--no-startup-window")
4411
+ );
4412
+ } catch {
4413
+ return false;
4414
+ }
4415
+ }
4416
+ function openNewVisibleMacosBrowserInstance(appName, targetUrl, runner = defaultBrowserCommandRunner) {
4417
+ try {
4418
+ runner.execFileSync(
4419
+ "open",
4420
+ ["-na", appName, "--args", "--new-window", targetUrl],
4421
+ {
4422
+ stdio: "ignore"
4423
+ }
4424
+ );
4425
+ return true;
4426
+ } catch {
4427
+ return false;
4428
+ }
4429
+ }
4389
4430
  function openUrlMacos(targetUrl, allowFocus, runner = defaultBrowserCommandRunner) {
4390
4431
  const defaultBundleId = readDefaultMacBrowserBundleId(runner);
4391
4432
  const appName = defaultBundleId ? browserAppNameFromBundleId(defaultBundleId) : "";
4392
4433
  const strategy = browserStrategyForBundleId(defaultBundleId);
4434
+ if (allowFocus && appName && strategy === "chromium" && hasOnlyHeadlessMacosBrowserInstances(appName, runner) && openNewVisibleMacosBrowserInstance(appName, targetUrl, runner)) {
4435
+ return true;
4436
+ }
4393
4437
  if (appName && strategy === "chromium" && retargetChromiumMacos(appName, targetUrl, allowFocus, runner)) {
4394
4438
  return true;
4395
4439
  }
package/dist/index.js CHANGED
@@ -421,10 +421,10 @@ var SDK_RELEASE = {
421
421
  // 0.1.111 ships dataset-native tool list getters and result row datasets.
422
422
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
423
423
  // fields shipped in 0.1.153.
424
- version: "0.1.171",
424
+ version: "0.1.173",
425
425
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
426
426
  supportPolicy: {
427
- latest: "0.1.171",
427
+ latest: "0.1.173",
428
428
  minimumSupported: "0.1.53",
429
429
  deprecatedBelow: "0.1.53",
430
430
  commandMinimumSupported: [
@@ -2011,7 +2011,11 @@ function normalizePlayStatus(raw) {
2011
2011
  function normalizePlayRunStart(raw) {
2012
2012
  const runPackage = isPlayRunPackage(raw) ? raw : isPlayRunPackage(raw.package) ? raw.package : null;
2013
2013
  if (!runPackage) {
2014
- return raw;
2014
+ const workflowId = typeof raw.workflowId === "string" && raw.workflowId ? raw.workflowId : typeof raw.runId === "string" ? raw.runId : "";
2015
+ return {
2016
+ ...raw,
2017
+ workflowId
2018
+ };
2015
2019
  }
2016
2020
  const status = typeof runPackage.run.status === "string" ? runPackage.run.status : "running";
2017
2021
  return {
package/dist/index.mjs CHANGED
@@ -351,10 +351,10 @@ var SDK_RELEASE = {
351
351
  // 0.1.111 ships dataset-native tool list getters and result row datasets.
352
352
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
353
353
  // fields shipped in 0.1.153.
354
- version: "0.1.171",
354
+ version: "0.1.173",
355
355
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
356
356
  supportPolicy: {
357
- latest: "0.1.171",
357
+ latest: "0.1.173",
358
358
  minimumSupported: "0.1.53",
359
359
  deprecatedBelow: "0.1.53",
360
360
  commandMinimumSupported: [
@@ -1941,7 +1941,11 @@ function normalizePlayStatus(raw) {
1941
1941
  function normalizePlayRunStart(raw) {
1942
1942
  const runPackage = isPlayRunPackage(raw) ? raw : isPlayRunPackage(raw.package) ? raw.package : null;
1943
1943
  if (!runPackage) {
1944
- return raw;
1944
+ const workflowId = typeof raw.workflowId === "string" && raw.workflowId ? raw.workflowId : typeof raw.runId === "string" ? raw.runId : "";
1945
+ return {
1946
+ ...raw,
1947
+ workflowId
1948
+ };
1945
1949
  }
1946
1950
  const status = typeof runPackage.run.status === "string" ? runPackage.run.status : "running";
1947
1951
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.171",
3
+ "version": "0.1.173",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {