deepline 0.1.169 → 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/dedup-do.ts +1 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +65 -6
- 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 +132 -4
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +7 -3
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.mjs +2 -2
- 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());
|
|
@@ -6280,6 +6335,10 @@ function createMinimalWorkerCtx(
|
|
|
6280
6335
|
force: request.force === true,
|
|
6281
6336
|
staleAfterSeconds: request.staleAfterSeconds,
|
|
6282
6337
|
},
|
|
6338
|
+
{
|
|
6339
|
+
timeoutMs: request.timeoutMs,
|
|
6340
|
+
receiptWaitMs: request.receiptWaitMs,
|
|
6341
|
+
},
|
|
6283
6342
|
);
|
|
6284
6343
|
},
|
|
6285
6344
|
},
|
|
@@ -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.170',
|
|
108
108
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
109
109
|
supportPolicy: {
|
|
110
|
-
latest: '0.1.
|
|
110
|
+
latest: '0.1.170',
|
|
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,7 @@ export class PlayContextImpl {
|
|
|
1301
1314
|
staleAfterSeconds?: number | null;
|
|
1302
1315
|
repairRunningReceiptForSameRun?: boolean;
|
|
1303
1316
|
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
1317
|
+
runningReceiptWaitMaxAttempts?: number;
|
|
1304
1318
|
reclaimRunning?: boolean;
|
|
1305
1319
|
markSkipped?: (output: T) => Promise<void> | void;
|
|
1306
1320
|
onRecovered?: (
|
|
@@ -1332,6 +1346,7 @@ export class PlayContextImpl {
|
|
|
1332
1346
|
repairRunningReceiptForSameRun: opts.repairRunningReceiptForSameRun,
|
|
1333
1347
|
repairRunningReceiptForSameRunAfterWaitTimeout:
|
|
1334
1348
|
opts.repairRunningReceiptForSameRunAfterWaitTimeout,
|
|
1349
|
+
runningReceiptWaitMaxAttempts: opts.runningReceiptWaitMaxAttempts,
|
|
1335
1350
|
reclaimRunning: opts.reclaimRunning,
|
|
1336
1351
|
markSkipped: opts.markSkipped,
|
|
1337
1352
|
onRecovered: opts.onRecovered,
|
|
@@ -3449,7 +3464,9 @@ export class PlayContextImpl {
|
|
|
3449
3464
|
});
|
|
3450
3465
|
}
|
|
3451
3466
|
this.log(`Calling tool: ${toolId}`);
|
|
3452
|
-
const execution = await this.callToolExecutionAPI(toolId, input
|
|
3467
|
+
const execution = await this.callToolExecutionAPI(toolId, input, {
|
|
3468
|
+
timeoutMs: options?.timeoutMs,
|
|
3469
|
+
});
|
|
3453
3470
|
const wrapped = await this.wrapToolExecutionResult({
|
|
3454
3471
|
toolId,
|
|
3455
3472
|
status: execution.status,
|
|
@@ -3535,6 +3552,12 @@ export class PlayContextImpl {
|
|
|
3535
3552
|
fieldName,
|
|
3536
3553
|
toolId,
|
|
3537
3554
|
input,
|
|
3555
|
+
...(typeof options?.timeoutMs === 'number'
|
|
3556
|
+
? { timeoutMs: options.timeoutMs }
|
|
3557
|
+
: {}),
|
|
3558
|
+
...(typeof options?.receiptWaitMs === 'number'
|
|
3559
|
+
? { receiptWaitMs: options.receiptWaitMs }
|
|
3560
|
+
: {}),
|
|
3538
3561
|
tableNamespace: store.tableNamespace,
|
|
3539
3562
|
rowKey: store.rowKey ?? null,
|
|
3540
3563
|
description: normalizeStepDescription(options?.description),
|
|
@@ -3573,6 +3596,11 @@ export class PlayContextImpl {
|
|
|
3573
3596
|
receiptKey: durableCacheKey,
|
|
3574
3597
|
}),
|
|
3575
3598
|
),
|
|
3599
|
+
runningReceiptWaitMaxAttempts: resolveRuntimeToolReceiptWaitMaxAttempts(
|
|
3600
|
+
typeof options?.receiptWaitMs === 'number'
|
|
3601
|
+
? { max_wait_ms: options.receiptWaitMs }
|
|
3602
|
+
: input,
|
|
3603
|
+
),
|
|
3576
3604
|
execute: executeTool,
|
|
3577
3605
|
},
|
|
3578
3606
|
);
|
|
@@ -4334,6 +4362,49 @@ export class PlayContextImpl {
|
|
|
4334
4362
|
await this.rejectToolCall(toolId, request, error);
|
|
4335
4363
|
}
|
|
4336
4364
|
};
|
|
4365
|
+
const requestsWithLiveFollowers = (
|
|
4366
|
+
owner: ToolCallRequest,
|
|
4367
|
+
): ToolCallRequest[] => [
|
|
4368
|
+
owner,
|
|
4369
|
+
...(liveFollowersByOwnerCallId.get(owner.callId) ?? []),
|
|
4370
|
+
];
|
|
4371
|
+
const resolveRuntimeTimeoutMsForRequests = (
|
|
4372
|
+
requests: ToolCallRequest[],
|
|
4373
|
+
): number | undefined => {
|
|
4374
|
+
const timeoutMs = Math.max(
|
|
4375
|
+
...requests
|
|
4376
|
+
.map((request) => request.timeoutMs)
|
|
4377
|
+
.filter(
|
|
4378
|
+
(candidate): candidate is number =>
|
|
4379
|
+
typeof candidate === 'number' &&
|
|
4380
|
+
Number.isFinite(candidate) &&
|
|
4381
|
+
candidate > 0,
|
|
4382
|
+
),
|
|
4383
|
+
0,
|
|
4384
|
+
);
|
|
4385
|
+
return timeoutMs > 0 ? timeoutMs : undefined;
|
|
4386
|
+
};
|
|
4387
|
+
const resolveRuntimeTimeoutMsForClaimedOwners = (
|
|
4388
|
+
owners: ToolCallRequest[],
|
|
4389
|
+
): number | undefined => {
|
|
4390
|
+
let timeoutMs = 0;
|
|
4391
|
+
for (const owner of owners) {
|
|
4392
|
+
if (
|
|
4393
|
+
typeof owner.timeoutMs !== 'number' ||
|
|
4394
|
+
!Number.isFinite(owner.timeoutMs) ||
|
|
4395
|
+
owner.timeoutMs <= 0
|
|
4396
|
+
) {
|
|
4397
|
+
return undefined;
|
|
4398
|
+
}
|
|
4399
|
+
timeoutMs = Math.max(
|
|
4400
|
+
timeoutMs,
|
|
4401
|
+
resolveRuntimeTimeoutMsForRequests(
|
|
4402
|
+
requestsWithLiveFollowers(owner),
|
|
4403
|
+
) ?? 0,
|
|
4404
|
+
);
|
|
4405
|
+
}
|
|
4406
|
+
return timeoutMs > 0 ? timeoutMs : undefined;
|
|
4407
|
+
};
|
|
4337
4408
|
|
|
4338
4409
|
const durableExistingRunningHandlers: Promise<void>[] = [];
|
|
4339
4410
|
if (
|
|
@@ -4452,10 +4523,25 @@ export class PlayContextImpl {
|
|
|
4452
4523
|
receiptKey: string,
|
|
4453
4524
|
requestsForKey: ToolCallRequest[],
|
|
4454
4525
|
): Promise<void> => {
|
|
4526
|
+
const waitMaxAttempts =
|
|
4527
|
+
requestsForKey.length > 0
|
|
4528
|
+
? Math.max(
|
|
4529
|
+
...requestsForKey.map((request) =>
|
|
4530
|
+
resolveRuntimeToolReceiptWaitMaxAttempts(
|
|
4531
|
+
typeof request.receiptWaitMs === 'number'
|
|
4532
|
+
? { max_wait_ms: request.receiptWaitMs }
|
|
4533
|
+
: request.input,
|
|
4534
|
+
),
|
|
4535
|
+
),
|
|
4536
|
+
)
|
|
4537
|
+
: undefined;
|
|
4455
4538
|
try {
|
|
4456
4539
|
await resolveRequestsFromReceipt(
|
|
4457
4540
|
requestsForKey,
|
|
4458
|
-
await this.waitForCompletedRuntimeToolReceipt(
|
|
4541
|
+
await this.waitForCompletedRuntimeToolReceipt(
|
|
4542
|
+
receiptKey,
|
|
4543
|
+
waitMaxAttempts,
|
|
4544
|
+
),
|
|
4459
4545
|
'in_flight',
|
|
4460
4546
|
);
|
|
4461
4547
|
return;
|
|
@@ -4511,6 +4597,9 @@ export class PlayContextImpl {
|
|
|
4511
4597
|
const execution = await this.callToolExecutionAPI(
|
|
4512
4598
|
toolId,
|
|
4513
4599
|
owner.input,
|
|
4600
|
+
{
|
|
4601
|
+
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([owner]),
|
|
4602
|
+
},
|
|
4514
4603
|
);
|
|
4515
4604
|
const result = await this.resolveToolCall(
|
|
4516
4605
|
toolId,
|
|
@@ -4619,6 +4708,11 @@ export class PlayContextImpl {
|
|
|
4619
4708
|
await this.callToolAPI(
|
|
4620
4709
|
batch.batchOperation,
|
|
4621
4710
|
batch.batchPayload,
|
|
4711
|
+
{
|
|
4712
|
+
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners(
|
|
4713
|
+
batch.memberRequests,
|
|
4714
|
+
),
|
|
4715
|
+
},
|
|
4622
4716
|
),
|
|
4623
4717
|
onChunkComplete: async (chunkResults) => {
|
|
4624
4718
|
for (const entry of chunkResults) {
|
|
@@ -4674,6 +4768,11 @@ export class PlayContextImpl {
|
|
|
4674
4768
|
const execution = await this.callToolExecutionAPI(
|
|
4675
4769
|
toolId,
|
|
4676
4770
|
request.input,
|
|
4771
|
+
{
|
|
4772
|
+
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([
|
|
4773
|
+
request,
|
|
4774
|
+
]),
|
|
4775
|
+
},
|
|
4677
4776
|
);
|
|
4678
4777
|
const result = await this.resolveToolCall(
|
|
4679
4778
|
toolId,
|
|
@@ -4778,8 +4877,9 @@ export class PlayContextImpl {
|
|
|
4778
4877
|
private async callToolAPI(
|
|
4779
4878
|
toolId: string,
|
|
4780
4879
|
input: Record<string, unknown>,
|
|
4880
|
+
options?: { timeoutMs?: number },
|
|
4781
4881
|
): Promise<unknown> {
|
|
4782
|
-
const execution = await this.callToolExecutionAPI(toolId, input);
|
|
4882
|
+
const execution = await this.callToolExecutionAPI(toolId, input, options);
|
|
4783
4883
|
if (execution.toolResponse && 'raw' in execution.toolResponse) {
|
|
4784
4884
|
return execution.toolResponse.raw;
|
|
4785
4885
|
}
|
|
@@ -4794,6 +4894,7 @@ export class PlayContextImpl {
|
|
|
4794
4894
|
private async callToolExecutionAPI(
|
|
4795
4895
|
toolId: string,
|
|
4796
4896
|
input: Record<string, unknown>,
|
|
4897
|
+
options?: { timeoutMs?: number },
|
|
4797
4898
|
): Promise<ParsedToolExecuteResponse> {
|
|
4798
4899
|
if (!this.#options.executorToken || !this.#options.baseUrl) {
|
|
4799
4900
|
throw new Error(
|
|
@@ -4801,6 +4902,11 @@ export class PlayContextImpl {
|
|
|
4801
4902
|
);
|
|
4802
4903
|
}
|
|
4803
4904
|
const url = `${this.#options.baseUrl}/api/v2/integrations/${encodeURIComponent(toolId)}/execute`;
|
|
4905
|
+
const timeoutMs =
|
|
4906
|
+
typeof options?.timeoutMs === 'number' &&
|
|
4907
|
+
Number.isFinite(options.timeoutMs)
|
|
4908
|
+
? Math.max(1, Math.ceil(options.timeoutMs))
|
|
4909
|
+
: undefined;
|
|
4804
4910
|
|
|
4805
4911
|
// The Governor's tool slot is the single seam for tool-call budget + the
|
|
4806
4912
|
// global tool-concurrency backstop + per-(org, provider) pacing. It blocks
|
|
@@ -4831,9 +4937,20 @@ export class PlayContextImpl {
|
|
|
4831
4937
|
|
|
4832
4938
|
while (true) {
|
|
4833
4939
|
let response: Response;
|
|
4940
|
+
const abortController = timeoutMs ? new AbortController() : null;
|
|
4941
|
+
const timeoutHandle = timeoutMs
|
|
4942
|
+
? setTimeout(() => {
|
|
4943
|
+
abortController?.abort(
|
|
4944
|
+
new Error(
|
|
4945
|
+
`Tool ${toolId} runtime API call timed out after ${timeoutMs}ms.`,
|
|
4946
|
+
),
|
|
4947
|
+
);
|
|
4948
|
+
}, timeoutMs)
|
|
4949
|
+
: null;
|
|
4834
4950
|
try {
|
|
4835
4951
|
response = await fetch(url, {
|
|
4836
4952
|
method: 'POST',
|
|
4953
|
+
signal: abortController?.signal,
|
|
4837
4954
|
headers: {
|
|
4838
4955
|
'Content-Type': 'application/json',
|
|
4839
4956
|
Authorization: `Bearer ${this.#options.executorToken}`,
|
|
@@ -4856,6 +4973,13 @@ export class PlayContextImpl {
|
|
|
4856
4973
|
}),
|
|
4857
4974
|
});
|
|
4858
4975
|
} catch (error) {
|
|
4976
|
+
if (abortController?.signal.aborted) {
|
|
4977
|
+
throw abortController.signal.reason instanceof Error
|
|
4978
|
+
? abortController.signal.reason
|
|
4979
|
+
: new Error(
|
|
4980
|
+
`Tool ${toolId} runtime API call timed out after ${timeoutMs}ms.`,
|
|
4981
|
+
);
|
|
4982
|
+
}
|
|
4859
4983
|
transportAttempt += 1;
|
|
4860
4984
|
const message =
|
|
4861
4985
|
error instanceof Error ? error.message : String(error);
|
|
@@ -4876,6 +5000,10 @@ export class PlayContextImpl {
|
|
|
4876
5000
|
throw new Error(
|
|
4877
5001
|
`Tool ${toolId} transport failed calling ${url} after ${transportAttempt}/${TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS} attempts: ${message}`,
|
|
4878
5002
|
);
|
|
5003
|
+
} finally {
|
|
5004
|
+
if (timeoutHandle) {
|
|
5005
|
+
clearTimeout(timeoutHandle);
|
|
5006
|
+
}
|
|
4879
5007
|
}
|
|
4880
5008
|
|
|
4881
5009
|
span.setAttribute('plays.http_status_code', response.status);
|
|
@@ -28,6 +28,8 @@ export interface ToolCallRequest {
|
|
|
28
28
|
fieldName?: string;
|
|
29
29
|
toolId: string;
|
|
30
30
|
input: Record<string, unknown>;
|
|
31
|
+
timeoutMs?: number;
|
|
32
|
+
receiptWaitMs?: number;
|
|
31
33
|
tableNamespace?: string;
|
|
32
34
|
rowKey?: string | null;
|
|
33
35
|
description?: string;
|
|
@@ -169,6 +171,8 @@ export interface ToolCallOptions {
|
|
|
169
171
|
description?: string;
|
|
170
172
|
force?: boolean;
|
|
171
173
|
staleAfterSeconds?: number;
|
|
174
|
+
timeoutMs?: number;
|
|
175
|
+
receiptWaitMs?: number;
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
export interface ToolExecutionRequest {
|
|
@@ -178,6 +182,8 @@ export interface ToolExecutionRequest {
|
|
|
178
182
|
description?: string;
|
|
179
183
|
force?: boolean;
|
|
180
184
|
staleAfterSeconds?: number;
|
|
185
|
+
timeoutMs?: number;
|
|
186
|
+
receiptWaitMs?: number;
|
|
181
187
|
}
|
|
182
188
|
|
|
183
189
|
export type SqlQuery = {
|
|
@@ -453,9 +459,7 @@ export interface ContextOptions {
|
|
|
453
459
|
getToolRetryPolicy?: (toolId: string) => Promise<{
|
|
454
460
|
retrySafeTransientHttp?: boolean;
|
|
455
461
|
} | null>;
|
|
456
|
-
getToolActionCacheVersion?: (
|
|
457
|
-
toolId: string,
|
|
458
|
-
) => Promise<string> | string;
|
|
462
|
+
getToolActionCacheVersion?: (toolId: string) => Promise<string> | string;
|
|
459
463
|
getToolTargetGetters?: (
|
|
460
464
|
toolId: string,
|
|
461
465
|
output: string,
|
package/dist/cli/index.js
CHANGED
|
@@ -622,10 +622,10 @@ var SDK_RELEASE = {
|
|
|
622
622
|
// 0.1.111 ships dataset-native tool list getters and result row datasets.
|
|
623
623
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
624
624
|
// fields shipped in 0.1.153.
|
|
625
|
-
version: "0.1.
|
|
625
|
+
version: "0.1.170",
|
|
626
626
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
627
627
|
supportPolicy: {
|
|
628
|
-
latest: "0.1.
|
|
628
|
+
latest: "0.1.170",
|
|
629
629
|
minimumSupported: "0.1.53",
|
|
630
630
|
deprecatedBelow: "0.1.53",
|
|
631
631
|
commandMinimumSupported: [
|
package/dist/cli/index.mjs
CHANGED
|
@@ -607,10 +607,10 @@ var SDK_RELEASE = {
|
|
|
607
607
|
// 0.1.111 ships dataset-native tool list getters and result row datasets.
|
|
608
608
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
609
609
|
// fields shipped in 0.1.153.
|
|
610
|
-
version: "0.1.
|
|
610
|
+
version: "0.1.170",
|
|
611
611
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
612
612
|
supportPolicy: {
|
|
613
|
-
latest: "0.1.
|
|
613
|
+
latest: "0.1.170",
|
|
614
614
|
minimumSupported: "0.1.53",
|
|
615
615
|
deprecatedBelow: "0.1.53",
|
|
616
616
|
commandMinimumSupported: [
|
package/dist/index.d.mts
CHANGED
|
@@ -2948,6 +2948,10 @@ type ToolExecutionRequest = {
|
|
|
2948
2948
|
force?: boolean;
|
|
2949
2949
|
/** Numeric TTL in seconds for this tool checkpoint. */
|
|
2950
2950
|
staleAfterSeconds?: number;
|
|
2951
|
+
/** Runtime transport timeout in milliseconds. This is not sent to the provider. */
|
|
2952
|
+
timeoutMs?: number;
|
|
2953
|
+
/** Follower wait budget in milliseconds before a running receipt is reclaimable. */
|
|
2954
|
+
receiptWaitMs?: number;
|
|
2951
2955
|
};
|
|
2952
2956
|
type StepResolver<Row, Value> = (row: Row, ctx: DeeplinePlayRuntimeContext, index: number, previousCell?: PreviousCell<Value>) => Value | Promise<Value>;
|
|
2953
2957
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -2948,6 +2948,10 @@ type ToolExecutionRequest = {
|
|
|
2948
2948
|
force?: boolean;
|
|
2949
2949
|
/** Numeric TTL in seconds for this tool checkpoint. */
|
|
2950
2950
|
staleAfterSeconds?: number;
|
|
2951
|
+
/** Runtime transport timeout in milliseconds. This is not sent to the provider. */
|
|
2952
|
+
timeoutMs?: number;
|
|
2953
|
+
/** Follower wait budget in milliseconds before a running receipt is reclaimable. */
|
|
2954
|
+
receiptWaitMs?: number;
|
|
2951
2955
|
};
|
|
2952
2956
|
type StepResolver<Row, Value> = (row: Row, ctx: DeeplinePlayRuntimeContext, index: number, previousCell?: PreviousCell<Value>) => Value | Promise<Value>;
|
|
2953
2957
|
/**
|
package/dist/index.js
CHANGED
|
@@ -421,10 +421,10 @@ var SDK_RELEASE = {
|
|
|
421
421
|
// 0.1.111 ships dataset-native tool list getters and result row datasets.
|
|
422
422
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
423
423
|
// fields shipped in 0.1.153.
|
|
424
|
-
version: "0.1.
|
|
424
|
+
version: "0.1.170",
|
|
425
425
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
426
426
|
supportPolicy: {
|
|
427
|
-
latest: "0.1.
|
|
427
|
+
latest: "0.1.170",
|
|
428
428
|
minimumSupported: "0.1.53",
|
|
429
429
|
deprecatedBelow: "0.1.53",
|
|
430
430
|
commandMinimumSupported: [
|
package/dist/index.mjs
CHANGED
|
@@ -351,10 +351,10 @@ var SDK_RELEASE = {
|
|
|
351
351
|
// 0.1.111 ships dataset-native tool list getters and result row datasets.
|
|
352
352
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
353
353
|
// fields shipped in 0.1.153.
|
|
354
|
-
version: "0.1.
|
|
354
|
+
version: "0.1.170",
|
|
355
355
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
356
356
|
supportPolicy: {
|
|
357
|
-
latest: "0.1.
|
|
357
|
+
latest: "0.1.170",
|
|
358
358
|
minimumSupported: "0.1.53",
|
|
359
359
|
deprecatedBelow: "0.1.53",
|
|
360
360
|
commandMinimumSupported: [
|