deepline 0.1.169 → 0.1.171
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/dedup-do.ts +1 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +82 -16
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +1 -1
- 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 +54 -0
- package/dist/bundling-sources/sdk/src/play.ts +4 -0
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +136 -4
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +7 -3
- package/dist/cli/index.js +111 -28
- package/dist/cli/index.mjs +111 -28
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
|
@@ -1153,7 +1153,6 @@ export class PlayDedup implements DurableObject {
|
|
|
1153
1153
|
if (!state && timeoutMs > 0) {
|
|
1154
1154
|
await new Promise<void>((resolve) => {
|
|
1155
1155
|
let settled = false;
|
|
1156
|
-
let timeout: ReturnType<typeof setTimeout>;
|
|
1157
1156
|
const finish = () => {
|
|
1158
1157
|
if (settled) return;
|
|
1159
1158
|
settled = true;
|
|
@@ -1164,7 +1163,7 @@ export class PlayDedup implements DurableObject {
|
|
|
1164
1163
|
clearTimeout(timeout);
|
|
1165
1164
|
resolve();
|
|
1166
1165
|
};
|
|
1167
|
-
timeout = setTimeout(finish, timeoutMs);
|
|
1166
|
+
const timeout = setTimeout(finish, timeoutMs);
|
|
1168
1167
|
if (!this.childTerminalWaiters.has(eventKey)) {
|
|
1169
1168
|
this.childTerminalWaiters.set(eventKey, new Set());
|
|
1170
1169
|
}
|
|
@@ -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
|
|
@@ -563,11 +565,13 @@ function resolveRuntimeDeadlineRemainingMs(runtimeDeadlineMs?: number): number {
|
|
|
563
565
|
|
|
564
566
|
function resolveToolRuntimeApiTimeout(input: {
|
|
565
567
|
requestInput: Record<string, unknown>;
|
|
568
|
+
timeoutMs?: number;
|
|
566
569
|
runtimeDeadlineMs?: number;
|
|
567
570
|
}): { timeoutMs: number; timeoutErrorMessage?: string } {
|
|
568
|
-
const toolTimeoutMs =
|
|
569
|
-
input.
|
|
570
|
-
|
|
571
|
+
const toolTimeoutMs =
|
|
572
|
+
typeof input.timeoutMs === 'number' && Number.isFinite(input.timeoutMs)
|
|
573
|
+
? Math.max(1, Math.ceil(input.timeoutMs))
|
|
574
|
+
: resolveRuntimeToolReceiptWaitTimeoutMs(input.requestInput);
|
|
571
575
|
const remainingMs = resolveRuntimeDeadlineRemainingMs(
|
|
572
576
|
input.runtimeDeadlineMs,
|
|
573
577
|
);
|
|
@@ -1265,6 +1269,7 @@ async function executeTool(
|
|
|
1265
1269
|
transientHttpRetrySafe = false,
|
|
1266
1270
|
abortSignal?: AbortSignal,
|
|
1267
1271
|
runtimeDeadlineMs?: number,
|
|
1272
|
+
runtimeTimeoutMs?: number,
|
|
1268
1273
|
): Promise<ToolExecuteResult> {
|
|
1269
1274
|
if (args.toolId === 'test_wait_for_event' && workflowStep) {
|
|
1270
1275
|
const result = await waitForSyntheticIntegrationEvent(
|
|
@@ -1287,6 +1292,7 @@ async function executeTool(
|
|
|
1287
1292
|
transientHttpRetrySafe,
|
|
1288
1293
|
abortSignal,
|
|
1289
1294
|
runtimeDeadlineMs,
|
|
1295
|
+
runtimeTimeoutMs,
|
|
1290
1296
|
);
|
|
1291
1297
|
}
|
|
1292
1298
|
|
|
@@ -1300,6 +1306,7 @@ async function executeToolWithLifecycle(
|
|
|
1300
1306
|
transientHttpRetrySafe = false,
|
|
1301
1307
|
abortSignal?: AbortSignal,
|
|
1302
1308
|
runtimeDeadlineMs?: number,
|
|
1309
|
+
runtimeTimeoutMs?: number,
|
|
1303
1310
|
): Promise<ToolExecuteResult> {
|
|
1304
1311
|
callbacks?.onToolCalled?.(args.toolId, nowMs());
|
|
1305
1312
|
try {
|
|
@@ -1312,6 +1319,7 @@ async function executeToolWithLifecycle(
|
|
|
1312
1319
|
transientHttpRetrySafe,
|
|
1313
1320
|
abortSignal,
|
|
1314
1321
|
runtimeDeadlineMs,
|
|
1322
|
+
runtimeTimeoutMs,
|
|
1315
1323
|
);
|
|
1316
1324
|
} catch (error) {
|
|
1317
1325
|
callbacks?.onToolFailed?.(args.toolId, nowMs());
|
|
@@ -1329,6 +1337,8 @@ function normalizeToolExecuteArgs(request: unknown): {
|
|
|
1329
1337
|
input: Record<string, unknown>;
|
|
1330
1338
|
force?: boolean;
|
|
1331
1339
|
staleAfterSeconds?: number;
|
|
1340
|
+
timeoutMs?: number;
|
|
1341
|
+
receiptWaitMs?: number;
|
|
1332
1342
|
} {
|
|
1333
1343
|
if (!isToolExecuteRecord(request)) {
|
|
1334
1344
|
throw new Error(
|
|
@@ -1354,6 +1364,14 @@ function normalizeToolExecuteArgs(request: unknown): {
|
|
|
1354
1364
|
...(typeof request.staleAfterSeconds === 'number'
|
|
1355
1365
|
? { staleAfterSeconds: request.staleAfterSeconds }
|
|
1356
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
|
+
: {}),
|
|
1357
1375
|
};
|
|
1358
1376
|
}
|
|
1359
1377
|
|
|
@@ -1450,6 +1468,7 @@ async function callToolDirect(
|
|
|
1450
1468
|
transientHttpRetrySafe = false,
|
|
1451
1469
|
abortSignal?: AbortSignal,
|
|
1452
1470
|
runtimeDeadlineMs?: number,
|
|
1471
|
+
runtimeTimeoutMs?: number,
|
|
1453
1472
|
): Promise<ToolExecuteResult> {
|
|
1454
1473
|
const { id, toolId, input } = args;
|
|
1455
1474
|
const path = `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`;
|
|
@@ -1464,6 +1483,7 @@ async function callToolDirect(
|
|
|
1464
1483
|
try {
|
|
1465
1484
|
const runtimeApiTimeout = resolveToolRuntimeApiTimeout({
|
|
1466
1485
|
requestInput: input,
|
|
1486
|
+
timeoutMs: runtimeTimeoutMs,
|
|
1467
1487
|
runtimeDeadlineMs,
|
|
1468
1488
|
});
|
|
1469
1489
|
res = await fetchRuntimeApi(
|
|
@@ -1640,6 +1660,7 @@ type WorkerToolBatchRequest = {
|
|
|
1640
1660
|
receiptKey: string | null;
|
|
1641
1661
|
force: boolean;
|
|
1642
1662
|
receiptWaitMaxAttempts: number;
|
|
1663
|
+
runtimeTimeoutMs?: number;
|
|
1643
1664
|
toolId: string;
|
|
1644
1665
|
input: Record<string, unknown>;
|
|
1645
1666
|
workflowStep?: WorkflowStep;
|
|
@@ -1658,6 +1679,20 @@ type PreparedWorkerToolBatchRequests = {
|
|
|
1658
1679
|
deferredClaimedRequests: Promise<ClaimedWorkerToolBatchRequest[]>[];
|
|
1659
1680
|
};
|
|
1660
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
|
+
|
|
1661
1696
|
const WORKER_TOOL_BATCH_GRACE_MS = 250;
|
|
1662
1697
|
const MAP_EXECUTION_HEARTBEAT_INTERVAL_MS = 5_000;
|
|
1663
1698
|
const MAP_INCREMENTAL_PERSIST_CHUNK_ROWS = 100;
|
|
@@ -1802,6 +1837,7 @@ class WorkerToolBatchScheduler {
|
|
|
1802
1837
|
input: Record<string, unknown>,
|
|
1803
1838
|
workflowStep?: WorkflowStep,
|
|
1804
1839
|
options?: { force?: boolean; staleAfterSeconds?: number | null },
|
|
1840
|
+
runtimeOptions?: { timeoutMs?: number; receiptWaitMs?: number },
|
|
1805
1841
|
): Promise<unknown> {
|
|
1806
1842
|
const providerActionVersion =
|
|
1807
1843
|
await this.resolveToolActionCacheVersion(toolId);
|
|
@@ -1819,7 +1855,14 @@ class WorkerToolBatchScheduler {
|
|
|
1819
1855
|
cacheKey: receiptKey,
|
|
1820
1856
|
receiptKey,
|
|
1821
1857
|
force: options?.force === true,
|
|
1822
|
-
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
|
+
: {}),
|
|
1823
1866
|
toolId,
|
|
1824
1867
|
input,
|
|
1825
1868
|
workflowStep,
|
|
@@ -2005,7 +2048,10 @@ class WorkerToolBatchScheduler {
|
|
|
2005
2048
|
try {
|
|
2006
2049
|
const receipt = await this.waitForDurableToolReceipt({
|
|
2007
2050
|
receiptKey: input.receiptKey,
|
|
2008
|
-
maxAttempts:
|
|
2051
|
+
maxAttempts: resolveWorkerToolReceiptGroupWaitMaxAttempts(
|
|
2052
|
+
input.group,
|
|
2053
|
+
(groupRequest) => groupRequest.receiptWaitMaxAttempts,
|
|
2054
|
+
),
|
|
2009
2055
|
});
|
|
2010
2056
|
await this.resolveCompletedDurableToolReceiptGroup({
|
|
2011
2057
|
group: input.group,
|
|
@@ -2114,7 +2160,10 @@ class WorkerToolBatchScheduler {
|
|
|
2114
2160
|
try {
|
|
2115
2161
|
const receipt = await this.waitForDurableToolReceipt({
|
|
2116
2162
|
receiptKey: input.receiptKey,
|
|
2117
|
-
maxAttempts:
|
|
2163
|
+
maxAttempts: resolveWorkerToolReceiptGroupWaitMaxAttempts(
|
|
2164
|
+
input.group,
|
|
2165
|
+
(groupRequest) => groupRequest.receiptWaitMaxAttempts,
|
|
2166
|
+
),
|
|
2118
2167
|
});
|
|
2119
2168
|
await this.resolveCompletedDurableToolReceiptGroup({
|
|
2120
2169
|
group: input.group,
|
|
@@ -2516,6 +2565,9 @@ class WorkerToolBatchScheduler {
|
|
|
2516
2565
|
toolContract?.retrySafeTransientHttp === true,
|
|
2517
2566
|
this.abortSignal,
|
|
2518
2567
|
this.runtimeDeadlineMs,
|
|
2568
|
+
resolveClaimedWorkerToolRuntimeTimeoutMs([claimed], {
|
|
2569
|
+
runtimeDeadlineMs: this.runtimeDeadlineMs,
|
|
2570
|
+
}),
|
|
2519
2571
|
);
|
|
2520
2572
|
} catch (error) {
|
|
2521
2573
|
this.rejectRequests(
|
|
@@ -2686,6 +2738,9 @@ async function executeBatchedWorkerToolGroup(input: {
|
|
|
2686
2738
|
toolContract?.retrySafeTransientHttp === true,
|
|
2687
2739
|
input.abortSignal,
|
|
2688
2740
|
input.runtimeDeadlineMs,
|
|
2741
|
+
resolveClaimedWorkerToolRuntimeTimeoutMs(batch.memberRequests, {
|
|
2742
|
+
runtimeDeadlineMs: input.runtimeDeadlineMs,
|
|
2743
|
+
}),
|
|
2689
2744
|
);
|
|
2690
2745
|
} catch (error) {
|
|
2691
2746
|
input.callbacks?.onToolFailed?.(batch.batchOperation, nowMs());
|
|
@@ -4474,9 +4529,12 @@ function createMinimalWorkerCtx(
|
|
|
4474
4529
|
const executeWithRuntimeReceipt = async <T>(
|
|
4475
4530
|
key: string,
|
|
4476
4531
|
execute: () => Promise<T> | T,
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4532
|
+
options: {
|
|
4533
|
+
repairRunningReceiptForSameRun?: boolean;
|
|
4534
|
+
reclaimRunning?: boolean;
|
|
4535
|
+
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
4536
|
+
runningReceiptWaitMaxAttempts?: number;
|
|
4537
|
+
} = {},
|
|
4480
4538
|
): Promise<T> => {
|
|
4481
4539
|
const serialized = await runWorkerRuntimeReceiptBoundary<unknown>({
|
|
4482
4540
|
orgId: req.orgId,
|
|
@@ -4485,9 +4543,12 @@ function createMinimalWorkerCtx(
|
|
|
4485
4543
|
key,
|
|
4486
4544
|
receiptStore,
|
|
4487
4545
|
execute: async () => serializeDurableStepValue(await execute()),
|
|
4488
|
-
repairRunningReceiptForSameRun
|
|
4489
|
-
|
|
4490
|
-
|
|
4546
|
+
repairRunningReceiptForSameRun:
|
|
4547
|
+
options.repairRunningReceiptForSameRun ?? false,
|
|
4548
|
+
reclaimRunning: options.reclaimRunning ?? false,
|
|
4549
|
+
repairRunningReceiptForSameRunAfterWaitTimeout:
|
|
4550
|
+
options.repairRunningReceiptForSameRunAfterWaitTimeout ?? false,
|
|
4551
|
+
runningReceiptWaitMaxAttempts: options.runningReceiptWaitMaxAttempts,
|
|
4491
4552
|
});
|
|
4492
4553
|
return deserializeDurableStepValue(serialized) as T;
|
|
4493
4554
|
};
|
|
@@ -4509,7 +4570,6 @@ function createMinimalWorkerCtx(
|
|
|
4509
4570
|
)(name, async () => serializeDurableStepValue(await execute()));
|
|
4510
4571
|
return deserializeDurableStepValue(serialized) as T;
|
|
4511
4572
|
},
|
|
4512
|
-
false,
|
|
4513
4573
|
);
|
|
4514
4574
|
};
|
|
4515
4575
|
const nextCtxStepReceiptKey = (
|
|
@@ -6280,6 +6340,10 @@ function createMinimalWorkerCtx(
|
|
|
6280
6340
|
force: request.force === true,
|
|
6281
6341
|
staleAfterSeconds: request.staleAfterSeconds,
|
|
6282
6342
|
},
|
|
6343
|
+
{
|
|
6344
|
+
timeoutMs: request.timeoutMs,
|
|
6345
|
+
receiptWaitMs: request.receiptWaitMs,
|
|
6346
|
+
},
|
|
6283
6347
|
);
|
|
6284
6348
|
},
|
|
6285
6349
|
},
|
|
@@ -6674,9 +6738,11 @@ function createMinimalWorkerCtx(
|
|
|
6674
6738
|
childPlaySlot?.release();
|
|
6675
6739
|
}
|
|
6676
6740
|
},
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6741
|
+
{
|
|
6742
|
+
repairRunningReceiptForSameRunAfterWaitTimeout: true,
|
|
6743
|
+
runningReceiptWaitMaxAttempts:
|
|
6744
|
+
resolveRuntimeToolReceiptWaitMaxAttempts(input),
|
|
6745
|
+
},
|
|
6680
6746
|
);
|
|
6681
6747
|
},
|
|
6682
6748
|
async fetch(
|
|
@@ -17,7 +17,7 @@ export { TOOL_CALLING_MAP_CHUNK_SIZE };
|
|
|
17
17
|
// Paid Cloudflare Workers support a much higher configured subrequest limit.
|
|
18
18
|
// Keep a large buffer for coordinator/storage calls around row-level unbatched
|
|
19
19
|
// tool RPCs, but avoid pathological one-row chunks for provider waterfalls.
|
|
20
|
-
export const UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET =
|
|
20
|
+
export const UNBATCHED_TOOL_SUBREQUESTS_PER_CHUNK_BUDGET = 200;
|
|
21
21
|
// Fresh unbatched tool calls use one RUNTIME_API integration execute RPC and
|
|
22
22
|
// one HARNESS durable-receipt completion RPC. Batch-cap rows by both.
|
|
23
23
|
export const SUBREQUESTS_PER_UNBATCHED_TOOL_CALL = 2;
|
|
@@ -27,8 +27,11 @@ type RuntimeReceiptContext = {
|
|
|
27
27
|
receiptStore: WorkerRuntimeReceiptStore;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
const
|
|
30
|
+
const WORKER_RECEIPT_DEFAULT_WAIT_MS = 300_000;
|
|
31
31
|
const WORKER_RECEIPT_WAIT_DELAY_MS = 250;
|
|
32
|
+
const WORKER_RECEIPT_WAIT_MAX_ATTEMPTS = Math.ceil(
|
|
33
|
+
WORKER_RECEIPT_DEFAULT_WAIT_MS / WORKER_RECEIPT_WAIT_DELAY_MS,
|
|
34
|
+
);
|
|
32
35
|
|
|
33
36
|
class RuntimeReceiptWaitTimeoutError extends Error {
|
|
34
37
|
constructor(key: string) {
|
|
@@ -95,6 +95,60 @@ export function planWorkerToolReceiptGroups<TRequest>(
|
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
export function resolveWorkerToolReceiptGroupWaitMaxAttempts<TRequest>(
|
|
99
|
+
requests: TRequest[],
|
|
100
|
+
getWaitMaxAttempts: (request: TRequest) => number,
|
|
101
|
+
): number {
|
|
102
|
+
return requests.reduce((maxAttempts, request) => {
|
|
103
|
+
const attempts = getWaitMaxAttempts(request);
|
|
104
|
+
return Number.isFinite(attempts) && attempts > maxAttempts
|
|
105
|
+
? attempts
|
|
106
|
+
: maxAttempts;
|
|
107
|
+
}, 1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type WorkerToolRuntimeTimeoutRequest = {
|
|
111
|
+
input: Record<string, unknown>;
|
|
112
|
+
runtimeTimeoutMs?: number;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export type WorkerToolRuntimeTimeoutClaim = {
|
|
116
|
+
request: WorkerToolRuntimeTimeoutRequest;
|
|
117
|
+
followers: WorkerToolRuntimeTimeoutRequest[];
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export function resolveWorkerToolRuntimeTimeoutMs(
|
|
121
|
+
claimedRequests: WorkerToolRuntimeTimeoutClaim[],
|
|
122
|
+
input: {
|
|
123
|
+
resolveOwnerTimeoutMs: (
|
|
124
|
+
request: WorkerToolRuntimeTimeoutRequest,
|
|
125
|
+
) => number | undefined;
|
|
126
|
+
},
|
|
127
|
+
): number | undefined {
|
|
128
|
+
let timeoutMs = 0;
|
|
129
|
+
for (const claimed of claimedRequests) {
|
|
130
|
+
const ownerTimeoutMs = input.resolveOwnerTimeoutMs(claimed.request);
|
|
131
|
+
if (
|
|
132
|
+
typeof ownerTimeoutMs === 'number' &&
|
|
133
|
+
Number.isFinite(ownerTimeoutMs) &&
|
|
134
|
+
ownerTimeoutMs > timeoutMs
|
|
135
|
+
) {
|
|
136
|
+
timeoutMs = ownerTimeoutMs;
|
|
137
|
+
}
|
|
138
|
+
for (const follower of claimed.followers) {
|
|
139
|
+
const followerTimeoutMs = follower.runtimeTimeoutMs;
|
|
140
|
+
if (
|
|
141
|
+
typeof followerTimeoutMs === 'number' &&
|
|
142
|
+
Number.isFinite(followerTimeoutMs) &&
|
|
143
|
+
followerTimeoutMs > timeoutMs
|
|
144
|
+
) {
|
|
145
|
+
timeoutMs = followerTimeoutMs;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return timeoutMs > 0 ? timeoutMs : undefined;
|
|
150
|
+
}
|
|
151
|
+
|
|
98
152
|
export function markWorkerToolReceiptResultCached(
|
|
99
153
|
value: unknown,
|
|
100
154
|
cacheKey: string,
|
|
@@ -300,6 +300,10 @@ export type ToolExecutionRequest = {
|
|
|
300
300
|
force?: boolean;
|
|
301
301
|
/** Numeric TTL in seconds for this tool checkpoint. */
|
|
302
302
|
staleAfterSeconds?: number;
|
|
303
|
+
/** Runtime transport timeout in milliseconds. This is not sent to the provider. */
|
|
304
|
+
timeoutMs?: number;
|
|
305
|
+
/** Follower wait budget in milliseconds before a running receipt is reclaimable. */
|
|
306
|
+
receiptWaitMs?: number;
|
|
303
307
|
};
|
|
304
308
|
|
|
305
309
|
export type StepResolver<Row, Value> = (
|
|
@@ -104,10 +104,10 @@ export const SDK_RELEASE = {
|
|
|
104
104
|
// 0.1.111 ships dataset-native tool list getters and result row datasets.
|
|
105
105
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
106
106
|
// fields shipped in 0.1.153.
|
|
107
|
-
version: '0.1.
|
|
107
|
+
version: '0.1.171',
|
|
108
108
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
109
109
|
supportPolicy: {
|
|
110
|
-
latest: '0.1.
|
|
110
|
+
latest: '0.1.171',
|
|
111
111
|
minimumSupported: '0.1.53',
|
|
112
112
|
deprecatedBelow: '0.1.53',
|
|
113
113
|
commandMinimumSupported: [
|
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
import {
|
|
73
73
|
RuntimeReceiptWaitTimeoutError,
|
|
74
74
|
executeWithDurableRuntimeReceipt,
|
|
75
|
+
resolveRuntimeToolReceiptWaitMaxAttempts,
|
|
75
76
|
runtimeReceiptOutput as durableRuntimeReceiptOutput,
|
|
76
77
|
waitForCompletedRuntimeReceipt,
|
|
77
78
|
type DurableReceiptExecutionStore,
|
|
@@ -709,6 +710,8 @@ export class PlayContextImpl {
|
|
|
709
710
|
description?: string;
|
|
710
711
|
force?: boolean;
|
|
711
712
|
staleAfterSeconds?: number;
|
|
713
|
+
timeoutMs?: number;
|
|
714
|
+
receiptWaitMs?: number;
|
|
712
715
|
}): Promise<TOutput> => {
|
|
713
716
|
if (!request || typeof request !== 'object' || Array.isArray(request)) {
|
|
714
717
|
throw new Error(
|
|
@@ -735,7 +738,9 @@ export class PlayContextImpl {
|
|
|
735
738
|
request.input,
|
|
736
739
|
request.description ||
|
|
737
740
|
request.force === true ||
|
|
738
|
-
request.staleAfterSeconds !== undefined
|
|
741
|
+
request.staleAfterSeconds !== undefined ||
|
|
742
|
+
request.timeoutMs !== undefined ||
|
|
743
|
+
request.receiptWaitMs !== undefined
|
|
739
744
|
? {
|
|
740
745
|
...(request.description
|
|
741
746
|
? { description: request.description }
|
|
@@ -744,6 +749,12 @@ export class PlayContextImpl {
|
|
|
744
749
|
...(request.staleAfterSeconds !== undefined
|
|
745
750
|
? { staleAfterSeconds: request.staleAfterSeconds }
|
|
746
751
|
: {}),
|
|
752
|
+
...(request.timeoutMs !== undefined
|
|
753
|
+
? { timeoutMs: request.timeoutMs }
|
|
754
|
+
: {}),
|
|
755
|
+
...(request.receiptWaitMs !== undefined
|
|
756
|
+
? { receiptWaitMs: request.receiptWaitMs }
|
|
757
|
+
: {}),
|
|
747
758
|
}
|
|
748
759
|
: undefined,
|
|
749
760
|
) as Promise<TOutput>;
|
|
@@ -1283,10 +1294,12 @@ export class PlayContextImpl {
|
|
|
1283
1294
|
|
|
1284
1295
|
private async waitForCompletedRuntimeToolReceipt(
|
|
1285
1296
|
key: string,
|
|
1297
|
+
maxAttempts?: number,
|
|
1286
1298
|
): Promise<RuntimeStepReceipt> {
|
|
1287
1299
|
return await waitForCompletedRuntimeReceipt({
|
|
1288
1300
|
receiptKey: key,
|
|
1289
1301
|
store: this.durableReceiptExecutionStore(),
|
|
1302
|
+
maxAttempts,
|
|
1290
1303
|
});
|
|
1291
1304
|
}
|
|
1292
1305
|
|
|
@@ -1301,6 +1314,8 @@ export class PlayContextImpl {
|
|
|
1301
1314
|
staleAfterSeconds?: number | null;
|
|
1302
1315
|
repairRunningReceiptForSameRun?: boolean;
|
|
1303
1316
|
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
1317
|
+
runningReceiptWaitMaxAttempts?: number;
|
|
1318
|
+
runningReceiptWaitDelayMs?: number;
|
|
1304
1319
|
reclaimRunning?: boolean;
|
|
1305
1320
|
markSkipped?: (output: T) => Promise<void> | void;
|
|
1306
1321
|
onRecovered?: (
|
|
@@ -1332,6 +1347,8 @@ export class PlayContextImpl {
|
|
|
1332
1347
|
repairRunningReceiptForSameRun: opts.repairRunningReceiptForSameRun,
|
|
1333
1348
|
repairRunningReceiptForSameRunAfterWaitTimeout:
|
|
1334
1349
|
opts.repairRunningReceiptForSameRunAfterWaitTimeout,
|
|
1350
|
+
runningReceiptWaitMaxAttempts: opts.runningReceiptWaitMaxAttempts,
|
|
1351
|
+
runningReceiptWaitDelayMs: opts.runningReceiptWaitDelayMs,
|
|
1335
1352
|
reclaimRunning: opts.reclaimRunning,
|
|
1336
1353
|
markSkipped: opts.markSkipped,
|
|
1337
1354
|
onRecovered: opts.onRecovered,
|
|
@@ -3449,7 +3466,9 @@ export class PlayContextImpl {
|
|
|
3449
3466
|
});
|
|
3450
3467
|
}
|
|
3451
3468
|
this.log(`Calling tool: ${toolId}`);
|
|
3452
|
-
const execution = await this.callToolExecutionAPI(toolId, input
|
|
3469
|
+
const execution = await this.callToolExecutionAPI(toolId, input, {
|
|
3470
|
+
timeoutMs: options?.timeoutMs,
|
|
3471
|
+
});
|
|
3453
3472
|
const wrapped = await this.wrapToolExecutionResult({
|
|
3454
3473
|
toolId,
|
|
3455
3474
|
status: execution.status,
|
|
@@ -3535,6 +3554,12 @@ export class PlayContextImpl {
|
|
|
3535
3554
|
fieldName,
|
|
3536
3555
|
toolId,
|
|
3537
3556
|
input,
|
|
3557
|
+
...(typeof options?.timeoutMs === 'number'
|
|
3558
|
+
? { timeoutMs: options.timeoutMs }
|
|
3559
|
+
: {}),
|
|
3560
|
+
...(typeof options?.receiptWaitMs === 'number'
|
|
3561
|
+
? { receiptWaitMs: options.receiptWaitMs }
|
|
3562
|
+
: {}),
|
|
3538
3563
|
tableNamespace: store.tableNamespace,
|
|
3539
3564
|
rowKey: store.rowKey ?? null,
|
|
3540
3565
|
description: normalizeStepDescription(options?.description),
|
|
@@ -3573,6 +3598,11 @@ export class PlayContextImpl {
|
|
|
3573
3598
|
receiptKey: durableCacheKey,
|
|
3574
3599
|
}),
|
|
3575
3600
|
),
|
|
3601
|
+
runningReceiptWaitMaxAttempts: resolveRuntimeToolReceiptWaitMaxAttempts(
|
|
3602
|
+
typeof options?.receiptWaitMs === 'number'
|
|
3603
|
+
? { max_wait_ms: options.receiptWaitMs }
|
|
3604
|
+
: input,
|
|
3605
|
+
),
|
|
3576
3606
|
execute: executeTool,
|
|
3577
3607
|
},
|
|
3578
3608
|
);
|
|
@@ -3853,6 +3883,8 @@ export class PlayContextImpl {
|
|
|
3853
3883
|
);
|
|
3854
3884
|
},
|
|
3855
3885
|
repairRunningReceiptForSameRunAfterWaitTimeout: true,
|
|
3886
|
+
runningReceiptWaitMaxAttempts:
|
|
3887
|
+
resolveRuntimeToolReceiptWaitMaxAttempts(input),
|
|
3856
3888
|
execute: executePlayCall,
|
|
3857
3889
|
},
|
|
3858
3890
|
);
|
|
@@ -4334,6 +4366,49 @@ export class PlayContextImpl {
|
|
|
4334
4366
|
await this.rejectToolCall(toolId, request, error);
|
|
4335
4367
|
}
|
|
4336
4368
|
};
|
|
4369
|
+
const requestsWithLiveFollowers = (
|
|
4370
|
+
owner: ToolCallRequest,
|
|
4371
|
+
): ToolCallRequest[] => [
|
|
4372
|
+
owner,
|
|
4373
|
+
...(liveFollowersByOwnerCallId.get(owner.callId) ?? []),
|
|
4374
|
+
];
|
|
4375
|
+
const resolveRuntimeTimeoutMsForRequests = (
|
|
4376
|
+
requests: ToolCallRequest[],
|
|
4377
|
+
): number | undefined => {
|
|
4378
|
+
const timeoutMs = Math.max(
|
|
4379
|
+
...requests
|
|
4380
|
+
.map((request) => request.timeoutMs)
|
|
4381
|
+
.filter(
|
|
4382
|
+
(candidate): candidate is number =>
|
|
4383
|
+
typeof candidate === 'number' &&
|
|
4384
|
+
Number.isFinite(candidate) &&
|
|
4385
|
+
candidate > 0,
|
|
4386
|
+
),
|
|
4387
|
+
0,
|
|
4388
|
+
);
|
|
4389
|
+
return timeoutMs > 0 ? timeoutMs : undefined;
|
|
4390
|
+
};
|
|
4391
|
+
const resolveRuntimeTimeoutMsForClaimedOwners = (
|
|
4392
|
+
owners: ToolCallRequest[],
|
|
4393
|
+
): number | undefined => {
|
|
4394
|
+
let timeoutMs = 0;
|
|
4395
|
+
for (const owner of owners) {
|
|
4396
|
+
if (
|
|
4397
|
+
typeof owner.timeoutMs !== 'number' ||
|
|
4398
|
+
!Number.isFinite(owner.timeoutMs) ||
|
|
4399
|
+
owner.timeoutMs <= 0
|
|
4400
|
+
) {
|
|
4401
|
+
return undefined;
|
|
4402
|
+
}
|
|
4403
|
+
timeoutMs = Math.max(
|
|
4404
|
+
timeoutMs,
|
|
4405
|
+
resolveRuntimeTimeoutMsForRequests(
|
|
4406
|
+
requestsWithLiveFollowers(owner),
|
|
4407
|
+
) ?? 0,
|
|
4408
|
+
);
|
|
4409
|
+
}
|
|
4410
|
+
return timeoutMs > 0 ? timeoutMs : undefined;
|
|
4411
|
+
};
|
|
4337
4412
|
|
|
4338
4413
|
const durableExistingRunningHandlers: Promise<void>[] = [];
|
|
4339
4414
|
if (
|
|
@@ -4452,10 +4527,25 @@ export class PlayContextImpl {
|
|
|
4452
4527
|
receiptKey: string,
|
|
4453
4528
|
requestsForKey: ToolCallRequest[],
|
|
4454
4529
|
): Promise<void> => {
|
|
4530
|
+
const waitMaxAttempts =
|
|
4531
|
+
requestsForKey.length > 0
|
|
4532
|
+
? Math.max(
|
|
4533
|
+
...requestsForKey.map((request) =>
|
|
4534
|
+
resolveRuntimeToolReceiptWaitMaxAttempts(
|
|
4535
|
+
typeof request.receiptWaitMs === 'number'
|
|
4536
|
+
? { max_wait_ms: request.receiptWaitMs }
|
|
4537
|
+
: request.input,
|
|
4538
|
+
),
|
|
4539
|
+
),
|
|
4540
|
+
)
|
|
4541
|
+
: undefined;
|
|
4455
4542
|
try {
|
|
4456
4543
|
await resolveRequestsFromReceipt(
|
|
4457
4544
|
requestsForKey,
|
|
4458
|
-
await this.waitForCompletedRuntimeToolReceipt(
|
|
4545
|
+
await this.waitForCompletedRuntimeToolReceipt(
|
|
4546
|
+
receiptKey,
|
|
4547
|
+
waitMaxAttempts,
|
|
4548
|
+
),
|
|
4459
4549
|
'in_flight',
|
|
4460
4550
|
);
|
|
4461
4551
|
return;
|
|
@@ -4511,6 +4601,9 @@ export class PlayContextImpl {
|
|
|
4511
4601
|
const execution = await this.callToolExecutionAPI(
|
|
4512
4602
|
toolId,
|
|
4513
4603
|
owner.input,
|
|
4604
|
+
{
|
|
4605
|
+
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([owner]),
|
|
4606
|
+
},
|
|
4514
4607
|
);
|
|
4515
4608
|
const result = await this.resolveToolCall(
|
|
4516
4609
|
toolId,
|
|
@@ -4619,6 +4712,11 @@ export class PlayContextImpl {
|
|
|
4619
4712
|
await this.callToolAPI(
|
|
4620
4713
|
batch.batchOperation,
|
|
4621
4714
|
batch.batchPayload,
|
|
4715
|
+
{
|
|
4716
|
+
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners(
|
|
4717
|
+
batch.memberRequests,
|
|
4718
|
+
),
|
|
4719
|
+
},
|
|
4622
4720
|
),
|
|
4623
4721
|
onChunkComplete: async (chunkResults) => {
|
|
4624
4722
|
for (const entry of chunkResults) {
|
|
@@ -4674,6 +4772,11 @@ export class PlayContextImpl {
|
|
|
4674
4772
|
const execution = await this.callToolExecutionAPI(
|
|
4675
4773
|
toolId,
|
|
4676
4774
|
request.input,
|
|
4775
|
+
{
|
|
4776
|
+
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([
|
|
4777
|
+
request,
|
|
4778
|
+
]),
|
|
4779
|
+
},
|
|
4677
4780
|
);
|
|
4678
4781
|
const result = await this.resolveToolCall(
|
|
4679
4782
|
toolId,
|
|
@@ -4778,8 +4881,9 @@ export class PlayContextImpl {
|
|
|
4778
4881
|
private async callToolAPI(
|
|
4779
4882
|
toolId: string,
|
|
4780
4883
|
input: Record<string, unknown>,
|
|
4884
|
+
options?: { timeoutMs?: number },
|
|
4781
4885
|
): Promise<unknown> {
|
|
4782
|
-
const execution = await this.callToolExecutionAPI(toolId, input);
|
|
4886
|
+
const execution = await this.callToolExecutionAPI(toolId, input, options);
|
|
4783
4887
|
if (execution.toolResponse && 'raw' in execution.toolResponse) {
|
|
4784
4888
|
return execution.toolResponse.raw;
|
|
4785
4889
|
}
|
|
@@ -4794,6 +4898,7 @@ export class PlayContextImpl {
|
|
|
4794
4898
|
private async callToolExecutionAPI(
|
|
4795
4899
|
toolId: string,
|
|
4796
4900
|
input: Record<string, unknown>,
|
|
4901
|
+
options?: { timeoutMs?: number },
|
|
4797
4902
|
): Promise<ParsedToolExecuteResponse> {
|
|
4798
4903
|
if (!this.#options.executorToken || !this.#options.baseUrl) {
|
|
4799
4904
|
throw new Error(
|
|
@@ -4801,6 +4906,11 @@ export class PlayContextImpl {
|
|
|
4801
4906
|
);
|
|
4802
4907
|
}
|
|
4803
4908
|
const url = `${this.#options.baseUrl}/api/v2/integrations/${encodeURIComponent(toolId)}/execute`;
|
|
4909
|
+
const timeoutMs =
|
|
4910
|
+
typeof options?.timeoutMs === 'number' &&
|
|
4911
|
+
Number.isFinite(options.timeoutMs)
|
|
4912
|
+
? Math.max(1, Math.ceil(options.timeoutMs))
|
|
4913
|
+
: undefined;
|
|
4804
4914
|
|
|
4805
4915
|
// The Governor's tool slot is the single seam for tool-call budget + the
|
|
4806
4916
|
// global tool-concurrency backstop + per-(org, provider) pacing. It blocks
|
|
@@ -4831,9 +4941,20 @@ export class PlayContextImpl {
|
|
|
4831
4941
|
|
|
4832
4942
|
while (true) {
|
|
4833
4943
|
let response: Response;
|
|
4944
|
+
const abortController = timeoutMs ? new AbortController() : null;
|
|
4945
|
+
const timeoutHandle = timeoutMs
|
|
4946
|
+
? setTimeout(() => {
|
|
4947
|
+
abortController?.abort(
|
|
4948
|
+
new Error(
|
|
4949
|
+
`Tool ${toolId} runtime API call timed out after ${timeoutMs}ms.`,
|
|
4950
|
+
),
|
|
4951
|
+
);
|
|
4952
|
+
}, timeoutMs)
|
|
4953
|
+
: null;
|
|
4834
4954
|
try {
|
|
4835
4955
|
response = await fetch(url, {
|
|
4836
4956
|
method: 'POST',
|
|
4957
|
+
signal: abortController?.signal,
|
|
4837
4958
|
headers: {
|
|
4838
4959
|
'Content-Type': 'application/json',
|
|
4839
4960
|
Authorization: `Bearer ${this.#options.executorToken}`,
|
|
@@ -4856,6 +4977,13 @@ export class PlayContextImpl {
|
|
|
4856
4977
|
}),
|
|
4857
4978
|
});
|
|
4858
4979
|
} catch (error) {
|
|
4980
|
+
if (abortController?.signal.aborted) {
|
|
4981
|
+
throw abortController.signal.reason instanceof Error
|
|
4982
|
+
? abortController.signal.reason
|
|
4983
|
+
: new Error(
|
|
4984
|
+
`Tool ${toolId} runtime API call timed out after ${timeoutMs}ms.`,
|
|
4985
|
+
);
|
|
4986
|
+
}
|
|
4859
4987
|
transportAttempt += 1;
|
|
4860
4988
|
const message =
|
|
4861
4989
|
error instanceof Error ? error.message : String(error);
|
|
@@ -4876,6 +5004,10 @@ export class PlayContextImpl {
|
|
|
4876
5004
|
throw new Error(
|
|
4877
5005
|
`Tool ${toolId} transport failed calling ${url} after ${transportAttempt}/${TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS} attempts: ${message}`,
|
|
4878
5006
|
);
|
|
5007
|
+
} finally {
|
|
5008
|
+
if (timeoutHandle) {
|
|
5009
|
+
clearTimeout(timeoutHandle);
|
|
5010
|
+
}
|
|
4879
5011
|
}
|
|
4880
5012
|
|
|
4881
5013
|
span.setAttribute('plays.http_status_code', response.status);
|