deepline 0.1.168 → 0.1.170
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/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +317 -26
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +100 -8
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +294 -81
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +119 -33
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +4 -1
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +56 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-instance-create.ts +3 -0
- package/dist/bundling-sources/sdk/src/client.ts +29 -1
- package/dist/bundling-sources/sdk/src/play.ts +4 -0
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/column-names.ts +50 -8
- package/dist/bundling-sources/shared_libs/play-data-plane/sheet-contract.ts +40 -1
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +135 -4
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +9 -3
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +1 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +202 -45
- package/dist/cli/index.js +70 -113
- package/dist/cli/index.mjs +70 -113
- package/dist/{compiler-manifest-VhtM9n24.d.mts → compiler-manifest-OwORQ07f.d.mts} +1 -0
- package/dist/{compiler-manifest-VhtM9n24.d.ts → compiler-manifest-OwORQ07f.d.ts} +1 -0
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +26 -5
- package/dist/index.mjs +26 -5
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/package.json +1 -1
|
@@ -159,6 +159,8 @@ import {
|
|
|
159
159
|
markWorkerToolReceiptResultCached,
|
|
160
160
|
markWorkerToolReceiptResultExecution,
|
|
161
161
|
planWorkerToolReceiptGroups,
|
|
162
|
+
resolveWorkerToolReceiptGroupWaitMaxAttempts,
|
|
163
|
+
resolveWorkerToolRuntimeTimeoutMs,
|
|
162
164
|
} from './runtime/tool-receipts';
|
|
163
165
|
// The harness stub forwards leaf calls (validation, runtime-api HTTP) into
|
|
164
166
|
// the long-lived Play Harness Worker via env.HARNESS. We import the
|
|
@@ -263,6 +265,7 @@ type RunRequest = {
|
|
|
263
265
|
callbackUrl: string;
|
|
264
266
|
executorToken: string;
|
|
265
267
|
baseUrl: string;
|
|
268
|
+
integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
|
|
266
269
|
orgId: string;
|
|
267
270
|
playName: string;
|
|
268
271
|
graphHash?: string | null;
|
|
@@ -325,6 +328,14 @@ function getStringField(value: unknown, key: string): string | null {
|
|
|
325
328
|
return typeof field === 'string' && field.trim() ? field : null;
|
|
326
329
|
}
|
|
327
330
|
|
|
331
|
+
function normalizeIntegrationMode(
|
|
332
|
+
value: unknown,
|
|
333
|
+
): 'live' | 'eval_stub' | 'fixture' | null {
|
|
334
|
+
return value === 'live' || value === 'eval_stub' || value === 'fixture'
|
|
335
|
+
? value
|
|
336
|
+
: null;
|
|
337
|
+
}
|
|
338
|
+
|
|
328
339
|
function getObjectField(
|
|
329
340
|
value: unknown,
|
|
330
341
|
key: string,
|
|
@@ -554,11 +565,13 @@ function resolveRuntimeDeadlineRemainingMs(runtimeDeadlineMs?: number): number {
|
|
|
554
565
|
|
|
555
566
|
function resolveToolRuntimeApiTimeout(input: {
|
|
556
567
|
requestInput: Record<string, unknown>;
|
|
568
|
+
timeoutMs?: number;
|
|
557
569
|
runtimeDeadlineMs?: number;
|
|
558
570
|
}): { timeoutMs: number; timeoutErrorMessage?: string } {
|
|
559
|
-
const toolTimeoutMs =
|
|
560
|
-
input.
|
|
561
|
-
|
|
571
|
+
const toolTimeoutMs =
|
|
572
|
+
typeof input.timeoutMs === 'number' && Number.isFinite(input.timeoutMs)
|
|
573
|
+
? Math.max(1, Math.ceil(input.timeoutMs))
|
|
574
|
+
: resolveRuntimeToolReceiptWaitTimeoutMs(input.requestInput);
|
|
562
575
|
const remainingMs = resolveRuntimeDeadlineRemainingMs(
|
|
563
576
|
input.runtimeDeadlineMs,
|
|
564
577
|
);
|
|
@@ -1256,6 +1269,7 @@ async function executeTool(
|
|
|
1256
1269
|
transientHttpRetrySafe = false,
|
|
1257
1270
|
abortSignal?: AbortSignal,
|
|
1258
1271
|
runtimeDeadlineMs?: number,
|
|
1272
|
+
runtimeTimeoutMs?: number,
|
|
1259
1273
|
): Promise<ToolExecuteResult> {
|
|
1260
1274
|
if (args.toolId === 'test_wait_for_event' && workflowStep) {
|
|
1261
1275
|
const result = await waitForSyntheticIntegrationEvent(
|
|
@@ -1278,6 +1292,7 @@ async function executeTool(
|
|
|
1278
1292
|
transientHttpRetrySafe,
|
|
1279
1293
|
abortSignal,
|
|
1280
1294
|
runtimeDeadlineMs,
|
|
1295
|
+
runtimeTimeoutMs,
|
|
1281
1296
|
);
|
|
1282
1297
|
}
|
|
1283
1298
|
|
|
@@ -1291,6 +1306,7 @@ async function executeToolWithLifecycle(
|
|
|
1291
1306
|
transientHttpRetrySafe = false,
|
|
1292
1307
|
abortSignal?: AbortSignal,
|
|
1293
1308
|
runtimeDeadlineMs?: number,
|
|
1309
|
+
runtimeTimeoutMs?: number,
|
|
1294
1310
|
): Promise<ToolExecuteResult> {
|
|
1295
1311
|
callbacks?.onToolCalled?.(args.toolId, nowMs());
|
|
1296
1312
|
try {
|
|
@@ -1303,6 +1319,7 @@ async function executeToolWithLifecycle(
|
|
|
1303
1319
|
transientHttpRetrySafe,
|
|
1304
1320
|
abortSignal,
|
|
1305
1321
|
runtimeDeadlineMs,
|
|
1322
|
+
runtimeTimeoutMs,
|
|
1306
1323
|
);
|
|
1307
1324
|
} catch (error) {
|
|
1308
1325
|
callbacks?.onToolFailed?.(args.toolId, nowMs());
|
|
@@ -1320,6 +1337,8 @@ function normalizeToolExecuteArgs(request: unknown): {
|
|
|
1320
1337
|
input: Record<string, unknown>;
|
|
1321
1338
|
force?: boolean;
|
|
1322
1339
|
staleAfterSeconds?: number;
|
|
1340
|
+
timeoutMs?: number;
|
|
1341
|
+
receiptWaitMs?: number;
|
|
1323
1342
|
} {
|
|
1324
1343
|
if (!isToolExecuteRecord(request)) {
|
|
1325
1344
|
throw new Error(
|
|
@@ -1345,6 +1364,14 @@ function normalizeToolExecuteArgs(request: unknown): {
|
|
|
1345
1364
|
...(typeof request.staleAfterSeconds === 'number'
|
|
1346
1365
|
? { staleAfterSeconds: request.staleAfterSeconds }
|
|
1347
1366
|
: {}),
|
|
1367
|
+
...(typeof request.timeoutMs === 'number' &&
|
|
1368
|
+
Number.isFinite(request.timeoutMs)
|
|
1369
|
+
? { timeoutMs: request.timeoutMs }
|
|
1370
|
+
: {}),
|
|
1371
|
+
...(typeof request.receiptWaitMs === 'number' &&
|
|
1372
|
+
Number.isFinite(request.receiptWaitMs)
|
|
1373
|
+
? { receiptWaitMs: request.receiptWaitMs }
|
|
1374
|
+
: {}),
|
|
1348
1375
|
};
|
|
1349
1376
|
}
|
|
1350
1377
|
|
|
@@ -1441,6 +1468,7 @@ async function callToolDirect(
|
|
|
1441
1468
|
transientHttpRetrySafe = false,
|
|
1442
1469
|
abortSignal?: AbortSignal,
|
|
1443
1470
|
runtimeDeadlineMs?: number,
|
|
1471
|
+
runtimeTimeoutMs?: number,
|
|
1444
1472
|
): Promise<ToolExecuteResult> {
|
|
1445
1473
|
const { id, toolId, input } = args;
|
|
1446
1474
|
const path = `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`;
|
|
@@ -1455,6 +1483,7 @@ async function callToolDirect(
|
|
|
1455
1483
|
try {
|
|
1456
1484
|
const runtimeApiTimeout = resolveToolRuntimeApiTimeout({
|
|
1457
1485
|
requestInput: input,
|
|
1486
|
+
timeoutMs: runtimeTimeoutMs,
|
|
1458
1487
|
runtimeDeadlineMs,
|
|
1459
1488
|
});
|
|
1460
1489
|
res = await fetchRuntimeApi(
|
|
@@ -1472,6 +1501,9 @@ async function callToolDirect(
|
|
|
1472
1501
|
body: JSON.stringify({
|
|
1473
1502
|
payload: input,
|
|
1474
1503
|
metadata: { parent_run_id: req.runId },
|
|
1504
|
+
...(req.integrationMode
|
|
1505
|
+
? { integration_mode: req.integrationMode }
|
|
1506
|
+
: {}),
|
|
1475
1507
|
}),
|
|
1476
1508
|
},
|
|
1477
1509
|
{
|
|
@@ -1628,6 +1660,7 @@ type WorkerToolBatchRequest = {
|
|
|
1628
1660
|
receiptKey: string | null;
|
|
1629
1661
|
force: boolean;
|
|
1630
1662
|
receiptWaitMaxAttempts: number;
|
|
1663
|
+
runtimeTimeoutMs?: number;
|
|
1631
1664
|
toolId: string;
|
|
1632
1665
|
input: Record<string, unknown>;
|
|
1633
1666
|
workflowStep?: WorkflowStep;
|
|
@@ -1646,17 +1679,62 @@ type PreparedWorkerToolBatchRequests = {
|
|
|
1646
1679
|
deferredClaimedRequests: Promise<ClaimedWorkerToolBatchRequest[]>[];
|
|
1647
1680
|
};
|
|
1648
1681
|
|
|
1682
|
+
function resolveClaimedWorkerToolRuntimeTimeoutMs(
|
|
1683
|
+
claimedRequests: ClaimedWorkerToolBatchRequest[],
|
|
1684
|
+
input: { runtimeDeadlineMs?: number },
|
|
1685
|
+
): number | undefined {
|
|
1686
|
+
return resolveWorkerToolRuntimeTimeoutMs(claimedRequests, {
|
|
1687
|
+
resolveOwnerTimeoutMs: (request) =>
|
|
1688
|
+
resolveToolRuntimeApiTimeout({
|
|
1689
|
+
requestInput: request.input,
|
|
1690
|
+
timeoutMs: request.runtimeTimeoutMs,
|
|
1691
|
+
runtimeDeadlineMs: input.runtimeDeadlineMs,
|
|
1692
|
+
}).timeoutMs,
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1649
1696
|
const WORKER_TOOL_BATCH_GRACE_MS = 250;
|
|
1650
1697
|
const MAP_EXECUTION_HEARTBEAT_INTERVAL_MS = 5_000;
|
|
1651
1698
|
const MAP_INCREMENTAL_PERSIST_CHUNK_ROWS = 100;
|
|
1652
1699
|
const MAP_INCREMENTAL_PERSIST_CHUNK_BYTES = 1 * 1024 * 1024;
|
|
1653
|
-
const MAP_INCREMENTAL_PERSIST_INTERVAL_MS =
|
|
1700
|
+
const MAP_INCREMENTAL_PERSIST_INTERVAL_MS = 1_000;
|
|
1701
|
+
const MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS = 1_000;
|
|
1654
1702
|
/**
|
|
1655
1703
|
* Bounded number of per-row failure samples carried in chunk summaries and the
|
|
1656
1704
|
* map's terminal partial-failure log. Every failed row is persisted with its
|
|
1657
1705
|
* full error in the runtime sheet; the samples just keep run logs readable.
|
|
1658
1706
|
*/
|
|
1659
1707
|
const MAP_ROW_FAILURE_SAMPLE_LIMIT = 3;
|
|
1708
|
+
|
|
1709
|
+
class RuntimeReceiptPersistenceError extends Error {
|
|
1710
|
+
readonly errors: unknown[];
|
|
1711
|
+
|
|
1712
|
+
constructor(message: string, errors: unknown[] = []) {
|
|
1713
|
+
super(message);
|
|
1714
|
+
this.name = 'RuntimeReceiptPersistenceError';
|
|
1715
|
+
this.errors = errors;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
function isRuntimeReceiptPersistenceError(error: unknown): boolean {
|
|
1720
|
+
if (!error || typeof error !== 'object') return false;
|
|
1721
|
+
if (error instanceof RuntimeReceiptPersistenceError) return true;
|
|
1722
|
+
if (error instanceof Error && error.name === 'RuntimeReceiptPersistenceError') {
|
|
1723
|
+
return true;
|
|
1724
|
+
}
|
|
1725
|
+
const nestedErrors = (error as { errors?: unknown }).errors;
|
|
1726
|
+
return (
|
|
1727
|
+
Array.isArray(nestedErrors) &&
|
|
1728
|
+
nestedErrors.some(isRuntimeReceiptPersistenceError)
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
function isRunFatalWorkerRowError(error: unknown): boolean {
|
|
1733
|
+
return (
|
|
1734
|
+
isRowIsolationExemptError(error) || isRuntimeReceiptPersistenceError(error)
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1660
1738
|
// Fallback batch-chunk parallelism when a tool declares no provider rate hints.
|
|
1661
1739
|
// Matches the prior hardcoded `Math.min(4, ...)` so undeclared providers keep
|
|
1662
1740
|
// their previous batching behavior; declared providers tighten via the
|
|
@@ -1759,6 +1837,7 @@ class WorkerToolBatchScheduler {
|
|
|
1759
1837
|
input: Record<string, unknown>,
|
|
1760
1838
|
workflowStep?: WorkflowStep,
|
|
1761
1839
|
options?: { force?: boolean; staleAfterSeconds?: number | null },
|
|
1840
|
+
runtimeOptions?: { timeoutMs?: number; receiptWaitMs?: number },
|
|
1762
1841
|
): Promise<unknown> {
|
|
1763
1842
|
const providerActionVersion =
|
|
1764
1843
|
await this.resolveToolActionCacheVersion(toolId);
|
|
@@ -1776,7 +1855,14 @@ class WorkerToolBatchScheduler {
|
|
|
1776
1855
|
cacheKey: receiptKey,
|
|
1777
1856
|
receiptKey,
|
|
1778
1857
|
force: options?.force === true,
|
|
1779
|
-
receiptWaitMaxAttempts: resolveRuntimeToolReceiptWaitMaxAttempts(
|
|
1858
|
+
receiptWaitMaxAttempts: resolveRuntimeToolReceiptWaitMaxAttempts(
|
|
1859
|
+
typeof runtimeOptions?.receiptWaitMs === 'number'
|
|
1860
|
+
? { max_wait_ms: runtimeOptions.receiptWaitMs }
|
|
1861
|
+
: input,
|
|
1862
|
+
),
|
|
1863
|
+
...(typeof runtimeOptions?.timeoutMs === 'number'
|
|
1864
|
+
? { runtimeTimeoutMs: runtimeOptions.timeoutMs }
|
|
1865
|
+
: {}),
|
|
1780
1866
|
toolId,
|
|
1781
1867
|
input,
|
|
1782
1868
|
workflowStep,
|
|
@@ -1962,7 +2048,10 @@ class WorkerToolBatchScheduler {
|
|
|
1962
2048
|
try {
|
|
1963
2049
|
const receipt = await this.waitForDurableToolReceipt({
|
|
1964
2050
|
receiptKey: input.receiptKey,
|
|
1965
|
-
maxAttempts:
|
|
2051
|
+
maxAttempts: resolveWorkerToolReceiptGroupWaitMaxAttempts(
|
|
2052
|
+
input.group,
|
|
2053
|
+
(groupRequest) => groupRequest.receiptWaitMaxAttempts,
|
|
2054
|
+
),
|
|
1966
2055
|
});
|
|
1967
2056
|
await this.resolveCompletedDurableToolReceiptGroup({
|
|
1968
2057
|
group: input.group,
|
|
@@ -1982,6 +2071,7 @@ class WorkerToolBatchScheduler {
|
|
|
1982
2071
|
!canReclaimTimedOutWorkerToolReceipt({
|
|
1983
2072
|
ownerRunId: input.runningReceipt.runId,
|
|
1984
2073
|
currentRunId: this.req.runId,
|
|
2074
|
+
updatedAt: input.runningReceipt.updatedAt,
|
|
1985
2075
|
})
|
|
1986
2076
|
) {
|
|
1987
2077
|
this.rejectRawRequests(input.group, waitError);
|
|
@@ -2070,7 +2160,10 @@ class WorkerToolBatchScheduler {
|
|
|
2070
2160
|
try {
|
|
2071
2161
|
const receipt = await this.waitForDurableToolReceipt({
|
|
2072
2162
|
receiptKey: input.receiptKey,
|
|
2073
|
-
maxAttempts:
|
|
2163
|
+
maxAttempts: resolveWorkerToolReceiptGroupWaitMaxAttempts(
|
|
2164
|
+
input.group,
|
|
2165
|
+
(groupRequest) => groupRequest.receiptWaitMaxAttempts,
|
|
2166
|
+
),
|
|
2074
2167
|
});
|
|
2075
2168
|
await this.resolveCompletedDurableToolReceiptGroup({
|
|
2076
2169
|
group: input.group,
|
|
@@ -2100,9 +2193,9 @@ class WorkerToolBatchScheduler {
|
|
|
2100
2193
|
await this.failDurableToolRequest(claimed, error);
|
|
2101
2194
|
return error;
|
|
2102
2195
|
} catch (receiptError) {
|
|
2103
|
-
return new
|
|
2104
|
-
[error, receiptError],
|
|
2196
|
+
return new RuntimeReceiptPersistenceError(
|
|
2105
2197
|
'Tool call failed and durable receipt could not be marked failed',
|
|
2198
|
+
[error, receiptError],
|
|
2106
2199
|
);
|
|
2107
2200
|
}
|
|
2108
2201
|
}
|
|
@@ -2459,25 +2552,41 @@ class WorkerToolBatchScheduler {
|
|
|
2459
2552
|
signal: this.abortSignal,
|
|
2460
2553
|
});
|
|
2461
2554
|
try {
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2555
|
+
let result: unknown;
|
|
2556
|
+
try {
|
|
2557
|
+
result = await executeToolWithLifecycle(
|
|
2558
|
+
this.req,
|
|
2559
|
+
{ id: request.id, toolId, input: request.input },
|
|
2560
|
+
request.workflowStep,
|
|
2561
|
+
this.callbacks,
|
|
2562
|
+
(retryAfterMs) =>
|
|
2563
|
+
this.reportBackpressure(toolId, retryAfterMs),
|
|
2564
|
+
() => this.governor.chargeBudget('retry'),
|
|
2565
|
+
toolContract?.retrySafeTransientHttp === true,
|
|
2566
|
+
this.abortSignal,
|
|
2567
|
+
this.runtimeDeadlineMs,
|
|
2568
|
+
resolveClaimedWorkerToolRuntimeTimeoutMs([claimed], {
|
|
2569
|
+
runtimeDeadlineMs: this.runtimeDeadlineMs,
|
|
2570
|
+
}),
|
|
2571
|
+
);
|
|
2572
|
+
} catch (error) {
|
|
2573
|
+
this.rejectRequests(
|
|
2574
|
+
claimed,
|
|
2575
|
+
await this.failureForRejectedToolRequest(claimed, error),
|
|
2576
|
+
);
|
|
2577
|
+
return;
|
|
2578
|
+
}
|
|
2473
2579
|
this.settleRequests(
|
|
2474
2580
|
claimed,
|
|
2475
2581
|
await this.completeDurableToolRequest(claimed, result),
|
|
2476
2582
|
);
|
|
2477
|
-
} catch (
|
|
2583
|
+
} catch (receiptError) {
|
|
2478
2584
|
this.rejectRequests(
|
|
2479
2585
|
claimed,
|
|
2480
|
-
|
|
2586
|
+
new RuntimeReceiptPersistenceError(
|
|
2587
|
+
'Tool call succeeded but durable receipt could not be marked completed',
|
|
2588
|
+
[receiptError],
|
|
2589
|
+
),
|
|
2481
2590
|
);
|
|
2482
2591
|
} finally {
|
|
2483
2592
|
slot.release();
|
|
@@ -2629,6 +2738,9 @@ async function executeBatchedWorkerToolGroup(input: {
|
|
|
2629
2738
|
toolContract?.retrySafeTransientHttp === true,
|
|
2630
2739
|
input.abortSignal,
|
|
2631
2740
|
input.runtimeDeadlineMs,
|
|
2741
|
+
resolveClaimedWorkerToolRuntimeTimeoutMs(batch.memberRequests, {
|
|
2742
|
+
runtimeDeadlineMs: input.runtimeDeadlineMs,
|
|
2743
|
+
}),
|
|
2632
2744
|
);
|
|
2633
2745
|
} catch (error) {
|
|
2634
2746
|
input.callbacks?.onToolFailed?.(batch.batchOperation, nowMs());
|
|
@@ -2651,9 +2763,9 @@ async function executeBatchedWorkerToolGroup(input: {
|
|
|
2651
2763
|
try {
|
|
2652
2764
|
await input.failRequests(entry.request.memberRequests, entry.error);
|
|
2653
2765
|
} catch (receiptError) {
|
|
2654
|
-
rejection = new
|
|
2655
|
-
[entry.error, receiptError],
|
|
2766
|
+
rejection = new RuntimeReceiptPersistenceError(
|
|
2656
2767
|
'Tool call failed and durable receipts could not be marked failed',
|
|
2768
|
+
[entry.error, receiptError],
|
|
2657
2769
|
);
|
|
2658
2770
|
}
|
|
2659
2771
|
for (const claimed of entry.request.memberRequests) {
|
|
@@ -2668,16 +2780,28 @@ async function executeBatchedWorkerToolGroup(input: {
|
|
|
2668
2780
|
batchResult != null
|
|
2669
2781
|
? entry.request.splitResults(batchResult)
|
|
2670
2782
|
: entry.request.memberRequests.map(() => null);
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
claimed
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2783
|
+
let completedResults: unknown[];
|
|
2784
|
+
try {
|
|
2785
|
+
completedResults = await input.completeRequests(
|
|
2786
|
+
entry.request.memberRequests.map((claimed, index) => ({
|
|
2787
|
+
claimed,
|
|
2788
|
+
result: wrapWorkerToolResult(
|
|
2789
|
+
claimed.request.toolId,
|
|
2790
|
+
splitResults[index] ?? null,
|
|
2791
|
+
toolMetadataFallback(claimed.request.toolId),
|
|
2792
|
+
),
|
|
2793
|
+
})),
|
|
2794
|
+
);
|
|
2795
|
+
} catch (receiptError) {
|
|
2796
|
+
const rejection = new RuntimeReceiptPersistenceError(
|
|
2797
|
+
'Tool call succeeded but durable receipts could not be marked completed',
|
|
2798
|
+
[receiptError],
|
|
2799
|
+
);
|
|
2800
|
+
for (const claimed of entry.request.memberRequests) {
|
|
2801
|
+
input.rejectRequest(claimed, rejection);
|
|
2802
|
+
}
|
|
2803
|
+
continue;
|
|
2804
|
+
}
|
|
2681
2805
|
for (let index = 0; index < completedResults.length; index += 1) {
|
|
2682
2806
|
const claimed = entry.request.memberRequests[index]!;
|
|
2683
2807
|
const request = claimed.request;
|
|
@@ -2690,9 +2814,9 @@ async function executeBatchedWorkerToolGroup(input: {
|
|
|
2690
2814
|
try {
|
|
2691
2815
|
await input.failRequests(input.requests, error);
|
|
2692
2816
|
} catch (receiptError) {
|
|
2693
|
-
rejection = new
|
|
2694
|
-
[error, receiptError],
|
|
2817
|
+
rejection = new RuntimeReceiptPersistenceError(
|
|
2695
2818
|
'Tool call failed and durable receipts could not be marked failed',
|
|
2819
|
+
[error, receiptError],
|
|
2696
2820
|
);
|
|
2697
2821
|
}
|
|
2698
2822
|
for (const claimed of input.requests) {
|
|
@@ -2728,6 +2852,7 @@ type WorkerMapChunkSummary<T extends Record<string, unknown>> = {
|
|
|
2728
2852
|
stepCellsSkipped: number;
|
|
2729
2853
|
outputDatasetId: string;
|
|
2730
2854
|
hash: string;
|
|
2855
|
+
fatalError?: string;
|
|
2731
2856
|
preview: T[];
|
|
2732
2857
|
cachedRows?: T[];
|
|
2733
2858
|
};
|
|
@@ -3816,6 +3941,15 @@ async function persistCompletedMapRows(input: {
|
|
|
3816
3941
|
rows,
|
|
3817
3942
|
outputFields,
|
|
3818
3943
|
});
|
|
3944
|
+
console.warn('[play-runner.persist_completed_map_rows.start]', {
|
|
3945
|
+
runId: req.runId,
|
|
3946
|
+
playName: req.playName,
|
|
3947
|
+
tableNamespace,
|
|
3948
|
+
rows: rows.length,
|
|
3949
|
+
outputFields,
|
|
3950
|
+
extraOutputFields: input.extraOutputFields ?? [],
|
|
3951
|
+
contractColumnCount: sheetContract.columns.length,
|
|
3952
|
+
});
|
|
3819
3953
|
const persistRequest = {
|
|
3820
3954
|
...sessionScope,
|
|
3821
3955
|
tableNamespace,
|
|
@@ -3830,6 +3964,8 @@ async function persistCompletedMapRows(input: {
|
|
|
3830
3964
|
.filter((key): key is string => Boolean(key)),
|
|
3831
3965
|
),
|
|
3832
3966
|
];
|
|
3967
|
+
const expectedVisibleRows =
|
|
3968
|
+
expectedKeys.length > 0 ? expectedKeys.length : rows.length;
|
|
3833
3969
|
const readVisibleRowCount = async () => {
|
|
3834
3970
|
if (expectedKeys.length > 0) {
|
|
3835
3971
|
const result = await harnessReadSheetDatasetRowKeys({
|
|
@@ -3852,12 +3988,24 @@ async function persistCompletedMapRows(input: {
|
|
|
3852
3988
|
return result.rows.length;
|
|
3853
3989
|
};
|
|
3854
3990
|
const result = await harnessPersistCompletedSheetRows(persistRequest);
|
|
3991
|
+
console.warn('[play-runner.persist_completed_map_rows.result]', {
|
|
3992
|
+
runId: req.runId,
|
|
3993
|
+
playName: req.playName,
|
|
3994
|
+
tableNamespace,
|
|
3995
|
+
rows: rows.length,
|
|
3996
|
+
rowsWritten: result.rowsWritten,
|
|
3997
|
+
expectedVisibleRows,
|
|
3998
|
+
expectedKeyCount: expectedKeys.length,
|
|
3999
|
+
});
|
|
3855
4000
|
let visibleRows = -1;
|
|
3856
4001
|
let retryWritten: number | null = null;
|
|
3857
4002
|
let retryVisible: number | null = null;
|
|
3858
|
-
if (
|
|
4003
|
+
if (
|
|
4004
|
+
rows.length <= MAP_INCREMENTAL_PERSIST_CHUNK_ROWS ||
|
|
4005
|
+
result.rowsWritten !== rows.length
|
|
4006
|
+
) {
|
|
3859
4007
|
visibleRows = await readVisibleRowCount();
|
|
3860
|
-
if (visibleRows <
|
|
4008
|
+
if (visibleRows < expectedVisibleRows) {
|
|
3861
4009
|
await harnessStartSheetDataset({
|
|
3862
4010
|
...sessionScope,
|
|
3863
4011
|
tableNamespace,
|
|
@@ -3869,15 +4017,23 @@ async function persistCompletedMapRows(input: {
|
|
|
3869
4017
|
const retry = await harnessPersistCompletedSheetRows(persistRequest);
|
|
3870
4018
|
retryWritten = retry.rowsWritten;
|
|
3871
4019
|
retryVisible = await readVisibleRowCount();
|
|
3872
|
-
if (retryVisible
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
4020
|
+
if (retryVisible >= expectedVisibleRows) {
|
|
4021
|
+
return {
|
|
4022
|
+
rows: rows.length,
|
|
4023
|
+
written: result.rowsWritten,
|
|
4024
|
+
visible: visibleRows,
|
|
4025
|
+
retryWritten,
|
|
4026
|
+
retryVisible,
|
|
4027
|
+
};
|
|
4028
|
+
}
|
|
4029
|
+
throw new Error(
|
|
4030
|
+
`Runtime sheet persistence mismatch for ${tableNamespace}: wrote ${result.rowsWritten}/${rows.length}; visible ${visibleRows}/${expectedVisibleRows}; retry wrote ${retryWritten}/${rows.length}; retry visible ${retryVisible}/${expectedVisibleRows}; run ${req.runId}.`,
|
|
4031
|
+
);
|
|
3876
4032
|
}
|
|
3877
4033
|
}
|
|
3878
|
-
if (result.rowsWritten !== rows.length && visibleRows <
|
|
4034
|
+
if (result.rowsWritten !== rows.length && visibleRows < expectedVisibleRows) {
|
|
3879
4035
|
throw new Error(
|
|
3880
|
-
`Runtime sheet persistence mismatch for ${tableNamespace}: wrote ${result.rowsWritten}/${rows.length}; run ${req.runId}.`,
|
|
4036
|
+
`Runtime sheet persistence mismatch for ${tableNamespace}: wrote ${result.rowsWritten}/${rows.length}; visible ${visibleRows}/${expectedVisibleRows}; run ${req.runId}.`,
|
|
3881
4037
|
);
|
|
3882
4038
|
}
|
|
3883
4039
|
return {
|
|
@@ -4932,7 +5088,7 @@ function createMinimalWorkerCtx(
|
|
|
4932
5088
|
let persistFailure: unknown = null;
|
|
4933
5089
|
let scheduledLiveUpdateTimer: ReturnType<typeof setTimeout> | null = null;
|
|
4934
5090
|
let liveUpdateFlushChain: Promise<void> = Promise.resolve();
|
|
4935
|
-
let
|
|
5091
|
+
let liveUpdateFailureCount = 0;
|
|
4936
5092
|
|
|
4937
5093
|
const clearScheduledPersistTimer = () => {
|
|
4938
5094
|
if (scheduledPersistTimer) {
|
|
@@ -5070,25 +5226,35 @@ function createMinimalWorkerCtx(
|
|
|
5070
5226
|
const updates = pendingLiveRowUpdates.splice(0);
|
|
5071
5227
|
const extraOutputFields = Array.from(generatedOutputFields);
|
|
5072
5228
|
const task = liveUpdateFlushChain.then(async () => {
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5229
|
+
try {
|
|
5230
|
+
await applyLiveMapRowUpdates({
|
|
5231
|
+
req,
|
|
5232
|
+
tableNamespace: name,
|
|
5233
|
+
outputFields,
|
|
5234
|
+
extraOutputFields,
|
|
5235
|
+
updates,
|
|
5236
|
+
});
|
|
5237
|
+
} catch (error) {
|
|
5238
|
+
liveUpdateFailureCount += 1;
|
|
5239
|
+
if (liveUpdateFailureCount <= MAP_ROW_FAILURE_SAMPLE_LIMIT) {
|
|
5240
|
+
emitEvent({
|
|
5241
|
+
type: 'log',
|
|
5242
|
+
level: 'warn',
|
|
5243
|
+
message:
|
|
5244
|
+
`Live row update flush failed for ctx.dataset("${name}") ` +
|
|
5245
|
+
`(non-fatal; terminal rows still persist): ${formatWorkerRowFailureMessage(error)}`,
|
|
5246
|
+
ts: nowMs(),
|
|
5247
|
+
});
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
5084
5250
|
});
|
|
5251
|
+
liveUpdateFlushChain = task.catch(() => undefined);
|
|
5085
5252
|
return task;
|
|
5086
5253
|
};
|
|
5087
5254
|
|
|
5088
5255
|
const scheduleLiveRowUpdates = () => {
|
|
5089
|
-
if (liveUpdateFailure) return;
|
|
5090
5256
|
if (
|
|
5091
|
-
pendingLiveRowUpdates.length >=
|
|
5257
|
+
pendingLiveRowUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS
|
|
5092
5258
|
) {
|
|
5093
5259
|
void flushLiveRowUpdates().catch(() => undefined);
|
|
5094
5260
|
return;
|
|
@@ -5103,14 +5269,12 @@ function createMinimalWorkerCtx(
|
|
|
5103
5269
|
const enqueueLiveRowUpdate = (
|
|
5104
5270
|
update: Omit<PlayRowUpdate, 'rowId'> & { runId?: string },
|
|
5105
5271
|
): Promise<void> => {
|
|
5106
|
-
if (liveUpdateFailure) {
|
|
5107
|
-
return Promise.reject(liveUpdateFailure);
|
|
5108
|
-
}
|
|
5109
5272
|
pendingLiveRowUpdates.push(update);
|
|
5110
5273
|
if (
|
|
5111
|
-
pendingLiveRowUpdates.length >=
|
|
5274
|
+
pendingLiveRowUpdates.length >= MAP_LIVE_UPDATE_FLUSH_CHUNK_ROWS
|
|
5112
5275
|
) {
|
|
5113
|
-
|
|
5276
|
+
void flushLiveRowUpdates().catch(() => undefined);
|
|
5277
|
+
return Promise.resolve();
|
|
5114
5278
|
}
|
|
5115
5279
|
scheduleLiveRowUpdates();
|
|
5116
5280
|
return Promise.resolve();
|
|
@@ -5293,7 +5457,7 @@ function createMinimalWorkerCtx(
|
|
|
5293
5457
|
} catch (rowError) {
|
|
5294
5458
|
// Abort/budget errors stay run-fatal and leave no partial
|
|
5295
5459
|
// state: rethrow immediately without recording the row.
|
|
5296
|
-
if (
|
|
5460
|
+
if (isRunFatalWorkerRowError(rowError)) {
|
|
5297
5461
|
throw rowError;
|
|
5298
5462
|
}
|
|
5299
5463
|
const message = formatWorkerRowFailureMessage(rowError);
|
|
@@ -5393,6 +5557,43 @@ function createMinimalWorkerCtx(
|
|
|
5393
5557
|
concurrency,
|
|
5394
5558
|
},
|
|
5395
5559
|
});
|
|
5560
|
+
const buildRowFailureSamples = () =>
|
|
5561
|
+
failedRowEntries
|
|
5562
|
+
.map((failure, executedIndex) =>
|
|
5563
|
+
failure
|
|
5564
|
+
? {
|
|
5565
|
+
rowKey: uniqueRowsToExecuteEntries[executedIndex]!.rowKey,
|
|
5566
|
+
error: failure.error,
|
|
5567
|
+
}
|
|
5568
|
+
: null,
|
|
5569
|
+
)
|
|
5570
|
+
.filter(
|
|
5571
|
+
(sample): sample is { rowKey: string; error: string } =>
|
|
5572
|
+
sample !== null,
|
|
5573
|
+
)
|
|
5574
|
+
.slice(0, MAP_ROW_FAILURE_SAMPLE_LIMIT);
|
|
5575
|
+
const fatalMapChunkSummary = async (
|
|
5576
|
+
error: unknown,
|
|
5577
|
+
): Promise<WorkerMapChunkSummary<T & Record<string, unknown>>> => ({
|
|
5578
|
+
chunkIndex,
|
|
5579
|
+
rangeStart: baseOffset + chunkStart,
|
|
5580
|
+
rangeEnd: baseOffset + chunkStart,
|
|
5581
|
+
rowsRead: chunkRows.length,
|
|
5582
|
+
rowsWritten: 0,
|
|
5583
|
+
rowsExecuted: rowsToExecute.length,
|
|
5584
|
+
rowsCached: 0,
|
|
5585
|
+
rowsDuplicateReused: duplicateInputReuseCount,
|
|
5586
|
+
rowsInserted,
|
|
5587
|
+
rowsSkipped,
|
|
5588
|
+
rowsFailed: failedExecutedRows,
|
|
5589
|
+
rowFailureSamples: buildRowFailureSamples(),
|
|
5590
|
+
stepCellsCompleted,
|
|
5591
|
+
stepCellsSkipped,
|
|
5592
|
+
outputDatasetId: `map:${name}`,
|
|
5593
|
+
hash: await hashJson([]),
|
|
5594
|
+
fatalError: formatWorkerRowFailureMessage(error),
|
|
5595
|
+
preview: [],
|
|
5596
|
+
});
|
|
5396
5597
|
const persistRowsStartedAt = nowMs();
|
|
5397
5598
|
recordRunnerPerfTrace({
|
|
5398
5599
|
req,
|
|
@@ -5407,7 +5608,6 @@ function createMinimalWorkerCtx(
|
|
|
5407
5608
|
try {
|
|
5408
5609
|
await flushLiveRowUpdates();
|
|
5409
5610
|
await liveUpdateFlushChain;
|
|
5410
|
-
if (liveUpdateFailure) throw liveUpdateFailure;
|
|
5411
5611
|
await enqueuePersistExecutedRows();
|
|
5412
5612
|
await persistFlushChain;
|
|
5413
5613
|
if (persistFailure) throw persistFailure;
|
|
@@ -5433,13 +5633,16 @@ function createMinimalWorkerCtx(
|
|
|
5433
5633
|
error: error instanceof Error ? error.message : String(error),
|
|
5434
5634
|
},
|
|
5435
5635
|
});
|
|
5436
|
-
|
|
5636
|
+
return await fatalMapChunkSummary(error);
|
|
5437
5637
|
}
|
|
5438
5638
|
const rejectedWorker = workerResults.find(
|
|
5439
5639
|
(result): result is PromiseRejectedResult =>
|
|
5440
5640
|
result.status === 'rejected',
|
|
5441
5641
|
);
|
|
5442
5642
|
if (rejectedWorker) {
|
|
5643
|
+
if (isRuntimeReceiptPersistenceError(rejectedWorker.reason)) {
|
|
5644
|
+
return await fatalMapChunkSummary(rejectedWorker.reason);
|
|
5645
|
+
}
|
|
5443
5646
|
throw rejectedWorker.reason;
|
|
5444
5647
|
}
|
|
5445
5648
|
const resultByKey = new Map<string, T & Record<string, unknown>>();
|
|
@@ -5494,20 +5697,7 @@ function createMinimalWorkerCtx(
|
|
|
5494
5697
|
0,
|
|
5495
5698
|
executedRows.length - failedExecutedRows,
|
|
5496
5699
|
);
|
|
5497
|
-
const rowFailureSamples =
|
|
5498
|
-
.map((failure, executedIndex) =>
|
|
5499
|
-
failure
|
|
5500
|
-
? {
|
|
5501
|
-
rowKey: uniqueRowsToExecuteEntries[executedIndex]!.rowKey,
|
|
5502
|
-
error: failure.error,
|
|
5503
|
-
}
|
|
5504
|
-
: null,
|
|
5505
|
-
)
|
|
5506
|
-
.filter(
|
|
5507
|
-
(sample): sample is { rowKey: string; error: string } =>
|
|
5508
|
-
sample !== null,
|
|
5509
|
-
)
|
|
5510
|
-
.slice(0, MAP_ROW_FAILURE_SAMPLE_LIMIT);
|
|
5700
|
+
const rowFailureSamples = buildRowFailureSamples();
|
|
5511
5701
|
const publicOut = out.map((row) => publicCsvOutputRow(row));
|
|
5512
5702
|
const keyedOut = outEntries.map(({ key, inputIndex, row }) => ({
|
|
5513
5703
|
...row,
|
|
@@ -5756,6 +5946,15 @@ function createMinimalWorkerCtx(
|
|
|
5756
5946
|
continue;
|
|
5757
5947
|
}
|
|
5758
5948
|
const chunkResult = await runChunkStep(chunkRows, chunkStart, chunkIndex);
|
|
5949
|
+
if (chunkResult.fatalError) {
|
|
5950
|
+
throw new Error(
|
|
5951
|
+
`ctx.dataset("${name}") stopped after a runtime persistence failure ` +
|
|
5952
|
+
`outside the retryable chunk step. Provider calls already executed for ` +
|
|
5953
|
+
`${chunkResult.rowsExecuted} row(s), so the chunk was not retried. ` +
|
|
5954
|
+
`Fix the runtime persistence cause and re-run to resume. ` +
|
|
5955
|
+
`First error: ${chunkResult.fatalError}`,
|
|
5956
|
+
);
|
|
5957
|
+
}
|
|
5759
5958
|
totalRowsWritten += chunkResult.rowsWritten;
|
|
5760
5959
|
totalRowsExecuted += chunkResult.rowsExecuted;
|
|
5761
5960
|
totalRowsCached += chunkResult.rowsCached;
|
|
@@ -6136,6 +6335,10 @@ function createMinimalWorkerCtx(
|
|
|
6136
6335
|
force: request.force === true,
|
|
6137
6336
|
staleAfterSeconds: request.staleAfterSeconds,
|
|
6138
6337
|
},
|
|
6338
|
+
{
|
|
6339
|
+
timeoutMs: request.timeoutMs,
|
|
6340
|
+
receiptWaitMs: request.receiptWaitMs,
|
|
6341
|
+
},
|
|
6139
6342
|
);
|
|
6140
6343
|
},
|
|
6141
6344
|
},
|
|
@@ -6274,6 +6477,7 @@ function createMinimalWorkerCtx(
|
|
|
6274
6477
|
orgId: req.orgId,
|
|
6275
6478
|
callbackBaseUrl: req.callbackUrl,
|
|
6276
6479
|
baseUrl: req.baseUrl,
|
|
6480
|
+
integrationMode: req.integrationMode ?? null,
|
|
6277
6481
|
parentExecutorToken: req.executorToken,
|
|
6278
6482
|
userEmail: req.userEmail ?? '',
|
|
6279
6483
|
profile: 'workers_edge',
|
|
@@ -7652,11 +7856,18 @@ function extractMaxCreditsPerRun(contractSnapshot: unknown): number | null {
|
|
|
7652
7856
|
: null;
|
|
7653
7857
|
}
|
|
7654
7858
|
|
|
7859
|
+
function shouldSkipWorkerComputeBilling(req: RunRequest): boolean {
|
|
7860
|
+
return req.integrationMode === 'fixture' || req.integrationMode === 'eval_stub';
|
|
7861
|
+
}
|
|
7862
|
+
|
|
7655
7863
|
async function finalizeWorkerComputeBilling(input: {
|
|
7656
7864
|
req: RunRequest;
|
|
7657
7865
|
success: boolean;
|
|
7658
7866
|
actionEstimate: number;
|
|
7659
7867
|
}): Promise<void> {
|
|
7868
|
+
if (shouldSkipWorkerComputeBilling(input.req)) {
|
|
7869
|
+
return;
|
|
7870
|
+
}
|
|
7660
7871
|
const maxCreditsPerRun = extractMaxCreditsPerRun(input.req.contractSnapshot);
|
|
7661
7872
|
await postRuntimeApi(input.req.baseUrl, input.req.executorToken, {
|
|
7662
7873
|
action: 'compute_billing_finalize',
|
|
@@ -7712,6 +7923,7 @@ function runRequestFromWorkflowParams(
|
|
|
7712
7923
|
callbackUrl: String(params.baseUrl ?? ''),
|
|
7713
7924
|
executorToken: String(params.executorToken ?? ''),
|
|
7714
7925
|
baseUrl: String(params.baseUrl ?? ''),
|
|
7926
|
+
integrationMode: normalizeIntegrationMode(params.integrationMode),
|
|
7715
7927
|
orgId: String(params.orgId ?? ''),
|
|
7716
7928
|
playName: String(params.playName ?? ''),
|
|
7717
7929
|
userEmail: typeof params.userEmail === 'string' ? params.userEmail : null,
|
|
@@ -8051,7 +8263,7 @@ export class TenantWorkflow extends WorkflowEntrypoint<
|
|
|
8051
8263
|
// somewhere inside executeRunRequest. If it doesn't appear, the
|
|
8052
8264
|
// throw is in the framework wrapper between the loader and run().
|
|
8053
8265
|
console.log(
|
|
8054
|
-
`${runPrefix} TenantWorkflow.run entered baseUrl=${req.baseUrl}`,
|
|
8266
|
+
`${runPrefix} TenantWorkflow.run entered baseUrl=${req.baseUrl} integrationMode=${req.integrationMode ?? 'default'}`,
|
|
8055
8267
|
);
|
|
8056
8268
|
captureCoordinatorBinding(this.env);
|
|
8057
8269
|
captureRuntimeApiBinding(this.env);
|
|
@@ -8065,6 +8277,7 @@ export class TenantWorkflow extends WorkflowEntrypoint<
|
|
|
8065
8277
|
ms: 0,
|
|
8066
8278
|
extra: {
|
|
8067
8279
|
hasWorkflowStep: true,
|
|
8280
|
+
integrationMode: req.integrationMode ?? null,
|
|
8068
8281
|
},
|
|
8069
8282
|
});
|
|
8070
8283
|
// Fire the one-time wiring probe (deduplicated across runs in the
|