deepline 0.1.40 → 0.1.42

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.
@@ -52,7 +52,10 @@ import {
52
52
  import {
53
53
  adaptV2ExecuteResponseToToolResult,
54
54
  createToolExecuteResult,
55
+ deserializeToolExecuteResult,
56
+ isSerializedToolExecuteResult,
55
57
  isToolExecuteResult,
58
+ serializeToolExecuteResult,
56
59
  type ToolExecuteResult,
57
60
  type ToolResultMetadataInput,
58
61
  } from '../../../shared_libs/play-runtime/tool-result';
@@ -448,7 +451,7 @@ async function probeHarnessOnce(
448
451
  */
449
452
  const RUNTIME_API_TIMEOUT_MS = 30_000;
450
453
  const RUNTIME_API_PLAY_RUN_TIMEOUT_MS = 75_000;
451
- const RUNTIME_API_RETRY_DELAYS_MS = [250, 750, 1500] as const;
454
+ const RUNTIME_API_RETRY_DELAYS_MS = [250, 750, 1500, 3000, 5000, 10000] as const;
452
455
  let loggedMissingRuntimeApiBinding = false;
453
456
 
454
457
  async function fetchRuntimeApi(
@@ -461,7 +464,17 @@ async function fetchRuntimeApi(
461
464
  ? RUNTIME_API_PLAY_RUN_TIMEOUT_MS
462
465
  : RUNTIME_API_TIMEOUT_MS;
463
466
  const controller = new AbortController();
464
- const timer = setTimeout(() => controller.abort(), timeoutMs);
467
+ let timeout: ReturnType<typeof setTimeout> | null = null;
468
+ const timeoutPromise = new Promise<never>((_, reject) => {
469
+ timeout = setTimeout(() => {
470
+ controller.abort();
471
+ reject(
472
+ new Error(
473
+ `[play-harness] runtime API call timed out after ${timeoutMs}ms. path=${path} baseUrl=${baseUrl}`,
474
+ ),
475
+ );
476
+ }, timeoutMs);
477
+ });
465
478
  try {
466
479
  const mergedInit: RequestInit = {
467
480
  ...init,
@@ -475,11 +488,15 @@ async function fetchRuntimeApi(
475
488
  `[play-harness] RUNTIME_API binding missing; using public runtime API transport. path=${path}`,
476
489
  );
477
490
  }
478
- return await fetch(`${baseUrl.replace(/\/$/, '')}${path}`, mergedInit);
491
+ return await Promise.race([
492
+ fetch(`${baseUrl.replace(/\/$/, '')}${path}`, mergedInit),
493
+ timeoutPromise,
494
+ ]);
479
495
  }
480
- return await cachedRuntimeApiBinding.fetch(
496
+ const responsePromise = cachedRuntimeApiBinding.fetch(
481
497
  new Request(`${baseUrl.replace(/\/$/, '')}${path}`, mergedInit),
482
498
  );
499
+ return await Promise.race([responsePromise, timeoutPromise]);
483
500
  } catch (err) {
484
501
  if (err instanceof Error && err.name === 'AbortError') {
485
502
  throw new Error(
@@ -488,7 +505,7 @@ async function fetchRuntimeApi(
488
505
  }
489
506
  throw err;
490
507
  } finally {
491
- clearTimeout(timer);
508
+ if (timeout) clearTimeout(timeout);
492
509
  }
493
510
  }
494
511
 
@@ -724,7 +741,8 @@ function isRetryableRuntimeApiResponse(status: number, body: string): boolean {
724
741
  status === 429 ||
725
742
  status === 502 ||
726
743
  status === 503 ||
727
- status === 504
744
+ status === 504 ||
745
+ status === 530
728
746
  ) {
729
747
  return true;
730
748
  }
@@ -1357,6 +1375,15 @@ async function executeSyntheticTestRateLimitBatch(
1357
1375
  req: RunRequest,
1358
1376
  input: Record<string, unknown>,
1359
1377
  ): Promise<Record<string, unknown>> {
1378
+ const delayMs =
1379
+ typeof input.simulated_delay_ms === 'number' &&
1380
+ Number.isInteger(input.simulated_delay_ms) &&
1381
+ input.simulated_delay_ms > 0
1382
+ ? input.simulated_delay_ms
1383
+ : 0;
1384
+ if (delayMs > 0) {
1385
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1386
+ }
1360
1387
  const rawItems = Array.isArray(input.items) ? input.items : [];
1361
1388
  const items = rawItems
1362
1389
  .filter((item): item is Record<string, unknown> =>
@@ -1454,6 +1481,21 @@ function executeSyntheticTransientRetry(
1454
1481
  function executeSyntheticTestRateLimit(
1455
1482
  input: Record<string, unknown>,
1456
1483
  ): Record<string, unknown> {
1484
+ if (
1485
+ typeof input.key === 'string' &&
1486
+ input.key.startsWith('public-error-message-regression')
1487
+ ) {
1488
+ throw new ToolHttpError(
1489
+ [
1490
+ 'tool test_rate_limit 422 attempt 1/1:',
1491
+ 'Synthetic public test error with a redacted token=[REDACTED].',
1492
+ 'code=TEST_PUBLIC_ERROR.',
1493
+ 'failure_description=The fake test provider intentionally raised a typed public error so V2 runner output preserves actionable details.',
1494
+ 'operator_hint=Use this no-bill test provider fixture when verifying play runner error rendering.',
1495
+ ].join(' '),
1496
+ null,
1497
+ );
1498
+ }
1457
1499
  const rowNumber =
1458
1500
  typeof input.row_number === 'number' && Number.isInteger(input.row_number)
1459
1501
  ? input.row_number
@@ -1850,8 +1892,39 @@ type WorkerMapChunkSummary<T extends Record<string, unknown>> = {
1850
1892
  cachedRows?: T[];
1851
1893
  };
1852
1894
 
1853
- function toWorkflowSerializableValue<T>(value: T): T {
1854
- return JSON.parse(JSON.stringify(serializeValue(value, 0))) as T;
1895
+ function serializeDurableStepValue<T>(value: T, depth = 0): T {
1896
+ if (depth > 20 || value == null) return value;
1897
+ if (isToolExecuteResult(value)) return serializeToolExecuteResult(value) as T;
1898
+ if (isDatasetHandle(value)) return serializeValue(value, depth) as T;
1899
+ if (Array.isArray(value)) {
1900
+ return value.map((entry) => serializeDurableStepValue(entry, depth + 1)) as T;
1901
+ }
1902
+ if (typeof value !== 'object') return value;
1903
+ return Object.fromEntries(
1904
+ Object.entries(value as Record<string, unknown>).map(([key, child]) => [
1905
+ key,
1906
+ serializeDurableStepValue(child, depth + 1),
1907
+ ]),
1908
+ ) as T;
1909
+ }
1910
+
1911
+ function deserializeDurableStepValue<T>(value: T, depth = 0): T {
1912
+ if (depth > 20 || value == null) return value;
1913
+ if (isSerializedToolExecuteResult(value)) {
1914
+ return deserializeToolExecuteResult(value) as T;
1915
+ }
1916
+ if (Array.isArray(value)) {
1917
+ return value.map((entry) =>
1918
+ deserializeDurableStepValue(entry, depth + 1),
1919
+ ) as T;
1920
+ }
1921
+ if (typeof value !== 'object') return value;
1922
+ return Object.fromEntries(
1923
+ Object.entries(value as Record<string, unknown>).map(([key, child]) => [
1924
+ key,
1925
+ deserializeDurableStepValue(child, depth + 1),
1926
+ ]),
1927
+ ) as T;
1855
1928
  }
1856
1929
 
1857
1930
  type WorkerStepResolver = (
@@ -1973,8 +2046,8 @@ async function executeWorkerStepProgram(
1973
2046
  let currentRow: Record<string, unknown> = cloneCsvAliasedRow(inputRow);
1974
2047
  for (const step of program.steps) {
1975
2048
  const stepPath = [...(recorder?.path ?? []), step.name];
1976
- const runStep = async () =>
1977
- await executeWorkerStepResolver(
2049
+ const runStep = async (): Promise<WorkerStepResolution> => {
2050
+ const resolution = await executeWorkerStepResolver(
1978
2051
  step.resolver,
1979
2052
  currentRow,
1980
2053
  ctx,
@@ -1982,10 +2055,15 @@ async function executeWorkerStepProgram(
1982
2055
  recorder
1983
2056
  ? {
1984
2057
  ...recorder,
1985
- path: stepPath,
1986
- }
1987
- : undefined,
2058
+ path: stepPath,
2059
+ }
2060
+ : undefined,
1988
2061
  );
2062
+ return {
2063
+ value: serializeDurableStepValue(resolution.value),
2064
+ ...(resolution.status ? { status: resolution.status } : {}),
2065
+ };
2066
+ };
1989
2067
  const resolution = workflowStep
1990
2068
  ? await (
1991
2069
  workflowStep.do as unknown as (
@@ -1994,7 +2072,7 @@ async function executeWorkerStepProgram(
1994
2072
  ) => Promise<WorkerStepResolution>
1995
2073
  )(stepPath.join('.'), runStep)
1996
2074
  : await runStep();
1997
- const value = resolution.value;
2075
+ const value = deserializeDurableStepValue(resolution.value);
1998
2076
  currentRow = cloneCsvAliasedRow(currentRow, { [step.name]: value });
1999
2077
  if (recorder) {
2000
2078
  const stepId = stepPath.join('.');
@@ -3553,10 +3631,10 @@ function createMinimalWorkerCtx(
3553
3631
  rowsSkipped,
3554
3632
  outputDatasetId: `map:${name}`,
3555
3633
  hash,
3556
- preview: toWorkflowSerializableValue(out.slice(0, 5)),
3634
+ preview: serializeDurableStepValue(out.slice(0, 5)),
3557
3635
  cachedRows:
3558
3636
  out.length <= WORKER_DATASET_IN_MEMORY_ROWS
3559
- ? toWorkflowSerializableValue(out)
3637
+ ? serializeDurableStepValue(out)
3560
3638
  : undefined,
3561
3639
  };
3562
3640
  };
@@ -3784,13 +3862,16 @@ function createMinimalWorkerCtx(
3784
3862
  ts: nowMs(),
3785
3863
  });
3786
3864
  }
3865
+ // Static pipeline JS blocks already execute inside a Workflow step.
3866
+ // Wrapping each generated waterfall step in another step.do can leave
3867
+ // Workers preview runs parked after the last provider callback.
3787
3868
  return (await executeWorkerStepProgram(
3788
3869
  program,
3789
3870
  input,
3790
3871
  ctx,
3791
3872
  0,
3792
3873
  undefined,
3793
- workflowStep,
3874
+ undefined,
3794
3875
  )) as T;
3795
3876
  },
3796
3877
  async csv<T extends Record<string, unknown> = Record<string, unknown>>(
@@ -626,10 +626,27 @@ export interface PlayCheckResult {
626
626
  valid: boolean;
627
627
  errors: string[];
628
628
  staticPipeline?: Record<string, unknown> | null;
629
+ toolGetterHints?: PlayCheckToolGetterHint[];
629
630
  artifactHash?: string | null;
630
631
  graphHash?: string | null;
631
632
  }
632
633
 
634
+ export interface PlayCheckToolGetterHint {
635
+ toolId: string;
636
+ lists: Array<{
637
+ name: string;
638
+ expression: string;
639
+ raw?: string;
640
+ }>;
641
+ values: Array<{
642
+ name: string;
643
+ expression: string;
644
+ raw?: string;
645
+ }>;
646
+ raw?: string;
647
+ unavailable?: string;
648
+ }
649
+
633
650
  /**
634
651
  * Request body for starting a play run via {@link DeeplineClient.startPlayRun}.
635
652
  *
@@ -1,2 +1,2 @@
1
- export const SDK_VERSION = "0.1.40";
2
- export const SDK_API_CONTRACT = "2026-05-cloud-play-search";
1
+ export const SDK_VERSION = "0.1.42";
2
+ export const SDK_API_CONTRACT = "2026-05-play-tool-result-errors";
@@ -1,4 +1,5 @@
1
1
  export type {
2
+ SerializedToolExecuteResult,
2
3
  ToolExecuteResult,
3
4
  ToolResultExecutionMetadata,
4
5
  ToolResultMetadata,
@@ -10,6 +11,7 @@ export type {
10
11
  } from './tool-result-types';
11
12
 
12
13
  import type {
14
+ SerializedToolExecuteResult,
13
15
  ToolExecuteResult,
14
16
  ToolResultExecutionMetadata,
15
17
  ToolResultListAccessor,
@@ -22,6 +24,8 @@ import type {
22
24
 
23
25
  type PathSegment = string | number | '*';
24
26
 
27
+ const SERIALIZED_TOOL_EXECUTE_RESULT_KIND = 'deepline.tool_execute_result.v1';
28
+
25
29
  const TARGET_FALLBACK_KEYS: Record<string, readonly RegExp[]> = {
26
30
  email: [/^email$/i, /^address$/i, /email/i],
27
31
  phone: [/^phone$/i, /mobile/i, /phone/i, /telephone/i],
@@ -455,11 +459,10 @@ function buildTargets(
455
459
  }
456
460
 
457
461
  function buildLists(
458
- result: unknown,
462
+ resolved: Record<string, { path: string; rows: Record<string, unknown>[] }>,
459
463
  metadata: ToolResultMetadataInput,
460
464
  ): Record<string, ToolResultListMetadata> {
461
465
  const lists: Record<string, ToolResultListMetadata> = {};
462
- const resolved = resolveListRows(result, metadata.listExtractorPaths);
463
466
  for (const [name, list] of Object.entries(resolved)) {
464
467
  lists[name] = {
465
468
  path: list.path,
@@ -499,11 +502,12 @@ function buildExtractedAccessors(
499
502
  }
500
503
 
501
504
  function buildListAccessors(
502
- result: unknown,
505
+ resolved: Record<string, { path: string; rows: Record<string, unknown>[] }>,
503
506
  lists: Record<string, ToolResultListMetadata>,
504
507
  ): Record<string, ToolResultListAccessor> {
505
508
  return Object.fromEntries(
506
509
  Object.entries(lists).map(([name, metadata]) => {
510
+ const rows = resolved[name]?.rows ?? [];
507
511
  const accessor = {
508
512
  path: metadata.path,
509
513
  count: metadata.count,
@@ -511,7 +515,7 @@ function buildListAccessors(
511
515
  } as ToolResultListAccessor;
512
516
  Object.defineProperty(accessor, 'get', {
513
517
  value() {
514
- return normalizeRows(getAtPath(result, metadata.path)) ?? [];
518
+ return rows;
515
519
  },
516
520
  enumerable: false,
517
521
  });
@@ -539,7 +543,11 @@ export function createToolExecuteResult<TResult = unknown>(input: {
539
543
  resultRoot,
540
544
  input.metadata.resultIdentityGetters,
541
545
  );
542
- const lists = buildLists(resultRoot, input.metadata);
546
+ const resolvedLists = resolveListRows(
547
+ resultRoot,
548
+ input.metadata.listExtractorPaths,
549
+ );
550
+ const lists = buildLists(resolvedLists, input.metadata);
543
551
  const metadata = {
544
552
  toolId: input.metadata.toolId,
545
553
  execution: input.execution,
@@ -551,7 +559,7 @@ export function createToolExecuteResult<TResult = unknown>(input: {
551
559
  ...(result.meta ? { meta: result.meta } : {}),
552
560
  };
553
561
  const extractedValues = buildExtractedAccessors(targets);
554
- const extractedLists = buildListAccessors(resultRoot, lists);
562
+ const extractedLists = buildListAccessors(resolvedLists, lists);
555
563
  const wrapper = {
556
564
  status: input.status,
557
565
  ...(input.jobId ? { job_id: input.jobId } : {}),
@@ -586,6 +594,70 @@ export function isToolExecuteResult(
586
594
  );
587
595
  }
588
596
 
597
+ function metadataInputFromToolExecuteResult(
598
+ value: ToolExecuteResult,
599
+ ): ToolResultMetadataInput {
600
+ return {
601
+ toolId: value._metadata.toolId,
602
+ resultIdentityGetters: Object.fromEntries(
603
+ Object.entries(value._metadata.targets).map(([target, info]) => [
604
+ target,
605
+ [info.path],
606
+ ]),
607
+ ),
608
+ listExtractorPaths: Object.values(value._metadata.lists).map(
609
+ (list) => list.path,
610
+ ),
611
+ listIdentityGetters: Object.fromEntries(
612
+ Object.values(value._metadata.lists)
613
+ .flatMap((list) => Object.entries(list.keys))
614
+ .map(([target, path]) => [target, [path]]),
615
+ ),
616
+ };
617
+ }
618
+
619
+ export function serializeToolExecuteResult(
620
+ value: ToolExecuteResult,
621
+ ): SerializedToolExecuteResult {
622
+ return {
623
+ __kind: SERIALIZED_TOOL_EXECUTE_RESULT_KIND,
624
+ status: value.status,
625
+ toolResponse: {
626
+ raw: value.toolResponse.raw,
627
+ ...(value.toolResponse.meta ? { meta: value.toolResponse.meta } : {}),
628
+ },
629
+ metadata: metadataInputFromToolExecuteResult(value),
630
+ execution: value._metadata.execution,
631
+ };
632
+ }
633
+
634
+ export function isSerializedToolExecuteResult(
635
+ value: unknown,
636
+ ): value is SerializedToolExecuteResult {
637
+ return (
638
+ isRecord(value) &&
639
+ value.__kind === SERIALIZED_TOOL_EXECUTE_RESULT_KIND &&
640
+ typeof value.status === 'string' &&
641
+ isRecord(value.toolResponse) &&
642
+ isRecord(value.metadata) &&
643
+ isRecord(value.execution)
644
+ );
645
+ }
646
+
647
+ export function deserializeToolExecuteResult(
648
+ value: SerializedToolExecuteResult,
649
+ ): ToolExecuteResult {
650
+ return createToolExecuteResult({
651
+ status: value.status,
652
+ result: {
653
+ data: value.toolResponse.raw,
654
+ ...(value.toolResponse.meta ? { meta: value.toolResponse.meta } : {}),
655
+ },
656
+ metadata: value.metadata,
657
+ execution: value.execution,
658
+ });
659
+ }
660
+
589
661
  export function cloneToolExecuteResultWithExecution<TResult>(
590
662
  value: ToolExecuteResult<TResult>,
591
663
  execution: ToolResultExecutionMetadata,
@@ -600,23 +672,7 @@ export function cloneToolExecuteResultWithExecution<TResult>(
600
672
  data: value.toolResponse.raw,
601
673
  ...(value.toolResponse.meta ? { meta: value.toolResponse.meta } : {}),
602
674
  } as TResult,
603
- metadata: {
604
- toolId: value._metadata.toolId,
605
- resultIdentityGetters: Object.fromEntries(
606
- Object.entries(value._metadata.targets).map(([target, info]) => [
607
- target,
608
- [info.path],
609
- ]),
610
- ),
611
- listExtractorPaths: Object.values(value._metadata.lists).map(
612
- (list) => list.path,
613
- ),
614
- listIdentityGetters: Object.fromEntries(
615
- Object.values(value._metadata.lists)
616
- .flatMap((list) => Object.entries(list.keys))
617
- .map(([target, path]) => [target, [path]]),
618
- ),
619
- },
675
+ metadata: metadataInputFromToolExecuteResult(value),
620
676
  execution,
621
677
  meta: isRecord(value.meta) ? value.meta : undefined,
622
678
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {