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 +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +23 -13
- package/dist/index.mjs +23 -13
- package/dist/repo/apps/play-runner-workers/src/entry.ts +71 -14
- package/dist/repo/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +31 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/receipts.ts +143 -70
- package/dist/repo/sdk/src/plays/harness-stub.ts +25 -0
- package/dist/repo/sdk/src/release.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +28 -15
- package/package.json +1 -1
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.
|
|
223
|
+
version: "0.1.63",
|
|
224
224
|
apiContract: "2026-05-play-bootstrap-dataset-summary",
|
|
225
225
|
supportPolicy: {
|
|
226
|
-
latest: "0.1.
|
|
226
|
+
latest: "0.1.63",
|
|
227
227
|
minimumSupported: "0.1.53",
|
|
228
228
|
deprecatedBelow: "0.1.53"
|
|
229
229
|
}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -197,10 +197,10 @@ function resolveConfig(options) {
|
|
|
197
197
|
|
|
198
198
|
// src/release.ts
|
|
199
199
|
var SDK_RELEASE = {
|
|
200
|
-
version: "0.1.
|
|
200
|
+
version: "0.1.63",
|
|
201
201
|
apiContract: "2026-05-play-bootstrap-dataset-summary",
|
|
202
202
|
supportPolicy: {
|
|
203
|
-
latest: "0.1.
|
|
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.
|
|
235
|
+
version: "0.1.63",
|
|
236
236
|
apiContract: "2026-05-play-bootstrap-dataset-summary",
|
|
237
237
|
supportPolicy: {
|
|
238
|
-
latest: "0.1.
|
|
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:
|
|
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.
|
|
173
|
+
version: "0.1.63",
|
|
174
174
|
apiContract: "2026-05-play-bootstrap-dataset-summary",
|
|
175
175
|
supportPolicy: {
|
|
176
|
-
latest: "0.1.
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
50
|
-
executorToken
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
138
|
-
|
|
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 (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
154
|
-
|
|
237
|
+
input.repairRunningReceiptForSameRun &&
|
|
238
|
+
claimed.receipt.runId === input.runId
|
|
155
239
|
) {
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
input.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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}
|
|
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
|
-
|
|
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.
|
|
53
|
+
version: '0.1.63',
|
|
54
54
|
apiContract: '2026-05-play-bootstrap-dataset-summary',
|
|
55
55
|
supportPolicy: {
|
|
56
|
-
latest: '0.1.
|
|
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:
|
|
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,
|