deepline 0.1.182 → 0.1.184
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/child-play-await.ts +203 -6
- package/dist/bundling-sources/apps/play-runner-workers/src/child-play-submit.ts +8 -1
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +639 -109
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +3 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +1752 -1899
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +116 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +542 -78
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +292 -11
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/run-work-dispatcher.ts +519 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +2381 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +18 -5
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/work-budget.ts +130 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/worker-platform-budget.ts +39 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +2 -0
- package/dist/bundling-sources/sdk/src/client.ts +41 -0
- package/dist/bundling-sources/sdk/src/http.ts +23 -8
- package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +56 -3
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +133 -10
- package/dist/bundling-sources/shared_libs/play-runtime/auth-scope-resolver.ts +92 -0
- package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +4 -4
- package/dist/bundling-sources/shared_libs/play-runtime/batching-types.ts +8 -0
- package/dist/bundling-sources/shared_libs/play-runtime/child-run-id.ts +101 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1612 -287
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +105 -6
- package/dist/bundling-sources/shared_libs/play-runtime/dedup-backend.ts +0 -0
- package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +116 -16
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +186 -20
- package/dist/bundling-sources/shared_libs/play-runtime/dynamic-worker-version.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-ledger-store.ts +133 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +245 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -5
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +14 -0
- package/dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts +52 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +124 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +6 -2
- package/dist/bundling-sources/shared_libs/play-runtime/row-isolation.ts +48 -2
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +158 -15
- package/dist/bundling-sources/shared_libs/play-runtime/run-terminal-source.ts +45 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-paths.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +2038 -262
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +42 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-attempt-state-machine.ts +183 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-errors.ts +88 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +8 -1
- package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +19 -1
- package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +343 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +437 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +83 -6
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +28 -11
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +38 -6
- package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +24 -17
- package/dist/cli/index.js +76 -17
- package/dist/cli/index.mjs +76 -17
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +63 -9
- package/dist/index.mjs +63 -9
- package/dist/plays/bundle-play-file.mjs +20 -6
- package/package.json +1 -1
|
@@ -7,6 +7,15 @@ import {
|
|
|
7
7
|
import { assertNoSecretTaint } from './secret-capability';
|
|
8
8
|
import type { RuntimeStepReceipt } from './ctx-types';
|
|
9
9
|
import type { DurableReceiptRecoverySource } from './tool-execution-outcome';
|
|
10
|
+
import {
|
|
11
|
+
isPlayExecutionSuspendedError,
|
|
12
|
+
isPlayRowExecutionSuspendedError,
|
|
13
|
+
} from './suspension';
|
|
14
|
+
import type { WorkReceiptFailureKind } from './work-receipts';
|
|
15
|
+
import {
|
|
16
|
+
PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
|
|
17
|
+
runtimeLeaseHeartbeatIntervalMs,
|
|
18
|
+
} from './lease-policy';
|
|
10
19
|
|
|
11
20
|
const DURABLE_RECEIPT_WAIT_MAX_ATTEMPTS = 240;
|
|
12
21
|
const DURABLE_RECEIPT_WAIT_DELAY_MS = 250;
|
|
@@ -21,6 +30,30 @@ export class RuntimeReceiptWaitTimeoutError extends Error {
|
|
|
21
30
|
}
|
|
22
31
|
}
|
|
23
32
|
|
|
33
|
+
export class RuntimeReceiptLeaseLostError extends Error {
|
|
34
|
+
constructor(input: { receiptKey: string; runId: string; leaseId: string }) {
|
|
35
|
+
super(
|
|
36
|
+
`Lost durable receipt lease for ${input.receiptKey}. runId=${input.runId} leaseId=${input.leaseId}`,
|
|
37
|
+
);
|
|
38
|
+
this.name = 'RuntimeReceiptLeaseLostError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isInFlightRuntimeReceipt(
|
|
43
|
+
receipt: RuntimeStepReceipt | null | undefined,
|
|
44
|
+
): receipt is RuntimeStepReceipt & { status: 'pending' | 'running' } {
|
|
45
|
+
return receipt?.status === 'pending' || receipt?.status === 'running';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function runtimeReceiptFailureKindForError(
|
|
49
|
+
error: unknown,
|
|
50
|
+
): WorkReceiptFailureKind {
|
|
51
|
+
return isPlayExecutionSuspendedError(error) ||
|
|
52
|
+
isPlayRowExecutionSuspendedError(error)
|
|
53
|
+
? 'repairable'
|
|
54
|
+
: 'terminal';
|
|
55
|
+
}
|
|
56
|
+
|
|
24
57
|
export function resolveRuntimeToolReceiptWaitTimeoutMs(
|
|
25
58
|
requestInput: Record<string, unknown>,
|
|
26
59
|
): number {
|
|
@@ -102,21 +135,107 @@ export type DurableReceiptExecutionStore = {
|
|
|
102
135
|
runId: string,
|
|
103
136
|
reclaimRunning?: boolean,
|
|
104
137
|
forceRefresh?: boolean,
|
|
138
|
+
forceFailedRefresh?: boolean,
|
|
105
139
|
): Promise<RuntimeStepReceipt | null>;
|
|
106
140
|
complete(
|
|
107
141
|
receiptKey: string,
|
|
108
142
|
runId: string,
|
|
109
143
|
output: unknown | null,
|
|
144
|
+
leaseId?: string | null,
|
|
145
|
+
): Promise<RuntimeStepReceipt | null>;
|
|
146
|
+
release(
|
|
147
|
+
receiptKey: string,
|
|
148
|
+
runId: string,
|
|
149
|
+
leaseId?: string | null,
|
|
150
|
+
): Promise<RuntimeStepReceipt | null>;
|
|
151
|
+
heartbeat?(
|
|
152
|
+
receiptKey: string,
|
|
153
|
+
runId: string,
|
|
154
|
+
leaseId: string,
|
|
110
155
|
): Promise<RuntimeStepReceipt | null>;
|
|
111
156
|
fail(
|
|
112
157
|
receiptKey: string,
|
|
113
158
|
runId: string,
|
|
114
159
|
error: string,
|
|
160
|
+
leaseId?: string | null,
|
|
161
|
+
failureKind?: WorkReceiptFailureKind | null,
|
|
115
162
|
): Promise<RuntimeStepReceipt | null>;
|
|
116
163
|
canPersistFailure: boolean;
|
|
117
164
|
canPersistCompletion: boolean;
|
|
118
165
|
};
|
|
119
166
|
|
|
167
|
+
async function executeWithDurableRuntimeReceiptHeartbeat<T>(input: {
|
|
168
|
+
receiptKey: string;
|
|
169
|
+
runId: string;
|
|
170
|
+
leaseId: string;
|
|
171
|
+
heartbeatIntervalMs?: number;
|
|
172
|
+
store: DurableReceiptExecutionStore;
|
|
173
|
+
execute: () => Promise<T>;
|
|
174
|
+
}): Promise<T> {
|
|
175
|
+
if (!input.store.heartbeat) {
|
|
176
|
+
return await input.execute();
|
|
177
|
+
}
|
|
178
|
+
const heartbeatIntervalMs =
|
|
179
|
+
input.heartbeatIntervalMs ??
|
|
180
|
+
runtimeLeaseHeartbeatIntervalMs(PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS);
|
|
181
|
+
if (!Number.isFinite(heartbeatIntervalMs) || heartbeatIntervalMs <= 0) {
|
|
182
|
+
throw new Error('Runtime receipt heartbeat interval must be positive.');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let stopped = false;
|
|
186
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
187
|
+
let rejectLeaseLost!: (error: RuntimeReceiptLeaseLostError) => void;
|
|
188
|
+
const leaseLost = new Promise<never>((_, reject) => {
|
|
189
|
+
rejectLeaseLost = reject;
|
|
190
|
+
});
|
|
191
|
+
const stop = () => {
|
|
192
|
+
stopped = true;
|
|
193
|
+
if (timeout !== null) {
|
|
194
|
+
clearTimeout(timeout);
|
|
195
|
+
timeout = null;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const heartbeatOnce = async () => {
|
|
199
|
+
const receipt = await input.store.heartbeat!(
|
|
200
|
+
input.receiptKey,
|
|
201
|
+
input.runId,
|
|
202
|
+
input.leaseId,
|
|
203
|
+
);
|
|
204
|
+
if (
|
|
205
|
+
!receipt ||
|
|
206
|
+
receipt.status !== 'running' ||
|
|
207
|
+
receipt.leaseId !== input.leaseId
|
|
208
|
+
) {
|
|
209
|
+
throw new RuntimeReceiptLeaseLostError(input);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const schedule = () => {
|
|
213
|
+
timeout = setTimeout(() => {
|
|
214
|
+
void (async () => {
|
|
215
|
+
try {
|
|
216
|
+
await heartbeatOnce();
|
|
217
|
+
if (!stopped) schedule();
|
|
218
|
+
} catch (error) {
|
|
219
|
+
stop();
|
|
220
|
+
rejectLeaseLost(
|
|
221
|
+
error instanceof RuntimeReceiptLeaseLostError
|
|
222
|
+
? error
|
|
223
|
+
: new RuntimeReceiptLeaseLostError(input),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
})();
|
|
227
|
+
}, heartbeatIntervalMs);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
await heartbeatOnce();
|
|
231
|
+
schedule();
|
|
232
|
+
try {
|
|
233
|
+
return await Promise.race([input.execute(), leaseLost]);
|
|
234
|
+
} finally {
|
|
235
|
+
stop();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
120
239
|
export function runtimeReceiptOutput<T>(receipt: RuntimeStepReceipt): T {
|
|
121
240
|
return isSerializedToolExecuteResult(receipt.output)
|
|
122
241
|
? (deserializeToolExecuteResult(receipt.output) as T)
|
|
@@ -171,12 +290,14 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
171
290
|
source: DurableReceiptRecoverySource,
|
|
172
291
|
) => T;
|
|
173
292
|
onClaimedResult?: (output: T, receiptKey: string) => T;
|
|
293
|
+
shouldPersistFailure?: (error: unknown) => boolean;
|
|
294
|
+
heartbeatIntervalMs?: number;
|
|
174
295
|
formatError: (error: unknown) => string;
|
|
175
296
|
log: (message: string) => void;
|
|
176
|
-
execute: () => Promise<T>;
|
|
297
|
+
execute: (context: { leaseId: string | null }) => Promise<T>;
|
|
177
298
|
}): Promise<T> {
|
|
178
299
|
if (!input.store.enabled) {
|
|
179
|
-
return await input.execute();
|
|
300
|
+
return await input.execute({ leaseId: null });
|
|
180
301
|
}
|
|
181
302
|
|
|
182
303
|
const recoverCompletedReceipt = async (
|
|
@@ -199,7 +320,9 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
199
320
|
return recovered;
|
|
200
321
|
};
|
|
201
322
|
|
|
202
|
-
|
|
323
|
+
let ownedLeaseId: string | null = null;
|
|
324
|
+
|
|
325
|
+
const reclaimReceipt = async (): Promise<
|
|
203
326
|
{ kind: 'recovered'; output: T } | { kind: 'claimed' }
|
|
204
327
|
> => {
|
|
205
328
|
const reclaimed = await input.store.claim(
|
|
@@ -219,21 +342,21 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
219
342
|
);
|
|
220
343
|
}
|
|
221
344
|
if (
|
|
222
|
-
reclaimed
|
|
345
|
+
isInFlightRuntimeReceipt(reclaimed) &&
|
|
223
346
|
reclaimed.claimState === 'existing'
|
|
224
347
|
) {
|
|
225
348
|
throw new RuntimeReceiptWaitTimeoutError(input.receiptKey);
|
|
226
349
|
}
|
|
350
|
+
ownedLeaseId = reclaimed?.leaseId ?? null;
|
|
227
351
|
return { kind: 'claimed' };
|
|
228
352
|
};
|
|
229
353
|
const shouldRepairSameRunRunningReceipt = (
|
|
230
354
|
receipt: RuntimeStepReceipt,
|
|
231
355
|
): boolean =>
|
|
232
356
|
input.repairRunningReceiptForSameRun === true &&
|
|
233
|
-
receipt
|
|
357
|
+
isInFlightRuntimeReceipt(receipt) &&
|
|
234
358
|
typeof receipt.runId === 'string' &&
|
|
235
359
|
receipt.runId.trim() === input.runId;
|
|
236
|
-
|
|
237
360
|
const waitForRunningReceipt = async (): Promise<{
|
|
238
361
|
kind: 'recovered';
|
|
239
362
|
output: T;
|
|
@@ -257,7 +380,7 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
257
380
|
receipt: RuntimeStepReceipt,
|
|
258
381
|
): Promise<{ kind: 'recovered'; output: T } | { kind: 'claimed' }> => {
|
|
259
382
|
if (shouldRepairSameRunRunningReceipt(receipt)) {
|
|
260
|
-
const recovered = await
|
|
383
|
+
const recovered = await reclaimReceipt();
|
|
261
384
|
if (recovered.kind === 'recovered') return recovered;
|
|
262
385
|
return { kind: 'claimed' };
|
|
263
386
|
}
|
|
@@ -265,13 +388,20 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
265
388
|
return await waitForRunningReceiptOrTimeout();
|
|
266
389
|
} catch (error) {
|
|
267
390
|
if (error instanceof RuntimeReceiptWaitTimeoutError) {
|
|
268
|
-
const recovered = await
|
|
391
|
+
const recovered = await reclaimReceipt();
|
|
269
392
|
if (recovered.kind === 'recovered') return recovered;
|
|
270
393
|
return { kind: 'claimed' };
|
|
271
394
|
}
|
|
272
395
|
throw error;
|
|
273
396
|
}
|
|
274
397
|
};
|
|
398
|
+
const repairFailedReceipt = async (
|
|
399
|
+
receipt: RuntimeStepReceipt,
|
|
400
|
+
): Promise<never> => {
|
|
401
|
+
throw new Error(
|
|
402
|
+
`ctx.${input.operation}(${input.id}): receipt is failed and could not be claimed: ${receipt.error ?? 'unknown error'}.`,
|
|
403
|
+
);
|
|
404
|
+
};
|
|
275
405
|
|
|
276
406
|
const claimed = await input.store.claim(
|
|
277
407
|
input.receiptKey,
|
|
@@ -290,42 +420,70 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
290
420
|
if (latest?.status === 'completed' || latest?.status === 'skipped') {
|
|
291
421
|
return await recoverCompletedReceipt(latest);
|
|
292
422
|
}
|
|
293
|
-
if (latest
|
|
423
|
+
if (isInFlightRuntimeReceipt(latest)) {
|
|
294
424
|
const recovered = await repairOrWaitForRunningReceipt(latest);
|
|
295
425
|
if (recovered.kind === 'recovered') return recovered.output;
|
|
426
|
+
} else if (latest?.status === 'failed') {
|
|
427
|
+
await repairFailedReceipt(latest);
|
|
296
428
|
} else {
|
|
297
|
-
if (latest?.status === 'failed') {
|
|
298
|
-
throw new Error(
|
|
299
|
-
`ctx.${input.operation}(${input.id}): receipt is failed and could not be claimed: ${latest.error ?? 'unknown error'}.`,
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
429
|
throw new Error(
|
|
303
430
|
`ctx.${input.operation}(${input.id}): receipt claim did not return execution ownership.`,
|
|
304
431
|
);
|
|
305
432
|
}
|
|
306
|
-
} else if (claimed
|
|
433
|
+
} else if (isInFlightRuntimeReceipt(claimed)) {
|
|
307
434
|
if (claimed.claimState === 'existing') {
|
|
308
435
|
const recovered = await repairOrWaitForRunningReceipt(claimed);
|
|
309
436
|
if (recovered.kind === 'recovered') return recovered.output;
|
|
437
|
+
} else {
|
|
438
|
+
ownedLeaseId = claimed.leaseId ?? null;
|
|
310
439
|
}
|
|
311
440
|
} else if (claimed.status === 'failed') {
|
|
312
|
-
|
|
313
|
-
`ctx.${input.operation}(${input.id}): receipt is failed and could not be claimed: ${claimed.error ?? 'unknown error'}.`,
|
|
314
|
-
);
|
|
441
|
+
await repairFailedReceipt(claimed);
|
|
315
442
|
}
|
|
316
443
|
|
|
317
444
|
let result: T;
|
|
318
445
|
try {
|
|
319
|
-
const executed =
|
|
446
|
+
const executed =
|
|
447
|
+
ownedLeaseId && input.store.heartbeat
|
|
448
|
+
? await executeWithDurableRuntimeReceiptHeartbeat({
|
|
449
|
+
receiptKey: input.receiptKey,
|
|
450
|
+
runId: input.runId,
|
|
451
|
+
leaseId: ownedLeaseId,
|
|
452
|
+
heartbeatIntervalMs: input.heartbeatIntervalMs,
|
|
453
|
+
store: input.store,
|
|
454
|
+
execute: () => input.execute({ leaseId: ownedLeaseId }),
|
|
455
|
+
})
|
|
456
|
+
: await input.execute({ leaseId: ownedLeaseId });
|
|
320
457
|
result = input.onClaimedResult
|
|
321
458
|
? input.onClaimedResult(executed, input.receiptKey)
|
|
322
459
|
: executed;
|
|
323
460
|
assertNoSecretTaint(result, `ctx.${input.operation} result`);
|
|
324
461
|
} catch (error) {
|
|
462
|
+
if (input.shouldPersistFailure?.(error) === false) {
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
if (
|
|
466
|
+
isPlayExecutionSuspendedError(error) ||
|
|
467
|
+
isPlayRowExecutionSuspendedError(error)
|
|
468
|
+
) {
|
|
469
|
+
const released = await input.store.release(
|
|
470
|
+
input.receiptKey,
|
|
471
|
+
input.runId,
|
|
472
|
+
ownedLeaseId,
|
|
473
|
+
);
|
|
474
|
+
if (!released || released.status !== 'pending') {
|
|
475
|
+
throw new Error(
|
|
476
|
+
`ctx.${input.operation}(${input.id}): receipt suspended but ownership could not be released: ${input.formatError(error)}.`,
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
325
481
|
const failed = await input.store.fail(
|
|
326
482
|
input.receiptKey,
|
|
327
483
|
input.runId,
|
|
328
484
|
input.formatError(error),
|
|
485
|
+
ownedLeaseId,
|
|
486
|
+
runtimeReceiptFailureKindForError(error),
|
|
329
487
|
);
|
|
330
488
|
if (!failed && input.store.canPersistFailure) {
|
|
331
489
|
throw new Error(
|
|
@@ -339,9 +497,12 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
339
497
|
input.receiptKey,
|
|
340
498
|
input.runId,
|
|
341
499
|
isToolExecuteResult(result) ? serializeToolExecuteResult(result) : result,
|
|
500
|
+
ownedLeaseId,
|
|
342
501
|
);
|
|
343
502
|
if (!completed && input.store.canPersistCompletion) {
|
|
344
|
-
|
|
503
|
+
throw new Error(
|
|
504
|
+
`ctx.${input.operation}(${input.id}): lost durable receipt ownership before completion.`,
|
|
505
|
+
);
|
|
345
506
|
}
|
|
346
507
|
if (
|
|
347
508
|
completed &&
|
|
@@ -349,5 +510,10 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
349
510
|
) {
|
|
350
511
|
return await recoverCompletedReceipt(completed, 'owner');
|
|
351
512
|
}
|
|
513
|
+
if (input.store.canPersistCompletion) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
`ctx.${input.operation}(${input.id}): lost durable receipt ownership before completion.`,
|
|
516
|
+
);
|
|
517
|
+
}
|
|
352
518
|
return result;
|
|
353
519
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WorkReceipt,
|
|
3
|
+
WorkReceiptBatchClaimCommand,
|
|
4
|
+
WorkReceiptBatchCompleteCommand,
|
|
5
|
+
WorkReceiptBatchFailCommand,
|
|
6
|
+
WorkReceiptBatchHeartbeatCommand,
|
|
7
|
+
WorkReceiptClaim,
|
|
8
|
+
WorkReceiptCommand,
|
|
9
|
+
WorkReceiptFailureKind,
|
|
10
|
+
WorkReceiptGetCommand,
|
|
11
|
+
} from './work-receipts';
|
|
12
|
+
|
|
13
|
+
export type WorkReceiptBatchGetCommand = {
|
|
14
|
+
playName: string;
|
|
15
|
+
keys: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type WorkReceiptCompleteCommand = WorkReceiptCommand & {
|
|
19
|
+
output: unknown;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type WorkReceiptFailCommand = WorkReceiptCommand & {
|
|
23
|
+
error: string;
|
|
24
|
+
failureKind?: WorkReceiptFailureKind | null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type WorkReceiptReleaseCommand = WorkReceiptCommand;
|
|
28
|
+
|
|
29
|
+
export type WorkReceiptSkipCommand = WorkReceiptCommand & {
|
|
30
|
+
output?: unknown | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type ProviderRequestStatus =
|
|
34
|
+
| 'pending'
|
|
35
|
+
| 'running'
|
|
36
|
+
| 'completed'
|
|
37
|
+
| 'failed';
|
|
38
|
+
|
|
39
|
+
export type ProviderRequestRecord = {
|
|
40
|
+
key: string;
|
|
41
|
+
status: ProviderRequestStatus;
|
|
42
|
+
receiptKey: string;
|
|
43
|
+
providerIdempotencyKey: string;
|
|
44
|
+
runId: string;
|
|
45
|
+
provider: string;
|
|
46
|
+
operation: string;
|
|
47
|
+
method: string;
|
|
48
|
+
endpoint: string;
|
|
49
|
+
bodyHash: string;
|
|
50
|
+
output?: unknown;
|
|
51
|
+
error?: string | null;
|
|
52
|
+
createdAt?: string | null;
|
|
53
|
+
updatedAt?: string | null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type ProviderRequestClaim =
|
|
57
|
+
| {
|
|
58
|
+
disposition: 'claimed';
|
|
59
|
+
record: ProviderRequestRecord;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
disposition: 'reused';
|
|
63
|
+
record: ProviderRequestRecord;
|
|
64
|
+
}
|
|
65
|
+
| {
|
|
66
|
+
disposition: 'running';
|
|
67
|
+
record: ProviderRequestRecord;
|
|
68
|
+
}
|
|
69
|
+
| {
|
|
70
|
+
disposition: 'failed';
|
|
71
|
+
record: ProviderRequestRecord;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type ProviderRequestClaimCommand = {
|
|
75
|
+
key: string;
|
|
76
|
+
receiptKey: string;
|
|
77
|
+
providerIdempotencyKey: string;
|
|
78
|
+
runId: string;
|
|
79
|
+
provider: string;
|
|
80
|
+
operation: string;
|
|
81
|
+
method: string;
|
|
82
|
+
endpoint: string;
|
|
83
|
+
bodyHash: string;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export type ProviderRequestCompleteCommand = {
|
|
87
|
+
key: string;
|
|
88
|
+
runId: string;
|
|
89
|
+
output?: unknown;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export type ProviderRequestFailCommand = {
|
|
93
|
+
key: string;
|
|
94
|
+
runId: string;
|
|
95
|
+
error: string;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export interface ExecutionLedgerStore {
|
|
99
|
+
getReceipt(input: WorkReceiptGetCommand): Promise<WorkReceipt | null>;
|
|
100
|
+
getReceipts(
|
|
101
|
+
input: WorkReceiptBatchGetCommand,
|
|
102
|
+
): Promise<Array<WorkReceipt | null>>;
|
|
103
|
+
claimReceipt(input: WorkReceiptCommand): Promise<WorkReceiptClaim>;
|
|
104
|
+
claimReceipts(
|
|
105
|
+
input: WorkReceiptBatchClaimCommand,
|
|
106
|
+
): Promise<WorkReceiptClaim[]>;
|
|
107
|
+
completeReceipt(
|
|
108
|
+
input: WorkReceiptCompleteCommand,
|
|
109
|
+
): Promise<WorkReceipt | null>;
|
|
110
|
+
completeReceipts(
|
|
111
|
+
input: WorkReceiptBatchCompleteCommand,
|
|
112
|
+
): Promise<Array<WorkReceipt | null>>;
|
|
113
|
+
failReceipt(input: WorkReceiptFailCommand): Promise<WorkReceipt | null>;
|
|
114
|
+
failReceipts(
|
|
115
|
+
input: WorkReceiptBatchFailCommand,
|
|
116
|
+
): Promise<Array<WorkReceipt | null>>;
|
|
117
|
+
releaseReceipt(
|
|
118
|
+
input: WorkReceiptReleaseCommand,
|
|
119
|
+
): Promise<WorkReceipt | null>;
|
|
120
|
+
skipReceipt(input: WorkReceiptSkipCommand): Promise<WorkReceipt | null>;
|
|
121
|
+
heartbeatReceipts(
|
|
122
|
+
input: WorkReceiptBatchHeartbeatCommand,
|
|
123
|
+
): Promise<Array<WorkReceipt | null>>;
|
|
124
|
+
claimProviderRequest(
|
|
125
|
+
input: ProviderRequestClaimCommand,
|
|
126
|
+
): Promise<ProviderRequestClaim>;
|
|
127
|
+
completeProviderRequest(
|
|
128
|
+
input: ProviderRequestCompleteCommand,
|
|
129
|
+
): Promise<ProviderRequestRecord | null>;
|
|
130
|
+
failProviderRequest(
|
|
131
|
+
input: ProviderRequestFailCommand,
|
|
132
|
+
): Promise<ProviderRequestRecord | null>;
|
|
133
|
+
}
|