deepline 0.1.198 → 0.1.200
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 +5 -1
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +51 -17
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/README.md +1 -1
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +16 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +40 -9
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +108 -39
- package/dist/bundling-sources/sdk/src/http.ts +5 -2
- package/dist/bundling-sources/sdk/src/play.ts +1 -1
- package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +7 -0
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/worker-play-entry.ts +11 -55
- package/dist/bundling-sources/shared_libs/play-runtime/adaptive-tool-dispatcher.ts +355 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +172 -73
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1794 -377
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-contract.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +67 -4
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +161 -39
- package/dist/bundling-sources/shared_libs/play-runtime/governor/adaptive-admission.ts +218 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +115 -25
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +17 -17
- package/dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +2 -1
- package/dist/bundling-sources/shared_libs/play-runtime/live-events.ts +0 -1
- package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +265 -0
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +135 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-input.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +0 -3
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +10 -1
- package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +9 -15
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-completion-sink.ts +262 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-heartbeat-supervisor.ts +64 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +76 -8
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-state-sink.ts +59 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +269 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/run-terminal-source.ts +0 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +433 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +227 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +882 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +573 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/bundle.ts +264 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/index.ts +33 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runner-events.ts +35 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +53 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +0 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +459 -103
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-constants.ts +12 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-infra-mode.ts +23 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-attempt-state-machine.ts +23 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +16 -24
- package/dist/bundling-sources/shared_libs/play-runtime/serial-task-queue.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/submit-limits.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +81 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +54 -9
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +17 -3
- package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +0 -20
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +105 -4
- package/dist/bundling-sources/shared_libs/plays/row-identity.ts +4 -11
- package/dist/cli/index.js +441 -101
- package/dist/cli/index.mjs +441 -101
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -8
- package/dist/index.mjs +8 -8
- package/dist/plays/bundle-play-file.mjs +15 -20
- package/package.json +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-diagnosis.ts +0 -321
- package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-target.ts +0 -158
- package/dist/bundling-sources/shared_libs/temporal/constants.ts +0 -39
- package/dist/bundling-sources/shared_libs/temporal/preview-config.ts +0 -150
|
@@ -5745,7 +5745,11 @@ async function handleCoordinatorWarmup(
|
|
|
5745
5745
|
`https://deepline.coordinator.internal/workflow/${encodeURIComponent(runId)}/submit`,
|
|
5746
5746
|
{
|
|
5747
5747
|
method: 'POST',
|
|
5748
|
-
headers: {
|
|
5748
|
+
headers: {
|
|
5749
|
+
'content-type': 'application/json',
|
|
5750
|
+
[COORDINATOR_INTERNAL_TOKEN_HEADER]:
|
|
5751
|
+
env.DEEPLINE_INTERNAL_TOKEN?.trim() ?? '',
|
|
5752
|
+
},
|
|
5749
5753
|
body: JSON.stringify(params),
|
|
5750
5754
|
},
|
|
5751
5755
|
),
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
import {
|
|
40
40
|
STANDARD_PLAY_RUNTIME_LIMIT_LABEL,
|
|
41
41
|
STANDARD_PLAY_RUNTIME_LIMIT_SECONDS,
|
|
42
|
-
} from '../../../shared_libs/
|
|
42
|
+
} from '../../../shared_libs/play-runtime/runtime-constants';
|
|
43
43
|
import {
|
|
44
44
|
createPlayExecutionGovernor,
|
|
45
45
|
type GovernanceSnapshot,
|
|
@@ -194,6 +194,11 @@ import {
|
|
|
194
194
|
runtimeLeaseHeartbeatIntervalMs,
|
|
195
195
|
} from '../../../shared_libs/play-runtime/lease-policy';
|
|
196
196
|
import { RuntimeSheetRowsBlockedError } from '../../../shared_libs/play-runtime/runtime-sheet-errors';
|
|
197
|
+
import {
|
|
198
|
+
resolveRuntimeMapRowAdmission,
|
|
199
|
+
runtimeMapJsonByteLength,
|
|
200
|
+
} from '../../../shared_libs/play-runtime/map-memory-limits';
|
|
201
|
+
import { createSerialTaskQueue } from '../../../shared_libs/play-runtime/serial-task-queue';
|
|
197
202
|
// The harness stub forwards leaf calls (validation, runtime-api HTTP) into
|
|
198
203
|
// the long-lived Play Harness Worker via env.HARNESS. We import the
|
|
199
204
|
// `setHarnessBinding` setter eagerly so it's available the moment
|
|
@@ -4440,10 +4445,14 @@ function createMinimalWorkerCtx(
|
|
|
4440
4445
|
);
|
|
4441
4446
|
const executeWithRuntimeReceipt = async <T>(
|
|
4442
4447
|
key: string,
|
|
4443
|
-
execute: (
|
|
4448
|
+
execute: (
|
|
4449
|
+
context: WorkerRuntimeReceiptExecutionContext,
|
|
4450
|
+
wasFailed: boolean,
|
|
4451
|
+
) => Promise<T> | T,
|
|
4444
4452
|
options: {
|
|
4445
4453
|
repairRunningReceiptForSameRun?: boolean;
|
|
4446
4454
|
reclaimRunning?: boolean;
|
|
4455
|
+
forceFailedRefresh?: boolean;
|
|
4447
4456
|
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
4448
4457
|
runningReceiptWaitMaxAttempts?: number;
|
|
4449
4458
|
} = {},
|
|
@@ -4454,11 +4463,12 @@ function createMinimalWorkerCtx(
|
|
|
4454
4463
|
runId: req.runId,
|
|
4455
4464
|
key,
|
|
4456
4465
|
receiptStore,
|
|
4457
|
-
execute: async (context) =>
|
|
4458
|
-
serializeDurableStepValue(await execute(context)),
|
|
4466
|
+
execute: async (context, wasFailed) =>
|
|
4467
|
+
serializeDurableStepValue(await execute(context, wasFailed)),
|
|
4459
4468
|
repairRunningReceiptForSameRun:
|
|
4460
4469
|
options.repairRunningReceiptForSameRun ?? false,
|
|
4461
4470
|
reclaimRunning: options.reclaimRunning ?? false,
|
|
4471
|
+
forceFailedRefresh: options.forceFailedRefresh ?? false,
|
|
4462
4472
|
repairRunningReceiptForSameRunAfterWaitTimeout:
|
|
4463
4473
|
options.repairRunningReceiptForSameRunAfterWaitTimeout ?? false,
|
|
4464
4474
|
runningReceiptWaitMaxAttempts: options.runningReceiptWaitMaxAttempts,
|
|
@@ -5131,7 +5141,32 @@ function createMinimalWorkerCtx(
|
|
|
5131
5141
|
// body additionally acquires a global row slot (the Governor's rowMax
|
|
5132
5142
|
// semaphore) so total in-flight rows across all maps in this isolate stay
|
|
5133
5143
|
// bounded even when several maps run at once.
|
|
5134
|
-
const
|
|
5144
|
+
const requestedRowConcurrency = governor.resolveRowConcurrency();
|
|
5145
|
+
const largestActiveRowBytes = uniqueRowsToExecuteEntries.reduce(
|
|
5146
|
+
(largest, entry) =>
|
|
5147
|
+
Math.max(
|
|
5148
|
+
largest,
|
|
5149
|
+
runtimeMapJsonByteLength(
|
|
5150
|
+
runtimeCsvExecutionRow(
|
|
5151
|
+
entry.row,
|
|
5152
|
+
pendingRowsByKey.get(entry.rowKey),
|
|
5153
|
+
),
|
|
5154
|
+
),
|
|
5155
|
+
),
|
|
5156
|
+
0,
|
|
5157
|
+
);
|
|
5158
|
+
const rowAdmission = resolveRuntimeMapRowAdmission({
|
|
5159
|
+
rowCount: rowsToExecute.length,
|
|
5160
|
+
requestedConcurrency: requestedRowConcurrency,
|
|
5161
|
+
largestRowBytes: largestActiveRowBytes,
|
|
5162
|
+
activeRowsBudgetBytes: WORKERS_EDGE_MAP_RESIDENT_ROW_BYTES,
|
|
5163
|
+
});
|
|
5164
|
+
const concurrency = rowAdmission.concurrency;
|
|
5165
|
+
if (rowsToExecute.length > 0 && concurrency === 0) {
|
|
5166
|
+
throw new Error(
|
|
5167
|
+
`RUNTIME_MAP_MEMORY_LIMIT_EXCEEDED:${name} row needs ${rowAdmission.estimatedBytesPerActiveRow} bytes; limit ${WORKERS_EDGE_MAP_RESIDENT_ROW_BYTES}. Reduce or stage blobs.`,
|
|
5168
|
+
);
|
|
5169
|
+
}
|
|
5135
5170
|
const executedRows: Array<T & Record<string, unknown>> = new Array(
|
|
5136
5171
|
rowsToExecute.length,
|
|
5137
5172
|
);
|
|
@@ -5191,11 +5226,10 @@ function createMinimalWorkerCtx(
|
|
|
5191
5226
|
let pendingPersistRows = 0;
|
|
5192
5227
|
let pendingPersistBytes = 0;
|
|
5193
5228
|
let scheduledPersistTimer: ReturnType<typeof setTimeout> | null = null;
|
|
5194
|
-
let persistFlushChain: Promise<void> = Promise.resolve();
|
|
5195
5229
|
let persistFailure: unknown = null;
|
|
5196
5230
|
let liveUpdateTimer: ReturnType<typeof setTimeout> | null = null;
|
|
5197
|
-
let liveFlushChain: Promise<void> = Promise.resolve();
|
|
5198
5231
|
let liveUpdateFailures = 0;
|
|
5232
|
+
const sheetWriteQueue = createSerialTaskQueue();
|
|
5199
5233
|
|
|
5200
5234
|
const clearScheduledPersistTimer = () => {
|
|
5201
5235
|
if (scheduledPersistTimer) {
|
|
@@ -5326,12 +5360,12 @@ function createMinimalWorkerCtx(
|
|
|
5326
5360
|
clearScheduledPersistTimer();
|
|
5327
5361
|
pendingPersistRows = 0;
|
|
5328
5362
|
pendingPersistBytes = 0;
|
|
5329
|
-
const task =
|
|
5363
|
+
const task = sheetWriteQueue.enqueue(async () => {
|
|
5330
5364
|
const circuitError = runtimePersistenceCircuitError();
|
|
5331
5365
|
if (circuitError) throw circuitError;
|
|
5332
5366
|
await persistExecutedRows();
|
|
5333
5367
|
});
|
|
5334
|
-
|
|
5368
|
+
void task.catch((error) => {
|
|
5335
5369
|
persistFailure ??= error;
|
|
5336
5370
|
tripRuntimePersistenceLatch(persistenceLatch, error);
|
|
5337
5371
|
});
|
|
@@ -5363,11 +5397,11 @@ function createMinimalWorkerCtx(
|
|
|
5363
5397
|
const flushLiveRowUpdates = (): Promise<void> => {
|
|
5364
5398
|
clearLiveUpdateTimer();
|
|
5365
5399
|
if (pendingLiveUpdates.length === 0) {
|
|
5366
|
-
return
|
|
5400
|
+
return sheetWriteQueue.idle();
|
|
5367
5401
|
}
|
|
5368
5402
|
const updates = coalesceLiveUpdates(pendingLiveUpdates.splice(0));
|
|
5369
5403
|
const extraOutputFields = Array.from(generatedOutputFields);
|
|
5370
|
-
|
|
5404
|
+
return sheetWriteQueue.enqueue(async () => {
|
|
5371
5405
|
try {
|
|
5372
5406
|
await applyLiveMapRowUpdates({
|
|
5373
5407
|
req,
|
|
@@ -5390,8 +5424,6 @@ function createMinimalWorkerCtx(
|
|
|
5390
5424
|
}
|
|
5391
5425
|
}
|
|
5392
5426
|
});
|
|
5393
|
-
liveFlushChain = task.catch(() => undefined);
|
|
5394
|
-
return task;
|
|
5395
5427
|
};
|
|
5396
5428
|
|
|
5397
5429
|
const scheduleLiveRowUpdates = () => {
|
|
@@ -5849,9 +5881,8 @@ function createMinimalWorkerCtx(
|
|
|
5849
5881
|
});
|
|
5850
5882
|
try {
|
|
5851
5883
|
await flushLiveRowUpdates();
|
|
5852
|
-
await liveFlushChain;
|
|
5853
5884
|
await enqueuePersistExecutedRows();
|
|
5854
|
-
await
|
|
5885
|
+
await sheetWriteQueue.idle();
|
|
5855
5886
|
if (persistFailure) throw persistFailure;
|
|
5856
5887
|
recordRunnerPerfTrace({
|
|
5857
5888
|
req,
|
|
@@ -6897,7 +6928,7 @@ function createMinimalWorkerCtx(
|
|
|
6897
6928
|
});
|
|
6898
6929
|
return await executeWithRuntimeReceipt(
|
|
6899
6930
|
receiptKey,
|
|
6900
|
-
async () => {
|
|
6931
|
+
async (receiptContext, wasFailed) => {
|
|
6901
6932
|
// The Governor owns the play-call lineage: forkChild does the cycle
|
|
6902
6933
|
// guard, depth/per-parent/playCall/descendant budget charges, and
|
|
6903
6934
|
// returns the snapshot to thread into the child so budgets accumulate
|
|
@@ -6980,7 +7011,9 @@ function createMinimalWorkerCtx(
|
|
|
6980
7011
|
options?.timeoutMs == null && !childNeedsWorkflowScheduler,
|
|
6981
7012
|
body: {
|
|
6982
7013
|
name: resolvedName,
|
|
6983
|
-
childIdempotencyKey:
|
|
7014
|
+
childIdempotencyKey: wasFailed && receiptContext.leaseId
|
|
7015
|
+
? `${receiptKey}:claim:${receiptContext.leaseId}`
|
|
7016
|
+
: receiptKey,
|
|
6984
7017
|
input: isRecord(input) ? input : {},
|
|
6985
7018
|
orgId: req.orgId,
|
|
6986
7019
|
callbackUrl: req.callbackUrl,
|
|
@@ -7259,6 +7292,7 @@ function createMinimalWorkerCtx(
|
|
|
7259
7292
|
}
|
|
7260
7293
|
},
|
|
7261
7294
|
{
|
|
7295
|
+
forceFailedRefresh: true,
|
|
7262
7296
|
repairRunningReceiptForSameRunAfterWaitTimeout: true,
|
|
7263
7297
|
runningReceiptWaitMaxAttempts:
|
|
7264
7298
|
resolveRuntimeToolReceiptWaitMaxAttempts(input),
|
|
@@ -7,7 +7,7 @@ These modules are bundled into every `esm_workers` play artifact. Keep them:
|
|
|
7
7
|
- specific to the Cloudflare Worker execution path
|
|
8
8
|
|
|
9
9
|
Do not import broad `shared_libs/play-runtime/*` modules here. Shared runtime
|
|
10
|
-
modules are optimized for the Node
|
|
10
|
+
modules are optimized for the Node runner and often bring along code
|
|
11
11
|
that is correct but expensive in a per-graphHash Worker isolate.
|
|
12
12
|
|
|
13
13
|
If a helper needs zod, Neon, runtime API transport, validation registries, or
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
harnessFailRuntimeReceipts,
|
|
8
8
|
harnessGetRuntimeReceipt,
|
|
9
9
|
harnessHeartbeatRuntimeReceipts,
|
|
10
|
+
harnessMarkRuntimeReceiptsRunning,
|
|
10
11
|
harnessReleaseRuntimeReceipt,
|
|
11
12
|
} from '../../../../sdk/src/plays/harness-stub';
|
|
12
13
|
import type { PreloadedRuntimeDbSessionInput } from '../../../play-harness-worker/src/rpc-types';
|
|
@@ -124,6 +125,21 @@ export function createHarnessWorkerReceiptStore(input: {
|
|
|
124
125
|
...command,
|
|
125
126
|
});
|
|
126
127
|
},
|
|
128
|
+
markReceiptsRunning(command) {
|
|
129
|
+
return harnessMarkRuntimeReceiptsRunning({
|
|
130
|
+
executorToken: input.executorToken,
|
|
131
|
+
orgId: input.orgId,
|
|
132
|
+
preloadedDbSessions: input.preloadedDbSessions ?? null,
|
|
133
|
+
userEmail: input.userEmail ?? null,
|
|
134
|
+
runtimeTestFaultHeader: input.runtimeTestFaultHeader ?? null,
|
|
135
|
+
...leaseTtlField,
|
|
136
|
+
...command,
|
|
137
|
+
receipts: command.receipts.map((receipt) => ({
|
|
138
|
+
...runAttemptField,
|
|
139
|
+
...receipt,
|
|
140
|
+
})),
|
|
141
|
+
});
|
|
142
|
+
},
|
|
127
143
|
completeReceipt(command) {
|
|
128
144
|
return harnessCompleteRuntimeReceipt({
|
|
129
145
|
executorToken: input.executorToken,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildScopedWorkReceiptKey,
|
|
3
3
|
type WorkReceipt,
|
|
4
|
+
type WorkReceiptBatchMarkRunningCommand,
|
|
4
5
|
type WorkReceiptClaim,
|
|
5
6
|
type WorkReceiptCommand,
|
|
6
7
|
type WorkReceiptFailureKind,
|
|
@@ -41,7 +42,14 @@ export type WorkerRuntimeReceiptStore = Pick<
|
|
|
41
42
|
| 'releaseReceipt'
|
|
42
43
|
| 'heartbeatReceipts'
|
|
43
44
|
>
|
|
44
|
-
|
|
45
|
+
> & {
|
|
46
|
+
markReceiptsRunning?: (
|
|
47
|
+
input: WorkReceiptBatchMarkRunningCommand,
|
|
48
|
+
) => Promise<Array<WorkReceipt | null>>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type WorkerRuntimeReceiptMarkRunningCommand =
|
|
52
|
+
WorkReceiptBatchMarkRunningCommand;
|
|
45
53
|
|
|
46
54
|
type RuntimeReceiptContext = {
|
|
47
55
|
orgId?: string | null;
|
|
@@ -57,6 +65,11 @@ export type WorkerRuntimeReceiptExecutionContext = {
|
|
|
57
65
|
leaseId: string | null;
|
|
58
66
|
};
|
|
59
67
|
|
|
68
|
+
type WorkerRuntimeReceiptExecute<T> = (
|
|
69
|
+
context: WorkerRuntimeReceiptExecutionContext,
|
|
70
|
+
wasFailed: boolean,
|
|
71
|
+
) => Promise<T> | T;
|
|
72
|
+
|
|
60
73
|
const WORKER_RECEIPT_DEFAULT_WAIT_MS = 300_000;
|
|
61
74
|
const WORKER_RECEIPT_WAIT_DELAY_MS = 250;
|
|
62
75
|
const WORKER_RECEIPT_WAIT_MAX_ATTEMPTS = Math.ceil(
|
|
@@ -194,6 +207,21 @@ function buildLeaseLostError(input: {
|
|
|
194
207
|
return new RuntimeLeaseLostError(input);
|
|
195
208
|
}
|
|
196
209
|
|
|
210
|
+
function receiptHeartbeatStillOwnsOrSettled(input: {
|
|
211
|
+
receipt: WorkerRuntimeReceipt | null | undefined;
|
|
212
|
+
leaseId: string;
|
|
213
|
+
}): boolean {
|
|
214
|
+
const { receipt } = input;
|
|
215
|
+
if (!receipt) return false;
|
|
216
|
+
if (receipt.status === 'completed' || receipt.status === 'skipped') {
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
if (receipt.status !== 'running' && receipt.status !== 'queued') {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
return receipt.leaseId === input.leaseId;
|
|
223
|
+
}
|
|
224
|
+
|
|
197
225
|
export async function executeWithWorkerReceiptHeartbeats<T>(input: {
|
|
198
226
|
playName: string;
|
|
199
227
|
runId: string;
|
|
@@ -244,9 +272,10 @@ export async function executeWithWorkerReceiptHeartbeats<T>(input: {
|
|
|
244
272
|
for (let index = 0; index < group.keys.length; index += 1) {
|
|
245
273
|
const receipt = receipts[index];
|
|
246
274
|
if (
|
|
247
|
-
!
|
|
248
|
-
|
|
249
|
-
|
|
275
|
+
!receiptHeartbeatStillOwnsOrSettled({
|
|
276
|
+
receipt,
|
|
277
|
+
leaseId: group.leaseId,
|
|
278
|
+
})
|
|
250
279
|
) {
|
|
251
280
|
throw buildLeaseLostError({
|
|
252
281
|
keys: group.keys,
|
|
@@ -316,7 +345,8 @@ async function executeAndPersistReceipt<T>(input: {
|
|
|
316
345
|
runId: string;
|
|
317
346
|
runAttempt?: number | null;
|
|
318
347
|
leaseId?: string | null;
|
|
319
|
-
|
|
348
|
+
wasFailed?: boolean;
|
|
349
|
+
execute: WorkerRuntimeReceiptExecute<T>;
|
|
320
350
|
receiptStore: WorkerRuntimeReceiptStore;
|
|
321
351
|
ownership: 'claimed' | 'reconciled';
|
|
322
352
|
budgetMeter?: WorkerWorkBudgetMeter;
|
|
@@ -334,9 +364,7 @@ async function executeAndPersistReceipt<T>(input: {
|
|
|
334
364
|
heartbeatIntervalMs: input.heartbeatIntervalMs,
|
|
335
365
|
targets: [{ key: input.key, leaseId }],
|
|
336
366
|
execute: () =>
|
|
337
|
-
input.execute({
|
|
338
|
-
leaseId,
|
|
339
|
-
}),
|
|
367
|
+
input.execute({ leaseId }, input.wasFailed === true),
|
|
340
368
|
});
|
|
341
369
|
} catch (error) {
|
|
342
370
|
if (error instanceof RuntimeLeaseLostError) {
|
|
@@ -406,7 +434,7 @@ async function executeAndPersistReceipt<T>(input: {
|
|
|
406
434
|
|
|
407
435
|
export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
408
436
|
input: RuntimeReceiptContext & {
|
|
409
|
-
execute:
|
|
437
|
+
execute: WorkerRuntimeReceiptExecute<T>;
|
|
410
438
|
repairRunningReceiptForSameRun?: boolean;
|
|
411
439
|
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
412
440
|
runningReceiptWaitMaxAttempts?: number;
|
|
@@ -414,6 +442,7 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
414
442
|
runtimeReceiptHeartbeatIntervalMs?: number;
|
|
415
443
|
runtimeReceiptLeaseTtlMs?: number;
|
|
416
444
|
reclaimRunning?: boolean;
|
|
445
|
+
forceFailedRefresh?: boolean;
|
|
417
446
|
},
|
|
418
447
|
): Promise<T> {
|
|
419
448
|
const key = scopedReceiptKey(input);
|
|
@@ -426,6 +455,7 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
426
455
|
leaseAware: true,
|
|
427
456
|
leaseTtlMs: input.runtimeReceiptLeaseTtlMs,
|
|
428
457
|
...(input.reclaimRunning === true ? { reclaimRunning: true } : {}),
|
|
458
|
+
...(input.forceFailedRefresh === true ? { forceFailedRefresh: true } : {}),
|
|
429
459
|
});
|
|
430
460
|
if (claimed.disposition === 'reused') {
|
|
431
461
|
return receiptOutput<T>(claimed.receipt);
|
|
@@ -540,6 +570,7 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
540
570
|
runId: input.runId,
|
|
541
571
|
...(input.runAttempt != null ? { runAttempt: input.runAttempt } : {}),
|
|
542
572
|
leaseId: claimed.receipt.leaseId ?? null,
|
|
573
|
+
wasFailed: claimed.wasFailed === true,
|
|
543
574
|
execute: input.execute,
|
|
544
575
|
receiptStore,
|
|
545
576
|
ownership: 'claimed',
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
RuntimeReceiptWaitTimeoutError,
|
|
17
17
|
resolveRuntimeToolReceiptWaitMaxAttempts,
|
|
18
|
+
runtimeReceiptFailureKindForError,
|
|
18
19
|
waitForCompletedRuntimeReceipt,
|
|
19
20
|
} from '../../../../shared_libs/play-runtime/durable-receipt-execution';
|
|
20
21
|
import type { PlayExecutionGovernor } from '../../../../shared_libs/play-runtime/governor/governor';
|
|
21
|
-
import { getToolHttpErrorReceiptFailureKind } from '../../../../shared_libs/play-runtime/tool-http-errors';
|
|
22
22
|
import { ToolExecuteAuthScopeChangedError } from '../../../../shared_libs/play-runtime/tool-execute-retry-policy';
|
|
23
23
|
import { getPlayRuntimeBatchStrategy } from '../../../../shared_libs/play-runtime/play-runtime-batching-registry';
|
|
24
24
|
import {
|
|
@@ -46,10 +46,7 @@ import {
|
|
|
46
46
|
type ToolResultMetadataInput,
|
|
47
47
|
} from '../../../../shared_libs/play-runtime/tool-result';
|
|
48
48
|
import { toolExecutionMetadataForOutcome } from '../../../../shared_libs/play-runtime/tool-execution-outcome';
|
|
49
|
-
import {
|
|
50
|
-
buildScopedWorkReceiptKey,
|
|
51
|
-
type WorkReceiptFailureKind,
|
|
52
|
-
} from '../../../../shared_libs/play-runtime/work-receipts';
|
|
49
|
+
import { buildScopedWorkReceiptKey } from '../../../../shared_libs/play-runtime/work-receipts';
|
|
53
50
|
import {
|
|
54
51
|
canReclaimFailedWorkerToolReceipt,
|
|
55
52
|
canReclaimTimedOutWorkerToolReceipt,
|
|
@@ -213,6 +210,7 @@ type ClaimedWorkerToolBatchRequest = {
|
|
|
213
210
|
request: WorkerToolBatchRequest;
|
|
214
211
|
receiptKey: string | null;
|
|
215
212
|
receiptLeaseId: string | null;
|
|
213
|
+
receiptStatus: WorkerRuntimeReceipt['status'] | null;
|
|
216
214
|
providerForce?: boolean;
|
|
217
215
|
followers: WorkerToolBatchRequest[];
|
|
218
216
|
};
|
|
@@ -281,18 +279,6 @@ async function sleepWorkerMs(ms: number): Promise<void> {
|
|
|
281
279
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
282
280
|
}
|
|
283
281
|
|
|
284
|
-
function workReceiptFailureKindForError(
|
|
285
|
-
error: unknown,
|
|
286
|
-
): WorkReceiptFailureKind {
|
|
287
|
-
if (
|
|
288
|
-
isPlayExecutionSuspendedError(error) ||
|
|
289
|
-
isPlayRowExecutionSuspendedError(error)
|
|
290
|
-
) {
|
|
291
|
-
return 'repairable';
|
|
292
|
-
}
|
|
293
|
-
return getToolHttpErrorReceiptFailureKind(error) ?? 'terminal';
|
|
294
|
-
}
|
|
295
|
-
|
|
296
282
|
export class RuntimeReceiptPersistenceError extends Error {
|
|
297
283
|
readonly receiptKeys: string[];
|
|
298
284
|
readonly errors: unknown[];
|
|
@@ -312,7 +298,7 @@ export class RuntimeReceiptPersistenceError extends Error {
|
|
|
312
298
|
}
|
|
313
299
|
|
|
314
300
|
function receiptPersistenceError(input: {
|
|
315
|
-
operation: 'complete' | 'fail';
|
|
301
|
+
operation: 'mark-running' | 'complete' | 'fail';
|
|
316
302
|
receiptKeys: string[];
|
|
317
303
|
cause: unknown;
|
|
318
304
|
}): RuntimeReceiptPersistenceError {
|
|
@@ -434,7 +420,18 @@ function toolMetadataFallback(toolId: string): ToolResultMetadataInput {
|
|
|
434
420
|
return { toolId };
|
|
435
421
|
}
|
|
436
422
|
|
|
437
|
-
function
|
|
423
|
+
function durableReceiptInputForTool(
|
|
424
|
+
toolId: string,
|
|
425
|
+
input: Record<string, unknown>,
|
|
426
|
+
): Record<string, unknown> {
|
|
427
|
+
if (toolId !== 'test_rate_limit') {
|
|
428
|
+
return input;
|
|
429
|
+
}
|
|
430
|
+
const { simulated_delay_ms: _simulatedDelayMs, ...semanticInput } = input;
|
|
431
|
+
return semanticInput;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export function workerDurableToolCallCacheKey(input: {
|
|
438
435
|
req: WorkerToolDispatchRunRequest;
|
|
439
436
|
toolId: string;
|
|
440
437
|
requestInput: Record<string, unknown>;
|
|
@@ -446,7 +443,7 @@ function workerDurableToolCallCacheKey(input: {
|
|
|
446
443
|
orgId: input.req.orgId,
|
|
447
444
|
playLocalScope: input.req.playName,
|
|
448
445
|
toolId: input.toolId,
|
|
449
|
-
requestInput: input.requestInput,
|
|
446
|
+
requestInput: durableReceiptInputForTool(input.toolId, input.requestInput),
|
|
450
447
|
authScopeDigest: buildDurableToolCallAuthScopeDigest({
|
|
451
448
|
orgId: input.req.orgId,
|
|
452
449
|
toolId: input.toolId,
|
|
@@ -928,6 +925,7 @@ export class WorkerToolBatchScheduler {
|
|
|
928
925
|
request,
|
|
929
926
|
receiptKey: input.receiptKey,
|
|
930
927
|
receiptLeaseId: claim.receipt.leaseId ?? null,
|
|
928
|
+
receiptStatus: claim.receipt.status,
|
|
931
929
|
providerForce: true,
|
|
932
930
|
followers,
|
|
933
931
|
},
|
|
@@ -995,6 +993,7 @@ export class WorkerToolBatchScheduler {
|
|
|
995
993
|
request,
|
|
996
994
|
receiptKey: input.receiptKey,
|
|
997
995
|
receiptLeaseId: claim.receipt.leaseId ?? null,
|
|
996
|
+
receiptStatus: claim.receipt.status,
|
|
998
997
|
providerForce: true,
|
|
999
998
|
followers,
|
|
1000
999
|
},
|
|
@@ -1136,6 +1135,7 @@ export class WorkerToolBatchScheduler {
|
|
|
1136
1135
|
request,
|
|
1137
1136
|
receiptKey: null,
|
|
1138
1137
|
receiptLeaseId: null,
|
|
1138
|
+
receiptStatus: null,
|
|
1139
1139
|
followers: [],
|
|
1140
1140
|
})),
|
|
1141
1141
|
deferredClaimedRequests: [],
|
|
@@ -1159,6 +1159,7 @@ export class WorkerToolBatchScheduler {
|
|
|
1159
1159
|
request,
|
|
1160
1160
|
receiptKey: null,
|
|
1161
1161
|
receiptLeaseId: null,
|
|
1162
|
+
receiptStatus: null,
|
|
1162
1163
|
followers: [],
|
|
1163
1164
|
});
|
|
1164
1165
|
}
|
|
@@ -1277,6 +1278,7 @@ export class WorkerToolBatchScheduler {
|
|
|
1277
1278
|
request,
|
|
1278
1279
|
receiptKey,
|
|
1279
1280
|
receiptLeaseId: claim.receipt.leaseId ?? null,
|
|
1281
|
+
receiptStatus: claim.receipt.status,
|
|
1280
1282
|
providerForce: groupState.forceDurableRefresh,
|
|
1281
1283
|
followers,
|
|
1282
1284
|
});
|
|
@@ -1340,6 +1342,77 @@ export class WorkerToolBatchScheduler {
|
|
|
1340
1342
|
throw completedResult.error;
|
|
1341
1343
|
}
|
|
1342
1344
|
|
|
1345
|
+
private async markAdmittedToolRequestsRunning(
|
|
1346
|
+
claimedRequests: ClaimedWorkerToolBatchRequest[],
|
|
1347
|
+
): Promise<void> {
|
|
1348
|
+
const queued = claimedRequests.filter(
|
|
1349
|
+
(claimed) =>
|
|
1350
|
+
claimed.receiptKey !== null && claimed.receiptStatus !== 'running',
|
|
1351
|
+
);
|
|
1352
|
+
if (queued.length === 0) return;
|
|
1353
|
+
|
|
1354
|
+
const markReceiptsRunning = this.options.receiptStore?.markReceiptsRunning;
|
|
1355
|
+
const receiptKeys = queued.map((claimed) => claimed.receiptKey!);
|
|
1356
|
+
if (!markReceiptsRunning) {
|
|
1357
|
+
throw receiptPersistenceError({
|
|
1358
|
+
operation: 'mark-running',
|
|
1359
|
+
receiptKeys,
|
|
1360
|
+
cause: new Error(
|
|
1361
|
+
'Queued runtime receipts require a fenced mark-running store operation.',
|
|
1362
|
+
),
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const runId = this.options.req.runId;
|
|
1367
|
+
const runAttempt = this.options.req.runAttempt ?? 0;
|
|
1368
|
+
this.options.budgetMeter?.count('receipt');
|
|
1369
|
+
let marked: Array<WorkerRuntimeReceipt | null>;
|
|
1370
|
+
try {
|
|
1371
|
+
marked = await markReceiptsRunning({
|
|
1372
|
+
playName: this.options.req.playName,
|
|
1373
|
+
leaseTtlMs: this.options.runtimeReceiptLeaseTtlMs,
|
|
1374
|
+
receipts: queued.map((claimed) => ({
|
|
1375
|
+
runId,
|
|
1376
|
+
runAttempt,
|
|
1377
|
+
key: claimed.receiptKey!,
|
|
1378
|
+
leaseId: claimed.receiptLeaseId,
|
|
1379
|
+
})),
|
|
1380
|
+
});
|
|
1381
|
+
} catch (error) {
|
|
1382
|
+
throw receiptPersistenceError({
|
|
1383
|
+
operation: 'mark-running',
|
|
1384
|
+
receiptKeys,
|
|
1385
|
+
cause: error,
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
for (let index = 0; index < queued.length; index += 1) {
|
|
1390
|
+
const claimed = queued[index]!;
|
|
1391
|
+
const receipt = marked[index];
|
|
1392
|
+
const leaseId = claimed.receiptLeaseId?.trim() || null;
|
|
1393
|
+
const ownerRunId = receipt?.leaseOwnerRunId ?? receipt?.runId ?? null;
|
|
1394
|
+
if (
|
|
1395
|
+
!receipt ||
|
|
1396
|
+
receipt.key !== claimed.receiptKey ||
|
|
1397
|
+
receipt.status !== 'running' ||
|
|
1398
|
+
!leaseId ||
|
|
1399
|
+
receipt.leaseId !== leaseId ||
|
|
1400
|
+
ownerRunId !== runId ||
|
|
1401
|
+
receipt.leaseOwnerAttempt !== runAttempt
|
|
1402
|
+
) {
|
|
1403
|
+
throw new RuntimeLeaseLostError({
|
|
1404
|
+
keys: [claimed.receiptKey!],
|
|
1405
|
+
runId,
|
|
1406
|
+
leaseId: leaseId ?? 'missing',
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
for (const claimed of queued) {
|
|
1412
|
+
claimed.receiptStatus = 'running';
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1343
1416
|
private completedDurableToolRequestResult(input: {
|
|
1344
1417
|
claimed: ClaimedWorkerToolBatchRequest;
|
|
1345
1418
|
ownerResult: unknown;
|
|
@@ -1380,14 +1453,8 @@ export class WorkerToolBatchScheduler {
|
|
|
1380
1453
|
};
|
|
1381
1454
|
}
|
|
1382
1455
|
return {
|
|
1383
|
-
ok:
|
|
1384
|
-
|
|
1385
|
-
operation: 'complete',
|
|
1386
|
-
receiptKeys: [claimed.receiptKey ?? claimed.request.cacheKey],
|
|
1387
|
-
cause: new Error(
|
|
1388
|
-
`Durable tool call ${claimed.receiptKey ?? claimed.request.cacheKey} lost receipt ownership before completion.`,
|
|
1389
|
-
),
|
|
1390
|
-
}),
|
|
1456
|
+
ok: true,
|
|
1457
|
+
value: ownerResult,
|
|
1391
1458
|
};
|
|
1392
1459
|
}
|
|
1393
1460
|
|
|
@@ -1803,7 +1870,7 @@ export class WorkerToolBatchScheduler {
|
|
|
1803
1870
|
key: claimed.receiptKey,
|
|
1804
1871
|
leaseId: claimed.receiptLeaseId,
|
|
1805
1872
|
error: error instanceof Error ? error.message : String(error),
|
|
1806
|
-
failureKind:
|
|
1873
|
+
failureKind: runtimeReceiptFailureKindForError(error),
|
|
1807
1874
|
});
|
|
1808
1875
|
} catch (receiptError) {
|
|
1809
1876
|
if (this.options.persistenceLatch) {
|
|
@@ -1900,7 +1967,7 @@ export class WorkerToolBatchScheduler {
|
|
|
1900
1967
|
key: claimed.receiptKey!,
|
|
1901
1968
|
leaseId: claimed.receiptLeaseId,
|
|
1902
1969
|
error: error instanceof Error ? error.message : String(error),
|
|
1903
|
-
failureKind:
|
|
1970
|
+
failureKind: runtimeReceiptFailureKindForError(error),
|
|
1904
1971
|
})),
|
|
1905
1972
|
});
|
|
1906
1973
|
} catch (receiptError) {
|
|
@@ -1991,17 +2058,18 @@ export class WorkerToolBatchScheduler {
|
|
|
1991
2058
|
);
|
|
1992
2059
|
return;
|
|
1993
2060
|
}
|
|
1994
|
-
this.options.budgetMeter?.count('provider');
|
|
1995
|
-
this.options.budgetMeter?.count(
|
|
1996
|
-
'egress',
|
|
1997
|
-
Math.max(
|
|
1998
|
-
0,
|
|
1999
|
-
WORKER_PLATFORM_SUBREQUESTS_PER_UNBATCHED_TOOL_CALL - 1,
|
|
2000
|
-
),
|
|
2001
|
-
);
|
|
2002
2061
|
try {
|
|
2003
2062
|
let result: unknown;
|
|
2004
2063
|
try {
|
|
2064
|
+
await this.markAdmittedToolRequestsRunning([claimed]);
|
|
2065
|
+
this.options.budgetMeter?.count('provider');
|
|
2066
|
+
this.options.budgetMeter?.count(
|
|
2067
|
+
'egress',
|
|
2068
|
+
Math.max(
|
|
2069
|
+
0,
|
|
2070
|
+
WORKER_PLATFORM_SUBREQUESTS_PER_UNBATCHED_TOOL_CALL - 1,
|
|
2071
|
+
),
|
|
2072
|
+
);
|
|
2005
2073
|
const durableReceiptKey = claimed.receiptKey
|
|
2006
2074
|
? durableCallReceiptKeyForClaimed({
|
|
2007
2075
|
req: this.options.req,
|
|
@@ -2176,8 +2244,9 @@ export class WorkerToolBatchScheduler {
|
|
|
2176
2244
|
signal: this.options.abortSignal,
|
|
2177
2245
|
},
|
|
2178
2246
|
);
|
|
2179
|
-
this.options.budgetMeter?.count('provider');
|
|
2180
2247
|
try {
|
|
2248
|
+
await this.markAdmittedToolRequestsRunning(batch.memberRequests);
|
|
2249
|
+
this.options.budgetMeter?.count('provider');
|
|
2181
2250
|
const receiptKeys = batch.memberRequests.map((member) =>
|
|
2182
2251
|
durableCallReceiptKeyForClaimed({
|
|
2183
2252
|
req: this.options.req,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - **Max attempts**: `1 + maxRetries` (default: 4 total attempts)
|
|
10
10
|
* - **Backoff**: exponential — 1s, 2s, 4s, 8s, ... capped at 30s
|
|
11
11
|
* - **Retryable**: network errors, timeouts, HTTP 429 (rate limit)
|
|
12
|
-
* - **Not retryable**: HTTP 401/403 (auth errors), other 4xx
|
|
12
|
+
* - **Not retryable**: HTTP 401/403 (auth errors), other 4xx API errors
|
|
13
13
|
*
|
|
14
14
|
* ## Localhost failover
|
|
15
15
|
*
|
|
@@ -504,7 +504,10 @@ export class HttpClient {
|
|
|
504
504
|
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
505
505
|
*/
|
|
506
506
|
async get<T = unknown>(path: string): Promise<T> {
|
|
507
|
-
return this.request<T>(path, {
|
|
507
|
+
return this.request<T>(path, {
|
|
508
|
+
method: 'GET',
|
|
509
|
+
retryApiErrors: true,
|
|
510
|
+
});
|
|
508
511
|
}
|
|
509
512
|
|
|
510
513
|
async *streamSse<TEvent extends LiveEventEnvelope = LiveEventEnvelope>(
|
|
@@ -936,7 +936,7 @@ export interface DeeplinePlayRuntimeContext {
|
|
|
936
936
|
*
|
|
937
937
|
* // Poll status
|
|
938
938
|
* const status = await job.status();
|
|
939
|
-
* console.log(status.
|
|
939
|
+
* console.log(status.status); // 'running'
|
|
940
940
|
*
|
|
941
941
|
* // Stream logs until completion
|
|
942
942
|
* const finalStatus = await job.tail({
|
|
@@ -37,6 +37,7 @@ import type {
|
|
|
37
37
|
FailRuntimeReceiptInput,
|
|
38
38
|
FailRuntimeReceiptsInput,
|
|
39
39
|
HeartbeatRuntimeReceiptsInput,
|
|
40
|
+
MarkRuntimeReceiptsRunningInput,
|
|
40
41
|
ReleaseRuntimeReceiptInput,
|
|
41
42
|
RuntimeApiCallInput,
|
|
42
43
|
RuntimeApiCallResult,
|
|
@@ -157,6 +158,12 @@ export async function harnessGetRuntimeReceipt(
|
|
|
157
158
|
return requireBinding().getRuntimeReceipt(input);
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
export async function harnessMarkRuntimeReceiptsRunning(
|
|
162
|
+
input: MarkRuntimeReceiptsRunningInput,
|
|
163
|
+
): Promise<Array<WorkReceipt | null>> {
|
|
164
|
+
return requireBinding().markRuntimeReceiptsRunning(input);
|
|
165
|
+
}
|
|
166
|
+
|
|
160
167
|
export async function harnessCompleteRuntimeReceipt(
|
|
161
168
|
input: CompleteRuntimeReceiptInput,
|
|
162
169
|
): Promise<WorkReceipt | null> {
|