deepline 0.1.88 → 0.1.89

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/dist/cli/index.js CHANGED
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
229
229
 
230
230
  // src/release.ts
231
231
  var SDK_RELEASE = {
232
- version: "0.1.88",
232
+ version: "0.1.89",
233
233
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
234
234
  supportPolicy: {
235
- latest: "0.1.88",
235
+ latest: "0.1.89",
236
236
  minimumSupported: "0.1.53",
237
237
  deprecatedBelow: "0.1.53"
238
238
  }
@@ -206,10 +206,10 @@ import { join as join2 } from "path";
206
206
 
207
207
  // src/release.ts
208
208
  var SDK_RELEASE = {
209
- version: "0.1.88",
209
+ version: "0.1.89",
210
210
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
211
211
  supportPolicy: {
212
- latest: "0.1.88",
212
+ latest: "0.1.89",
213
213
  minimumSupported: "0.1.53",
214
214
  deprecatedBelow: "0.1.53"
215
215
  }
package/dist/index.d.mts CHANGED
@@ -13,7 +13,17 @@ declare const PLAY_ARTIFACT_KINDS: {
13
13
  };
14
14
  type PlayArtifactKind = (typeof PLAY_ARTIFACT_KINDS)[keyof typeof PLAY_ARTIFACT_KINDS];
15
15
 
16
- interface PlayStaticPipelineSnapshot {
16
+ /**
17
+ * A top-level key the play's function literally `return`s. Derived from the
18
+ * `return { ... }` object literal — NOT from dataset `.withColumn(...)` names —
19
+ * so the "Returns" graph node mirrors the function's real output shape.
20
+ * `isDataset` is true when the key's value is a `PlayDataset` handle (a table).
21
+ */
22
+ interface PlayStaticReturnField {
23
+ name: string;
24
+ isDataset: boolean;
25
+ }
26
+ interface PlayStaticPipeline {
17
27
  tableNamespace?: string;
18
28
  inputFields?: string[];
19
29
  rowKeyFields?: string[];
@@ -22,16 +32,22 @@ interface PlayStaticPipelineSnapshot {
22
32
  csvDescription?: string;
23
33
  datasetDescription?: string;
24
34
  fields: string[];
25
- stages?: PlayStaticSubstepSnapshot[];
26
- substeps: PlayStaticSubstepSnapshot[];
27
- sheetContract?: PlaySheetContractSnapshot | null;
35
+ /**
36
+ * Top-level keys of the play's `return { ... }` object literal, in source
37
+ * order. Undefined when the terminal return isn't a statically-known object
38
+ * literal (bare value, dataset handle, conditional returns, etc.).
39
+ */
40
+ returnFields?: PlayStaticReturnField[];
41
+ stages?: PlayStaticSubstep[];
42
+ substeps: PlayStaticSubstep[];
43
+ sheetContract?: PlaySheetContract | null;
28
44
  sheetContractErrors?: string[];
29
45
  }
30
- type PlaySheetColumnSourceSnapshot = 'input' | 'datasetColumn' | 'waterfallStep' | 'childPlayColumn';
31
- interface PlaySheetColumnContractSnapshot {
46
+ type PlaySheetColumnSource = 'input' | 'datasetColumn' | 'waterfallStep' | 'childPlayColumn';
47
+ interface PlaySheetColumnContract {
32
48
  id: string;
33
49
  sqlName: string;
34
- source: PlaySheetColumnSourceSnapshot;
50
+ source: PlaySheetColumnSource;
35
51
  field?: string;
36
52
  parentField?: string;
37
53
  playId?: string;
@@ -42,44 +58,49 @@ interface PlaySheetColumnContractSnapshot {
42
58
  toolId?: string;
43
59
  isRowKey?: boolean;
44
60
  }
45
- interface PlaySheetContractSnapshot {
61
+ interface PlaySheetContract {
46
62
  tableNamespace: string;
47
- columns: PlaySheetColumnContractSnapshot[];
63
+ columns: PlaySheetColumnContract[];
48
64
  }
49
- type PlayStaticColumnProducerKindSnapshot = 'tool' | 'waterfall' | 'stepProgram' | 'playCall' | 'controlFlow' | 'transform';
50
- interface PlayStaticColumnProducerSnapshot {
65
+ type PlayStaticColumnProducerKind = 'tool' | 'waterfall' | 'stepProgram' | 'playCall' | 'controlFlow' | 'transform';
66
+ interface PlayStaticColumnProducer {
51
67
  id: string;
52
- kind: PlayStaticColumnProducerKindSnapshot;
68
+ kind: PlayStaticColumnProducerKind;
53
69
  field: string;
54
70
  toolId?: string;
55
71
  playId?: string;
72
+ dependsOnFields?: string[];
73
+ staleAfterSeconds?: number;
56
74
  conditional?: boolean;
57
- sourceRange?: PlayStaticSourceRangeSnapshot;
58
- steps?: PlayStaticColumnProducerSnapshot[];
59
- substep: PlayStaticSubstepSnapshot;
75
+ sourceRange?: PlayStaticSourceRange;
76
+ steps?: PlayStaticColumnProducer[];
77
+ substep: PlayStaticSubstep;
60
78
  }
61
- interface PlayStaticDatasetColumnSnapshot {
79
+ interface PlayStaticDatasetColumn {
62
80
  id: string;
63
- source: PlaySheetColumnSourceSnapshot;
81
+ source: PlaySheetColumnSource;
64
82
  sqlName?: string;
65
- producers: PlayStaticColumnProducerSnapshot[];
83
+ staleAfterSeconds?: number;
84
+ producers: PlayStaticColumnProducer[];
66
85
  }
67
- interface PlayStaticSourceRangeSnapshot {
86
+ interface PlayStaticSourceRange {
68
87
  sourcePath?: string;
69
88
  startLine: number;
70
89
  endLine: number;
71
90
  startColumn: number;
72
91
  endColumn: number;
73
92
  }
74
- type PlayStaticSubstepMetadataSnapshot = {
93
+ type PlayStaticSubstepMetadata = {
75
94
  conditional?: boolean;
95
+ dependsOnFields?: string[];
96
+ staleAfterSeconds?: number;
76
97
  };
77
- type PlayStaticSubstepSnapshot = PlayStaticSubstepMetadataSnapshot & ({
98
+ type PlayStaticSubstep = PlayStaticSubstepMetadata & ({
78
99
  type: 'csv';
79
100
  field: string;
80
101
  path?: string;
81
102
  description?: string;
82
- sourceRange?: PlayStaticSourceRangeSnapshot;
103
+ sourceRange?: PlayStaticSourceRange;
83
104
  callDepth?: number;
84
105
  callPath?: string[];
85
106
  } | {
@@ -90,22 +111,24 @@ type PlayStaticSubstepSnapshot = PlayStaticSubstepMetadataSnapshot & ({
90
111
  inputFields?: string[];
91
112
  rowKeyFields?: string[];
92
113
  outputFields?: string[];
93
- columns?: PlayStaticDatasetColumnSnapshot[];
114
+ columns?: PlayStaticDatasetColumn[];
94
115
  waterfallIds?: string[];
95
- steps?: PlayStaticSubstepSnapshot[];
96
- sheetContract?: PlaySheetContractSnapshot | null;
116
+ steps?: PlayStaticSubstep[];
117
+ sheetContract?: PlaySheetContract | null;
97
118
  description?: string;
98
- sourceRange?: PlayStaticSourceRangeSnapshot;
119
+ sourceRange?: PlayStaticSourceRange;
99
120
  callDepth?: number;
100
121
  callPath?: string[];
101
122
  } | {
102
123
  type: 'tool';
103
124
  toolId: string;
104
125
  field: string;
126
+ paramsSource?: string;
127
+ sourceText?: string;
105
128
  description?: string;
106
129
  inLoop?: boolean;
107
130
  isEventWait?: boolean;
108
- sourceRange?: PlayStaticSourceRangeSnapshot;
131
+ sourceRange?: PlayStaticSourceRange;
109
132
  callDepth?: number;
110
133
  callPath?: string[];
111
134
  } | {
@@ -124,16 +147,16 @@ type PlayStaticSubstepSnapshot = PlayStaticSubstepMetadataSnapshot & ({
124
147
  paramsSource?: string;
125
148
  }>;
126
149
  description?: string;
127
- sourceRange?: PlayStaticSourceRangeSnapshot;
150
+ sourceRange?: PlayStaticSourceRange;
128
151
  callDepth?: number;
129
152
  callPath?: string[];
130
153
  } | {
131
154
  type: 'step_suite';
132
155
  field: string;
133
- steps: PlayStaticSubstepSnapshot[];
156
+ steps: PlayStaticSubstep[];
134
157
  returnSource?: string;
135
158
  description?: string;
136
- sourceRange?: PlayStaticSourceRangeSnapshot;
159
+ sourceRange?: PlayStaticSourceRange;
137
160
  callDepth?: number;
138
161
  callPath?: string[];
139
162
  } | {
@@ -141,37 +164,40 @@ type PlayStaticSubstepSnapshot = PlayStaticSubstepMetadataSnapshot & ({
141
164
  playId: string;
142
165
  field: string;
143
166
  inLoop?: boolean;
144
- pipeline?: PlayStaticPipelineSnapshot | null;
167
+ pipeline?: PlayStaticPipeline | null;
145
168
  cycleDetected?: boolean;
146
169
  resolutionError?: string;
147
170
  description?: string;
148
- sourceRange?: PlayStaticSourceRangeSnapshot;
171
+ sourceRange?: PlayStaticSourceRange;
149
172
  callDepth?: number;
150
173
  callPath?: string[];
151
174
  } | {
152
175
  type: 'control_flow';
153
176
  kind: 'conditional' | 'loop';
154
177
  field: string;
155
- steps: PlayStaticSubstepSnapshot[];
178
+ steps: PlayStaticSubstep[];
156
179
  description?: string;
157
- sourceRange?: PlayStaticSourceRangeSnapshot;
180
+ sourceRange?: PlayStaticSourceRange;
158
181
  callDepth?: number;
159
182
  callPath?: string[];
160
183
  } | {
161
184
  type: 'run_javascript';
162
185
  alias: string;
186
+ sourceText?: string;
163
187
  description?: string;
164
- sourceRange?: PlayStaticSourceRangeSnapshot;
188
+ sourceRange?: PlayStaticSourceRange;
165
189
  callDepth?: number;
166
190
  callPath?: string[];
167
191
  } | {
168
192
  type: 'code';
169
193
  field: string;
194
+ sourceText?: string;
170
195
  description?: string;
171
- sourceRange?: PlayStaticSourceRangeSnapshot;
196
+ sourceRange?: PlayStaticSourceRange;
172
197
  callDepth?: number;
173
198
  callPath?: string[];
174
199
  });
200
+
175
201
  type PlayCompilerDependencyManifest = {
176
202
  playName: string;
177
203
  /** Original source path for direct imported definePlay dependencies. */
@@ -179,7 +205,7 @@ type PlayCompilerDependencyManifest = {
179
205
  sourceHash: string;
180
206
  graphHash: string;
181
207
  artifactHash: string;
182
- staticPipeline: PlayStaticPipelineSnapshot;
208
+ staticPipeline: PlayStaticPipeline;
183
209
  };
184
210
  type PlayCompilerManifest = {
185
211
  compilerVersion: number;
@@ -188,7 +214,7 @@ type PlayCompilerManifest = {
188
214
  graphHash: string;
189
215
  artifactHash: string;
190
216
  artifactKind?: PlayArtifactKind;
191
- staticPipeline: PlayStaticPipelineSnapshot;
217
+ staticPipeline: PlayStaticPipeline;
192
218
  importedPlayDependencies: PlayCompilerDependencyManifest[];
193
219
  };
194
220
 
package/dist/index.d.ts CHANGED
@@ -13,7 +13,17 @@ declare const PLAY_ARTIFACT_KINDS: {
13
13
  };
14
14
  type PlayArtifactKind = (typeof PLAY_ARTIFACT_KINDS)[keyof typeof PLAY_ARTIFACT_KINDS];
15
15
 
16
- interface PlayStaticPipelineSnapshot {
16
+ /**
17
+ * A top-level key the play's function literally `return`s. Derived from the
18
+ * `return { ... }` object literal — NOT from dataset `.withColumn(...)` names —
19
+ * so the "Returns" graph node mirrors the function's real output shape.
20
+ * `isDataset` is true when the key's value is a `PlayDataset` handle (a table).
21
+ */
22
+ interface PlayStaticReturnField {
23
+ name: string;
24
+ isDataset: boolean;
25
+ }
26
+ interface PlayStaticPipeline {
17
27
  tableNamespace?: string;
18
28
  inputFields?: string[];
19
29
  rowKeyFields?: string[];
@@ -22,16 +32,22 @@ interface PlayStaticPipelineSnapshot {
22
32
  csvDescription?: string;
23
33
  datasetDescription?: string;
24
34
  fields: string[];
25
- stages?: PlayStaticSubstepSnapshot[];
26
- substeps: PlayStaticSubstepSnapshot[];
27
- sheetContract?: PlaySheetContractSnapshot | null;
35
+ /**
36
+ * Top-level keys of the play's `return { ... }` object literal, in source
37
+ * order. Undefined when the terminal return isn't a statically-known object
38
+ * literal (bare value, dataset handle, conditional returns, etc.).
39
+ */
40
+ returnFields?: PlayStaticReturnField[];
41
+ stages?: PlayStaticSubstep[];
42
+ substeps: PlayStaticSubstep[];
43
+ sheetContract?: PlaySheetContract | null;
28
44
  sheetContractErrors?: string[];
29
45
  }
30
- type PlaySheetColumnSourceSnapshot = 'input' | 'datasetColumn' | 'waterfallStep' | 'childPlayColumn';
31
- interface PlaySheetColumnContractSnapshot {
46
+ type PlaySheetColumnSource = 'input' | 'datasetColumn' | 'waterfallStep' | 'childPlayColumn';
47
+ interface PlaySheetColumnContract {
32
48
  id: string;
33
49
  sqlName: string;
34
- source: PlaySheetColumnSourceSnapshot;
50
+ source: PlaySheetColumnSource;
35
51
  field?: string;
36
52
  parentField?: string;
37
53
  playId?: string;
@@ -42,44 +58,49 @@ interface PlaySheetColumnContractSnapshot {
42
58
  toolId?: string;
43
59
  isRowKey?: boolean;
44
60
  }
45
- interface PlaySheetContractSnapshot {
61
+ interface PlaySheetContract {
46
62
  tableNamespace: string;
47
- columns: PlaySheetColumnContractSnapshot[];
63
+ columns: PlaySheetColumnContract[];
48
64
  }
49
- type PlayStaticColumnProducerKindSnapshot = 'tool' | 'waterfall' | 'stepProgram' | 'playCall' | 'controlFlow' | 'transform';
50
- interface PlayStaticColumnProducerSnapshot {
65
+ type PlayStaticColumnProducerKind = 'tool' | 'waterfall' | 'stepProgram' | 'playCall' | 'controlFlow' | 'transform';
66
+ interface PlayStaticColumnProducer {
51
67
  id: string;
52
- kind: PlayStaticColumnProducerKindSnapshot;
68
+ kind: PlayStaticColumnProducerKind;
53
69
  field: string;
54
70
  toolId?: string;
55
71
  playId?: string;
72
+ dependsOnFields?: string[];
73
+ staleAfterSeconds?: number;
56
74
  conditional?: boolean;
57
- sourceRange?: PlayStaticSourceRangeSnapshot;
58
- steps?: PlayStaticColumnProducerSnapshot[];
59
- substep: PlayStaticSubstepSnapshot;
75
+ sourceRange?: PlayStaticSourceRange;
76
+ steps?: PlayStaticColumnProducer[];
77
+ substep: PlayStaticSubstep;
60
78
  }
61
- interface PlayStaticDatasetColumnSnapshot {
79
+ interface PlayStaticDatasetColumn {
62
80
  id: string;
63
- source: PlaySheetColumnSourceSnapshot;
81
+ source: PlaySheetColumnSource;
64
82
  sqlName?: string;
65
- producers: PlayStaticColumnProducerSnapshot[];
83
+ staleAfterSeconds?: number;
84
+ producers: PlayStaticColumnProducer[];
66
85
  }
67
- interface PlayStaticSourceRangeSnapshot {
86
+ interface PlayStaticSourceRange {
68
87
  sourcePath?: string;
69
88
  startLine: number;
70
89
  endLine: number;
71
90
  startColumn: number;
72
91
  endColumn: number;
73
92
  }
74
- type PlayStaticSubstepMetadataSnapshot = {
93
+ type PlayStaticSubstepMetadata = {
75
94
  conditional?: boolean;
95
+ dependsOnFields?: string[];
96
+ staleAfterSeconds?: number;
76
97
  };
77
- type PlayStaticSubstepSnapshot = PlayStaticSubstepMetadataSnapshot & ({
98
+ type PlayStaticSubstep = PlayStaticSubstepMetadata & ({
78
99
  type: 'csv';
79
100
  field: string;
80
101
  path?: string;
81
102
  description?: string;
82
- sourceRange?: PlayStaticSourceRangeSnapshot;
103
+ sourceRange?: PlayStaticSourceRange;
83
104
  callDepth?: number;
84
105
  callPath?: string[];
85
106
  } | {
@@ -90,22 +111,24 @@ type PlayStaticSubstepSnapshot = PlayStaticSubstepMetadataSnapshot & ({
90
111
  inputFields?: string[];
91
112
  rowKeyFields?: string[];
92
113
  outputFields?: string[];
93
- columns?: PlayStaticDatasetColumnSnapshot[];
114
+ columns?: PlayStaticDatasetColumn[];
94
115
  waterfallIds?: string[];
95
- steps?: PlayStaticSubstepSnapshot[];
96
- sheetContract?: PlaySheetContractSnapshot | null;
116
+ steps?: PlayStaticSubstep[];
117
+ sheetContract?: PlaySheetContract | null;
97
118
  description?: string;
98
- sourceRange?: PlayStaticSourceRangeSnapshot;
119
+ sourceRange?: PlayStaticSourceRange;
99
120
  callDepth?: number;
100
121
  callPath?: string[];
101
122
  } | {
102
123
  type: 'tool';
103
124
  toolId: string;
104
125
  field: string;
126
+ paramsSource?: string;
127
+ sourceText?: string;
105
128
  description?: string;
106
129
  inLoop?: boolean;
107
130
  isEventWait?: boolean;
108
- sourceRange?: PlayStaticSourceRangeSnapshot;
131
+ sourceRange?: PlayStaticSourceRange;
109
132
  callDepth?: number;
110
133
  callPath?: string[];
111
134
  } | {
@@ -124,16 +147,16 @@ type PlayStaticSubstepSnapshot = PlayStaticSubstepMetadataSnapshot & ({
124
147
  paramsSource?: string;
125
148
  }>;
126
149
  description?: string;
127
- sourceRange?: PlayStaticSourceRangeSnapshot;
150
+ sourceRange?: PlayStaticSourceRange;
128
151
  callDepth?: number;
129
152
  callPath?: string[];
130
153
  } | {
131
154
  type: 'step_suite';
132
155
  field: string;
133
- steps: PlayStaticSubstepSnapshot[];
156
+ steps: PlayStaticSubstep[];
134
157
  returnSource?: string;
135
158
  description?: string;
136
- sourceRange?: PlayStaticSourceRangeSnapshot;
159
+ sourceRange?: PlayStaticSourceRange;
137
160
  callDepth?: number;
138
161
  callPath?: string[];
139
162
  } | {
@@ -141,37 +164,40 @@ type PlayStaticSubstepSnapshot = PlayStaticSubstepMetadataSnapshot & ({
141
164
  playId: string;
142
165
  field: string;
143
166
  inLoop?: boolean;
144
- pipeline?: PlayStaticPipelineSnapshot | null;
167
+ pipeline?: PlayStaticPipeline | null;
145
168
  cycleDetected?: boolean;
146
169
  resolutionError?: string;
147
170
  description?: string;
148
- sourceRange?: PlayStaticSourceRangeSnapshot;
171
+ sourceRange?: PlayStaticSourceRange;
149
172
  callDepth?: number;
150
173
  callPath?: string[];
151
174
  } | {
152
175
  type: 'control_flow';
153
176
  kind: 'conditional' | 'loop';
154
177
  field: string;
155
- steps: PlayStaticSubstepSnapshot[];
178
+ steps: PlayStaticSubstep[];
156
179
  description?: string;
157
- sourceRange?: PlayStaticSourceRangeSnapshot;
180
+ sourceRange?: PlayStaticSourceRange;
158
181
  callDepth?: number;
159
182
  callPath?: string[];
160
183
  } | {
161
184
  type: 'run_javascript';
162
185
  alias: string;
186
+ sourceText?: string;
163
187
  description?: string;
164
- sourceRange?: PlayStaticSourceRangeSnapshot;
188
+ sourceRange?: PlayStaticSourceRange;
165
189
  callDepth?: number;
166
190
  callPath?: string[];
167
191
  } | {
168
192
  type: 'code';
169
193
  field: string;
194
+ sourceText?: string;
170
195
  description?: string;
171
- sourceRange?: PlayStaticSourceRangeSnapshot;
196
+ sourceRange?: PlayStaticSourceRange;
172
197
  callDepth?: number;
173
198
  callPath?: string[];
174
199
  });
200
+
175
201
  type PlayCompilerDependencyManifest = {
176
202
  playName: string;
177
203
  /** Original source path for direct imported definePlay dependencies. */
@@ -179,7 +205,7 @@ type PlayCompilerDependencyManifest = {
179
205
  sourceHash: string;
180
206
  graphHash: string;
181
207
  artifactHash: string;
182
- staticPipeline: PlayStaticPipelineSnapshot;
208
+ staticPipeline: PlayStaticPipeline;
183
209
  };
184
210
  type PlayCompilerManifest = {
185
211
  compilerVersion: number;
@@ -188,7 +214,7 @@ type PlayCompilerManifest = {
188
214
  graphHash: string;
189
215
  artifactHash: string;
190
216
  artifactKind?: PlayArtifactKind;
191
- staticPipeline: PlayStaticPipelineSnapshot;
217
+ staticPipeline: PlayStaticPipeline;
192
218
  importedPlayDependencies: PlayCompilerDependencyManifest[];
193
219
  };
194
220
 
package/dist/index.js CHANGED
@@ -246,10 +246,10 @@ var import_node_path2 = require("path");
246
246
 
247
247
  // src/release.ts
248
248
  var SDK_RELEASE = {
249
- version: "0.1.88",
249
+ version: "0.1.89",
250
250
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
251
251
  supportPolicy: {
252
- latest: "0.1.88",
252
+ latest: "0.1.89",
253
253
  minimumSupported: "0.1.53",
254
254
  deprecatedBelow: "0.1.53"
255
255
  }
package/dist/index.mjs CHANGED
@@ -179,10 +179,10 @@ import { join as join2 } from "path";
179
179
 
180
180
  // src/release.ts
181
181
  var SDK_RELEASE = {
182
- version: "0.1.88",
182
+ version: "0.1.89",
183
183
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
184
184
  supportPolicy: {
185
- latest: "0.1.88",
185
+ latest: "0.1.89",
186
186
  minimumSupported: "0.1.53",
187
187
  deprecatedBelow: "0.1.53"
188
188
  }
@@ -62,7 +62,7 @@ import {
62
62
  } from './child-play-await';
63
63
  import type { AnyBatchOperationStrategy } from '../../../shared_libs/play-runtime/batching-types';
64
64
  import {
65
- adaptV2ExecuteResponseToToolResult,
65
+ parseToolExecuteResponse,
66
66
  createToolExecuteResult,
67
67
  deserializeToolExecuteResult,
68
68
  isSerializedToolExecuteResult,
@@ -1289,18 +1289,12 @@ async function callToolDirect(
1289
1289
  });
1290
1290
  if (res.ok) {
1291
1291
  const body = (await res.json()) as Record<string, unknown>;
1292
- const { result } = adaptV2ExecuteResponseToToolResult(body);
1293
- const status =
1294
- typeof body.status === 'string'
1295
- ? body.status
1296
- : result == null
1297
- ? 'no_result'
1298
- : 'completed';
1292
+ const parsed = parseToolExecuteResponse(toolId, body);
1299
1293
  return wrapWorkerToolResult(
1300
1294
  toolId,
1301
- result,
1302
- parseExecuteToolMetadata(toolId, body),
1303
- status,
1295
+ parsed.result,
1296
+ parsed.metadata,
1297
+ parsed.status,
1304
1298
  );
1305
1299
  }
1306
1300
 
@@ -1338,121 +1332,6 @@ async function callToolDirect(
1338
1332
  throw lastError ?? new Error(`tool ${toolId} failed before execution.`);
1339
1333
  }
1340
1334
 
1341
- function parseExecuteToolMetadata(
1342
- toolId: string,
1343
- data: Record<string, unknown>,
1344
- ): ToolResultMetadataInput | null {
1345
- const metadata = data._metadata;
1346
- if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {
1347
- return null;
1348
- }
1349
- const tool = (metadata as Record<string, unknown>).tool;
1350
- if (!tool || typeof tool !== 'object' || Array.isArray(tool)) return null;
1351
- const record = tool as Record<string, unknown>;
1352
- const metadataToolId =
1353
- typeof record.toolId === 'string' && record.toolId.trim()
1354
- ? record.toolId.trim()
1355
- : toolId;
1356
- return {
1357
- toolId: metadataToolId,
1358
- extractors: parseExtractorMetadata(record.extractors),
1359
- targetGetters: parseGetterMetadata(record.targetGetters),
1360
- listExtractorPaths: parseStringArray(record.listExtractorPaths),
1361
- listIdentityGetters: parseGetterMetadata(record.listIdentityGetters),
1362
- };
1363
- }
1364
-
1365
- function parseExtractorMetadata(
1366
- value: unknown,
1367
- ): ToolResultMetadataInput['extractors'] {
1368
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
1369
- return undefined;
1370
- }
1371
- const entries = Object.entries(value as Record<string, unknown>).flatMap(
1372
- ([key, descriptor]) => {
1373
- if (
1374
- !descriptor ||
1375
- typeof descriptor !== 'object' ||
1376
- Array.isArray(descriptor)
1377
- ) {
1378
- return [];
1379
- }
1380
- const record = descriptor as Record<string, unknown>;
1381
- const paths = parseStringArray(record.paths);
1382
- if (paths.length === 0) return [];
1383
- const transforms = parseStringArray(record.transforms);
1384
- const enumValues = parseStringArray(record.enum);
1385
- const overrides = parseExtractorOverrides(record.overrides);
1386
- return [
1387
- [
1388
- key,
1389
- {
1390
- paths,
1391
- ...(transforms.length > 0 ? { transforms } : {}),
1392
- ...(enumValues.length > 0 ? { enum: enumValues } : {}),
1393
- ...(overrides.length > 0 ? { overrides } : {}),
1394
- },
1395
- ],
1396
- ] as const;
1397
- },
1398
- );
1399
- return entries.length > 0 ? Object.fromEntries(entries) : undefined;
1400
- }
1401
-
1402
- function parseExtractorOverrides(
1403
- value: unknown,
1404
- ): NonNullable<
1405
- NonNullable<ToolResultMetadataInput['extractors']>[string]['overrides']
1406
- > {
1407
- if (!Array.isArray(value)) return [];
1408
- return value.flatMap((entry) => {
1409
- if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
1410
- return [];
1411
- }
1412
- const record = entry as Record<string, unknown>;
1413
- const paths = parseStringArray(record.paths);
1414
- if (paths.length === 0) return [];
1415
- const equals =
1416
- record.equals === null ||
1417
- typeof record.equals === 'string' ||
1418
- typeof record.equals === 'number' ||
1419
- typeof record.equals === 'boolean'
1420
- ? record.equals
1421
- : true;
1422
- const overrideValue = record.value;
1423
- if (
1424
- overrideValue !== null &&
1425
- typeof overrideValue !== 'string' &&
1426
- typeof overrideValue !== 'number' &&
1427
- typeof overrideValue !== 'boolean'
1428
- ) {
1429
- return [];
1430
- }
1431
- return [{ paths, equals, value: overrideValue }];
1432
- });
1433
- }
1434
-
1435
- function parseGetterMetadata(
1436
- value: unknown,
1437
- ): Record<string, readonly string[]> | undefined {
1438
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
1439
- return undefined;
1440
- }
1441
- const entries = Object.entries(value as Record<string, unknown>)
1442
- .map(([key, paths]) => [key, parseStringArray(paths)] as const)
1443
- .filter(
1444
- (entry): entry is readonly [string, string[]] => entry[1].length > 0,
1445
- );
1446
- return entries.length > 0 ? Object.fromEntries(entries) : undefined;
1447
- }
1448
-
1449
- function parseStringArray(value: unknown): string[] {
1450
- if (!Array.isArray(value)) return [];
1451
- return value
1452
- .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
1453
- .filter(Boolean);
1454
- }
1455
-
1456
1335
  function toolMetadataFallback(toolId: string): ToolResultMetadataInput {
1457
1336
  if (toolId === 'test_rate_limit') {
1458
1337
  return {
@@ -2956,42 +2835,71 @@ function augmentSheetContractWithDatasetFields(input: {
2956
2835
  outputFields?: readonly string[];
2957
2836
  }): PlaySheetContract {
2958
2837
  const outputFields = new Set(input.outputFields ?? []);
2959
- const existingFields = new Set(
2960
- input.contract.columns.flatMap((column) =>
2961
- typeof column.field === 'string' ? [column.field] : [],
2962
- ),
2963
- );
2964
- const existingSqlNames = new Set(
2965
- input.contract.columns.map((column) => column.sqlName),
2966
- );
2967
- const columns = [...input.contract.columns];
2968
2838
  const candidateFields = new Set<string>();
2969
2839
  for (const row of input.rows) {
2970
2840
  for (const field of Object.keys(row)) {
2971
- candidateFields.add(field);
2841
+ if (isDatasetPayloadField(field)) {
2842
+ candidateFields.add(field);
2843
+ }
2972
2844
  }
2973
2845
  }
2974
2846
  for (const field of outputFields) {
2975
- candidateFields.add(field);
2976
- }
2977
- for (const field of candidateFields) {
2978
- if (!isDatasetPayloadField(field) || existingFields.has(field)) {
2979
- continue;
2847
+ if (isDatasetPayloadField(field)) {
2848
+ candidateFields.add(field);
2980
2849
  }
2981
- const sqlName = sqlSafePlayColumnName(field);
2982
- if (existingSqlNames.has(sqlName)) {
2983
- continue;
2850
+ }
2851
+
2852
+ const existingFields = new Set<string>();
2853
+ const existingSqlNames = new Set<string>();
2854
+ const inputColumns: PlaySheetContract['columns'] = [];
2855
+ const outputColumns: PlaySheetContract['columns'] = [];
2856
+ const appendColumn = (
2857
+ target: PlaySheetContract['columns'],
2858
+ column: PlaySheetContract['columns'][number],
2859
+ ) => {
2860
+ const field = typeof column.field === 'string' ? column.field : column.id;
2861
+ const sqlName = column.sqlName.trim();
2862
+ if (
2863
+ !field ||
2864
+ !sqlName ||
2865
+ existingFields.has(field) ||
2866
+ existingSqlNames.has(sqlName)
2867
+ ) {
2868
+ return;
2984
2869
  }
2985
2870
  existingFields.add(field);
2986
2871
  existingSqlNames.add(sqlName);
2987
- columns.push({
2872
+ target.push(column);
2873
+ };
2874
+
2875
+ for (const column of input.contract.columns) {
2876
+ const field = typeof column.field === 'string' ? column.field : column.id;
2877
+ if (
2878
+ column.source === 'input' &&
2879
+ field === input.contract.tableNamespace &&
2880
+ !candidateFields.has(field)
2881
+ ) {
2882
+ continue;
2883
+ }
2884
+ appendColumn(
2885
+ column.source === 'input' ? inputColumns : outputColumns,
2886
+ column,
2887
+ );
2888
+ }
2889
+
2890
+ for (const field of candidateFields) {
2891
+ if (existingFields.has(field)) continue;
2892
+ const sqlName = sqlSafePlayColumnName(field);
2893
+ if (existingSqlNames.has(sqlName)) continue;
2894
+ appendColumn(outputFields.has(field) ? outputColumns : inputColumns, {
2988
2895
  id: `runtime:${input.contract.tableNamespace}:${field}`,
2989
2896
  sqlName,
2990
2897
  source: outputFields.has(field) ? 'datasetColumn' : 'input',
2991
2898
  field,
2992
2899
  });
2993
2900
  }
2994
- return { ...input.contract, columns };
2901
+
2902
+ return { ...input.contract, columns: [...inputColumns, ...outputColumns] };
2995
2903
  }
2996
2904
 
2997
2905
  async function persistCompletedMapRows(input: {
@@ -50,10 +50,10 @@ export type SdkRelease = {
50
50
  };
51
51
 
52
52
  export const SDK_RELEASE = {
53
- version: '0.1.88',
53
+ version: '0.1.89',
54
54
  apiContract: '2026-06-dataset-column-cell-stale-hard-cutover',
55
55
  supportPolicy: {
56
- latest: '0.1.88',
56
+ latest: '0.1.89',
57
57
  minimumSupported: '0.1.53',
58
58
  deprecatedBelow: '0.1.53',
59
59
  },
@@ -17,7 +17,10 @@ export type {
17
17
  EmailStatusVerdict,
18
18
  } from './email-status';
19
19
 
20
- import { buildEmailStatus } from './email-status';
20
+ import {
21
+ buildEmailStatus,
22
+ type EmailStatusExtractorConfig,
23
+ } from './email-status';
21
24
  import {
22
25
  JOB_CHANGE_STATUS_VALUES,
23
26
  type JobChangeGetterValue,
@@ -56,12 +59,12 @@ function isRecord(value: unknown): value is Record<string, unknown> {
56
59
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
57
60
  }
58
61
 
59
- export type V2ToolExecuteOutput = {
62
+ type V2ToolExecuteOutput = {
60
63
  raw: unknown;
61
64
  meta?: Record<string, unknown>;
62
65
  };
63
66
 
64
- export function parseV2ToolExecuteOutput(
67
+ function parseV2ToolExecuteOutput(
65
68
  data: Record<string, unknown>,
66
69
  ): V2ToolExecuteOutput | null {
67
70
  const toolResponse = data.toolResponse;
@@ -77,9 +80,10 @@ export function parseV2ToolExecuteOutput(
77
80
  return null;
78
81
  }
79
82
 
80
- export function adaptV2ExecuteResponseToToolResult(
81
- data: Record<string, unknown>,
82
- ): { output?: V2ToolExecuteOutput; result: unknown } {
83
+ function adaptV2ExecuteResponseToToolResult(data: Record<string, unknown>): {
84
+ output?: V2ToolExecuteOutput;
85
+ result: unknown;
86
+ } {
83
87
  const output = parseV2ToolExecuteOutput(data);
84
88
  if (!output) {
85
89
  return { result: data.result ?? data };
@@ -93,7 +97,195 @@ export function adaptV2ExecuteResponseToToolResult(
93
97
  };
94
98
  }
95
99
 
96
- export function toV2RawToolOutputPath(path: string): string {
100
+ /**
101
+ * Parsed view of a raw `/api/v2/integrations/:toolId/execute` JSON body.
102
+ *
103
+ * This is the single runtime-side seam from a V2 execute response to the
104
+ * inputs of {@link createToolExecuteResult}. Both runner substrates (the
105
+ * in-process cjs runtime in `shared_libs/play-runtime/context.ts` and the
106
+ * Workers runtime in `apps/play-runner-workers`) must go through
107
+ * {@link parseToolExecuteResponse}; the intermediate envelope shapes
108
+ * (`toolResponse` adaptation, `_metadata.tool` parsing, status derivation)
109
+ * are private to this module.
110
+ *
111
+ * Boundary note: provider-specific redaction (wiza/apify/bettercontact billing
112
+ * fields) happens server-side in
113
+ * `src/lib/integrations/execute-result-normalization.ts` BEFORE the response
114
+ * leaves the API. This module only ever sees the already-redacted public
115
+ * response — do not move redaction here, it would leak provider internals
116
+ * into the shared runtime bundles.
117
+ */
118
+ export type ParsedToolExecuteResponse = {
119
+ status: string;
120
+ jobId?: string;
121
+ meta?: Record<string, unknown>;
122
+ toolResponse?: {
123
+ raw?: unknown;
124
+ meta?: Record<string, unknown>;
125
+ };
126
+ /** Legacy `{ data, meta }` envelope consumed by createToolExecuteResult. */
127
+ result: unknown;
128
+ /** Tool extractor metadata parsed from `_metadata.tool`, if present. */
129
+ metadata: ToolResultMetadataInput | null;
130
+ };
131
+
132
+ export function parseToolExecuteResponse(
133
+ toolId: string,
134
+ body: Record<string, unknown>,
135
+ ): ParsedToolExecuteResponse {
136
+ const { result } = adaptV2ExecuteResponseToToolResult(body);
137
+ const status =
138
+ typeof body.status === 'string'
139
+ ? body.status
140
+ : result == null
141
+ ? 'no_result'
142
+ : 'completed';
143
+ return {
144
+ status,
145
+ jobId: typeof body.job_id === 'string' ? body.job_id : undefined,
146
+ meta: isRecord(body.meta) ? body.meta : undefined,
147
+ toolResponse: isRecord(body.toolResponse)
148
+ ? (body.toolResponse as ParsedToolExecuteResponse['toolResponse'])
149
+ : undefined,
150
+ result,
151
+ metadata: parseExecuteToolMetadata(toolId, body),
152
+ };
153
+ }
154
+
155
+ function parseExecuteToolMetadata(
156
+ toolId: string,
157
+ data: Record<string, unknown>,
158
+ ): ToolResultMetadataInput | null {
159
+ const metadata = data._metadata;
160
+ if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {
161
+ return null;
162
+ }
163
+ const tool = (metadata as Record<string, unknown>).tool;
164
+ if (!tool || typeof tool !== 'object' || Array.isArray(tool)) return null;
165
+ const record = tool as Record<string, unknown>;
166
+ const metadataToolId =
167
+ typeof record.toolId === 'string' && record.toolId.trim()
168
+ ? record.toolId
169
+ : toolId;
170
+ const readGetters = (value: unknown): Record<string, readonly string[]> => {
171
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
172
+ return Object.fromEntries(
173
+ Object.entries(value as Record<string, unknown>).flatMap(
174
+ ([key, paths]) => {
175
+ if (!Array.isArray(paths)) return [];
176
+ const normalized = paths.filter(
177
+ (path): path is string =>
178
+ typeof path === 'string' && path.trim().length > 0,
179
+ );
180
+ return normalized.length > 0 ? [[key, normalized]] : [];
181
+ },
182
+ ),
183
+ );
184
+ };
185
+ const readExtractors = (
186
+ value: unknown,
187
+ ): ToolResultMetadataInput['extractors'] => {
188
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
189
+ return Object.fromEntries(
190
+ Object.entries(value as Record<string, unknown>).flatMap(
191
+ ([key, entry]) => {
192
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
193
+ return [];
194
+ const recordEntry = entry as Record<string, unknown>;
195
+ if (!Array.isArray(recordEntry.paths)) return [];
196
+ const paths = recordEntry.paths.filter(
197
+ (path): path is string =>
198
+ typeof path === 'string' && path.trim().length > 0,
199
+ );
200
+ if (paths.length === 0) return [];
201
+ const overrides = Array.isArray(recordEntry.overrides)
202
+ ? recordEntry.overrides.flatMap((override) => {
203
+ if (
204
+ !override ||
205
+ typeof override !== 'object' ||
206
+ Array.isArray(override)
207
+ ) {
208
+ return [];
209
+ }
210
+ const overrideRecord = override as Record<string, unknown>;
211
+ const overridePaths = Array.isArray(overrideRecord.paths)
212
+ ? overrideRecord.paths.filter(
213
+ (path): path is string =>
214
+ typeof path === 'string' && path.trim().length > 0,
215
+ )
216
+ : [];
217
+ if (overridePaths.length === 0) return [];
218
+ const readOverridePrimitive = (
219
+ candidate: unknown,
220
+ ): string | number | boolean | null | undefined => {
221
+ if (candidate === null) return null;
222
+ if (typeof candidate === 'string') return candidate;
223
+ if (typeof candidate === 'number') return candidate;
224
+ if (typeof candidate === 'boolean') return candidate;
225
+ return undefined;
226
+ };
227
+ const value = readOverridePrimitive(overrideRecord.value);
228
+ if (value === undefined) {
229
+ return [];
230
+ }
231
+ const equals: string | number | boolean | null =
232
+ readOverridePrimitive(overrideRecord.equals) ?? true;
233
+ return [{ paths: overridePaths, equals, value }];
234
+ })
235
+ : [];
236
+ const emailStatus =
237
+ recordEntry.emailStatus &&
238
+ typeof recordEntry.emailStatus === 'object' &&
239
+ !Array.isArray(recordEntry.emailStatus)
240
+ ? (recordEntry.emailStatus as EmailStatusExtractorConfig)
241
+ : undefined;
242
+ return [
243
+ [
244
+ key,
245
+ {
246
+ paths,
247
+ ...(Array.isArray(recordEntry.transforms)
248
+ ? {
249
+ transforms: recordEntry.transforms.filter(
250
+ (transform): transform is string =>
251
+ typeof transform === 'string' &&
252
+ transform.trim().length > 0,
253
+ ),
254
+ }
255
+ : {}),
256
+ ...(Array.isArray(recordEntry.enum)
257
+ ? {
258
+ enum: recordEntry.enum.filter(
259
+ (value): value is string =>
260
+ typeof value === 'string' && value.trim().length > 0,
261
+ ),
262
+ }
263
+ : {}),
264
+ ...(overrides.length > 0 ? { overrides } : {}),
265
+ ...(emailStatus ? { emailStatus } : {}),
266
+ },
267
+ ],
268
+ ];
269
+ },
270
+ ),
271
+ );
272
+ };
273
+ const listExtractorPaths = Array.isArray(record.listExtractorPaths)
274
+ ? record.listExtractorPaths.filter(
275
+ (path): path is string =>
276
+ typeof path === 'string' && path.trim().length > 0,
277
+ )
278
+ : [];
279
+ return {
280
+ toolId: metadataToolId,
281
+ extractors: readExtractors(record.extractors),
282
+ targetGetters: readGetters(record.targetGetters),
283
+ listExtractorPaths,
284
+ listIdentityGetters: readGetters(record.listIdentityGetters),
285
+ };
286
+ }
287
+
288
+ function toV2RawToolOutputPath(path: string): string {
97
289
  const normalized = String(path || '')
98
290
  .trim()
99
291
  .replace(/^\./, '');
@@ -445,9 +637,7 @@ function normalizeJobChangeStatus(value: unknown): unknown {
445
637
  if (['false', 'no', 'same', 'no_change'].includes(normalized))
446
638
  return 'no_change';
447
639
  if (['left', 'left_company'].includes(normalized)) return 'left_company';
448
- if (
449
- (JOB_CHANGE_STATUS_VALUES as readonly string[]).includes(normalized)
450
- ) {
640
+ if ((JOB_CHANGE_STATUS_VALUES as readonly string[]).includes(normalized)) {
451
641
  return normalized;
452
642
  }
453
643
  return 'unknown';
@@ -481,12 +671,12 @@ function normalizeJobChange(value: unknown): JobChangeGetterValue {
481
671
  return {
482
672
  status,
483
673
  date: moved
484
- ? normalizeString(
674
+ ? (normalizeString(
485
675
  output.date ??
486
676
  output.job_change_date ??
487
677
  output.change_date ??
488
678
  output.changed_at,
489
- ) ?? firstExperienceDate(person.experiences)
679
+ ) ?? firstExperienceDate(person.experiences))
490
680
  : null,
491
681
  new_company: moved
492
682
  ? normalizeString(
@@ -169,19 +169,25 @@ export function normalizePlayName(value: string): string {
169
169
  return validateIdentifierPart(value, 'Play name', PLAY_NAME_MAX_LENGTH);
170
170
  }
171
171
 
172
+ /**
173
+ * Normalize a play name into the leading segment of a physical sheet table name.
174
+ *
175
+ * A qualified reference like "prebuilt/name-and-domain-to-email-waterfall" folds
176
+ * its namespace separator into a plain identifier
177
+ * ("prebuilt_name_and_domain_to_email_waterfall") so the physical table reads
178
+ * cleanly. `validatePlaySheetTableName` enforces the 63-char Postgres identifier
179
+ * limit on the play + namespace combination and fails loudly when a name is
180
+ * genuinely too long — no opaque content digest is mixed into the table name.
181
+ */
172
182
  export function normalizePlayNameForSheet(value: string): string {
173
183
  if (!value.includes('/')) {
174
184
  return normalizePlayName(value);
175
185
  }
176
- const digest = sha256Hex(value).slice(0, 12);
177
- const normalizedReference = sanitizeIdentifierPart(
178
- value.replace(/\//g, '__'),
186
+ return validateIdentifierPart(
187
+ value.replace(/\//g, '_'),
188
+ 'Play name',
189
+ PLAY_NAME_MAX_LENGTH,
179
190
  );
180
- const prefixLength = Math.max(1, PLAY_NAME_MAX_LENGTH - digest.length - 1);
181
- const prefix =
182
- normalizedReference.slice(0, prefixLength).replace(/_+$/g, '') ||
183
- 'qualified_play';
184
- return `${prefix}_${digest}`;
185
191
  }
186
192
 
187
193
  export function normalizeTableNamespace(value: string): string {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.88",
3
+ "version": "0.1.89",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {