deepline 0.3.332 → 0.3.334
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/sdk/src/play.ts +1 -1
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +200 -43
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +8 -2
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-invocation-fence.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +30 -9
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +11 -7
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +14 -14
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +12 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-scope.ts +95 -24
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +132 -37
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +13 -5
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +87 -3
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +7 -0
- package/dist/cli/{auth-V4IWMXTG.mjs → auth-PAEBUPRS.mjs} +4 -4
- package/dist/cli/auth-WR3XFOHN.js +31 -0
- package/dist/cli/{billing-GYW4BCPG.mjs → billing-QGQJUM5G.mjs} +4 -4
- package/dist/cli/{billing-BW37QW4H.js → billing-V746QMQC.js} +71 -71
- package/dist/cli/{chunk-GOH5BRGN.js → chunk-6466HVYR.js} +26 -26
- package/dist/cli/{chunk-JHF3GWKX.js → chunk-6MVMTDL7.js} +4 -4
- package/dist/cli/{chunk-TTJ7KELW.js → chunk-7XXNMV6G.js} +45 -45
- package/dist/cli/{chunk-WQI642QX.mjs → chunk-A7S4PRK2.mjs} +1 -1
- package/dist/cli/{chunk-HKQMCTEE.mjs → chunk-EJLUQARV.mjs} +4 -4
- package/dist/cli/{chunk-SBPJWA6Y.mjs → chunk-EJVAWHQ7.mjs} +6 -6
- package/dist/cli/{chunk-GDBJYM5N.js → chunk-FLMBKUHB.js} +60 -60
- package/dist/cli/{chunk-U2B5KDNH.js → chunk-GNHI4WZC.js} +15 -15
- package/dist/cli/{chunk-KYQPEIKA.mjs → chunk-GUMAWNWZ.mjs} +5 -5
- package/dist/cli/{chunk-CYLZE6RJ.js → chunk-GX2YGFQV.js} +1 -1
- package/dist/cli/{chunk-X6MY7BC2.js → chunk-HIPRSI5H.js} +225 -225
- package/dist/cli/{chunk-AF6XLGZT.js → chunk-NLHRXDIT.js} +2 -2
- package/dist/cli/{chunk-UMKN2BZK.mjs → chunk-O564PHDU.mjs} +1 -1
- package/dist/cli/{chunk-MXJVBJYC.mjs → chunk-O64RSXDZ.mjs} +2 -2
- package/dist/cli/{chunk-3E7P62NA.mjs → chunk-P3ZX3NSE.mjs} +1 -1
- package/dist/cli/{chunk-ERPCUHOQ.js → chunk-R6O3YVIG.js} +8 -8
- package/dist/cli/{chunk-PK4EE6T3.js → chunk-SABEM6FL.js} +48 -48
- package/dist/cli/{chunk-J5Z5BQGE.mjs → chunk-SBEWKGUA.mjs} +1 -1
- package/dist/cli/{chunk-4RLN2DHZ.mjs → chunk-TT2CLWMK.mjs} +1 -1
- package/dist/cli/{chunk-B6R2V3TH.mjs → chunk-TVIWEQ4V.mjs} +3 -3
- package/dist/cli/{chunk-MSQK2FLJ.mjs → chunk-U724V33M.mjs} +2 -2
- package/dist/cli/{chunk-T5TGWH4J.js → chunk-UUB3FTKZ.js} +6 -6
- package/dist/cli/{chunk-LAJ56FLL.mjs → chunk-VB7TNIUB.mjs} +1 -1
- package/dist/cli/{chunk-ZZWCK6F3.js → chunk-XXZSLV7B.js} +6 -6
- package/dist/cli/{chunk-5FHGKEAD.mjs → chunk-Y2VN54ME.mjs} +2 -2
- package/dist/cli/{chunk-XON4H3CO.js → chunk-Z6VHC643.js} +9 -9
- package/dist/cli/client-6OBIO6GC.js +20 -0
- package/dist/cli/{client-TBCDCJ7T.mjs → client-JIZWAGVW.mjs} +3 -3
- package/dist/cli/{csv-O3NEPSU3.mjs → csv-7DCY7Z6R.mjs} +3 -3
- package/dist/cli/{csv-EFQ7UL3M.js → csv-KAFKXAFU.js} +12 -12
- package/dist/cli/{db-2FQ3T4LT.js → db-5LJPO2VA.js} +29 -29
- package/dist/cli/{db-RUWCFINP.mjs → db-KJBWUFPL.mjs} +4 -4
- package/dist/cli/{describe-4KSVRYPM.mjs → describe-CPF4KDKZ.mjs} +8 -8
- package/dist/cli/{describe-YJFDN54P.js → describe-VJEEVOH6.js} +15 -15
- package/dist/cli/{executions-MG63LYMV.mjs → executions-LJN5QOK7.mjs} +4 -4
- package/dist/cli/{executions-O7IIWVRV.js → executions-LPVCV5SC.js} +6 -6
- package/dist/cli/{feedback-WMYRDK2J.mjs → feedback-3HYS6ECC.mjs} +3 -3
- package/dist/cli/{feedback-SR2X2H4N.js → feedback-K26WP7HJ.js} +6 -6
- package/dist/cli/{git-NIGE3SIJ.mjs → git-U4VBLXSS.mjs} +4 -4
- package/dist/cli/git-VMQTXKJQ.js +23 -0
- package/dist/cli/index.js +79 -79
- package/dist/cli/index.mjs +31 -31
- package/dist/cli/{legacy-noops-TXSBNOG6.js → legacy-noops-32YL3VF4.js} +7 -7
- package/dist/cli/{legacy-noops-YXYYCDSR.mjs → legacy-noops-ORHX2FMA.mjs} +4 -4
- package/dist/cli/{monitors-QYAGRL6V.mjs → monitors-5JJBEJS7.mjs} +4 -4
- package/dist/cli/{monitors-ZZUQMDUA.js → monitors-NFE4ICED.js} +85 -85
- package/dist/cli/{org-QQSCJJMM.js → org-7CJS35TF.js} +60 -60
- package/dist/cli/{org-6NUUN2P7.mjs → org-RJYSDJ3W.mjs} +4 -4
- package/dist/cli/play-FUZYMPHF.js +153 -0
- package/dist/cli/{play-3G2A53MD.mjs → play-H2QUTJR2.mjs} +7 -7
- package/dist/cli/{providers-IGD3ZYTA.mjs → providers-TEVRE4Q6.mjs} +4 -4
- package/dist/cli/{providers-AASOAAWT.js → providers-VUKRZWZY.js} +7 -7
- package/dist/cli/{quickstart-RRCLOSHQ.mjs → quickstart-2MIHVSZO.mjs} +3 -3
- package/dist/cli/{quickstart-NDUT5BVF.js → quickstart-36OGYQ2P.js} +5 -5
- package/dist/cli/search-DFSHYVZ5.js +26 -0
- package/dist/cli/{search-37MO75IS.mjs → search-IRRQSN6R.mjs} +5 -5
- package/dist/cli/{secrets-3OTOG64E.mjs → secrets-3W6OVDVE.mjs} +4 -4
- package/dist/cli/{secrets-QXY6DCJA.js → secrets-K67ZKZZM.js} +13 -13
- package/dist/cli/{sessions-FML35Y64.mjs → sessions-AOMOV4HG.mjs} +4 -4
- package/dist/cli/{sessions-3P6DOM3H.js → sessions-SLQ2QFLK.js} +5 -5
- package/dist/cli/{settings-SD6MDI67.js → settings-2X7H6O4I.js} +74 -74
- package/dist/cli/{settings-MWFWIGHW.mjs → settings-L4IFYF5D.mjs} +7 -7
- package/dist/cli/{setup-HY35HR26.js → setup-2RY5APQS.js} +41 -41
- package/dist/cli/{setup-HDYPYPFU.mjs → setup-CZYQTZ2K.mjs} +6 -6
- package/dist/cli/{skills-TGEDPYTP.mjs → skills-D2QQH5S7.mjs} +4 -4
- package/dist/cli/skills-RIFYRYWL.js +28 -0
- package/dist/cli/tools-M7LTH6ZT.js +40 -0
- package/dist/cli/{tools-VBARDH32.mjs → tools-TYIEROJJ.mjs} +6 -6
- package/dist/cli/{update-OIDLOWGB.mjs → update-7YCSUWFP.mjs} +6 -6
- package/dist/cli/update-HC62SVSQ.js +28 -0
- package/dist/cli/{workflow-K2SJUC5Z.js → workflow-42IFLYPN.js} +15 -15
- package/dist/cli/{workflow-ACHWLHPT.mjs → workflow-GGRUCLCI.mjs} +5 -5
- package/dist/{client-l-IoLh8Z.d.ts → client-DkoxT4wF.d.ts} +1 -1
- package/dist/{client-D5XOQKsA.d.mts → client-PDAfbylh.d.mts} +1 -1
- package/dist/{compiler-manifest-82q4jtfQ.d.ts → compiler-manifest-CButyeZ8.d.mts} +7 -0
- package/dist/{compiler-manifest-82q4jtfQ.d.mts → compiler-manifest-CButyeZ8.d.ts} +7 -0
- package/dist/{index-BPbW2XHF.d.mts → index-BMsNUoL5.d.mts} +1 -1
- package/dist/{index-k0rquHIm.d.ts → index-LDRzwo09.d.ts} +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +6 -6
- package/dist/index.mjs +3 -3
- package/dist/install-integrity.json +80 -80
- package/dist/plays/bundle-play-file.d.mts +3 -3
- package/dist/plays/bundle-play-file.d.ts +3 -3
- package/dist/plays/run-request.d.mts +2 -2
- package/dist/plays/run-request.d.ts +2 -2
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli/auth-6SVPO5HS.js +0 -31
- package/dist/cli/client-BPQKKACG.js +0 -20
- package/dist/cli/git-ZZU4LXWX.js +0 -23
- package/dist/cli/play-LLC44DQW.js +0 -153
- package/dist/cli/search-JWUKJSNY.js +0 -26
- package/dist/cli/skills-T3F4A4BQ.js +0 -28
- package/dist/cli/tools-PXC5VIRU.js +0 -40
- package/dist/cli/update-PIT4RK74.js +0 -28
|
@@ -477,7 +477,7 @@ export type CsvOptions = PlayAuthoringCsvOptions;
|
|
|
477
477
|
* // Emit a log line (visible in `play tail`)
|
|
478
478
|
* ctx.log(`Loaded ${await leads.count()} leads`);
|
|
479
479
|
*
|
|
480
|
-
* //
|
|
480
|
+
* // Wait at least one second before continuing.
|
|
481
481
|
* await ctx.sleep(1000);
|
|
482
482
|
*
|
|
483
483
|
* // Access submitted input through the handler's second argument.
|
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.334',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
206
206
|
packageCapabilities: {
|
|
@@ -222,7 +222,8 @@ import {
|
|
|
222
222
|
classifyToolExecuteHttpFailure,
|
|
223
223
|
createToolExecuteHttpFailureAttemptTracker,
|
|
224
224
|
parseToolExecuteRetryAfterMs,
|
|
225
|
-
|
|
225
|
+
parseToolExecuteProviderRetryAtMs,
|
|
226
|
+
parseToolExecuteExactProviderRetryAtMs,
|
|
226
227
|
parseToolExecuteAuthScopeChangedError,
|
|
227
228
|
ToolExecuteAuthScopeChangedError,
|
|
228
229
|
type ToolExecuteHttpFailureOutcome,
|
|
@@ -653,6 +654,7 @@ const TOOL_RETRY_AFTER_FALLBACK_MS = 1_000;
|
|
|
653
654
|
export const TOOL_EXECUTE_RESPONSE_BODY_TIMEOUT_MS = 30_000;
|
|
654
655
|
const TOOL_RETRY_HEARTBEAT_INTERVAL_MS = 30_000;
|
|
655
656
|
const TOOL_PROVIDER_RESIDENT_WAIT_BUDGET_MS = 5_000;
|
|
657
|
+
const AUTHORED_SLEEP_RESIDENT_LIMIT_MS = 10_000;
|
|
656
658
|
const DEEPLINEAGENT_TOOL_RUNTIME_TIMEOUT_MS = 15 * 60 * 1000;
|
|
657
659
|
// Every runtime-API tool fetch needs a client-side deadline. Without one, a
|
|
658
660
|
// stalled execute request (e.g. a credential-less call whose gateway connection
|
|
@@ -2928,6 +2930,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2928
2930
|
*/
|
|
2929
2931
|
private readonly persistenceLatch: RuntimePersistenceLatch =
|
|
2930
2932
|
createRuntimePersistenceLatch();
|
|
2933
|
+
private sleepMetadataPersistenceFailed = false;
|
|
2931
2934
|
/**
|
|
2932
2935
|
* The single source of every concurrency, budget, and pacing decision for this
|
|
2933
2936
|
* run-attempt. Tool/play/row slots are blocking semaphores; budgets are charged
|
|
@@ -3153,6 +3156,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
3153
3156
|
*/
|
|
3154
3157
|
assertNoPendingDurableSuspension(): void {
|
|
3155
3158
|
this.#options.executionSignal?.throwIfAborted();
|
|
3159
|
+
if (this.sleepMetadataPersistenceFailed) {
|
|
3160
|
+
throw new RuntimePersistenceCircuitOpenError(this.persistenceLatch);
|
|
3161
|
+
}
|
|
3156
3162
|
this.assertNoSlackHitlTimeoutFailure();
|
|
3157
3163
|
this.assertNoRowProviderBackpressure();
|
|
3158
3164
|
if (this.pendingDurableSuspension) {
|
|
@@ -6234,7 +6240,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
6234
6240
|
runningReceiptWaitMaxAttempts?: number;
|
|
6235
6241
|
runningReceiptWaitDelayMs?: number;
|
|
6236
6242
|
reclaimRunning?: boolean;
|
|
6237
|
-
markSkipped?: (
|
|
6243
|
+
markSkipped?: (
|
|
6244
|
+
output: T,
|
|
6245
|
+
receipt: RuntimeStepReceipt,
|
|
6246
|
+
) => Promise<void> | void;
|
|
6238
6247
|
onRecovered?: (
|
|
6239
6248
|
output: T,
|
|
6240
6249
|
receipt: RuntimeStepReceipt,
|
|
@@ -14580,6 +14589,111 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14580
14589
|
this.suspendDurably({ kind: 'sleep', boundaryId, delayMs });
|
|
14581
14590
|
}
|
|
14582
14591
|
|
|
14592
|
+
private sleepMetadataReceiptKey(
|
|
14593
|
+
boundaryId: string,
|
|
14594
|
+
intent: 'wake' | 'count',
|
|
14595
|
+
): string {
|
|
14596
|
+
return durableCtxKey({
|
|
14597
|
+
orgId: this.#options.orgId,
|
|
14598
|
+
playId: this.currentExecutionScope.receipt.namespace,
|
|
14599
|
+
operation: 'step',
|
|
14600
|
+
id: `__deepline_sleep_${intent}__`,
|
|
14601
|
+
semanticKey: stableStringify({
|
|
14602
|
+
runId: this.currentReceiptOwnerRunId,
|
|
14603
|
+
boundaryId,
|
|
14604
|
+
}),
|
|
14605
|
+
});
|
|
14606
|
+
}
|
|
14607
|
+
|
|
14608
|
+
private async readStepSleepCount(boundaryId: string): Promise<number | null> {
|
|
14609
|
+
try {
|
|
14610
|
+
const receipt = await this.getRuntimeStepReceipt(
|
|
14611
|
+
this.sleepMetadataReceiptKey(boundaryId, 'count'),
|
|
14612
|
+
);
|
|
14613
|
+
if (receipt?.status !== 'completed') return null;
|
|
14614
|
+
const output = receipt.output;
|
|
14615
|
+
const sleepCount =
|
|
14616
|
+
output && typeof output === 'object' && 'sleepCount' in output
|
|
14617
|
+
? output.sleepCount
|
|
14618
|
+
: undefined;
|
|
14619
|
+
if (
|
|
14620
|
+
typeof sleepCount !== 'number' ||
|
|
14621
|
+
!Number.isSafeInteger(sleepCount) ||
|
|
14622
|
+
sleepCount < 0
|
|
14623
|
+
) {
|
|
14624
|
+
throw new Error(`Durable step ${boundaryId} has no valid sleep count.`);
|
|
14625
|
+
}
|
|
14626
|
+
return sleepCount;
|
|
14627
|
+
} catch (error) {
|
|
14628
|
+
this.sleepMetadataPersistenceFailed = true;
|
|
14629
|
+
tripRuntimePersistenceLatch(this.persistenceLatch, error);
|
|
14630
|
+
throw error;
|
|
14631
|
+
}
|
|
14632
|
+
}
|
|
14633
|
+
|
|
14634
|
+
private async persistStepSleepCount(
|
|
14635
|
+
boundaryId: string,
|
|
14636
|
+
sleepCount: number,
|
|
14637
|
+
): Promise<void> {
|
|
14638
|
+
try {
|
|
14639
|
+
await this.completeRuntimeStepReceipt(
|
|
14640
|
+
this.sleepMetadataReceiptKey(boundaryId, 'count'),
|
|
14641
|
+
this.currentReceiptOwnerRunId,
|
|
14642
|
+
{ sleepCount },
|
|
14643
|
+
COMPLETED_RECEIPT_CACHE_INSERT_LEASE_ID,
|
|
14644
|
+
);
|
|
14645
|
+
if ((await this.readStepSleepCount(boundaryId)) === null) {
|
|
14646
|
+
throw new Error(
|
|
14647
|
+
`Durable step ${boundaryId} did not persist its sleep count.`,
|
|
14648
|
+
);
|
|
14649
|
+
}
|
|
14650
|
+
} catch (error) {
|
|
14651
|
+
this.sleepMetadataPersistenceFailed = true;
|
|
14652
|
+
tripRuntimePersistenceLatch(this.persistenceLatch, error);
|
|
14653
|
+
throw error;
|
|
14654
|
+
}
|
|
14655
|
+
}
|
|
14656
|
+
|
|
14657
|
+
private async resolveCanonicalSleepWakeAtMs(
|
|
14658
|
+
boundaryId: string,
|
|
14659
|
+
delayMs: number,
|
|
14660
|
+
): Promise<number> {
|
|
14661
|
+
const key = this.sleepMetadataReceiptKey(boundaryId, 'wake');
|
|
14662
|
+
const proposedWakeAtMs = Date.now() + delayMs;
|
|
14663
|
+
try {
|
|
14664
|
+
let receipt = await this.getRuntimeStepReceipt(key);
|
|
14665
|
+
if (receipt?.status !== 'completed') {
|
|
14666
|
+
await this.completeRuntimeStepReceipt(
|
|
14667
|
+
key,
|
|
14668
|
+
this.currentReceiptOwnerRunId,
|
|
14669
|
+
{ wakeAtMs: proposedWakeAtMs },
|
|
14670
|
+
COMPLETED_RECEIPT_CACHE_INSERT_LEASE_ID,
|
|
14671
|
+
);
|
|
14672
|
+
receipt = await this.getRuntimeStepReceipt(key);
|
|
14673
|
+
}
|
|
14674
|
+
const output = receipt?.output;
|
|
14675
|
+
const wakeAtMs =
|
|
14676
|
+
output && typeof output === 'object' && 'wakeAtMs' in output
|
|
14677
|
+
? output.wakeAtMs
|
|
14678
|
+
: undefined;
|
|
14679
|
+
if (
|
|
14680
|
+
receipt?.status !== 'completed' ||
|
|
14681
|
+
typeof wakeAtMs !== 'number' ||
|
|
14682
|
+
!Number.isSafeInteger(wakeAtMs) ||
|
|
14683
|
+
wakeAtMs < 0
|
|
14684
|
+
) {
|
|
14685
|
+
throw new Error(
|
|
14686
|
+
`Durable sleep intent ${boundaryId} has no valid wake timestamp.`,
|
|
14687
|
+
);
|
|
14688
|
+
}
|
|
14689
|
+
return wakeAtMs;
|
|
14690
|
+
} catch (error) {
|
|
14691
|
+
this.sleepMetadataPersistenceFailed = true;
|
|
14692
|
+
tripRuntimePersistenceLatch(this.persistenceLatch, error);
|
|
14693
|
+
throw error;
|
|
14694
|
+
}
|
|
14695
|
+
}
|
|
14696
|
+
|
|
14583
14697
|
async sleep(ms: number): Promise<void> {
|
|
14584
14698
|
if (!rowContext.getStore()) this.assertRowAdmissionOpen();
|
|
14585
14699
|
this.assertNoPendingDurableSuspension();
|
|
@@ -14653,17 +14767,49 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14653
14767
|
if (existing?.kind === 'sleep' && existing.completedAt !== undefined) {
|
|
14654
14768
|
return;
|
|
14655
14769
|
}
|
|
14770
|
+
const wakeAtMs =
|
|
14771
|
+
existing?.kind === 'sleep' && existing.wakeAtMs !== undefined
|
|
14772
|
+
? existing.wakeAtMs
|
|
14773
|
+
: this.durableDirectToolResultsBackedByReceipts
|
|
14774
|
+
? await this.resolveCanonicalSleepWakeAtMs(boundaryId, delayMs)
|
|
14775
|
+
: undefined;
|
|
14776
|
+
this.assertNoPendingDurableSuspension();
|
|
14656
14777
|
this.checkpoint.resolvedBoundaries = {
|
|
14657
14778
|
...(this.checkpoint.resolvedBoundaries ?? {}),
|
|
14658
14779
|
[boundaryId]: {
|
|
14780
|
+
...existing,
|
|
14659
14781
|
kind: 'sleep',
|
|
14660
14782
|
delayMs,
|
|
14783
|
+
...(wakeAtMs !== undefined ? { wakeAtMs } : {}),
|
|
14661
14784
|
},
|
|
14662
14785
|
};
|
|
14786
|
+
if (
|
|
14787
|
+
wakeAtMs !== undefined &&
|
|
14788
|
+
!rowStore &&
|
|
14789
|
+
this.durableDirectToolResultsBackedByReceipts &&
|
|
14790
|
+
(wakeAtMs <= Date.now() ||
|
|
14791
|
+
(delayMs <= AUTHORED_SLEEP_RESIDENT_LIMIT_MS &&
|
|
14792
|
+
wakeAtMs < (this.#options.executionDrainAtMs ?? Infinity)))
|
|
14793
|
+
) {
|
|
14794
|
+
const remainingMs = Math.max(0, wakeAtMs - Date.now());
|
|
14795
|
+
if (remainingMs > 0)
|
|
14796
|
+
await this.sleepWithCheckpointHeartbeat(remainingMs);
|
|
14797
|
+
this.assertNoPendingDurableSuspension();
|
|
14798
|
+
this.checkpoint.resolvedBoundaries[boundaryId] = {
|
|
14799
|
+
...this.checkpoint.resolvedBoundaries[boundaryId],
|
|
14800
|
+
kind: 'sleep',
|
|
14801
|
+
delayMs,
|
|
14802
|
+
wakeAtMs,
|
|
14803
|
+
completedAt: Date.now(),
|
|
14804
|
+
};
|
|
14805
|
+
this.#options.onBatchComplete?.(this.checkpoint);
|
|
14806
|
+
return;
|
|
14807
|
+
}
|
|
14663
14808
|
this.suspendDurably({
|
|
14664
14809
|
kind: 'sleep',
|
|
14665
14810
|
boundaryId,
|
|
14666
14811
|
delayMs,
|
|
14812
|
+
...(wakeAtMs !== undefined ? { wakeAtMs } : {}),
|
|
14667
14813
|
});
|
|
14668
14814
|
}
|
|
14669
14815
|
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
@@ -15000,30 +15146,33 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15000
15146
|
const boundaryId = this.durableBoundaryId(
|
|
15001
15147
|
`step-${scope}:${normalizedKey}${boundarySuffix}`,
|
|
15002
15148
|
);
|
|
15149
|
+
const advanceRecoveredSleepCursor = async (
|
|
15150
|
+
receipt?: RuntimeStepReceipt,
|
|
15151
|
+
) => {
|
|
15152
|
+
if (rowStore?.replayCallIndexes?.recoverStep(boundaryId)) return;
|
|
15153
|
+
const existing = assertKnownResolvedBoundary(
|
|
15154
|
+
boundaryId,
|
|
15155
|
+
this.checkpoint.resolvedBoundaries?.[boundaryId],
|
|
15156
|
+
);
|
|
15157
|
+
const sleepCount =
|
|
15158
|
+
existing?.kind === 'step' && existing.sleepCount !== undefined
|
|
15159
|
+
? existing.sleepCount
|
|
15160
|
+
: receipt?.runId === this.currentReceiptOwnerRunId
|
|
15161
|
+
? ((await this.readStepSleepCount(boundaryId)) ?? 0)
|
|
15162
|
+
: 0;
|
|
15163
|
+
if (Number.isSafeInteger(sleepCount) && sleepCount > 0)
|
|
15164
|
+
this.sleepBoundaryIndex += sleepCount;
|
|
15165
|
+
};
|
|
15003
15166
|
const executeStep = async (): Promise<T> => {
|
|
15004
15167
|
const existing = assertKnownResolvedBoundary(
|
|
15005
15168
|
boundaryId,
|
|
15006
15169
|
this.checkpoint.resolvedBoundaries?.[boundaryId],
|
|
15007
15170
|
);
|
|
15008
15171
|
if (existing?.kind === 'step' && 'output' in existing) {
|
|
15009
|
-
const replayedCalls =
|
|
15010
|
-
rowStore?.replayCallIndexes?.recoverStep(boundaryId) ?? false;
|
|
15011
15172
|
this.runtimeLog(
|
|
15012
15173
|
`ctx.step(${normalizedKey}): recovered result from checkpoint`,
|
|
15013
15174
|
);
|
|
15014
|
-
|
|
15015
|
-
// contained would otherwise disappear from the positional sleep
|
|
15016
|
-
// cursor. Preserve the authored boundary sequence before evaluating
|
|
15017
|
-
// the next sibling operation. This is deliberately run-local state;
|
|
15018
|
-
// it is not a cross-run sleep cache or an input-derived identity.
|
|
15019
|
-
if (
|
|
15020
|
-
!replayedCalls &&
|
|
15021
|
-
Number.isSafeInteger(existing.sleepCount) &&
|
|
15022
|
-
existing.sleepCount !== undefined &&
|
|
15023
|
-
existing.sleepCount > 0
|
|
15024
|
-
) {
|
|
15025
|
-
this.sleepBoundaryIndex += existing.sleepCount;
|
|
15026
|
-
}
|
|
15175
|
+
await advanceRecoveredSleepCursor();
|
|
15027
15176
|
return existing.output as T;
|
|
15028
15177
|
}
|
|
15029
15178
|
|
|
@@ -15046,15 +15195,20 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15046
15195
|
// publish an enclosing step fallback that would make a later resume skip
|
|
15047
15196
|
// the suspended work.
|
|
15048
15197
|
this.assertNoPendingDurableSuspension();
|
|
15198
|
+
const sleepCount = rowStore?.replayCallIndexes
|
|
15199
|
+
? (callAdvances.get('sleep:legacy') ?? 0)
|
|
15200
|
+
: this.sleepBoundaryIndex - sleepBoundaryIndexBefore;
|
|
15201
|
+
if (sleepCount > 0 && this.durableDirectToolResultsBackedByReceipts) {
|
|
15202
|
+
await this.persistStepSleepCount(boundaryId, sleepCount);
|
|
15203
|
+
}
|
|
15204
|
+
this.assertNoPendingDurableSuspension();
|
|
15049
15205
|
this.checkpoint.resolvedBoundaries = {
|
|
15050
15206
|
...(this.checkpoint.resolvedBoundaries ?? {}),
|
|
15051
15207
|
[boundaryId]: {
|
|
15052
15208
|
kind: 'step',
|
|
15053
15209
|
stepId: normalizedKey,
|
|
15054
15210
|
output,
|
|
15055
|
-
sleepCount
|
|
15056
|
-
? (callAdvances.get('sleep:legacy') ?? 0)
|
|
15057
|
-
: this.sleepBoundaryIndex - sleepBoundaryIndexBefore,
|
|
15211
|
+
sleepCount,
|
|
15058
15212
|
completedAt: Date.now(),
|
|
15059
15213
|
},
|
|
15060
15214
|
};
|
|
@@ -15099,8 +15253,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15099
15253
|
: options?.semanticKey,
|
|
15100
15254
|
staleAfterSeconds: options?.staleAfterSeconds,
|
|
15101
15255
|
force: this.#options.cachePolicy?.forceStepRefresh === true,
|
|
15102
|
-
markSkipped: (output) => {
|
|
15256
|
+
markSkipped: async (output, receipt) => {
|
|
15103
15257
|
assertJsonSerializableStepOutput(normalizedKey, output);
|
|
15258
|
+
await advanceRecoveredSleepCursor(receipt);
|
|
15104
15259
|
},
|
|
15105
15260
|
execute: executeStep,
|
|
15106
15261
|
},
|
|
@@ -17849,8 +18004,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
17849
18004
|
const executionRequestNonce = crypto.randomUUID();
|
|
17850
18005
|
// Transport ambiguity must retain the billing identity. An ordinary
|
|
17851
18006
|
// (unfenced) call advances it after a definite rejection released
|
|
17852
|
-
// that attempt's reservation
|
|
17853
|
-
//
|
|
18007
|
+
// that attempt's reservation. A proven app pacing refusal also
|
|
18008
|
+
// renews fenced billing attribution, keeping its invocation and
|
|
18009
|
+
// provider-operation identities unchanged. Ambiguous deliveries retain it.
|
|
17854
18010
|
let deeplineRequestId = `ctx-tool-${stableDigest(executionRequestNonce).slice(0, 32)}`;
|
|
17855
18011
|
// The provider operation has a semantic identity independent of
|
|
17856
18012
|
// the response receipt. Its key is supplied by the caller that owns
|
|
@@ -18647,13 +18803,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18647
18803
|
// settle the row as failed instead of pacing.
|
|
18648
18804
|
[TOOL_EXECUTE_RUNNER_ACCEPTS_HEADER]:
|
|
18649
18805
|
TOOL_EXECUTE_RUNNER_ACCEPTS_VALUE,
|
|
18650
|
-
|
|
18651
|
-
|
|
18652
|
-
? {
|
|
18653
|
-
[TOOL_EXECUTE_APP_PACING_REFUSAL_HEADER]:
|
|
18654
|
-
TOOL_EXECUTE_APP_PACING_REFUSAL_VALUE,
|
|
18655
|
-
}
|
|
18656
|
-
: {}),
|
|
18806
|
+
[TOOL_EXECUTE_APP_PACING_REFUSAL_HEADER]:
|
|
18807
|
+
TOOL_EXECUTE_APP_PACING_REFUSAL_VALUE,
|
|
18657
18808
|
...(providerGatewayDispatchProof
|
|
18658
18809
|
? {
|
|
18659
18810
|
[TOOL_EXECUTE_PROVIDER_RATE_SCOPE_HEADER]:
|
|
@@ -18698,18 +18849,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18698
18849
|
recordProviderDispatch();
|
|
18699
18850
|
if (dispatchedResponse.status === 429) {
|
|
18700
18851
|
const observedAt = Date.now();
|
|
18701
|
-
const retryAfter =
|
|
18702
|
-
dispatchedResponse.headers.get('retry-after');
|
|
18703
18852
|
// Preserve absolute provider deadlines even when the
|
|
18704
18853
|
// response did not carry the gateway's counter
|
|
18705
18854
|
// attestation. Classification below still decides
|
|
18706
18855
|
// whether this 429 may update shared provider pacing.
|
|
18707
|
-
providerRetryAtMs =
|
|
18708
|
-
|
|
18709
|
-
|
|
18710
|
-
|
|
18711
|
-
|
|
18712
|
-
: undefined;
|
|
18856
|
+
providerRetryAtMs =
|
|
18857
|
+
parseToolExecuteProviderRetryAtMs(
|
|
18858
|
+
dispatchedResponse,
|
|
18859
|
+
observedAt,
|
|
18860
|
+
);
|
|
18713
18861
|
}
|
|
18714
18862
|
if (
|
|
18715
18863
|
isAttestedProvider429Response(dispatchedResponse)
|
|
@@ -18877,6 +19025,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18877
19025
|
schemaVersion: toolErrorSchemaVersion,
|
|
18878
19026
|
retryAfterHeader:
|
|
18879
19027
|
dispatchedResponse.headers.get('retry-after'),
|
|
19028
|
+
retryAtMs:
|
|
19029
|
+
parseToolExecuteExactProviderRetryAtMs(
|
|
19030
|
+
dispatchedResponse,
|
|
19031
|
+
),
|
|
18880
19032
|
transientHttpRetrySafe:
|
|
18881
19033
|
retrySafeTransientHttp || fixtureReplaySafe,
|
|
18882
19034
|
executionFenced: usesDurableInvocationFence,
|
|
@@ -18898,6 +19050,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18898
19050
|
schemaVersion: toolErrorSchemaVersion,
|
|
18899
19051
|
retryAfterHeader:
|
|
18900
19052
|
dispatchedResponse.headers.get('retry-after'),
|
|
19053
|
+
retryAtMs:
|
|
19054
|
+
parseToolExecuteExactProviderRetryAtMs(
|
|
19055
|
+
dispatchedResponse,
|
|
19056
|
+
),
|
|
18901
19057
|
transientHttpRetrySafe:
|
|
18902
19058
|
retrySafeTransientHttp || fixtureReplaySafe,
|
|
18903
19059
|
executionFenced: usesDurableInvocationFence,
|
|
@@ -19294,6 +19450,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19294
19450
|
bodyText: text,
|
|
19295
19451
|
schemaVersion: toolErrorSchemaVersion,
|
|
19296
19452
|
retryAfterHeader: response.headers.get('retry-after'),
|
|
19453
|
+
retryAtMs: parseToolExecuteExactProviderRetryAtMs(response),
|
|
19297
19454
|
transientHttpRetrySafe:
|
|
19298
19455
|
retrySafeTransientHttp || fixtureReplaySafe,
|
|
19299
19456
|
executionFenced: usesDurableInvocationFence,
|
|
@@ -19519,11 +19676,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19519
19676
|
);
|
|
19520
19677
|
logProvider429RetryPhase('retry_sleep_completed');
|
|
19521
19678
|
if (
|
|
19522
|
-
|
|
19523
|
-
(
|
|
19524
|
-
failure.
|
|
19525
|
-
|
|
19526
|
-
|
|
19679
|
+
failure.reason === 'provider_pacing_wait' ||
|
|
19680
|
+
(!usesDurableInvocationFence &&
|
|
19681
|
+
(failure.backpressureDelayMs !== null ||
|
|
19682
|
+
(failure.reason === 'rate_limit' &&
|
|
19683
|
+
failure.knownNotDispatched)))
|
|
19527
19684
|
) {
|
|
19528
19685
|
// These refusals release the prior reservation. App pacing
|
|
19529
19686
|
// does not advance invocationAttempt, so use its own HTTP
|
|
@@ -815,6 +815,8 @@ export interface ContextOptions {
|
|
|
815
815
|
rowAdmissionSignal?: AbortSignal;
|
|
816
816
|
/** Local runner cancellation; never serialized into a Play artifact. */
|
|
817
817
|
executionSignal?: AbortSignal;
|
|
818
|
+
/** Runner-local deadline for stopping new work, captured when its timer arms. */
|
|
819
|
+
executionDrainAtMs?: number;
|
|
818
820
|
/**
|
|
819
821
|
* Runtime-sheet transport selected by the runner. Daytona sandboxes use the
|
|
820
822
|
* execution gateway and must never fall back to minting direct DB sessions.
|
|
@@ -1244,6 +1246,8 @@ export interface PlayCheckpoint {
|
|
|
1244
1246
|
| {
|
|
1245
1247
|
kind: 'sleep';
|
|
1246
1248
|
delayMs: number;
|
|
1249
|
+
/** Original wake deadline. Historical boundaries retain relative waits. */
|
|
1250
|
+
wakeAtMs?: number;
|
|
1247
1251
|
completedAt?: number;
|
|
1248
1252
|
scope?: {
|
|
1249
1253
|
type: 'workflow' | 'map_row';
|
|
@@ -777,7 +777,10 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
777
777
|
runningReceiptWaitMaxAttempts?: number;
|
|
778
778
|
runningReceiptWaitDelayMs?: number;
|
|
779
779
|
reclaimRunning?: boolean;
|
|
780
|
-
markSkipped?: (
|
|
780
|
+
markSkipped?: (
|
|
781
|
+
output: T,
|
|
782
|
+
receipt: RuntimeStepReceipt,
|
|
783
|
+
) => Promise<void> | void;
|
|
781
784
|
onRecovered?: (
|
|
782
785
|
output: T,
|
|
783
786
|
receipt: RuntimeStepReceipt,
|
|
@@ -812,6 +815,9 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
812
815
|
): Promise<T> => {
|
|
813
816
|
input.log(`ctx.${input.operation}(${input.id}): reused completed work`);
|
|
814
817
|
if (receipt.output === undefined) {
|
|
818
|
+
if (input.operation === 'step' && input.markSkipped) {
|
|
819
|
+
await input.markSkipped(receipt.output as T, receipt);
|
|
820
|
+
}
|
|
815
821
|
return receipt.output as T;
|
|
816
822
|
}
|
|
817
823
|
const output = runtimeReceiptOutput<T>(receipt);
|
|
@@ -819,7 +825,7 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
819
825
|
? input.onRecovered(output, receipt, 'cache')
|
|
820
826
|
: output;
|
|
821
827
|
if (input.markSkipped) {
|
|
822
|
-
await input.markSkipped(recovered);
|
|
828
|
+
await input.markSkipped(recovered, receipt);
|
|
823
829
|
}
|
|
824
830
|
return recovered;
|
|
825
831
|
};
|
package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-invocation-fence.ts
CHANGED
|
@@ -62,6 +62,14 @@ export type DurableInvocationDiagnostic = {
|
|
|
62
62
|
error?: unknown;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
+
/** Authenticated transport proof that this owner sent no provider request. */
|
|
66
|
+
export class DurableInvocationDispatchNotStartedError extends Error {
|
|
67
|
+
constructor(readonly reason: string) {
|
|
68
|
+
super(reason);
|
|
69
|
+
this.name = 'DurableInvocationDispatchNotStartedError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
export class DurableInvocationConflictError extends Error {
|
|
66
74
|
readonly code = 'DURABLE_INVOCATION_REQUEST_CONFLICT';
|
|
67
75
|
|
|
@@ -313,6 +321,14 @@ export async function executeWithDurableInvocationFence(input: {
|
|
|
313
321
|
response = await input.execute();
|
|
314
322
|
} catch (error) {
|
|
315
323
|
diagnose('dispatch_failed', error);
|
|
324
|
+
if (error instanceof DurableInvocationDispatchNotStartedError) {
|
|
325
|
+
// An authenticated app refusal proves no provider request was sent.
|
|
326
|
+
// Release only this owner; ordinary transport failures stay unknown.
|
|
327
|
+
if (!(await input.store.abandon(begun))) {
|
|
328
|
+
throw new DurableInvocationInProgressError();
|
|
329
|
+
}
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
316
332
|
if (input.replaySafeAfterDispatch) {
|
|
317
333
|
return await retryReplaySafeInvocation(begun);
|
|
318
334
|
}
|
package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts
CHANGED
|
@@ -140,7 +140,6 @@ function sleepWithAbort(
|
|
|
140
140
|
* hold more than a moment of a shared provider window.
|
|
141
141
|
*/
|
|
142
142
|
const RATE_BLOCK_HORIZON_MS = 1_000;
|
|
143
|
-
const UNKNOWN_PROVIDER_RECOVERY_BLOCK_HORIZON_MS = 500;
|
|
144
143
|
const STORE_FAILURE_RETRY_DELAYS_MS = [100, 250] as const;
|
|
145
144
|
/**
|
|
146
145
|
* Jitter fraction applied to a server-supplied `waitMs` so a synchronized wave
|
|
@@ -667,23 +666,22 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
667
666
|
// a hot map is a round trip per permit. The store's own token floor, not
|
|
668
667
|
// this number, is what bounds the grant.
|
|
669
668
|
const learnedRps = this.effectiveRps.get(laneKey);
|
|
669
|
+
const currentRps = learnedRps ?? configuredRps;
|
|
670
670
|
const recoveringUnknownProvider =
|
|
671
671
|
learnedRps !== undefined &&
|
|
672
672
|
learnedRps < DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS &&
|
|
673
673
|
rules.some((rule) => rule.ruleId === 'default:provider-scope') &&
|
|
674
674
|
rules.every((rule) => rule.ruleId.startsWith('default:'));
|
|
675
|
-
// Below recovered capacity,
|
|
676
|
-
//
|
|
677
|
-
//
|
|
675
|
+
// Below recovered capacity, one dispatch interval (500/250/125ms at
|
|
676
|
+
// 2/4/8 RPS) can prove the next step. Reserve only that interval so cached
|
|
677
|
+
// old-pace tickets cannot postpone the next fresh-feedback refill.
|
|
678
678
|
const horizonMs = Math.min(
|
|
679
|
-
recoveringUnknownProvider
|
|
680
|
-
? UNKNOWN_PROVIDER_RECOVERY_BLOCK_HORIZON_MS
|
|
681
|
-
: RATE_BLOCK_HORIZON_MS,
|
|
679
|
+
recoveringUnknownProvider ? 1_000 / currentRps : RATE_BLOCK_HORIZON_MS,
|
|
682
680
|
leasedBlockTtlMs(rules),
|
|
683
681
|
);
|
|
684
682
|
const rateBudget = Math.max(
|
|
685
683
|
1,
|
|
686
|
-
Math.floor((
|
|
684
|
+
Math.floor((currentRps * horizonMs) / 1_000),
|
|
687
685
|
);
|
|
688
686
|
if (!Number.isFinite(rateBudget) || rateBudget <= 0) return floor;
|
|
689
687
|
const waiters = this.waiters.get(laneKey) ?? 1;
|
|
@@ -1018,7 +1016,8 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
1018
1016
|
const releaseGeneration = releaseSignal?.generation;
|
|
1019
1017
|
const successKey = pendingSuccessKey(bucketId, ordered);
|
|
1020
1018
|
const feedback = this.snapshotSuccessFeedback(successKey);
|
|
1021
|
-
const
|
|
1019
|
+
const requestStartedAtMs = this.now();
|
|
1020
|
+
const rawResponse = await this.port.acquire({
|
|
1022
1021
|
bucketId,
|
|
1023
1022
|
rateScopeToken,
|
|
1024
1023
|
rules: withAdaptiveMaxRps(ordered),
|
|
@@ -1026,6 +1025,28 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
1026
1025
|
...feedback,
|
|
1027
1026
|
schedulerSchema: this.schedulerSchema,
|
|
1028
1027
|
});
|
|
1028
|
+
const serverElapsedMs = rawResponse.serverClockElapsedMs;
|
|
1029
|
+
const validServerElapsed =
|
|
1030
|
+
typeof rawResponse.serverNowMs === 'number' &&
|
|
1031
|
+
Number.isFinite(rawResponse.serverNowMs) &&
|
|
1032
|
+
rawResponse.serverNowMs >= 0 &&
|
|
1033
|
+
typeof serverElapsedMs === 'number' &&
|
|
1034
|
+
Number.isSafeInteger(serverElapsedMs) &&
|
|
1035
|
+
serverElapsedMs >= 0 &&
|
|
1036
|
+
serverElapsedMs <= this.now() - requestStartedAtMs;
|
|
1037
|
+
// The clock was already this old when the response left the server.
|
|
1038
|
+
// Advance only by confirmed elapsed time; unknown transit remains
|
|
1039
|
+
// conservative, and an invalid/legacy response keeps the old clock.
|
|
1040
|
+
const response = validServerElapsed
|
|
1041
|
+
? {
|
|
1042
|
+
...rawResponse,
|
|
1043
|
+
serverNowMs: rawResponse.serverNowMs! + serverElapsedMs,
|
|
1044
|
+
waitMs:
|
|
1045
|
+
rawResponse.waitMs > 0
|
|
1046
|
+
? Math.max(1, rawResponse.waitMs - serverElapsedMs)
|
|
1047
|
+
: rawResponse.waitMs,
|
|
1048
|
+
}
|
|
1049
|
+
: rawResponse;
|
|
1029
1050
|
this.acknowledgeSuccessFeedback(successKey, feedback);
|
|
1030
1051
|
this.recordCooldown(bucketId, response);
|
|
1031
1052
|
if (response.scopeValidationSupported === true)
|
|
@@ -96,6 +96,12 @@ export type PlayRunnerRateStateAcquireResult = {
|
|
|
96
96
|
* provider spacing.
|
|
97
97
|
*/
|
|
98
98
|
serverNowMs?: number;
|
|
99
|
+
/**
|
|
100
|
+
* Confirmed elapsed time after receiving the database clock and before
|
|
101
|
+
* returning this grant. This is a lower bound on the clock's age: uncertain
|
|
102
|
+
* database and HTTP transit time is excluded. Absent on older receivers.
|
|
103
|
+
*/
|
|
104
|
+
serverClockElapsedMs?: number;
|
|
99
105
|
/**
|
|
100
106
|
* One globally reserved dispatch timestamp per granted permit. The runtime
|
|
101
107
|
* may cache the block, but must not dispatch a permit before its timestamp.
|
|
@@ -1159,6 +1159,7 @@ export interface ExecutePlayRunnerOptions {
|
|
|
1159
1159
|
signal?: AbortSignal;
|
|
1160
1160
|
/** Stops future row admission while already admitted rows can persist. */
|
|
1161
1161
|
rowAdmissionSignal?: AbortSignal;
|
|
1162
|
+
executionDrainAtMs?: number;
|
|
1162
1163
|
/** Runner-local terminal state made available to the deadline drain. */
|
|
1163
1164
|
onRuntimeReady?: (runtime: {
|
|
1164
1165
|
log: (line: string) => void;
|
|
@@ -2036,14 +2037,15 @@ async function run(
|
|
|
2036
2037
|
runnerRunAttempt,
|
|
2037
2038
|
);
|
|
2038
2039
|
const workCounterSegmentId = randomUUID();
|
|
2040
|
+
const runtimeLimitMs =
|
|
2041
|
+
validatePlaySandboxRuntimeLimits(
|
|
2042
|
+
config.context.sandboxRuntimeLimits ??
|
|
2043
|
+
STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
|
|
2044
|
+
).timeoutSeconds * 1_000;
|
|
2039
2045
|
const persistenceSignal =
|
|
2040
|
-
options?.signal ??
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
config.context.sandboxRuntimeLimits ??
|
|
2044
|
-
STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
|
|
2045
|
-
).timeoutSeconds * 1_000,
|
|
2046
|
-
);
|
|
2046
|
+
options?.signal ?? AbortSignal.timeout(runtimeLimitMs);
|
|
2047
|
+
const executionDrainAtMs =
|
|
2048
|
+
options?.executionDrainAtMs ?? runStartedAt + runtimeLimitMs;
|
|
2047
2049
|
const contextSetupStartedAt = Date.now();
|
|
2048
2050
|
const getRuntimeToolMetadata = createRuntimeToolMetadataResolver(config);
|
|
2049
2051
|
const getRuntimeToolAuthScopeDigest =
|
|
@@ -2225,6 +2227,7 @@ async function run(
|
|
|
2225
2227
|
artifactRuntimeContract,
|
|
2226
2228
|
rowAdmissionSignal: options?.rowAdmissionSignal,
|
|
2227
2229
|
executionSignal: persistenceSignal,
|
|
2230
|
+
executionDrainAtMs,
|
|
2228
2231
|
executorToken: config.context.executorToken,
|
|
2229
2232
|
baseUrl: config.context.baseUrl,
|
|
2230
2233
|
// Runtime control stays on baseUrl (the receipt gateway); provider calls
|
|
@@ -3894,6 +3897,7 @@ async function main() {
|
|
|
3894
3897
|
result = await executePlayRunner(config, {
|
|
3895
3898
|
signal: executionController.signal,
|
|
3896
3899
|
rowAdmissionSignal: rowAdmissionController.signal,
|
|
3900
|
+
executionDrainAtMs: runtimeDeadline.drainAtMs,
|
|
3897
3901
|
onRuntimeReady: (runtime) => {
|
|
3898
3902
|
runtimeState = runtime;
|
|
3899
3903
|
},
|