deepline 0.1.190 → 0.1.192

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.
@@ -131,6 +131,8 @@ export type PlayWorkflowParams = {
131
131
  dynamicWorkerCode?: string | null;
132
132
  executorToken: string;
133
133
  baseUrl: string;
134
+ runtimeApiBaseUrl?: string | null;
135
+ callbackUrl?: string | null;
134
136
  integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
135
137
  orgId: string;
136
138
  userEmail: string;
@@ -402,6 +404,15 @@ function recordCoordinatorPerfTrace(event: CoordinatorPerfTraceInput): void {
402
404
  logCoordinatorPerfTrace(payload);
403
405
  }
404
406
 
407
+ function safeOrigin(value: string | null | undefined): string | null {
408
+ if (!value?.trim()) return null;
409
+ try {
410
+ return new URL(value).origin;
411
+ } catch {
412
+ return '<invalid>';
413
+ }
414
+ }
415
+
405
416
  async function appendCoordinatorPerfTrace(
406
417
  env: CoordinatorEnv,
407
418
  payload: CoordinatorPerfTracePayload,
@@ -1330,9 +1341,23 @@ async function markWorkflowRuntimeFailure(input: {
1330
1341
  const backoffMs = [200, 500, 1500];
1331
1342
  let lastError: unknown = null;
1332
1343
  for (let attempt = 0; attempt <= backoffMs.length; attempt += 1) {
1344
+ const requestId = crypto.randomUUID();
1333
1345
  try {
1346
+ headers.set('x-deepline-request-id', requestId);
1334
1347
  const response = await fetch(url, { method: 'POST', headers, body });
1335
- if (response.ok) return;
1348
+ if (response.ok) {
1349
+ if (attempt > 0) {
1350
+ console.info('[coordinator.runtime_api.retry]', {
1351
+ action: 'append_run_events',
1352
+ status: 'ok',
1353
+ attempt,
1354
+ runId,
1355
+ requestId,
1356
+ baseOrigin: safeOrigin(baseUrl),
1357
+ });
1358
+ }
1359
+ return;
1360
+ }
1336
1361
  lastError = new Error(
1337
1362
  `runtime API responded ${response.status}: ${(await response.text().catch(() => '')).slice(0, 400)}`,
1338
1363
  );
@@ -1348,6 +1373,19 @@ async function markWorkflowRuntimeFailure(input: {
1348
1373
  lastError = error;
1349
1374
  }
1350
1375
  if (attempt < backoffMs.length) {
1376
+ console.warn('[coordinator.runtime_api.retry]', {
1377
+ action: 'append_run_events',
1378
+ status: 'retry',
1379
+ attempt: attempt + 1,
1380
+ retryDelayMs: backoffMs[attempt],
1381
+ runId,
1382
+ requestId,
1383
+ baseOrigin: safeOrigin(baseUrl),
1384
+ error:
1385
+ lastError instanceof Error
1386
+ ? lastError.message.slice(0, 300)
1387
+ : String(lastError).slice(0, 300),
1388
+ });
1351
1389
  await new Promise((resolve) => setTimeout(resolve, backoffMs[attempt]));
1352
1390
  }
1353
1391
  }
@@ -2203,6 +2241,26 @@ async function callRuntimeApiFromCoordinator(input: {
2203
2241
  });
2204
2242
 
2205
2243
  const fetchStartedAt = Date.now();
2244
+ const requestId = crypto.randomUUID();
2245
+ const action =
2246
+ body && typeof body === 'object' && !Array.isArray(body)
2247
+ ? String((body as { action?: unknown }).action ?? 'unknown')
2248
+ : 'unknown';
2249
+ const runId =
2250
+ body && typeof body === 'object' && !Array.isArray(body)
2251
+ ? String(
2252
+ (body as { runId?: unknown; playId?: unknown }).runId ??
2253
+ (body as { runId?: unknown; playId?: unknown }).playId ??
2254
+ '',
2255
+ ) || null
2256
+ : null;
2257
+ console.info('[coordinator.runtime_api.breadcrumb]', {
2258
+ phase: 'request',
2259
+ action,
2260
+ runId,
2261
+ requestId,
2262
+ baseOrigin: safeOrigin(input.baseUrl),
2263
+ });
2206
2264
  const response = await input.env.HARNESS.runtimeApiCall({
2207
2265
  contract: PLAY_RUNTIME_CONTRACT,
2208
2266
  executorToken: input.executorToken,
@@ -2210,11 +2268,20 @@ async function callRuntimeApiFromCoordinator(input: {
2210
2268
  path: PLAY_RUNTIME_API_COMPAT_PATH,
2211
2269
  body,
2212
2270
  headers: {
2213
- 'x-deepline-request-id': crypto.randomUUID(),
2271
+ 'x-deepline-request-id': requestId,
2214
2272
  [PLAY_RUNTIME_CONTRACT_HEADER]: String(PLAY_RUNTIME_CONTRACT),
2215
2273
  },
2216
2274
  });
2217
2275
  recordTiming('coordinator.runtime_api.fetch', fetchStartedAt);
2276
+ console.info('[coordinator.runtime_api.breadcrumb]', {
2277
+ phase: 'response',
2278
+ action,
2279
+ runId,
2280
+ requestId,
2281
+ status: response.status,
2282
+ ms: Date.now() - fetchStartedAt,
2283
+ baseOrigin: safeOrigin(input.baseUrl),
2284
+ });
2218
2285
 
2219
2286
  const bodyStartedAt = Date.now();
2220
2287
  const responseBody = response.body;
@@ -2361,12 +2428,75 @@ function normalizeRuntimeBaseUrl(value: unknown): string | null {
2361
2428
  return parsed.toString().replace(/\/$/, '');
2362
2429
  }
2363
2430
 
2364
- function resolveRuntimeBaseUrl(
2431
+ const DEEPLINE_PRODUCTION_APP_HOST = 'code.deepline.com';
2432
+ const DEEPLINE_VERCEL_DEPLOYMENT_HOST_RE =
2433
+ /^deepline-api(?:-[a-z0-9-]+)?-aero-team-0f650658\.vercel\.app$/i;
2434
+ const DEEPLINE_VERCEL_PROJECT_HOST_RE =
2435
+ /^deepline-[a-z0-9]+-aero-team-0f650658\.vercel\.app$/i;
2436
+ const DEEPLINE_WORKERS_DEV_SUBDOMAINS = new Set(['chirag-d94']);
2437
+
2438
+ function isDeeplineCoordinatorWorkerHost(hostname: string): boolean {
2439
+ const labels = hostname.toLowerCase().split('.');
2440
+ return (
2441
+ labels.length === 4 &&
2442
+ labels[0]?.startsWith('deepline-play-coordinator-') &&
2443
+ labels[2] === 'workers' &&
2444
+ labels[3] === 'dev' &&
2445
+ DEEPLINE_WORKERS_DEV_SUBDOMAINS.has(labels[1] ?? '')
2446
+ );
2447
+ }
2448
+
2449
+ function shouldCanonicalizeRuntimeApiBaseUrl(input: {
2450
+ configuredBaseUrl: URL;
2451
+ overrideBaseUrl: URL;
2452
+ }): boolean {
2453
+ if (input.overrideBaseUrl.host === input.configuredBaseUrl.host) {
2454
+ return false;
2455
+ }
2456
+ const configuredHostname = input.configuredBaseUrl.hostname.toLowerCase();
2457
+ const overrideHostname = input.overrideBaseUrl.hostname.toLowerCase();
2458
+ if (DEEPLINE_VERCEL_DEPLOYMENT_HOST_RE.test(overrideHostname)) {
2459
+ return true;
2460
+ }
2461
+ if (
2462
+ (configuredHostname === DEEPLINE_PRODUCTION_APP_HOST ||
2463
+ DEEPLINE_VERCEL_PROJECT_HOST_RE.test(configuredHostname)) &&
2464
+ DEEPLINE_VERCEL_PROJECT_HOST_RE.test(overrideHostname)
2465
+ ) {
2466
+ return true;
2467
+ }
2468
+ return isDeeplineCoordinatorWorkerHost(overrideHostname);
2469
+ }
2470
+
2471
+ function resolveRuntimeApiBaseUrl(
2472
+ env: CoordinatorEnv,
2473
+ body: Record<string, unknown>,
2474
+ ): string {
2475
+ const configuredBaseUrl = env.DEEPLINE_API_BASE_URL.replace(/\/$/, '');
2476
+ const overrideBaseUrl =
2477
+ normalizeRuntimeBaseUrl(body.runtimeApiBaseUrl) ??
2478
+ normalizeRuntimeBaseUrl(body.baseUrl);
2479
+ if (!overrideBaseUrl) {
2480
+ return configuredBaseUrl;
2481
+ }
2482
+ if (
2483
+ shouldCanonicalizeRuntimeApiBaseUrl({
2484
+ configuredBaseUrl: new URL(configuredBaseUrl),
2485
+ overrideBaseUrl: new URL(overrideBaseUrl),
2486
+ })
2487
+ ) {
2488
+ return configuredBaseUrl;
2489
+ }
2490
+ return overrideBaseUrl;
2491
+ }
2492
+
2493
+ function resolveWorkflowCallbackBaseUrl(
2365
2494
  env: CoordinatorEnv,
2366
2495
  body: Record<string, unknown>,
2367
2496
  ): string {
2368
2497
  return (
2369
2498
  normalizeRuntimeBaseUrl(body.callbackBaseUrl) ??
2499
+ normalizeRuntimeBaseUrl(body.callbackUrl) ??
2370
2500
  normalizeRuntimeBaseUrl(body.baseUrl) ??
2371
2501
  env.DEEPLINE_API_BASE_URL.replace(/\/$/, '')
2372
2502
  );
@@ -2388,7 +2518,8 @@ function validateChildSubmitBody(input: {
2388
2518
  const { parentRunId, body } = input;
2389
2519
  const manifest = body.manifest as PlayRuntimeManifest | undefined;
2390
2520
  const governance = body.internalRunPlay as
2391
- PlayCallGovernanceSnapshot | undefined;
2521
+ | PlayCallGovernanceSnapshot
2522
+ | undefined;
2392
2523
  const childPlayName =
2393
2524
  typeof body.name === 'string' && body.name.trim()
2394
2525
  ? body.name.trim()
@@ -2480,7 +2611,8 @@ function buildChildWorkflowParams(input: {
2480
2611
  dynamicWorkerCode,
2481
2612
  preloadedDbSessions,
2482
2613
  } = input;
2483
- const baseUrl = resolveRuntimeBaseUrl(env, body);
2614
+ const baseUrl = resolveRuntimeApiBaseUrl(env, body);
2615
+ const callbackUrl = resolveWorkflowCallbackBaseUrl(env, body);
2484
2616
  return {
2485
2617
  runId: childRunId,
2486
2618
  playId: childRunId,
@@ -2517,7 +2649,9 @@ function buildChildWorkflowParams(input: {
2517
2649
  preloadedDbSessions,
2518
2650
  dynamicWorkerCode,
2519
2651
  executorToken: childToken,
2520
- baseUrl,
2652
+ baseUrl: callbackUrl,
2653
+ runtimeApiBaseUrl: baseUrl,
2654
+ callbackUrl,
2521
2655
  integrationMode: normalizeIntegrationMode(body.integrationMode),
2522
2656
  force: body.force === true || body.forceToolRefresh === true,
2523
2657
  forceToolRefresh: body.forceToolRefresh === true,
@@ -2545,9 +2679,9 @@ function runRequestFromPlayWorkflowParams(
2545
2679
  params.inputFile?.r2Key ?? params.inputFile?.storageKey ?? null;
2546
2680
  return {
2547
2681
  runId: params.runId,
2548
- callbackUrl: params.baseUrl,
2682
+ callbackUrl: params.callbackUrl ?? params.baseUrl,
2549
2683
  executorToken: params.executorToken,
2550
- baseUrl: params.baseUrl,
2684
+ baseUrl: params.runtimeApiBaseUrl ?? params.baseUrl,
2551
2685
  integrationMode: normalizeIntegrationMode(params.integrationMode),
2552
2686
  orgId: params.orgId,
2553
2687
  playName: params.playName,
@@ -2742,7 +2876,7 @@ async function executeChildInline(input: {
2742
2876
  input.body.parentPlayName.trim()
2743
2877
  ? input.body.parentPlayName.trim()
2744
2878
  : governance.parentPlayName;
2745
- const baseUrl = resolveRuntimeBaseUrl(input.env, input.body);
2879
+ const baseUrl = resolveRuntimeApiBaseUrl(input.env, input.body);
2746
2880
  const { childToken, preloadedDbSessions, prepareTimings, transportTimings } =
2747
2881
  await prepareInlineChildRunWithRuntime({
2748
2882
  env: input.env,
@@ -3072,7 +3206,7 @@ async function submitChildWorkflowThroughCoordinator(input: {
3072
3206
  parentRunId: input.parentRunId,
3073
3207
  body: input.body,
3074
3208
  });
3075
- const baseUrl = resolveRuntimeBaseUrl(input.env, input.body);
3209
+ const baseUrl = resolveRuntimeApiBaseUrl(input.env, input.body);
3076
3210
 
3077
3211
  const tokenStartedAt = Date.now();
3078
3212
  const childToken = await mintChildWorkflowExecutorToken({
@@ -1386,9 +1386,18 @@ async function postRuntimeApi<T>(
1386
1386
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
1387
1387
  }
1388
1388
 
1389
+ const RETRYABLE_RUNTIME_API_ERROR_RE =
1390
+ /timeout|timed out|fetch failed|ECONNRESET|ECONNREFUSED|DeploymentNotFound|requested deployment .*exist|Authentication infrastructure/i;
1391
+
1389
1392
  function isRetryableRuntimeApiError(error: unknown): boolean {
1393
+ return RETRYABLE_RUNTIME_API_ERROR_RE.test(
1394
+ error instanceof Error ? error.message : String(error),
1395
+ );
1396
+ }
1397
+
1398
+ function isRetryableRuntimePersistenceError(error: unknown): boolean {
1390
1399
  const message = error instanceof Error ? error.message : String(error);
1391
- return /timed out|timeout|fetch failed|ECONNRESET|ECONNREFUSED|UND_ERR_CONNECT_TIMEOUT/i.test(
1400
+ return /deadlock detected|Runtime Postgres connection timed out|could not serialize access|tuple concurrently updated/i.test(
1392
1401
  message,
1393
1402
  );
1394
1403
  }
@@ -1404,12 +1413,7 @@ function isRetryableRuntimeApiResponse(status: number, body: string): boolean {
1404
1413
  ) {
1405
1414
  return true;
1406
1415
  }
1407
- return (
1408
- status === 500 &&
1409
- /timeout exceeded when trying to connect|timed out|fetch failed|ECONNRESET|UND_ERR_CONNECT_TIMEOUT/i.test(
1410
- body,
1411
- )
1412
- );
1416
+ return status === 500 && RETRYABLE_RUNTIME_API_ERROR_RE.test(body);
1413
1417
  }
1414
1418
 
1415
1419
  async function sleepRuntimeApiRetry(attempt: number): Promise<void> {
@@ -4495,6 +4499,10 @@ function createMinimalWorkerCtx(
4495
4499
  executionPlan: plan,
4496
4500
  staticPipeline: staticPipelineFromReq(req),
4497
4501
  });
4502
+ const mapHasExternalSideEffects =
4503
+ mapDispatchPlan.workEstimate.providerToolCallsPerRow > 0 ||
4504
+ mapDispatchPlan.workEstimate.childPlaySubmitsPerRow > 0 ||
4505
+ mapDispatchPlan.workEstimate.eventWaitsPerRow > 0;
4498
4506
  const rowsPerChunk = mapDispatchPlan.rowsPerChunk;
4499
4507
  recordRunnerPerfTrace({
4500
4508
  req,
@@ -5765,6 +5773,13 @@ function createMinimalWorkerCtx(
5765
5773
  },
5766
5774
  });
5767
5775
  } catch (error) {
5776
+ if (
5777
+ workflowStep &&
5778
+ !mapHasExternalSideEffects &&
5779
+ isRetryableRuntimePersistenceError(error)
5780
+ ) {
5781
+ throw error;
5782
+ }
5768
5783
  tripRuntimePersistenceLatch(persistenceLatch, error);
5769
5784
  recordRunnerPerfTrace({
5770
5785
  req,
@@ -8287,9 +8302,9 @@ function runRequestFromWorkflowParams(
8287
8302
  : null;
8288
8303
  return {
8289
8304
  runId: String(params.runId ?? ''),
8290
- callbackUrl: String(params.baseUrl ?? ''),
8305
+ callbackUrl: String(params.callbackUrl ?? params.baseUrl ?? ''),
8291
8306
  executorToken: String(params.executorToken ?? ''),
8292
- baseUrl: String(params.baseUrl ?? ''),
8307
+ baseUrl: String(params.runtimeApiBaseUrl ?? params.baseUrl ?? ''),
8293
8308
  integrationMode: normalizeIntegrationMode(params.integrationMode),
8294
8309
  orgId: String(params.orgId ?? ''),
8295
8310
  playName: String(params.playName ?? ''),
@@ -105,10 +105,10 @@ export const SDK_RELEASE = {
105
105
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
106
106
  // fields shipped in 0.1.153.
107
107
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
108
- version: '0.1.190',
108
+ version: '0.1.192',
109
109
  apiContract: '2026-06-dataset-handle-results-hard-cutover',
110
110
  supportPolicy: {
111
- latest: '0.1.190',
111
+ latest: '0.1.192',
112
112
  minimumSupported: '0.1.53',
113
113
  deprecatedBelow: '0.1.53',
114
114
  commandMinimumSupported: [
@@ -11,6 +11,7 @@ import {
11
11
  isPlayExecutionSuspendedError,
12
12
  isPlayRowExecutionSuspendedError,
13
13
  } from './suspension';
14
+ import { getToolHttpErrorReceiptFailureKind } from './tool-http-errors';
14
15
  import type { WorkReceiptFailureKind } from './work-receipts';
15
16
  import {
16
17
  PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
@@ -48,6 +49,8 @@ function isInFlightRuntimeReceipt(
48
49
  export function runtimeReceiptFailureKindForError(
49
50
  error: unknown,
50
51
  ): WorkReceiptFailureKind {
52
+ const toolFailureKind = getToolHttpErrorReceiptFailureKind(error);
53
+ if (toolFailureKind) return toolFailureKind;
51
54
  return isPlayExecutionSuspendedError(error) ||
52
55
  isPlayRowExecutionSuspendedError(error)
53
56
  ? 'repairable'
@@ -128,6 +128,11 @@ export type PlaySchedulerSubmitInput = {
128
128
  /** Optional immutable Worker module source for local Dynamic Worker loading. */
129
129
  dynamicWorkerCode?: string | null;
130
130
  executorToken: string;
131
+ /**
132
+ * Public app origin used by Workers to call Deepline runtime API routes.
133
+ * When omitted, legacy schedulers fall back to baseUrl.
134
+ */
135
+ runtimeApiBaseUrl?: string | null;
131
136
  baseUrl: string;
132
137
  /** Optional per-run provider execution mode. Defaults to server policy when omitted. */
133
138
  integrationMode?: 'live' | 'eval_stub' | 'fixture';
@@ -204,9 +204,7 @@ export function classifyToolExecuteHttpFailure(input: {
204
204
  });
205
205
  const shouldRetry =
206
206
  retryDecision.retryable && input.attempt < retryDecision.attemptCap;
207
- error.receiptFailureKind = retryDecision.retryable
208
- ? 'repairable'
209
- : 'terminal';
207
+ if (retryDecision.retryable) error.receiptFailureKind = 'repairable';
210
208
  const retryAfterMs = parseToolExecuteRetryAfterMs(
211
209
  input.retryAfterHeader,
212
210
  input.nowMs,
@@ -4,13 +4,13 @@ export class ToolHttpError extends Error {
4
4
  readonly billing: Record<string, unknown> | null;
5
5
  /** HTTP status of the failed tool-execute response (e.g. 429, 502). */
6
6
  readonly status: number;
7
- receiptFailureKind: WorkReceiptFailureKind | null;
7
+ receiptFailureKind: WorkReceiptFailureKind;
8
8
 
9
9
  constructor(
10
10
  message: string,
11
11
  billing: Record<string, unknown> | null,
12
12
  status: number,
13
- receiptFailureKind: WorkReceiptFailureKind | null = null,
13
+ receiptFailureKind: WorkReceiptFailureKind = 'terminal',
14
14
  ) {
15
15
  super(message);
16
16
  this.name = 'ToolHttpError';
package/dist/cli/index.js CHANGED
@@ -623,10 +623,10 @@ var SDK_RELEASE = {
623
623
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
624
624
  // fields shipped in 0.1.153.
625
625
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
626
- version: "0.1.190",
626
+ version: "0.1.192",
627
627
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
628
628
  supportPolicy: {
629
- latest: "0.1.190",
629
+ latest: "0.1.192",
630
630
  minimumSupported: "0.1.53",
631
631
  deprecatedBelow: "0.1.53",
632
632
  commandMinimumSupported: [
@@ -7443,6 +7443,9 @@ async function waitForRenderHealth(url, state) {
7443
7443
  if (isOwnedCsvRenderHealth(await fetchCsvRenderHealth(url), state)) {
7444
7444
  return true;
7445
7445
  }
7446
+ if (state.pid && !processAlive(state.pid)) {
7447
+ return false;
7448
+ }
7446
7449
  await new Promise((resolveDelay) => setTimeout(resolveDelay, 100));
7447
7450
  }
7448
7451
  return false;
@@ -7505,6 +7508,10 @@ const server = http.createServer((req, res) => {
7505
7508
  });
7506
7509
 
7507
7510
  server.listen(port, '127.0.0.1');
7511
+ server.on('error', (error) => {
7512
+ console.error(error && error.stack ? error.stack : error);
7513
+ process.exit(1);
7514
+ });
7508
7515
  process.on('SIGTERM', () => server.close(() => process.exit(0)));
7509
7516
  process.on('SIGINT', () => server.close(() => process.exit(0)));
7510
7517
  `;
@@ -7560,17 +7567,23 @@ async function handleCsvRenderStart(options) {
7560
7567
  }
7561
7568
  ensureCsvRenderStateDir();
7562
7569
  const logPath = csvRenderLogPath();
7570
+ const logFd = (0, import_node_fs7.openSync)(logPath, "w");
7563
7571
  const token = (0, import_node_crypto2.randomUUID)();
7564
- const child = (0, import_node_child_process.spawn)(process.execPath, ["-e", CSV_RENDER_SERVER_SOURCE], {
7565
- detached: true,
7566
- stdio: ["ignore", "ignore", "ignore"],
7567
- env: {
7568
- ...process.env,
7569
- DEEPLINE_CSV_RENDER_PORT: String(port),
7570
- DEEPLINE_CSV_RENDER_CSV: csvPath,
7571
- DEEPLINE_CSV_RENDER_TOKEN: token
7572
+ const child = (0, import_node_child_process.spawn)(
7573
+ process.execPath,
7574
+ ["-e", CSV_RENDER_SERVER_SOURCE],
7575
+ {
7576
+ detached: true,
7577
+ stdio: ["ignore", logFd, logFd],
7578
+ env: {
7579
+ ...process.env,
7580
+ DEEPLINE_CSV_RENDER_PORT: String(port),
7581
+ DEEPLINE_CSV_RENDER_CSV: csvPath,
7582
+ DEEPLINE_CSV_RENDER_TOKEN: token
7583
+ }
7572
7584
  }
7573
- });
7585
+ );
7586
+ (0, import_node_fs7.closeSync)(logFd);
7574
7587
  child.unref();
7575
7588
  const state = {
7576
7589
  pid: child.pid ?? null,
@@ -7586,7 +7599,19 @@ async function handleCsvRenderStart(options) {
7586
7599
  if (processAlive(child.pid)) {
7587
7600
  process.kill(child.pid, "SIGTERM");
7588
7601
  }
7589
- throw new Error(`Timed out waiting for CSV render to start at ${url}.`);
7602
+ let detail = "";
7603
+ try {
7604
+ const log = (0, import_node_fs7.readFileSync)(logPath, "utf8").trim();
7605
+ if (log) {
7606
+ detail = `
7607
+ CSV render log:
7608
+ ${clip(log, 2e3)}`;
7609
+ }
7610
+ } catch {
7611
+ }
7612
+ throw new Error(
7613
+ `Timed out waiting for CSV render to start at ${url}.${detail}`
7614
+ );
7590
7615
  }
7591
7616
  writeCsvRenderState(state);
7592
7617
  (0, import_node_fs7.writeFileSync)(
@@ -19360,6 +19385,30 @@ function coalesceExportableSheetRows(rows, sourceRowStart = 0) {
19360
19385
  bySourceRowIndex.set(rowIndex, next);
19361
19386
  mergedRows.push(next);
19362
19387
  }
19388
+ if (bySourceRowIndex.size > 0) {
19389
+ mergedRows.sort((left, right) => {
19390
+ const leftIndex = sourceRowIndexFromEnrichRow(
19391
+ left,
19392
+ sourceRowStart,
19393
+ Number.MAX_SAFE_INTEGER
19394
+ );
19395
+ const rightIndex = sourceRowIndexFromEnrichRow(
19396
+ right,
19397
+ sourceRowStart,
19398
+ Number.MAX_SAFE_INTEGER
19399
+ );
19400
+ if (leftIndex !== null && rightIndex !== null) {
19401
+ return leftIndex - rightIndex;
19402
+ }
19403
+ if (leftIndex !== null) {
19404
+ return -1;
19405
+ }
19406
+ if (rightIndex !== null) {
19407
+ return 1;
19408
+ }
19409
+ return 0;
19410
+ });
19411
+ }
19363
19412
  return mergedRows;
19364
19413
  }
19365
19414
  function sourceRowIndexFromEnrichRow(row, start, end) {
@@ -21202,7 +21251,9 @@ function registerEnrichCommand(program) {
21202
21251
  ...finalExportResult ? { rowSetComplete: !finalExportResult.partial } : {}
21203
21252
  });
21204
21253
  if (chunk.exportResult) {
21205
- currentChunkCommittedExportResult = await commitInPlaceOutput(chunk.exportResult);
21254
+ currentChunkCommittedExportResult = await commitInPlaceOutput(
21255
+ chunk.exportResult
21256
+ );
21206
21257
  finalExportResult = currentChunkCommittedExportResult;
21207
21258
  }
21208
21259
  const reportedExportResult = currentChunkCommittedExportResult ?? finalExportResult;
@@ -608,10 +608,10 @@ var SDK_RELEASE = {
608
608
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
609
609
  // fields shipped in 0.1.153.
610
610
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
611
- version: "0.1.190",
611
+ version: "0.1.192",
612
612
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
613
613
  supportPolicy: {
614
- latest: "0.1.190",
614
+ latest: "0.1.192",
615
615
  minimumSupported: "0.1.53",
616
616
  deprecatedBelow: "0.1.53",
617
617
  commandMinimumSupported: [
@@ -6670,8 +6670,10 @@ Examples:
6670
6670
  import { spawn } from "child_process";
6671
6671
  import { randomUUID } from "crypto";
6672
6672
  import {
6673
+ closeSync,
6673
6674
  existsSync as existsSync6,
6674
6675
  mkdirSync as mkdirSync5,
6676
+ openSync,
6675
6677
  readFileSync as readFileSync6,
6676
6678
  rmSync as rmSync3,
6677
6679
  writeFileSync as writeFileSync6
@@ -7446,6 +7448,9 @@ async function waitForRenderHealth(url, state) {
7446
7448
  if (isOwnedCsvRenderHealth(await fetchCsvRenderHealth(url), state)) {
7447
7449
  return true;
7448
7450
  }
7451
+ if (state.pid && !processAlive(state.pid)) {
7452
+ return false;
7453
+ }
7449
7454
  await new Promise((resolveDelay) => setTimeout(resolveDelay, 100));
7450
7455
  }
7451
7456
  return false;
@@ -7508,6 +7513,10 @@ const server = http.createServer((req, res) => {
7508
7513
  });
7509
7514
 
7510
7515
  server.listen(port, '127.0.0.1');
7516
+ server.on('error', (error) => {
7517
+ console.error(error && error.stack ? error.stack : error);
7518
+ process.exit(1);
7519
+ });
7511
7520
  process.on('SIGTERM', () => server.close(() => process.exit(0)));
7512
7521
  process.on('SIGINT', () => server.close(() => process.exit(0)));
7513
7522
  `;
@@ -7563,17 +7572,23 @@ async function handleCsvRenderStart(options) {
7563
7572
  }
7564
7573
  ensureCsvRenderStateDir();
7565
7574
  const logPath = csvRenderLogPath();
7575
+ const logFd = openSync(logPath, "w");
7566
7576
  const token = randomUUID();
7567
- const child = spawn(process.execPath, ["-e", CSV_RENDER_SERVER_SOURCE], {
7568
- detached: true,
7569
- stdio: ["ignore", "ignore", "ignore"],
7570
- env: {
7571
- ...process.env,
7572
- DEEPLINE_CSV_RENDER_PORT: String(port),
7573
- DEEPLINE_CSV_RENDER_CSV: csvPath,
7574
- DEEPLINE_CSV_RENDER_TOKEN: token
7577
+ const child = spawn(
7578
+ process.execPath,
7579
+ ["-e", CSV_RENDER_SERVER_SOURCE],
7580
+ {
7581
+ detached: true,
7582
+ stdio: ["ignore", logFd, logFd],
7583
+ env: {
7584
+ ...process.env,
7585
+ DEEPLINE_CSV_RENDER_PORT: String(port),
7586
+ DEEPLINE_CSV_RENDER_CSV: csvPath,
7587
+ DEEPLINE_CSV_RENDER_TOKEN: token
7588
+ }
7575
7589
  }
7576
- });
7590
+ );
7591
+ closeSync(logFd);
7577
7592
  child.unref();
7578
7593
  const state = {
7579
7594
  pid: child.pid ?? null,
@@ -7589,7 +7604,19 @@ async function handleCsvRenderStart(options) {
7589
7604
  if (processAlive(child.pid)) {
7590
7605
  process.kill(child.pid, "SIGTERM");
7591
7606
  }
7592
- throw new Error(`Timed out waiting for CSV render to start at ${url}.`);
7607
+ let detail = "";
7608
+ try {
7609
+ const log = readFileSync6(logPath, "utf8").trim();
7610
+ if (log) {
7611
+ detail = `
7612
+ CSV render log:
7613
+ ${clip(log, 2e3)}`;
7614
+ }
7615
+ } catch {
7616
+ }
7617
+ throw new Error(
7618
+ `Timed out waiting for CSV render to start at ${url}.${detail}`
7619
+ );
7593
7620
  }
7594
7621
  writeCsvRenderState(state);
7595
7622
  writeFileSync6(
@@ -8155,8 +8182,8 @@ import { parse as parseCsvSync2 } from "csv-parse/sync";
8155
8182
 
8156
8183
  // src/cli/commands/plays/bootstrap.ts
8157
8184
  import {
8158
- closeSync,
8159
- openSync,
8185
+ closeSync as closeSync2,
8186
+ openSync as openSync2,
8160
8187
  readSync,
8161
8188
  statSync as statSync2,
8162
8189
  writeFileSync as writeFileSync8
@@ -8734,11 +8761,11 @@ function inferCsvColumnSpecs(headers, rows) {
8734
8761
  function readCsvSample(csvPath) {
8735
8762
  const resolvedPath = resolve7(csvPath);
8736
8763
  const size = statSync2(resolvedPath).size;
8737
- const fd = openSync(resolvedPath, "r");
8764
+ const fd = openSync2(resolvedPath, "r");
8738
8765
  const byteLength = Math.min(size, CSV_HEADER_SAMPLE_BYTES);
8739
8766
  const buffer = Buffer.alloc(byteLength);
8740
8767
  const bytesRead = readSync(fd, buffer, 0, byteLength, 0);
8741
- closeSync(fd);
8768
+ closeSync2(fd);
8742
8769
  if (bytesRead === 0) {
8743
8770
  throw new PlayBootstrapUsageError(`--from csv:${csvPath} is empty.`);
8744
8771
  }
@@ -19387,6 +19414,30 @@ function coalesceExportableSheetRows(rows, sourceRowStart = 0) {
19387
19414
  bySourceRowIndex.set(rowIndex, next);
19388
19415
  mergedRows.push(next);
19389
19416
  }
19417
+ if (bySourceRowIndex.size > 0) {
19418
+ mergedRows.sort((left, right) => {
19419
+ const leftIndex = sourceRowIndexFromEnrichRow(
19420
+ left,
19421
+ sourceRowStart,
19422
+ Number.MAX_SAFE_INTEGER
19423
+ );
19424
+ const rightIndex = sourceRowIndexFromEnrichRow(
19425
+ right,
19426
+ sourceRowStart,
19427
+ Number.MAX_SAFE_INTEGER
19428
+ );
19429
+ if (leftIndex !== null && rightIndex !== null) {
19430
+ return leftIndex - rightIndex;
19431
+ }
19432
+ if (leftIndex !== null) {
19433
+ return -1;
19434
+ }
19435
+ if (rightIndex !== null) {
19436
+ return 1;
19437
+ }
19438
+ return 0;
19439
+ });
19440
+ }
19390
19441
  return mergedRows;
19391
19442
  }
19392
19443
  function sourceRowIndexFromEnrichRow(row, start, end) {
@@ -21229,7 +21280,9 @@ function registerEnrichCommand(program) {
21229
21280
  ...finalExportResult ? { rowSetComplete: !finalExportResult.partial } : {}
21230
21281
  });
21231
21282
  if (chunk.exportResult) {
21232
- currentChunkCommittedExportResult = await commitInPlaceOutput(chunk.exportResult);
21283
+ currentChunkCommittedExportResult = await commitInPlaceOutput(
21284
+ chunk.exportResult
21285
+ );
21233
21286
  finalExportResult = currentChunkCommittedExportResult;
21234
21287
  }
21235
21288
  const reportedExportResult = currentChunkCommittedExportResult ?? finalExportResult;
@@ -24354,9 +24407,9 @@ import { join as join11, resolve as resolve11 } from "path";
24354
24407
 
24355
24408
  // src/tool-output.ts
24356
24409
  import {
24357
- closeSync as closeSync2,
24410
+ closeSync as closeSync3,
24358
24411
  mkdirSync as mkdirSync8,
24359
- openSync as openSync2,
24412
+ openSync as openSync3,
24360
24413
  writeFileSync as writeFileSync12,
24361
24414
  writeSync
24362
24415
  } from "fs";
@@ -24509,7 +24562,7 @@ function writeCsvOutputFile(rows, stem, options) {
24509
24562
  }
24510
24563
  return normalized;
24511
24564
  };
24512
- const fd = openSync2(outputPath, "w");
24565
+ const fd = openSync3(outputPath, "w");
24513
24566
  try {
24514
24567
  writeSync(fd, `${columns.map(escapeCell).join(",")}
24515
24568
  `);
@@ -24521,7 +24574,7 @@ function writeCsvOutputFile(rows, stem, options) {
24521
24574
  );
24522
24575
  }
24523
24576
  } finally {
24524
- closeSync2(fd);
24577
+ closeSync3(fd);
24525
24578
  }
24526
24579
  const previewRows = rows.slice(0, 5);
24527
24580
  const previewColumns = columns.slice(0, 5);
package/dist/index.js CHANGED
@@ -422,10 +422,10 @@ var SDK_RELEASE = {
422
422
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
423
423
  // fields shipped in 0.1.153.
424
424
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
425
- version: "0.1.190",
425
+ version: "0.1.192",
426
426
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
427
427
  supportPolicy: {
428
- latest: "0.1.190",
428
+ latest: "0.1.192",
429
429
  minimumSupported: "0.1.53",
430
430
  deprecatedBelow: "0.1.53",
431
431
  commandMinimumSupported: [
package/dist/index.mjs CHANGED
@@ -352,10 +352,10 @@ var SDK_RELEASE = {
352
352
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
353
353
  // fields shipped in 0.1.153.
354
354
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
355
- version: "0.1.190",
355
+ version: "0.1.192",
356
356
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
357
357
  supportPolicy: {
358
- latest: "0.1.190",
358
+ latest: "0.1.192",
359
359
  minimumSupported: "0.1.53",
360
360
  deprecatedBelow: "0.1.53",
361
361
  commandMinimumSupported: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.190",
3
+ "version": "0.1.192",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {