deepline 0.1.36 → 0.1.38

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
  }
@@ -1454,6 +1472,21 @@ function executeSyntheticTransientRetry(
1454
1472
  function executeSyntheticTestRateLimit(
1455
1473
  input: Record<string, unknown>,
1456
1474
  ): Record<string, unknown> {
1475
+ if (
1476
+ typeof input.key === 'string' &&
1477
+ input.key.startsWith('public-error-message-regression')
1478
+ ) {
1479
+ throw new ToolHttpError(
1480
+ [
1481
+ 'tool test_rate_limit 422 attempt 1/1:',
1482
+ 'Synthetic public test error with a redacted token=[REDACTED].',
1483
+ 'code=TEST_PUBLIC_ERROR.',
1484
+ 'failure_description=The fake test provider intentionally raised a typed public error so V2 runner output preserves actionable details.',
1485
+ 'operator_hint=Use this no-bill test provider fixture when verifying play runner error rendering.',
1486
+ ].join(' '),
1487
+ null,
1488
+ );
1489
+ }
1457
1490
  const rowNumber =
1458
1491
  typeof input.row_number === 'number' && Number.isInteger(input.row_number)
1459
1492
  ? input.row_number
@@ -1850,8 +1883,39 @@ type WorkerMapChunkSummary<T extends Record<string, unknown>> = {
1850
1883
  cachedRows?: T[];
1851
1884
  };
1852
1885
 
1853
- function toWorkflowSerializableValue<T>(value: T): T {
1854
- return JSON.parse(JSON.stringify(serializeValue(value, 0))) as T;
1886
+ function serializeDurableStepValue<T>(value: T, depth = 0): T {
1887
+ if (depth > 20 || value == null) return value;
1888
+ if (isToolExecuteResult(value)) return serializeToolExecuteResult(value) as T;
1889
+ if (isDatasetHandle(value)) return serializeValue(value, depth) as T;
1890
+ if (Array.isArray(value)) {
1891
+ return value.map((entry) => serializeDurableStepValue(entry, depth + 1)) as T;
1892
+ }
1893
+ if (typeof value !== 'object') return value;
1894
+ return Object.fromEntries(
1895
+ Object.entries(value as Record<string, unknown>).map(([key, child]) => [
1896
+ key,
1897
+ serializeDurableStepValue(child, depth + 1),
1898
+ ]),
1899
+ ) as T;
1900
+ }
1901
+
1902
+ function deserializeDurableStepValue<T>(value: T, depth = 0): T {
1903
+ if (depth > 20 || value == null) return value;
1904
+ if (isSerializedToolExecuteResult(value)) {
1905
+ return deserializeToolExecuteResult(value) as T;
1906
+ }
1907
+ if (Array.isArray(value)) {
1908
+ return value.map((entry) =>
1909
+ deserializeDurableStepValue(entry, depth + 1),
1910
+ ) as T;
1911
+ }
1912
+ if (typeof value !== 'object') return value;
1913
+ return Object.fromEntries(
1914
+ Object.entries(value as Record<string, unknown>).map(([key, child]) => [
1915
+ key,
1916
+ deserializeDurableStepValue(child, depth + 1),
1917
+ ]),
1918
+ ) as T;
1855
1919
  }
1856
1920
 
1857
1921
  type WorkerStepResolver = (
@@ -1973,8 +2037,8 @@ async function executeWorkerStepProgram(
1973
2037
  let currentRow: Record<string, unknown> = cloneCsvAliasedRow(inputRow);
1974
2038
  for (const step of program.steps) {
1975
2039
  const stepPath = [...(recorder?.path ?? []), step.name];
1976
- const runStep = async () =>
1977
- await executeWorkerStepResolver(
2040
+ const runStep = async (): Promise<WorkerStepResolution> => {
2041
+ const resolution = await executeWorkerStepResolver(
1978
2042
  step.resolver,
1979
2043
  currentRow,
1980
2044
  ctx,
@@ -1982,10 +2046,15 @@ async function executeWorkerStepProgram(
1982
2046
  recorder
1983
2047
  ? {
1984
2048
  ...recorder,
1985
- path: stepPath,
1986
- }
1987
- : undefined,
2049
+ path: stepPath,
2050
+ }
2051
+ : undefined,
1988
2052
  );
2053
+ return {
2054
+ value: serializeDurableStepValue(resolution.value),
2055
+ ...(resolution.status ? { status: resolution.status } : {}),
2056
+ };
2057
+ };
1989
2058
  const resolution = workflowStep
1990
2059
  ? await (
1991
2060
  workflowStep.do as unknown as (
@@ -1994,7 +2063,7 @@ async function executeWorkerStepProgram(
1994
2063
  ) => Promise<WorkerStepResolution>
1995
2064
  )(stepPath.join('.'), runStep)
1996
2065
  : await runStep();
1997
- const value = resolution.value;
2066
+ const value = deserializeDurableStepValue(resolution.value);
1998
2067
  currentRow = cloneCsvAliasedRow(currentRow, { [step.name]: value });
1999
2068
  if (recorder) {
2000
2069
  const stepId = stepPath.join('.');
@@ -3553,10 +3622,10 @@ function createMinimalWorkerCtx(
3553
3622
  rowsSkipped,
3554
3623
  outputDatasetId: `map:${name}`,
3555
3624
  hash,
3556
- preview: toWorkflowSerializableValue(out.slice(0, 5)),
3625
+ preview: serializeDurableStepValue(out.slice(0, 5)),
3557
3626
  cachedRows:
3558
3627
  out.length <= WORKER_DATASET_IN_MEMORY_ROWS
3559
- ? toWorkflowSerializableValue(out)
3628
+ ? serializeDurableStepValue(out)
3560
3629
  : undefined,
3561
3630
  };
3562
3631
  };
@@ -3784,13 +3853,16 @@ function createMinimalWorkerCtx(
3784
3853
  ts: nowMs(),
3785
3854
  });
3786
3855
  }
3856
+ // Static pipeline JS blocks already execute inside a Workflow step.
3857
+ // Wrapping each generated waterfall step in another step.do can leave
3858
+ // Workers preview runs parked after the last provider callback.
3787
3859
  return (await executeWorkerStepProgram(
3788
3860
  program,
3789
3861
  input,
3790
3862
  ctx,
3791
3863
  0,
3792
3864
  undefined,
3793
- workflowStep,
3865
+ undefined,
3794
3866
  )) as T;
3795
3867
  },
3796
3868
  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.36";
2
- export const SDK_API_CONTRACT = "2026-05-v2-tool-response";
1
+ export const SDK_VERSION = "0.1.38";
2
+ export const SDK_API_CONTRACT = "2026-05-v2-tool-response-play-guardrails";
@@ -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.36",
3
+ "version": "0.1.38",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {