deepline 0.1.61 → 0.1.63

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
@@ -220,10 +220,10 @@ function resolveConfig(options) {
220
220
 
221
221
  // src/release.ts
222
222
  var SDK_RELEASE = {
223
- version: "0.1.61",
223
+ version: "0.1.63",
224
224
  apiContract: "2026-05-play-bootstrap-dataset-summary",
225
225
  supportPolicy: {
226
- latest: "0.1.61",
226
+ latest: "0.1.63",
227
227
  minimumSupported: "0.1.53",
228
228
  deprecatedBelow: "0.1.53"
229
229
  }
@@ -197,10 +197,10 @@ function resolveConfig(options) {
197
197
 
198
198
  // src/release.ts
199
199
  var SDK_RELEASE = {
200
- version: "0.1.61",
200
+ version: "0.1.63",
201
201
  apiContract: "2026-05-play-bootstrap-dataset-summary",
202
202
  supportPolicy: {
203
- latest: "0.1.61",
203
+ latest: "0.1.63",
204
204
  minimumSupported: "0.1.53",
205
205
  deprecatedBelow: "0.1.53"
206
206
  }
package/dist/index.d.mts CHANGED
@@ -1881,6 +1881,12 @@ type ToolResultExtractorDescriptor = {
1881
1881
  paths: readonly string[];
1882
1882
  transforms?: readonly string[];
1883
1883
  enum?: readonly string[];
1884
+ overrides?: readonly ToolResultExtractorOverride[];
1885
+ };
1886
+ type ToolResultExtractorOverride = {
1887
+ paths: readonly string[];
1888
+ equals?: string | number | boolean | null;
1889
+ value: string | number | boolean | null;
1884
1890
  };
1885
1891
  type ToolResultTargetAccessor<T = unknown> = ToolResultTargetMetadata & {
1886
1892
  get(): T | null;
package/dist/index.d.ts CHANGED
@@ -1881,6 +1881,12 @@ type ToolResultExtractorDescriptor = {
1881
1881
  paths: readonly string[];
1882
1882
  transforms?: readonly string[];
1883
1883
  enum?: readonly string[];
1884
+ overrides?: readonly ToolResultExtractorOverride[];
1885
+ };
1886
+ type ToolResultExtractorOverride = {
1887
+ paths: readonly string[];
1888
+ equals?: string | number | boolean | null;
1889
+ value: string | number | boolean | null;
1884
1890
  };
1885
1891
  type ToolResultTargetAccessor<T = unknown> = ToolResultTargetMetadata & {
1886
1892
  get(): T | null;
package/dist/index.js CHANGED
@@ -232,10 +232,10 @@ function resolveConfig(options) {
232
232
 
233
233
  // src/release.ts
234
234
  var SDK_RELEASE = {
235
- version: "0.1.61",
235
+ version: "0.1.63",
236
236
  apiContract: "2026-05-play-bootstrap-dataset-summary",
237
237
  supportPolicy: {
238
- latest: "0.1.61",
238
+ latest: "0.1.63",
239
239
  minimumSupported: "0.1.53",
240
240
  deprecatedBelow: "0.1.53"
241
241
  }
@@ -2073,9 +2073,7 @@ function deriveListKeys(input) {
2073
2073
  return keys;
2074
2074
  }
2075
2075
  const listPrefix = input.listPath.replace(/\[\d+\]$/, "");
2076
- for (const [target, paths] of Object.entries(
2077
- input.targetGetters ?? {}
2078
- )) {
2076
+ for (const [target, paths] of Object.entries(input.targetGetters ?? {})) {
2079
2077
  for (const rawPath of paths) {
2080
2078
  const path = String(rawPath || "").trim().replace(/^\./, "");
2081
2079
  if (!path) continue;
@@ -2121,21 +2119,20 @@ function buildTargets(result, extractors, targetGetters) {
2121
2119
  for (const [target, descriptor] of Object.entries(extractors ?? {})) {
2122
2120
  const fromExtractor = findFirstTargetByPath(result, descriptor.paths);
2123
2121
  if (!fromExtractor) continue;
2122
+ const transformed = coerceToEnum(
2123
+ applyExtractorTransforms(fromExtractor.value, descriptor),
2124
+ descriptor
2125
+ );
2126
+ const override = findExtractorOverride(result, descriptor);
2124
2127
  targets[target] = {
2125
2128
  path: fromExtractor.path,
2126
- value: coerceToEnum(
2127
- applyExtractorTransforms(fromExtractor.value, descriptor),
2128
- descriptor
2129
- )
2129
+ value: override?.value ?? transformed
2130
2130
  };
2131
2131
  }
2132
2132
  const metadataTargets = new Set(Object.keys(targetGetters ?? {}));
2133
2133
  for (const target of metadataTargets) {
2134
2134
  if (targets[target]) continue;
2135
- const fromMetadata = findFirstTargetByPath(
2136
- result,
2137
- targetGetters?.[target]
2138
- );
2135
+ const fromMetadata = findFirstTargetByPath(result, targetGetters?.[target]);
2139
2136
  if (fromMetadata) {
2140
2137
  targets[target] = fromMetadata;
2141
2138
  continue;
@@ -2152,6 +2149,19 @@ function buildTargets(result, extractors, targetGetters) {
2152
2149
  }
2153
2150
  return targets;
2154
2151
  }
2152
+ function findExtractorOverride(result, descriptor) {
2153
+ for (const override of descriptor.overrides ?? []) {
2154
+ const expected = Object.prototype.hasOwnProperty.call(override, "equals") ? override.equals : true;
2155
+ for (const path of override.paths) {
2156
+ const match = findFirstTargetByPath(result, [path]);
2157
+ if (!match) continue;
2158
+ if (match.value === expected) {
2159
+ return { value: override.value };
2160
+ }
2161
+ }
2162
+ }
2163
+ return null;
2164
+ }
2155
2165
  function buildLists(resolved, metadata) {
2156
2166
  const lists = {};
2157
2167
  for (const [name, list] of Object.entries(resolved)) {
package/dist/index.mjs CHANGED
@@ -170,10 +170,10 @@ function resolveConfig(options) {
170
170
 
171
171
  // src/release.ts
172
172
  var SDK_RELEASE = {
173
- version: "0.1.61",
173
+ version: "0.1.63",
174
174
  apiContract: "2026-05-play-bootstrap-dataset-summary",
175
175
  supportPolicy: {
176
- latest: "0.1.61",
176
+ latest: "0.1.63",
177
177
  minimumSupported: "0.1.53",
178
178
  deprecatedBelow: "0.1.53"
179
179
  }
@@ -2011,9 +2011,7 @@ function deriveListKeys(input) {
2011
2011
  return keys;
2012
2012
  }
2013
2013
  const listPrefix = input.listPath.replace(/\[\d+\]$/, "");
2014
- for (const [target, paths] of Object.entries(
2015
- input.targetGetters ?? {}
2016
- )) {
2014
+ for (const [target, paths] of Object.entries(input.targetGetters ?? {})) {
2017
2015
  for (const rawPath of paths) {
2018
2016
  const path = String(rawPath || "").trim().replace(/^\./, "");
2019
2017
  if (!path) continue;
@@ -2059,21 +2057,20 @@ function buildTargets(result, extractors, targetGetters) {
2059
2057
  for (const [target, descriptor] of Object.entries(extractors ?? {})) {
2060
2058
  const fromExtractor = findFirstTargetByPath(result, descriptor.paths);
2061
2059
  if (!fromExtractor) continue;
2060
+ const transformed = coerceToEnum(
2061
+ applyExtractorTransforms(fromExtractor.value, descriptor),
2062
+ descriptor
2063
+ );
2064
+ const override = findExtractorOverride(result, descriptor);
2062
2065
  targets[target] = {
2063
2066
  path: fromExtractor.path,
2064
- value: coerceToEnum(
2065
- applyExtractorTransforms(fromExtractor.value, descriptor),
2066
- descriptor
2067
- )
2067
+ value: override?.value ?? transformed
2068
2068
  };
2069
2069
  }
2070
2070
  const metadataTargets = new Set(Object.keys(targetGetters ?? {}));
2071
2071
  for (const target of metadataTargets) {
2072
2072
  if (targets[target]) continue;
2073
- const fromMetadata = findFirstTargetByPath(
2074
- result,
2075
- targetGetters?.[target]
2076
- );
2073
+ const fromMetadata = findFirstTargetByPath(result, targetGetters?.[target]);
2077
2074
  if (fromMetadata) {
2078
2075
  targets[target] = fromMetadata;
2079
2076
  continue;
@@ -2090,6 +2087,19 @@ function buildTargets(result, extractors, targetGetters) {
2090
2087
  }
2091
2088
  return targets;
2092
2089
  }
2090
+ function findExtractorOverride(result, descriptor) {
2091
+ for (const override of descriptor.overrides ?? []) {
2092
+ const expected = Object.prototype.hasOwnProperty.call(override, "equals") ? override.equals : true;
2093
+ for (const path of override.paths) {
2094
+ const match = findFirstTargetByPath(result, [path]);
2095
+ if (!match) continue;
2096
+ if (match.value === expected) {
2097
+ return { value: override.value };
2098
+ }
2099
+ }
2100
+ }
2101
+ return null;
2102
+ }
2093
2103
  function buildLists(resolved, metadata) {
2094
2104
  const lists = {};
2095
2105
  for (const [name, list] of Object.entries(resolved)) {
@@ -117,6 +117,7 @@ import {
117
117
  harnessStartSheetDataset,
118
118
  setHarnessBinding,
119
119
  } from '../../../sdk/src/plays/harness-stub';
120
+ import { createHarnessWorkerReceiptStore } from './runtime/harness-receipt-store';
120
121
  import {
121
122
  applyCsvRenameProjection,
122
123
  stripCsvProjectedFields,
@@ -397,7 +398,7 @@ async function fetchRuntimeApi(
397
398
  ? RUNTIME_API_PLAY_RUN_TIMEOUT_MS
398
399
  : /^\/api\/v2\/integrations\/[^/]+\/execute$/.test(path)
399
400
  ? RUNTIME_API_INTEGRATION_EXECUTE_TIMEOUT_MS
400
- : RUNTIME_API_TIMEOUT_MS;
401
+ : RUNTIME_API_TIMEOUT_MS;
401
402
  const controller = new AbortController();
402
403
  let timeout: ReturnType<typeof setTimeout> | null = null;
403
404
  const timeoutPromise = new Promise<never>((_, reject) => {
@@ -619,10 +620,7 @@ function publicCsvOutputRow<T extends Record<string, unknown>>(row: T): T {
619
620
  const stripped = stripCsvProjectionMetadata(row) as Record<string, unknown>;
620
621
  const publicRow: Record<string, unknown> = {};
621
622
  for (const fieldName of Reflect.ownKeys(stripped)) {
622
- if (
623
- typeof fieldName === 'string' &&
624
- fieldName.startsWith('__deepline')
625
- ) {
623
+ if (typeof fieldName === 'string' && fieldName.startsWith('__deepline')) {
626
624
  continue;
627
625
  }
628
626
  const descriptor = Object.getOwnPropertyDescriptor(stripped, fieldName);
@@ -1332,6 +1330,7 @@ function parseExtractorMetadata(
1332
1330
  if (paths.length === 0) return [];
1333
1331
  const transforms = parseStringArray(record.transforms);
1334
1332
  const enumValues = parseStringArray(record.enum);
1333
+ const overrides = parseExtractorOverrides(record.overrides);
1335
1334
  return [
1336
1335
  [
1337
1336
  key,
@@ -1339,6 +1338,7 @@ function parseExtractorMetadata(
1339
1338
  paths,
1340
1339
  ...(transforms.length > 0 ? { transforms } : {}),
1341
1340
  ...(enumValues.length > 0 ? { enum: enumValues } : {}),
1341
+ ...(overrides.length > 0 ? { overrides } : {}),
1342
1342
  },
1343
1343
  ],
1344
1344
  ] as const;
@@ -1347,6 +1347,39 @@ function parseExtractorMetadata(
1347
1347
  return entries.length > 0 ? Object.fromEntries(entries) : undefined;
1348
1348
  }
1349
1349
 
1350
+ function parseExtractorOverrides(
1351
+ value: unknown,
1352
+ ): NonNullable<
1353
+ NonNullable<ToolResultMetadataInput['extractors']>[string]['overrides']
1354
+ > {
1355
+ if (!Array.isArray(value)) return [];
1356
+ return value.flatMap((entry) => {
1357
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
1358
+ return [];
1359
+ }
1360
+ const record = entry as Record<string, unknown>;
1361
+ const paths = parseStringArray(record.paths);
1362
+ if (paths.length === 0) return [];
1363
+ const equals =
1364
+ record.equals === null ||
1365
+ typeof record.equals === 'string' ||
1366
+ typeof record.equals === 'number' ||
1367
+ typeof record.equals === 'boolean'
1368
+ ? record.equals
1369
+ : true;
1370
+ const overrideValue = record.value;
1371
+ if (
1372
+ overrideValue !== null &&
1373
+ typeof overrideValue !== 'string' &&
1374
+ typeof overrideValue !== 'number' &&
1375
+ typeof overrideValue !== 'boolean'
1376
+ ) {
1377
+ return [];
1378
+ }
1379
+ return [{ paths, equals, value: overrideValue }];
1380
+ });
1381
+ }
1382
+
1350
1383
  function parseGetterMetadata(
1351
1384
  value: unknown,
1352
1385
  ): Record<string, readonly string[]> | undefined {
@@ -1372,8 +1405,23 @@ function syntheticToolMetadata(toolId: string): ToolResultMetadataInput {
1372
1405
  if (toolId === 'test_rate_limit') {
1373
1406
  return {
1374
1407
  toolId,
1408
+ extractors: {
1409
+ email_status: {
1410
+ paths: ['email_status'],
1411
+ transforms: ['emailStatus'],
1412
+ enum: ['valid', 'invalid', 'catch_all', 'unknown'],
1413
+ overrides: [
1414
+ {
1415
+ paths: ['mx_security_gateway'],
1416
+ equals: true,
1417
+ value: 'catch_all',
1418
+ },
1419
+ ],
1420
+ },
1421
+ },
1375
1422
  targetGetters: {
1376
1423
  email: ['email', 'value'],
1424
+ email_status: ['email_status'],
1377
1425
  },
1378
1426
  };
1379
1427
  }
@@ -1541,6 +1589,10 @@ function executeSyntheticTestRateLimit(
1541
1589
  : 'match'));
1542
1590
  const matched = syntheticMatchWindow(input, rowNumber);
1543
1591
  const matchedEmail = matched ? `${matchedPrefix}@${matchedDomain}` : null;
1592
+ const securityGateway =
1593
+ input.emit_security_gateway === true
1594
+ ? { email_status: 'valid', mx_security_gateway: true }
1595
+ : {};
1544
1596
  return {
1545
1597
  status: 'completed',
1546
1598
  key: String(input.key || ''),
@@ -1551,6 +1603,7 @@ function executeSyntheticTestRateLimit(
1551
1603
  email: matchedEmail,
1552
1604
  value: matchedEmail,
1553
1605
  batch: false,
1606
+ ...securityGateway,
1554
1607
  };
1555
1608
  }
1556
1609
 
@@ -3180,6 +3233,9 @@ function createMinimalWorkerCtx(
3180
3233
  };
3181
3234
  const rootGovernance = req.playCallGovernance;
3182
3235
  const rootRunId = rootGovernance?.rootRunId ?? req.runId;
3236
+ const receiptStore = env.HARNESS
3237
+ ? createHarnessWorkerReceiptStore({ executorToken: req.executorToken })
3238
+ : undefined;
3183
3239
  const executeWithRuntimeReceipt = async <T>(
3184
3240
  key: string,
3185
3241
  execute: () => Promise<T> | T,
@@ -3193,6 +3249,7 @@ function createMinimalWorkerCtx(
3193
3249
  runId: req.runId,
3194
3250
  key,
3195
3251
  postRuntimeApi,
3252
+ receiptStore,
3196
3253
  execute: async () => serializeDurableStepValue(await execute()),
3197
3254
  repairRunningReceiptForSameRun,
3198
3255
  });
@@ -4301,15 +4358,15 @@ function createMinimalWorkerCtx(
4301
4358
  req,
4302
4359
  allowInline:
4303
4360
  options?.timeoutMs == null && !childNeedsWorkflowScheduler,
4304
- body: {
4305
- name: resolvedName,
4306
- input: isRecord(input) ? input : {},
4307
- orgId: req.orgId,
4308
- callbackBaseUrl: req.callbackUrl,
4309
- baseUrl: req.baseUrl,
4310
- parentExecutorToken: req.executorToken,
4311
- userEmail: req.userEmail ?? '',
4312
- profile: 'workers_edge',
4361
+ body: {
4362
+ name: resolvedName,
4363
+ input: isRecord(input) ? input : {},
4364
+ orgId: req.orgId,
4365
+ callbackBaseUrl: req.callbackUrl,
4366
+ baseUrl: req.baseUrl,
4367
+ parentExecutorToken: req.executorToken,
4368
+ userEmail: req.userEmail ?? '',
4369
+ profile: 'workers_edge',
4313
4370
  manifest: childManifest,
4314
4371
  childPlayManifests: req.childPlayManifests ?? null,
4315
4372
  internalRunPlay: {
@@ -0,0 +1,31 @@
1
+ import {
2
+ harnessClaimRuntimeReceipt,
3
+ harnessCompleteRuntimeReceipt,
4
+ harnessFailRuntimeReceipt,
5
+ } from '../../../../sdk/src/plays/harness-stub';
6
+ import type { WorkerRuntimeReceiptStore } from './receipts';
7
+
8
+ export function createHarnessWorkerReceiptStore(input: {
9
+ executorToken: string;
10
+ }): WorkerRuntimeReceiptStore {
11
+ return {
12
+ claimReceipt(command) {
13
+ return harnessClaimRuntimeReceipt({
14
+ executorToken: input.executorToken,
15
+ ...command,
16
+ });
17
+ },
18
+ completeReceipt(command) {
19
+ return harnessCompleteRuntimeReceipt({
20
+ executorToken: input.executorToken,
21
+ ...command,
22
+ });
23
+ },
24
+ failReceipt(command) {
25
+ return harnessFailRuntimeReceipt({
26
+ executorToken: input.executorToken,
27
+ ...command,
28
+ });
29
+ },
30
+ };
31
+ }
@@ -1,17 +1,14 @@
1
- export type RuntimeReceiptStatus =
2
- | 'pending'
3
- | 'running'
4
- | 'completed'
5
- | 'failed'
6
- | 'skipped';
7
-
8
- export type WorkerRuntimeReceipt = {
9
- key: string;
10
- status: RuntimeReceiptStatus;
11
- output?: unknown;
12
- error?: string;
13
- runId?: string | null;
14
- };
1
+ import type {
2
+ WorkReceipt,
3
+ WorkReceiptClaim,
4
+ WorkReceiptCommand,
5
+ WorkReceiptStatus,
6
+ WorkReceiptStore,
7
+ } from '../../../../shared_libs/play-runtime/work-receipts';
8
+
9
+ export type RuntimeReceiptStatus = WorkReceiptStatus;
10
+
11
+ export type WorkerRuntimeReceipt = WorkReceipt;
15
12
 
16
13
  export type WorkerRuntimeReceiptResponse = {
17
14
  receipt?: WorkerRuntimeReceipt | null;
@@ -45,24 +42,29 @@ export type WorkerRuntimeReceiptAction =
45
42
  error: string;
46
43
  };
47
44
 
45
+ export type WorkerRuntimeReceiptCommand = WorkReceiptCommand;
46
+
47
+ export type WorkerRuntimeReceiptClaim = WorkReceiptClaim;
48
+
49
+ export type WorkerRuntimeReceiptStore = WorkReceiptStore;
50
+
51
+ type PostRuntimeApi = (
52
+ baseUrl: string,
53
+ executorToken: string,
54
+ body: WorkerRuntimeReceiptAction,
55
+ ) => Promise<WorkerRuntimeReceiptResponse>;
56
+
48
57
  type RuntimeReceiptContext = {
49
- baseUrl: string;
50
- executorToken: string;
58
+ baseUrl?: string;
59
+ executorToken?: string;
51
60
  orgId?: string | null;
52
61
  playName: string;
53
62
  runId: string;
54
63
  key: string;
55
- postRuntimeApi: (
56
- baseUrl: string,
57
- executorToken: string,
58
- body: WorkerRuntimeReceiptAction,
59
- ) => Promise<WorkerRuntimeReceiptResponse>;
64
+ postRuntimeApi?: PostRuntimeApi;
65
+ receiptStore?: WorkerRuntimeReceiptStore;
60
66
  };
61
67
 
62
- type PostRuntimeReceiptAction = (
63
- body: WorkerRuntimeReceiptAction,
64
- ) => Promise<WorkerRuntimeReceiptResponse>;
65
-
66
68
  function scopedReceiptKey(input: {
67
69
  orgId?: string | null;
68
70
  playName: string;
@@ -79,32 +81,120 @@ function errorMessage(error: unknown): string {
79
81
  return error instanceof Error ? error.message : String(error);
80
82
  }
81
83
 
82
- function runningReceiptError(key: string, receipt: WorkerRuntimeReceipt): Error {
84
+ function runningReceiptError(
85
+ key: string,
86
+ receipt: WorkerRuntimeReceipt,
87
+ ): Error {
83
88
  return new Error(
84
89
  `Runtime receipt ${key} is already running for run ${receipt.runId ?? 'unknown'}.`,
85
90
  );
86
91
  }
87
92
 
93
+ function isReusableReceipt(receipt: WorkerRuntimeReceipt): boolean {
94
+ return receipt.status === 'completed' || receipt.status === 'skipped';
95
+ }
96
+
97
+ export function createRuntimeApiWorkerReceiptStore(input: {
98
+ baseUrl: string;
99
+ executorToken: string;
100
+ postRuntimeApi: PostRuntimeApi;
101
+ }): WorkerRuntimeReceiptStore {
102
+ const postRuntimeReceiptAction = (body: WorkerRuntimeReceiptAction) =>
103
+ input.postRuntimeApi(input.baseUrl, input.executorToken, body);
104
+
105
+ return {
106
+ async claimReceipt(command) {
107
+ const claimed = await postRuntimeReceiptAction({
108
+ action: 'claim_runtime_step_receipt',
109
+ playName: command.playName,
110
+ runId: command.runId,
111
+ key: command.key,
112
+ });
113
+ if (claimed.receipt) {
114
+ return { disposition: 'claimed', receipt: claimed.receipt };
115
+ }
116
+
117
+ const latest = await postRuntimeReceiptAction({
118
+ action: 'get_runtime_step_receipt',
119
+ playName: command.playName,
120
+ runId: command.runId,
121
+ key: command.key,
122
+ });
123
+ if (latest.receipt && isReusableReceipt(latest.receipt)) {
124
+ return { disposition: 'reused', receipt: latest.receipt };
125
+ }
126
+ if (latest.receipt?.status === 'running') {
127
+ return { disposition: 'running', receipt: latest.receipt };
128
+ }
129
+ if (latest.receipt?.status === 'failed') {
130
+ return { disposition: 'failed', receipt: latest.receipt };
131
+ }
132
+ throw new Error(
133
+ `Runtime receipt ${command.key} claim did not return execution ownership.`,
134
+ );
135
+ },
136
+
137
+ async completeReceipt(command) {
138
+ const completed = await postRuntimeReceiptAction({
139
+ action: 'complete_runtime_step_receipt',
140
+ playName: command.playName,
141
+ runId: command.runId,
142
+ key: command.key,
143
+ output: command.output,
144
+ });
145
+ return completed.receipt ?? null;
146
+ },
147
+
148
+ async failReceipt(command) {
149
+ const failed = await postRuntimeReceiptAction({
150
+ action: 'fail_runtime_step_receipt',
151
+ playName: command.playName,
152
+ runId: command.runId,
153
+ key: command.key,
154
+ error: command.error,
155
+ });
156
+ return failed.receipt ?? null;
157
+ },
158
+ };
159
+ }
160
+
161
+ function resolveReceiptStore(
162
+ input: RuntimeReceiptContext,
163
+ ): WorkerRuntimeReceiptStore {
164
+ if (input.receiptStore) {
165
+ return input.receiptStore;
166
+ }
167
+ if (!input.baseUrl || !input.executorToken || !input.postRuntimeApi) {
168
+ throw new Error(
169
+ 'Runtime receipts require either a receiptStore or Runtime API transport.',
170
+ );
171
+ }
172
+ return createRuntimeApiWorkerReceiptStore({
173
+ baseUrl: input.baseUrl,
174
+ executorToken: input.executorToken,
175
+ postRuntimeApi: input.postRuntimeApi,
176
+ });
177
+ }
178
+
88
179
  async function executeAndPersistReceipt<T>(input: {
89
180
  key: string;
90
181
  playName: string;
91
182
  runId: string;
92
183
  execute: () => Promise<T> | T;
93
- postRuntimeReceiptAction: PostRuntimeReceiptAction;
184
+ receiptStore: WorkerRuntimeReceiptStore;
94
185
  ownership: 'claimed' | 'workflow_replay';
95
186
  }): Promise<T> {
96
187
  let output: T;
97
188
  try {
98
189
  output = await input.execute();
99
190
  } catch (error) {
100
- const failed = await input.postRuntimeReceiptAction({
101
- action: 'fail_runtime_step_receipt',
191
+ const failed = await input.receiptStore.failReceipt({
102
192
  playName: input.playName,
103
193
  runId: input.runId,
104
194
  key: input.key,
105
195
  error: errorMessage(error),
106
196
  });
107
- if (!failed.receipt) {
197
+ if (!failed) {
108
198
  throw new Error(
109
199
  `Runtime receipt ${input.key} ${input.ownership} execution failed and failed receipt could not be persisted: ${errorMessage(error)}`,
110
200
  );
@@ -112,14 +202,13 @@ async function executeAndPersistReceipt<T>(input: {
112
202
  throw error;
113
203
  }
114
204
 
115
- const completed = await input.postRuntimeReceiptAction({
116
- action: 'complete_runtime_step_receipt',
205
+ const completed = await input.receiptStore.completeReceipt({
117
206
  playName: input.playName,
118
207
  runId: input.runId,
119
208
  key: input.key,
120
209
  output,
121
210
  });
122
- if (!completed.receipt) {
211
+ if (!completed) {
123
212
  throw new Error(
124
213
  `Runtime receipt ${input.key} ${input.ownership} execution completed but completed receipt could not be persisted.`,
125
214
  );
@@ -134,50 +223,34 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
134
223
  },
135
224
  ): Promise<T> {
136
225
  const key = scopedReceiptKey(input);
137
- const postRuntimeReceiptAction = (body: WorkerRuntimeReceiptAction) =>
138
- input.postRuntimeApi(input.baseUrl, input.executorToken, body);
139
- const claimed = await postRuntimeReceiptAction({
140
- action: 'claim_runtime_step_receipt',
226
+ const receiptStore = resolveReceiptStore(input);
227
+ const claimed = await receiptStore.claimReceipt({
141
228
  playName: input.playName,
142
229
  runId: input.runId,
143
230
  key,
144
231
  });
145
- if (!claimed.receipt) {
146
- const latest = await postRuntimeReceiptAction({
147
- action: 'get_runtime_step_receipt',
148
- playName: input.playName,
149
- runId: input.runId,
150
- key,
151
- });
232
+ if (claimed.disposition === 'reused') {
233
+ return receiptOutput<T>(claimed.receipt);
234
+ }
235
+ if (claimed.disposition === 'running') {
152
236
  if (
153
- latest.receipt?.status === 'completed' ||
154
- latest.receipt?.status === 'skipped'
237
+ input.repairRunningReceiptForSameRun &&
238
+ claimed.receipt.runId === input.runId
155
239
  ) {
156
- return receiptOutput<T>(latest.receipt);
157
- }
158
- if (latest.receipt?.status === 'running') {
159
- if (
160
- input.repairRunningReceiptForSameRun &&
161
- latest.receipt.runId === input.runId
162
- ) {
163
- return executeAndPersistReceipt({
164
- key,
165
- playName: input.playName,
166
- runId: input.runId,
167
- execute: input.execute,
168
- postRuntimeReceiptAction,
169
- ownership: 'workflow_replay',
170
- });
171
- }
172
- throw runningReceiptError(key, latest.receipt);
173
- }
174
- if (latest.receipt?.status === 'failed') {
175
- throw new Error(
176
- `Runtime receipt ${key} is failed and could not be claimed: ${latest.receipt.error ?? 'unknown error'}`,
177
- );
240
+ return executeAndPersistReceipt({
241
+ key,
242
+ playName: input.playName,
243
+ runId: input.runId,
244
+ execute: input.execute,
245
+ receiptStore,
246
+ ownership: 'workflow_replay',
247
+ });
178
248
  }
249
+ throw runningReceiptError(key, claimed.receipt);
250
+ }
251
+ if (claimed.disposition === 'failed') {
179
252
  throw new Error(
180
- `Runtime receipt ${key} claim did not return execution ownership.`,
253
+ `Runtime receipt ${key} is failed and could not be claimed: ${claimed.receipt.error ?? 'unknown error'}`,
181
254
  );
182
255
  }
183
256
 
@@ -186,7 +259,7 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
186
259
  playName: input.playName,
187
260
  runId: input.runId,
188
261
  execute: input.execute,
189
- postRuntimeReceiptAction,
262
+ receiptStore,
190
263
  ownership: 'claimed',
191
264
  });
192
265
  }
@@ -32,13 +32,20 @@
32
32
  import type {
33
33
  PlayHarnessRpc,
34
34
  PreloadedRuntimeDbSessionInput,
35
+ CompleteRuntimeReceiptInput,
36
+ FailRuntimeReceiptInput,
35
37
  RuntimeApiCallInput,
36
38
  RuntimeApiCallResult,
39
+ RuntimeReceiptInput,
37
40
  SheetDatasetRowsInput,
38
41
  SheetDatasetRowsResult,
39
42
  StagedFileChunkInput,
40
43
  StagedFileChunkResult,
41
44
  } from '../../../apps/play-harness-worker/src/rpc-types';
45
+ import type {
46
+ WorkReceipt,
47
+ WorkReceiptClaim,
48
+ } from '../../../shared_libs/play-runtime/work-receipts';
42
49
 
43
50
  /**
44
51
  * Service-binding RPC stub shape — what `env.HARNESS` looks like inside
@@ -119,6 +126,24 @@ export async function harnessRuntimeApiCall(
119
126
  return requireBinding().runtimeApiCall(input);
120
127
  }
121
128
 
129
+ export async function harnessClaimRuntimeReceipt(
130
+ input: RuntimeReceiptInput,
131
+ ): Promise<WorkReceiptClaim> {
132
+ return requireBinding().claimRuntimeReceipt(input);
133
+ }
134
+
135
+ export async function harnessCompleteRuntimeReceipt(
136
+ input: CompleteRuntimeReceiptInput,
137
+ ): Promise<WorkReceipt | null> {
138
+ return requireBinding().completeRuntimeReceipt(input);
139
+ }
140
+
141
+ export async function harnessFailRuntimeReceipt(
142
+ input: FailRuntimeReceiptInput,
143
+ ): Promise<WorkReceipt | null> {
144
+ return requireBinding().failRuntimeReceipt(input);
145
+ }
146
+
122
147
  /**
123
148
  * Read a bounded staged-file byte range through typed harness RPC. This is the
124
149
  * only staged-file data path for per-play Workers; there is intentionally no
@@ -50,10 +50,10 @@ export type SdkRelease = {
50
50
  };
51
51
 
52
52
  export const SDK_RELEASE = {
53
- version: '0.1.61',
53
+ version: '0.1.63',
54
54
  apiContract: '2026-05-play-bootstrap-dataset-summary',
55
55
  supportPolicy: {
56
- latest: '0.1.61',
56
+ latest: '0.1.63',
57
57
  minimumSupported: '0.1.53',
58
58
  deprecatedBelow: '0.1.53',
59
59
  },
@@ -454,19 +454,14 @@ function deriveListKeys(input: {
454
454
  }
455
455
 
456
456
  const listPrefix = input.listPath.replace(/\[\d+\]$/, '');
457
- for (const [target, paths] of Object.entries(
458
- input.targetGetters ?? {},
459
- )) {
457
+ for (const [target, paths] of Object.entries(input.targetGetters ?? {})) {
460
458
  for (const rawPath of paths) {
461
459
  const path = String(rawPath || '')
462
460
  .trim()
463
461
  .replace(/^\./, '');
464
462
  if (!path) continue;
465
463
  const firstRow = input.rows[0];
466
- if (
467
- firstRow &&
468
- Object.prototype.hasOwnProperty.call(firstRow, path)
469
- ) {
464
+ if (firstRow && Object.prototype.hasOwnProperty.call(firstRow, path)) {
470
465
  keys[target] = path;
471
466
  break;
472
467
  }
@@ -514,21 +509,20 @@ function buildTargets(
514
509
  for (const [target, descriptor] of Object.entries(extractors ?? {})) {
515
510
  const fromExtractor = findFirstTargetByPath(result, descriptor.paths);
516
511
  if (!fromExtractor) continue;
512
+ const transformed = coerceToEnum(
513
+ applyExtractorTransforms(fromExtractor.value, descriptor),
514
+ descriptor,
515
+ );
516
+ const override = findExtractorOverride(result, descriptor);
517
517
  targets[target] = {
518
518
  path: fromExtractor.path,
519
- value: coerceToEnum(
520
- applyExtractorTransforms(fromExtractor.value, descriptor),
521
- descriptor,
522
- ),
519
+ value: override?.value ?? transformed,
523
520
  };
524
521
  }
525
522
  const metadataTargets = new Set(Object.keys(targetGetters ?? {}));
526
523
  for (const target of metadataTargets) {
527
524
  if (targets[target]) continue;
528
- const fromMetadata = findFirstTargetByPath(
529
- result,
530
- targetGetters?.[target],
531
- );
525
+ const fromMetadata = findFirstTargetByPath(result, targetGetters?.[target]);
532
526
  if (fromMetadata) {
533
527
  targets[target] = fromMetadata;
534
528
  continue;
@@ -547,6 +541,25 @@ function buildTargets(
547
541
  return targets;
548
542
  }
549
543
 
544
+ function findExtractorOverride(
545
+ result: unknown,
546
+ descriptor: ToolResultExtractorDescriptor,
547
+ ): { value: string | number | boolean | null } | null {
548
+ for (const override of descriptor.overrides ?? []) {
549
+ const expected = Object.prototype.hasOwnProperty.call(override, 'equals')
550
+ ? override.equals
551
+ : true;
552
+ for (const path of override.paths) {
553
+ const match = findFirstTargetByPath(result, [path]);
554
+ if (!match) continue;
555
+ if (match.value === expected) {
556
+ return { value: override.value };
557
+ }
558
+ }
559
+ }
560
+ return null;
561
+ }
562
+
550
563
  function buildLists(
551
564
  resolved: Record<string, { path: string; rows: Record<string, unknown>[] }>,
552
565
  metadata: ToolResultMetadataInput,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.61",
3
+ "version": "0.1.63",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {