deepline 0.3.281 → 0.3.283
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/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/observability/redaction.ts +21 -13
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +19 -30
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +10 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +17 -8
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +238 -68
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +94 -7
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +26 -5
- package/dist/bundling-sources/shared_libs/security/secret-redaction.ts +6 -0
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -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.283',
|
|
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: {
|
|
@@ -59,19 +59,27 @@ function redactStructuredValue(
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function redactPlainTelemetryText(value: string): string {
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
return (
|
|
63
|
+
value
|
|
64
|
+
// Scoped to the provider host: `key` elsewhere remains an identifier.
|
|
65
|
+
.replace(
|
|
66
|
+
/https?:\/\/(?:api|bulkapi)\.millionverifier\.com(?=[:/?#]|$)[^\s"'<>]*/gi,
|
|
67
|
+
(url) =>
|
|
68
|
+
url.replace(/([?&](?:api|key)=)[^&#\s"'<>]*/gi, '$1[REDACTED]'),
|
|
69
|
+
)
|
|
70
|
+
.replace(PRIVATE_KEY_PATTERN, '[REDACTED]')
|
|
71
|
+
.replace(POSTGRES_CREDENTIAL_PATTERN, '$1[REDACTED]@')
|
|
72
|
+
.replace(AUTHORIZATION_CREDENTIAL_PATTERN, '$1$2$3 [REDACTED]')
|
|
73
|
+
.replace(BEARER_CREDENTIAL_PATTERN, 'Bearer [REDACTED]')
|
|
74
|
+
.replace(BASIC_CREDENTIAL_PATTERN, 'Basic [REDACTED]')
|
|
75
|
+
.replace(JWT_PATTERN, '[REDACTED_JWT]')
|
|
76
|
+
.replace(
|
|
77
|
+
JSON_SECRET_PATTERN,
|
|
78
|
+
(_match, keyQuote, key, separator, valueQuote) =>
|
|
79
|
+
`${keyQuote}${key}${keyQuote}${separator}${valueQuote}[REDACTED]${valueQuote}`,
|
|
80
|
+
)
|
|
81
|
+
.replace(INLINE_SECRET_PATTERN, '$1$2[REDACTED]')
|
|
82
|
+
);
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
/**
|
|
@@ -20567,10 +20567,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
20567
20567
|
logProvider429RetryPhase('retry_sleep_completed');
|
|
20568
20568
|
if (
|
|
20569
20569
|
!usesDurableInvocationFence &&
|
|
20570
|
-
failure.backpressureDelayMs !== null
|
|
20570
|
+
(failure.backpressureDelayMs !== null ||
|
|
20571
|
+
(failure.reason === 'rate_limit' &&
|
|
20572
|
+
failure.knownNotDispatched))
|
|
20571
20573
|
) {
|
|
20572
20574
|
// A classified provider rejection has no completed value to
|
|
20573
|
-
// charge.
|
|
20575
|
+
// charge. A credential guard refusal also releases its charge
|
|
20576
|
+
// before replying. The next attempt needs a new reservation;
|
|
20574
20577
|
// durable result and provider-operation keys stay unchanged.
|
|
20575
20578
|
deeplineRequestId = `ctx-tool-${stableDigest(`${executionRequestNonce}:provider-rate-limit-retry:${invocationAttempt}`).slice(0, 32)}`;
|
|
20576
20579
|
}
|
|
@@ -73,8 +73,10 @@ import { collectStartupEgressProbeLines } from './egress-probe';
|
|
|
73
73
|
import {
|
|
74
74
|
pushRunnerTerminal,
|
|
75
75
|
runnerTerminalResultForTransport,
|
|
76
|
+
runnerTerminalHandoffForTransport,
|
|
76
77
|
type PushRunnerTerminalOptions,
|
|
77
78
|
type PlayRunnerTerminalTransportResult,
|
|
79
|
+
type PlayRunnerTerminalTransportEvent,
|
|
78
80
|
} from './runtime-terminal-push';
|
|
79
81
|
import { vercelProtectionBypassHeader } from '../vercel-protection';
|
|
80
82
|
import { getPlayRuntimeBatchStrategy } from '../play-runtime-batching-registry';
|
|
@@ -1407,9 +1409,7 @@ function emitProgressSidecarEvent(event: PlayRunnerEvent): void {
|
|
|
1407
1409
|
}
|
|
1408
1410
|
|
|
1409
1411
|
async function writeRunnerEventToStdout(
|
|
1410
|
-
event:
|
|
1411
|
-
| PlayRunnerEvent
|
|
1412
|
-
| { type: 'result'; result: PlayRunnerTerminalTransportResult },
|
|
1412
|
+
event: PlayRunnerEvent | PlayRunnerTerminalTransportEvent,
|
|
1413
1413
|
): Promise<void> {
|
|
1414
1414
|
await new Promise<void>((resolve, reject) => {
|
|
1415
1415
|
process.stdout.write(`${JSON.stringify(event)}\n`, (error) => {
|
|
@@ -1482,9 +1482,7 @@ async function exitRunner(exitCode: number): Promise<never> {
|
|
|
1482
1482
|
}
|
|
1483
1483
|
|
|
1484
1484
|
async function emitAndExit(
|
|
1485
|
-
event:
|
|
1486
|
-
| PlayRunnerEvent
|
|
1487
|
-
| { type: 'result'; result: PlayRunnerTerminalTransportResult },
|
|
1485
|
+
event: PlayRunnerEvent | PlayRunnerTerminalTransportEvent,
|
|
1488
1486
|
exitCode: number,
|
|
1489
1487
|
): Promise<never> {
|
|
1490
1488
|
await writeRunnerEventToStdout(event);
|
|
@@ -3859,37 +3857,29 @@ async function main() {
|
|
|
3859
3857
|
// delays the terminal beyond the cap; no-op for undiagrammed plays.
|
|
3860
3858
|
// Runner progress joins the same terminal handoff immediately below.
|
|
3861
3859
|
await drainPendingDocflowObservationPosts();
|
|
3862
|
-
|
|
3860
|
+
const terminalHandoff = terminalResult
|
|
3861
|
+
? runnerTerminalHandoffForTransport(terminalResult, runnerProgressPusher)
|
|
3862
|
+
: null;
|
|
3863
|
+
if (terminalHandoff) runnerProgressPusher = null;
|
|
3864
|
+
if (terminalHandoff) {
|
|
3863
3865
|
// Persist the exact terminal line locally before any network settlement.
|
|
3864
3866
|
// If the gateway remains unavailable through the grace period, the shell
|
|
3865
3867
|
// epilogue can still salvage this authored result instead of inventing a
|
|
3866
3868
|
// runtime-limit failure after customer code already finished.
|
|
3867
|
-
await writeRunnerEventToStdout(
|
|
3868
|
-
type: 'result',
|
|
3869
|
-
result: terminalResult,
|
|
3870
|
-
});
|
|
3869
|
+
await writeRunnerEventToStdout(terminalHandoff.event);
|
|
3871
3870
|
}
|
|
3872
3871
|
// Push execution (B2-final): the pushed terminal is what WAKES the parked
|
|
3873
3872
|
// worker attempt. Push before exiting; on exhaustion the captured output
|
|
3874
3873
|
// file remains the ceiling-timeout salvage source.
|
|
3875
|
-
if (runnerTerminalPushContext &&
|
|
3874
|
+
if (runnerTerminalPushContext && terminalHandoff) {
|
|
3876
3875
|
// Every terminal boundary can wake a successor attempt. Transfer all
|
|
3877
3876
|
// unacknowledged observations into that same ordered handoff before the
|
|
3878
3877
|
// wake is visible; draining a suspended/failed attempt afterward lets
|
|
3879
3878
|
// the successor claim the log channel and fence its predecessor's tail.
|
|
3880
|
-
|
|
3881
|
-
events: [],
|
|
3882
|
-
liveLogOffset: 0,
|
|
3883
|
-
};
|
|
3884
|
-
runnerProgressPusher = null;
|
|
3885
|
-
await pushRunnerTerminalIfConfigured(terminalResult, {
|
|
3879
|
+
await pushRunnerTerminalIfConfigured(terminalHandoff.event.result, {
|
|
3886
3880
|
warn: createPostTerminalDiagnosticWriter(),
|
|
3887
|
-
finalProgressLogOffset:
|
|
3888
|
-
|
|
3889
|
-
? {
|
|
3890
|
-
finalProgressEvents: finalProgress.events,
|
|
3891
|
-
}
|
|
3892
|
-
: {}),
|
|
3881
|
+
finalProgressLogOffset: terminalHandoff.progress?.liveLogOffset ?? 0,
|
|
3882
|
+
finalProgressEvents: terminalHandoff.progress?.events,
|
|
3893
3883
|
});
|
|
3894
3884
|
return exitRunner(0);
|
|
3895
3885
|
}
|
|
@@ -4174,13 +4164,12 @@ async function terminateRunnerForRuntimeDeadline(
|
|
|
4174
4164
|
// the Daytona shell wrapper, whose crash epilogue owns the bounded gateway
|
|
4175
4165
|
// push during the outer grace period. Awaiting network/progress work here
|
|
4176
4166
|
// would let customer code continue dispatching after its deadline.
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
result: failedResult,
|
|
4181
|
-
},
|
|
4182
|
-
0,
|
|
4167
|
+
const terminalHandoff = runnerTerminalHandoffForTransport(
|
|
4168
|
+
failedResult,
|
|
4169
|
+
runnerProgressPusher,
|
|
4183
4170
|
);
|
|
4171
|
+
runnerProgressPusher = null;
|
|
4172
|
+
return emitAndExit(terminalHandoff.event, 0);
|
|
4184
4173
|
}
|
|
4185
4174
|
|
|
4186
4175
|
// The external watchdog samples the sandbox cgroup and raises SIGUSR2 at its
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
type RunnerGatewayTransportEvent,
|
|
19
19
|
} from './runtime-gateway-transport';
|
|
20
20
|
import { RUNTIME_RELIABILITY_POLICY } from '../runtime-reliability-policy';
|
|
21
|
+
import type { RunnerProgressPusher } from './runtime-progress-push';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Push-execution terminal push (runs INSIDE the Daytona sandbox, B2-final).
|
|
@@ -93,6 +94,39 @@ export type PlayRunnerTerminalTransportResult =
|
|
|
93
94
|
| (Omit<FailedRunnerResult, 'checkpoint'> &
|
|
94
95
|
(OmittedTerminalCheckpoint | AbsentCheckpoint));
|
|
95
96
|
|
|
97
|
+
export type PlayRunnerTerminalTransportEvent = {
|
|
98
|
+
type: 'result';
|
|
99
|
+
result: PlayRunnerTerminalTransportResult;
|
|
100
|
+
finalProgressEvents?: PlayRunnerEvent[];
|
|
101
|
+
finalProgressLogOffset?: number;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export function runnerTerminalHandoffForTransport(
|
|
105
|
+
result: PlayRunnerResult | PlayRunnerTerminalTransportResult,
|
|
106
|
+
progressPusher?: RunnerProgressPusher | null,
|
|
107
|
+
): {
|
|
108
|
+
event: PlayRunnerTerminalTransportEvent;
|
|
109
|
+
progress?: ReturnType<RunnerProgressPusher['sealForTerminal']>;
|
|
110
|
+
} {
|
|
111
|
+
const finalProgress = progressPusher?.sealForTerminal();
|
|
112
|
+
const terminalResult = runnerTerminalResultForTransport(result);
|
|
113
|
+
return {
|
|
114
|
+
event: {
|
|
115
|
+
type: 'result',
|
|
116
|
+
result: terminalResult,
|
|
117
|
+
...(finalProgress && terminalResult.status !== 'suspended'
|
|
118
|
+
? {
|
|
119
|
+
finalProgressLogOffset: finalProgress.liveLogOffset,
|
|
120
|
+
...(finalProgress.events.length
|
|
121
|
+
? { finalProgressEvents: finalProgress.events }
|
|
122
|
+
: {}),
|
|
123
|
+
}
|
|
124
|
+
: {}),
|
|
125
|
+
},
|
|
126
|
+
progress: finalProgress,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
96
130
|
type SettledRunnerTerminalTransportResult = Exclude<
|
|
97
131
|
PlayRunnerTerminalTransportResult,
|
|
98
132
|
SuspendedRunnerResult & IncludedResumeCheckpoint
|
|
@@ -205,7 +205,7 @@ function writeDiagnostic(line) {
|
|
|
205
205
|
postTerminalDiagnosticBytesRemaining -= Buffer.byteLength(bounded, 'utf8') + 1;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
function
|
|
208
|
+
function readResultEventFromOutput() {
|
|
209
209
|
try {
|
|
210
210
|
const stat = fs.statSync(outputPath);
|
|
211
211
|
const start = Math.max(0, stat.size - MAX_OUTPUT_BYTES);
|
|
@@ -225,7 +225,7 @@ function readResultFromOutput() {
|
|
|
225
225
|
event.result &&
|
|
226
226
|
['completed', 'failed', 'suspended'].includes(event.result.status)
|
|
227
227
|
) {
|
|
228
|
-
return event
|
|
228
|
+
return event;
|
|
229
229
|
}
|
|
230
230
|
} catch {}
|
|
231
231
|
}
|
|
@@ -396,7 +396,8 @@ async function main() {
|
|
|
396
396
|
writeDiagnostic('[crash-terminal] no push config; skipping');
|
|
397
397
|
return;
|
|
398
398
|
}
|
|
399
|
-
const
|
|
399
|
+
const parsedResultEvent = readResultEventFromOutput();
|
|
400
|
+
const parsedResult = parsedResultEvent && parsedResultEvent.result;
|
|
400
401
|
// Exit codes are authored-program output too: a play may intentionally call
|
|
401
402
|
// process.exit(124). Only the separate parent watchdog writes this marker.
|
|
402
403
|
const runtimeLimitExceeded = Boolean(
|
|
@@ -492,6 +493,12 @@ async function main() {
|
|
|
492
493
|
action: 'runner_terminal',
|
|
493
494
|
runId: push.runId,
|
|
494
495
|
result,
|
|
496
|
+
...(parsedResultEvent && parsedResultEvent.finalProgressEvents !== undefined
|
|
497
|
+
? { finalProgressEvents: parsedResultEvent.finalProgressEvents }
|
|
498
|
+
: {}),
|
|
499
|
+
...(parsedResultEvent && parsedResultEvent.finalProgressLogOffset !== undefined
|
|
500
|
+
? { finalProgressLogOffset: parsedResultEvent.finalProgressLogOffset }
|
|
501
|
+
: {}),
|
|
495
502
|
});
|
|
496
503
|
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
497
504
|
const controller = new AbortController();
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts
CHANGED
|
@@ -278,6 +278,7 @@ import {
|
|
|
278
278
|
import {
|
|
279
279
|
abandonRuntimeSandboxOperationReservation,
|
|
280
280
|
bindRuntimeSandboxCapacityLease,
|
|
281
|
+
createRuntimeSandboxOperationReservation,
|
|
281
282
|
readReservedDaytonaCancellationOperation,
|
|
282
283
|
recordRecoveredDaytonaCancellationSandbox,
|
|
283
284
|
reserveRuntimeSandboxOperation,
|
|
@@ -3889,14 +3890,22 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3889
3890
|
resumePendingDaytonaOperation: provider === 'daytona',
|
|
3890
3891
|
operationKey: `sandbox-operation:${runId}:${generation}:${provider}:${providerAttempt ?? 1}`,
|
|
3891
3892
|
};
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3893
|
+
// A pooled callback may retry after its INSERT committed.
|
|
3894
|
+
// Keep unsent-create authority outside those callback retries;
|
|
3895
|
+
// a later admission invocation must reconcile the prior key.
|
|
3896
|
+
const reserveOperation =
|
|
3897
|
+
provider === 'daytona'
|
|
3898
|
+
? createRuntimeSandboxOperationReservation(
|
|
3899
|
+
reservationInput,
|
|
3900
|
+
options,
|
|
3901
|
+
)
|
|
3902
|
+
: (client: PostgresSchedulerQueryClient) =>
|
|
3903
|
+
reserveRuntimeSandboxOperation(
|
|
3904
|
+
client,
|
|
3905
|
+
reservationInput,
|
|
3906
|
+
options,
|
|
3907
|
+
);
|
|
3908
|
+
const reserve = () => deps.runWithClient(reserveOperation);
|
|
3900
3909
|
const deferForSchedulerFailure = (error: unknown): never => {
|
|
3901
3910
|
console.warn(
|
|
3902
3911
|
'[absurd-runtime.worker] sandbox_capacity_admission_deferred',
|
|
@@ -7,6 +7,7 @@ import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
|
7
7
|
import { createHash, randomUUID } from 'node:crypto';
|
|
8
8
|
import { dirname, isAbsolute, join, relative, sep } from 'node:path';
|
|
9
9
|
import { tmpdir } from 'node:os';
|
|
10
|
+
import { decodeJwt } from 'jose';
|
|
10
11
|
import {
|
|
11
12
|
acquirePgNotifyHub,
|
|
12
13
|
resolveWorkflowRuntimePostgresUrl,
|
|
@@ -18796,6 +18797,35 @@ export async function dispatchRunLedgerOutboxToLedgerThroughEventWithClientScope
|
|
|
18796
18797
|
maxEvents?: number;
|
|
18797
18798
|
},
|
|
18798
18799
|
options?: Pick<PostgresSchedulerOptions, 'schema'>,
|
|
18800
|
+
): Promise<PostgresSchedulerOutboxDrainResult> {
|
|
18801
|
+
return dispatchRunLedgerOutboxThroughEventWithScope(
|
|
18802
|
+
withClient,
|
|
18803
|
+
input,
|
|
18804
|
+
options,
|
|
18805
|
+
);
|
|
18806
|
+
}
|
|
18807
|
+
|
|
18808
|
+
type RunLedgerOutboxDrainScope = {
|
|
18809
|
+
runId: string;
|
|
18810
|
+
leaseOwner: string;
|
|
18811
|
+
targetEventId: string;
|
|
18812
|
+
nextClaimed: PostgresSchedulerOutboxEvent[];
|
|
18813
|
+
authority: {
|
|
18814
|
+
runtimeApi: WorkerRuntimeApiContext;
|
|
18815
|
+
expiresAtMs: number;
|
|
18816
|
+
} | null;
|
|
18817
|
+
};
|
|
18818
|
+
|
|
18819
|
+
async function dispatchRunLedgerOutboxThroughEventWithScope(
|
|
18820
|
+
withClient: <T>(
|
|
18821
|
+
operation: (client: PostgresSchedulerQueryClient) => Promise<T>,
|
|
18822
|
+
) => Promise<T>,
|
|
18823
|
+
input: RunLedgerOutboxDispatchInput & {
|
|
18824
|
+
targetEventId: string;
|
|
18825
|
+
maxEvents?: number;
|
|
18826
|
+
},
|
|
18827
|
+
options?: Pick<PostgresSchedulerOptions, 'schema'>,
|
|
18828
|
+
scope?: RunLedgerOutboxDrainScope,
|
|
18799
18829
|
): Promise<PostgresSchedulerOutboxDrainResult> {
|
|
18800
18830
|
const runId = input.runId?.trim();
|
|
18801
18831
|
if (!runId) {
|
|
@@ -18822,18 +18852,17 @@ export async function dispatchRunLedgerOutboxToLedgerThroughEventWithClientScope
|
|
|
18822
18852
|
|
|
18823
18853
|
let remaining = maxEvents;
|
|
18824
18854
|
while (remaining === null || remaining > 0) {
|
|
18825
|
-
const result =
|
|
18826
|
-
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
|
|
18830
|
-
|
|
18831
|
-
|
|
18832
|
-
|
|
18833
|
-
|
|
18834
|
-
|
|
18835
|
-
|
|
18836
|
-
);
|
|
18855
|
+
const result = await dispatchRunLedgerOutboxBatchWithScope(
|
|
18856
|
+
withClient,
|
|
18857
|
+
{
|
|
18858
|
+
...input,
|
|
18859
|
+
waitForRunLock: true,
|
|
18860
|
+
continueThroughTerminal: false,
|
|
18861
|
+
limit: remaining === null ? batchSize : Math.min(batchSize, remaining),
|
|
18862
|
+
},
|
|
18863
|
+
options,
|
|
18864
|
+
scope,
|
|
18865
|
+
);
|
|
18837
18866
|
attempted += result.attempted;
|
|
18838
18867
|
projected += result.projected;
|
|
18839
18868
|
failed += result.failed;
|
|
@@ -18859,7 +18888,12 @@ export async function dispatchRunLedgerOutboxToLedgerThroughEventWithClientScope
|
|
|
18859
18888
|
// A projection failure is durably scheduled for retry or moved to terminal
|
|
18860
18889
|
// DLQ. Its policy owns the next attempt; spinning here would only reclaim
|
|
18861
18890
|
// the same failed predecessor and delay the park indefinitely.
|
|
18862
|
-
if (
|
|
18891
|
+
if (
|
|
18892
|
+
result.projected === 0 ||
|
|
18893
|
+
result.attempted === 0 ||
|
|
18894
|
+
(scope && scope.nextClaimed.length === 0)
|
|
18895
|
+
)
|
|
18896
|
+
break;
|
|
18863
18897
|
}
|
|
18864
18898
|
|
|
18865
18899
|
// A concurrent dispatcher can claim the target between batches. An empty
|
|
@@ -18905,11 +18939,37 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
18905
18939
|
) => Promise<T>,
|
|
18906
18940
|
input: RunLedgerOutboxDispatchInput & { waitForRunLock?: boolean },
|
|
18907
18941
|
options?: Pick<PostgresSchedulerOptions, 'schema'>,
|
|
18942
|
+
): Promise<PostgresSchedulerOutboxProjectionResult> {
|
|
18943
|
+
return dispatchRunLedgerOutboxBatchWithScope(withClient, input, options);
|
|
18944
|
+
}
|
|
18945
|
+
|
|
18946
|
+
async function dispatchRunLedgerOutboxBatchWithScope(
|
|
18947
|
+
withClient: <T>(
|
|
18948
|
+
operation: (client: PostgresSchedulerQueryClient) => Promise<T>,
|
|
18949
|
+
) => Promise<T>,
|
|
18950
|
+
input: RunLedgerOutboxDispatchInput & { waitForRunLock?: boolean },
|
|
18951
|
+
options?: Pick<PostgresSchedulerOptions, 'schema'>,
|
|
18952
|
+
scope?: RunLedgerOutboxDrainScope,
|
|
18908
18953
|
): Promise<PostgresSchedulerOutboxProjectionResult> {
|
|
18909
18954
|
const dispatchStartedAtMs = Date.now();
|
|
18910
18955
|
const leaseOwner =
|
|
18911
18956
|
input.leaseOwner?.trim() || `run-ledger-outbox-dispatch:${randomUUID()}`;
|
|
18912
|
-
const
|
|
18957
|
+
const continuation: { current: RunLedgerOutboxDrainScope | null } = {
|
|
18958
|
+
current: scope ?? null,
|
|
18959
|
+
};
|
|
18960
|
+
const releasePendingContinuation = async () => {
|
|
18961
|
+
const nextClaimed = continuation.current?.nextClaimed ?? [];
|
|
18962
|
+
if (nextClaimed.length === 0) return;
|
|
18963
|
+
await withClient((client) =>
|
|
18964
|
+
releasePostgresSchedulerOutboxLease(
|
|
18965
|
+
client,
|
|
18966
|
+
nextClaimed.map((event) => event.eventId),
|
|
18967
|
+
{ ...options, leaseOwner },
|
|
18968
|
+
),
|
|
18969
|
+
);
|
|
18970
|
+
nextClaimed.length = 0;
|
|
18971
|
+
};
|
|
18972
|
+
const claimEvents = async (client: PostgresSchedulerQueryClient) => {
|
|
18913
18973
|
// The checkout is exclusive. Hold the existing scheduler run's row lock
|
|
18914
18974
|
// only while choosing/claiming its outbox prefix, never over HTTP. A
|
|
18915
18975
|
// second statement takes a fresh READ COMMITTED snapshot after the lock:
|
|
@@ -19037,7 +19097,10 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
19037
19097
|
await client.query('ROLLBACK');
|
|
19038
19098
|
throw error;
|
|
19039
19099
|
}
|
|
19040
|
-
}
|
|
19100
|
+
};
|
|
19101
|
+
const events = scope?.nextClaimed.length
|
|
19102
|
+
? scope.nextClaimed.splice(0)
|
|
19103
|
+
: await withClient(claimEvents);
|
|
19041
19104
|
if (events.length === 0) {
|
|
19042
19105
|
return {
|
|
19043
19106
|
attempted: 0,
|
|
@@ -19057,9 +19120,19 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
19057
19120
|
try {
|
|
19058
19121
|
const first = events[0]!;
|
|
19059
19122
|
const tokenAuthority = input.tokenAuthority ?? 'local_mint';
|
|
19123
|
+
if (
|
|
19124
|
+
scope &&
|
|
19125
|
+
(scope.runId !== first.runId || scope.leaseOwner !== leaseOwner)
|
|
19126
|
+
) {
|
|
19127
|
+
throw new Error('Run-ledger continuation is outside its dispatch scope.');
|
|
19128
|
+
}
|
|
19129
|
+
if (scope?.authority && scope.authority.expiresAtMs <= Date.now()) {
|
|
19130
|
+
throw new Error('Run-ledger continuation authority expired.');
|
|
19131
|
+
}
|
|
19060
19132
|
const gatewayRuntimeApi =
|
|
19061
19133
|
tokenAuthority === 'gateway'
|
|
19062
|
-
?
|
|
19134
|
+
? (scope?.authority?.runtimeApi ??
|
|
19135
|
+
(await loadPostgresSchedulerRuntimeApiForRun(
|
|
19063
19136
|
withClient,
|
|
19064
19137
|
first.runId,
|
|
19065
19138
|
first.leaseOwner
|
|
@@ -19068,7 +19141,7 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
19068
19141
|
tokenAuthority,
|
|
19069
19142
|
options,
|
|
19070
19143
|
input.runtimeApi?.signal,
|
|
19071
|
-
)
|
|
19144
|
+
)))
|
|
19072
19145
|
: null;
|
|
19073
19146
|
const resolvedRuntimeApi =
|
|
19074
19147
|
gatewayRuntimeApi ??
|
|
@@ -19110,6 +19183,12 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
19110
19183
|
// The caller-owned admission deadline (see `signal` above) wins.
|
|
19111
19184
|
...(input.signal ? { signal: input.signal } : {}),
|
|
19112
19185
|
};
|
|
19186
|
+
const tokenExpiresAtMs = runtimeApiTokenExpiresAtMs(
|
|
19187
|
+
runtimeApi.executorToken,
|
|
19188
|
+
);
|
|
19189
|
+
if (scope && gatewayRuntimeApi && tokenExpiresAtMs !== null) {
|
|
19190
|
+
scope.authority = { runtimeApi, expiresAtMs: tokenExpiresAtMs };
|
|
19191
|
+
}
|
|
19113
19192
|
authorityReadyAtMs = Date.now();
|
|
19114
19193
|
const renewDelivery = () =>
|
|
19115
19194
|
withClient((client) =>
|
|
@@ -19123,13 +19202,14 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
19123
19202
|
options,
|
|
19124
19203
|
),
|
|
19125
19204
|
);
|
|
19126
|
-
await renewDelivery();
|
|
19127
19205
|
// A pending start is always claimed alone because later events require its
|
|
19128
19206
|
// snapshot. It cannot share the append endpoint, so retain that mutation
|
|
19129
19207
|
// as its own exact delivery.
|
|
19130
19208
|
if (first.eventType === RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runCreated) {
|
|
19209
|
+
await renewDelivery();
|
|
19131
19210
|
await projectPostgresSchedulerCreatedEvent(runtimeApi, first);
|
|
19132
19211
|
} else if (first.eventType === RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runStarted) {
|
|
19212
|
+
await renewDelivery();
|
|
19133
19213
|
const leaseOwner = first.leaseOwner;
|
|
19134
19214
|
const launch = leaseOwner
|
|
19135
19215
|
? await withClient((client) =>
|
|
@@ -19173,16 +19253,18 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
19173
19253
|
deliveryDigest: batchDeliveryKey,
|
|
19174
19254
|
}),
|
|
19175
19255
|
});
|
|
19256
|
+
} else {
|
|
19257
|
+
await renewDelivery();
|
|
19176
19258
|
}
|
|
19177
19259
|
}
|
|
19178
19260
|
appendFinishedAtMs = Date.now();
|
|
19179
|
-
const
|
|
19180
|
-
|
|
19181
|
-
|
|
19182
|
-
|
|
19183
|
-
{ ...options, leaseOwner },
|
|
19184
|
-
),
|
|
19261
|
+
const acknowledgement = await acknowledgeRunLedgerPrefixWithContinuation(
|
|
19262
|
+
withClient,
|
|
19263
|
+
{ input, events, leaseOwner, runtimeApi, scope, continuation },
|
|
19264
|
+
options,
|
|
19185
19265
|
);
|
|
19266
|
+
const { deliveredRows } = acknowledgement;
|
|
19267
|
+
if (!acknowledgement.continuation) await releasePendingContinuation();
|
|
19186
19268
|
acknowledgedAtMs = Date.now();
|
|
19187
19269
|
const markedDelivered = deliveredRows.length;
|
|
19188
19270
|
if (input.onDelivered && deliveredRows.length > 0) {
|
|
@@ -19210,19 +19292,16 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
19210
19292
|
retrying: 0,
|
|
19211
19293
|
retryingEventIds: [],
|
|
19212
19294
|
};
|
|
19213
|
-
if (
|
|
19214
|
-
input.continueThroughTerminal !== true ||
|
|
19215
|
-
input.headEventId ||
|
|
19216
|
-
markedDelivered !== events.length
|
|
19217
|
-
) {
|
|
19295
|
+
if (!acknowledgement.continuation || scope) {
|
|
19218
19296
|
return delivered;
|
|
19219
19297
|
}
|
|
19220
19298
|
return await continueRunLedgerOutboxThroughTerminal(
|
|
19221
19299
|
withClient,
|
|
19222
|
-
{ input,
|
|
19300
|
+
{ input, delivered, scope: acknowledgement.continuation },
|
|
19223
19301
|
options,
|
|
19224
19302
|
);
|
|
19225
19303
|
} catch (error) {
|
|
19304
|
+
await releasePendingContinuation();
|
|
19226
19305
|
if (input.signal?.aborted) {
|
|
19227
19306
|
// The caller's latency budget expired; this is not a delivery failure.
|
|
19228
19307
|
// Release the lease (fenced to this owner) without charging a
|
|
@@ -19310,19 +19389,10 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
|
19310
19389
|
}
|
|
19311
19390
|
|
|
19312
19391
|
/**
|
|
19313
|
-
*
|
|
19314
|
-
*
|
|
19315
|
-
*
|
|
19316
|
-
*
|
|
19317
|
-
* is mid-append on an earlier event of the same run, that run-scoped claim
|
|
19318
|
-
* finds the run leased and correctly claims nothing, and the terminal then
|
|
19319
|
-
* waited for the projector's next wave: seconds of a finished run still
|
|
19320
|
-
* reading "running" (live-run-observation-e2e, 4.2 s). So the lease holder,
|
|
19321
|
-
* having acknowledged its batch, carries the run's terminal fact through in
|
|
19322
|
-
* the same turn when it is now pending. The terminal is the run's last fact,
|
|
19323
|
-
* so this is one finite causal drain (the same leased, idempotent
|
|
19324
|
-
* `dispatchRunLedgerOutboxToLedgerThroughEvent` the worker uses), not a loop;
|
|
19325
|
-
* a run without a pending terminal costs one indexed read.
|
|
19392
|
+
* A terminal producer cannot claim an earlier prefix held by a projector.
|
|
19393
|
+
* The existing owner atomically retains each successor through the persisted
|
|
19394
|
+
* terminal, preserving ordered receipts without waiting for another wave.
|
|
19395
|
+
* With no pending terminal, the ACK transaction retains no successor.
|
|
19326
19396
|
*/
|
|
19327
19397
|
async function continueRunLedgerOutboxThroughTerminal(
|
|
19328
19398
|
withClient: <T>(
|
|
@@ -19330,36 +19400,22 @@ async function continueRunLedgerOutboxThroughTerminal(
|
|
|
19330
19400
|
) => Promise<T>,
|
|
19331
19401
|
input: {
|
|
19332
19402
|
input: RunLedgerOutboxDispatchInput & { waitForRunLock?: boolean };
|
|
19333
|
-
leaseOwner: string;
|
|
19334
|
-
runId: string;
|
|
19335
19403
|
delivered: PostgresSchedulerOutboxProjectionResult;
|
|
19336
|
-
|
|
19404
|
+
scope: RunLedgerOutboxDrainScope;
|
|
19337
19405
|
},
|
|
19338
19406
|
options?: Pick<PostgresSchedulerOptions, 'schema'>,
|
|
19339
19407
|
): Promise<PostgresSchedulerOutboxProjectionResult> {
|
|
19340
|
-
const
|
|
19341
|
-
|
|
19342
|
-
|
|
19343
|
-
|
|
19344
|
-
|
|
19345
|
-
|
|
19346
|
-
|
|
19347
|
-
|
|
19348
|
-
|
|
19349
|
-
|
|
19408
|
+
const drained = await dispatchRunLedgerOutboxThroughEventWithScope(
|
|
19409
|
+
withClient,
|
|
19410
|
+
{
|
|
19411
|
+
...input.input,
|
|
19412
|
+
runId: input.scope.runId,
|
|
19413
|
+
leaseOwner: input.scope.leaseOwner,
|
|
19414
|
+
targetEventId: input.scope.targetEventId,
|
|
19415
|
+
},
|
|
19416
|
+
options,
|
|
19417
|
+
input.scope,
|
|
19350
19418
|
);
|
|
19351
|
-
if (!terminal.exists || terminal.delivered) return input.delivered;
|
|
19352
|
-
const drained =
|
|
19353
|
-
await dispatchRunLedgerOutboxToLedgerThroughEventWithClientScope(
|
|
19354
|
-
withClient,
|
|
19355
|
-
{
|
|
19356
|
-
...input.input,
|
|
19357
|
-
runId: input.runId,
|
|
19358
|
-
leaseOwner: input.leaseOwner,
|
|
19359
|
-
targetEventId: terminalEventId,
|
|
19360
|
-
},
|
|
19361
|
-
options,
|
|
19362
|
-
);
|
|
19363
19419
|
return {
|
|
19364
19420
|
attempted: input.delivered.attempted + drained.attempted,
|
|
19365
19421
|
projected: input.delivered.projected + drained.projected,
|
|
@@ -19371,6 +19427,120 @@ async function continueRunLedgerOutboxThroughTerminal(
|
|
|
19371
19427
|
};
|
|
19372
19428
|
}
|
|
19373
19429
|
|
|
19430
|
+
function runtimeApiTokenExpiresAtMs(token: string): number | null {
|
|
19431
|
+
try {
|
|
19432
|
+
const expiresAt = decodeJwt(token).exp;
|
|
19433
|
+
return typeof expiresAt === 'number' && Number.isFinite(expiresAt)
|
|
19434
|
+
? expiresAt * 1_000
|
|
19435
|
+
: null;
|
|
19436
|
+
} catch {
|
|
19437
|
+
// Opaque legacy/test tokens retain the existing fresh-authority path.
|
|
19438
|
+
return null;
|
|
19439
|
+
}
|
|
19440
|
+
}
|
|
19441
|
+
|
|
19442
|
+
async function acknowledgeRunLedgerPrefixWithContinuation(
|
|
19443
|
+
withClient: <T>(
|
|
19444
|
+
operation: (client: PostgresSchedulerQueryClient) => Promise<T>,
|
|
19445
|
+
) => Promise<T>,
|
|
19446
|
+
input: {
|
|
19447
|
+
input: RunLedgerOutboxDispatchInput;
|
|
19448
|
+
events: PostgresSchedulerOutboxEvent[];
|
|
19449
|
+
leaseOwner: string;
|
|
19450
|
+
runtimeApi: WorkerRuntimeApiContext;
|
|
19451
|
+
scope?: RunLedgerOutboxDrainScope;
|
|
19452
|
+
continuation: { current: RunLedgerOutboxDrainScope | null };
|
|
19453
|
+
},
|
|
19454
|
+
options?: Pick<PostgresSchedulerOptions, 'schema'>,
|
|
19455
|
+
): Promise<{
|
|
19456
|
+
deliveredRows: RunLedgerOutboxDeliveredRow[];
|
|
19457
|
+
continuation: RunLedgerOutboxDrainScope | null;
|
|
19458
|
+
}> {
|
|
19459
|
+
const runId = input.events[0]!.runId;
|
|
19460
|
+
const targetEventId = input.scope?.targetEventId ?? `run-terminal:${runId}`;
|
|
19461
|
+
const retainContinuation =
|
|
19462
|
+
!!input.scope ||
|
|
19463
|
+
(input.input.continueThroughTerminal === true && !input.input.headEventId);
|
|
19464
|
+
return withClient(async (client) => {
|
|
19465
|
+
if (retainContinuation) {
|
|
19466
|
+
await client.query('BEGIN');
|
|
19467
|
+
}
|
|
19468
|
+
try {
|
|
19469
|
+
if (retainContinuation) {
|
|
19470
|
+
await client.query(
|
|
19471
|
+
`SELECT set_config('lock_timeout', $1, true), set_config('statement_timeout', $2, true)`,
|
|
19472
|
+
[
|
|
19473
|
+
`${RUNTIME_SCHEDULER_CANCEL_LOCK_TIMEOUT_MS}ms`,
|
|
19474
|
+
`${RUNTIME_SCHEDULER_CANCEL_STATEMENT_TIMEOUT_MS}ms`,
|
|
19475
|
+
],
|
|
19476
|
+
);
|
|
19477
|
+
// Keep the acknowledged prefix and its successor under one run lock.
|
|
19478
|
+
// No competing projector can take the next head while this ACK's
|
|
19479
|
+
// response is travelling back to the lease holder.
|
|
19480
|
+
await client.query(
|
|
19481
|
+
`SELECT run_id FROM ${tableName('runs', options)} WHERE run_id = $1 FOR UPDATE`,
|
|
19482
|
+
[runId],
|
|
19483
|
+
);
|
|
19484
|
+
}
|
|
19485
|
+
const deliveredRows = await markPostgresSchedulerOutboxDeliveredRows(
|
|
19486
|
+
client,
|
|
19487
|
+
input.events.map((event) => event.eventId),
|
|
19488
|
+
{ ...options, leaseOwner: input.leaseOwner },
|
|
19489
|
+
);
|
|
19490
|
+
let continuation: RunLedgerOutboxDrainScope | null = null;
|
|
19491
|
+
if (
|
|
19492
|
+
retainContinuation &&
|
|
19493
|
+
deliveredRows.length === input.events.length &&
|
|
19494
|
+
!input.events.some((event) => event.eventId === targetEventId)
|
|
19495
|
+
) {
|
|
19496
|
+
const target = await readPostgresSchedulerOutboxDeliveryStatus(
|
|
19497
|
+
client,
|
|
19498
|
+
{ runId, eventId: targetEventId },
|
|
19499
|
+
options,
|
|
19500
|
+
);
|
|
19501
|
+
if (target.exists && !target.delivered && !target.blocked) {
|
|
19502
|
+
const nextClaimed = await readPendingPostgresSchedulerOutboxEvents(
|
|
19503
|
+
client,
|
|
19504
|
+
{
|
|
19505
|
+
runId,
|
|
19506
|
+
leaseOwner: input.leaseOwner,
|
|
19507
|
+
leaseSeconds: input.input.leaseSeconds ?? 120,
|
|
19508
|
+
limit: Math.min(
|
|
19509
|
+
RUN_LEDGER_PROJECTION_PREFIX_MAX_FACTS,
|
|
19510
|
+
input.input.limit ?? 1,
|
|
19511
|
+
),
|
|
19512
|
+
inTransaction: true,
|
|
19513
|
+
},
|
|
19514
|
+
options,
|
|
19515
|
+
);
|
|
19516
|
+
if (nextClaimed.length > 0) {
|
|
19517
|
+
const expiresAtMs = runtimeApiTokenExpiresAtMs(
|
|
19518
|
+
input.runtimeApi.executorToken,
|
|
19519
|
+
);
|
|
19520
|
+
continuation = input.scope ?? {
|
|
19521
|
+
runId,
|
|
19522
|
+
leaseOwner: input.leaseOwner,
|
|
19523
|
+
targetEventId,
|
|
19524
|
+
nextClaimed: [],
|
|
19525
|
+
authority:
|
|
19526
|
+
input.input.tokenAuthority === 'gateway' && expiresAtMs !== null
|
|
19527
|
+
? { runtimeApi: input.runtimeApi, expiresAtMs }
|
|
19528
|
+
: null,
|
|
19529
|
+
};
|
|
19530
|
+
continuation.nextClaimed = nextClaimed;
|
|
19531
|
+
input.continuation.current = continuation;
|
|
19532
|
+
}
|
|
19533
|
+
}
|
|
19534
|
+
}
|
|
19535
|
+
if (retainContinuation) await client.query('COMMIT');
|
|
19536
|
+
return { deliveredRows, continuation };
|
|
19537
|
+
} catch (error) {
|
|
19538
|
+
if (retainContinuation) await client.query('ROLLBACK');
|
|
19539
|
+
throw error;
|
|
19540
|
+
}
|
|
19541
|
+
});
|
|
19542
|
+
}
|
|
19543
|
+
|
|
19374
19544
|
async function projectPostgresSchedulerCreatedEvent(
|
|
19375
19545
|
runtimeApi: WorkerRuntimeApiContext,
|
|
19376
19546
|
event: PostgresSchedulerOutboxEvent,
|
|
@@ -26,6 +26,10 @@ export const RUNTIME_SANDBOX_CAPACITY_LEASE_MS = 50 * 60 * 1_000;
|
|
|
26
26
|
*/
|
|
27
27
|
export type RuntimeSandboxCapacityAdmissionMode = 'observe' | 'enforce';
|
|
28
28
|
|
|
29
|
+
// Only a still-unresolved admission callback can prove its own create is unsent.
|
|
30
|
+
// This authority is process-local and cannot be reconstructed from stored keys.
|
|
31
|
+
const unsentReservationInputs = new WeakMap<object, string>();
|
|
32
|
+
|
|
29
33
|
export function runtimeSandboxCapacityAdmissionMode(
|
|
30
34
|
env: Record<string, string | undefined> = process.env,
|
|
31
35
|
): RuntimeSandboxCapacityAdmissionMode {
|
|
@@ -306,10 +310,12 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
306
310
|
const activeLeases = Number(observed.rows[0]?.active_leases ?? 0);
|
|
307
311
|
const wouldDefer = activeLeases >= limit;
|
|
308
312
|
const leaseId = input.leaseId?.trim() || randomUUID();
|
|
313
|
+
const unsentProviderOperationToken = unsentReservationInputs.get(input);
|
|
309
314
|
const providerOperationToken =
|
|
310
315
|
input.attempt === undefined
|
|
311
316
|
? null
|
|
312
|
-
:
|
|
317
|
+
: (unsentProviderOperationToken ??
|
|
318
|
+
runtimeSandboxProviderOperationToken(leaseId));
|
|
313
319
|
if (
|
|
314
320
|
input.resumePendingDaytonaOperation === true &&
|
|
315
321
|
input.provider === RuntimeSandboxProvider.Daytona &&
|
|
@@ -342,7 +348,15 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
342
348
|
);
|
|
343
349
|
}
|
|
344
350
|
const pendingOperation = pending.rows[0];
|
|
345
|
-
if (
|
|
351
|
+
if (
|
|
352
|
+
pendingOperation &&
|
|
353
|
+
!(
|
|
354
|
+
pendingOperation.lease_id === leaseId &&
|
|
355
|
+
pendingOperation.provider_operation_token ===
|
|
356
|
+
unsentProviderOperationToken &&
|
|
357
|
+
unsentProviderOperationToken !== undefined
|
|
358
|
+
)
|
|
359
|
+
) {
|
|
346
360
|
const pendingToken =
|
|
347
361
|
pendingOperation.provider_operation_token ??
|
|
348
362
|
(await backfillReservedProviderOperationToken(
|
|
@@ -424,7 +438,8 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
424
438
|
if (
|
|
425
439
|
input.attempt !== undefined &&
|
|
426
440
|
row.provider_operation_token !== null &&
|
|
427
|
-
row.provider_operation_token !== providerOperationToken
|
|
441
|
+
row.provider_operation_token !== providerOperationToken &&
|
|
442
|
+
unsentProviderOperationToken === undefined
|
|
428
443
|
) {
|
|
429
444
|
throw new Error(
|
|
430
445
|
'operationKey reservation has an incompatible provider operation token.',
|
|
@@ -450,7 +465,10 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
450
465
|
provider: input.provider,
|
|
451
466
|
attempt: input.attempt,
|
|
452
467
|
environment: input.environment ?? 'production',
|
|
453
|
-
providerOperationToken
|
|
468
|
+
providerOperationToken:
|
|
469
|
+
unsentProviderOperationToken === undefined
|
|
470
|
+
? providerOperationToken
|
|
471
|
+
: runtimeSandboxProviderOperationToken(leaseId),
|
|
454
472
|
},
|
|
455
473
|
options,
|
|
456
474
|
);
|
|
@@ -462,7 +480,9 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
462
480
|
limit,
|
|
463
481
|
activeLeases,
|
|
464
482
|
wouldDefer,
|
|
465
|
-
resumeExistingProviderOperation:
|
|
483
|
+
resumeExistingProviderOperation:
|
|
484
|
+
unsentProviderOperationToken !== undefined &&
|
|
485
|
+
resolvedProviderOperationToken !== unsentProviderOperationToken,
|
|
466
486
|
};
|
|
467
487
|
}
|
|
468
488
|
if (wouldDefer && (input.mode ?? 'enforce') === 'enforce') {
|
|
@@ -545,14 +565,33 @@ export async function reserveRuntimeSandboxCapacityLease(
|
|
|
545
565
|
'operationKey reservation did not persist the requested sandbox operation.',
|
|
546
566
|
);
|
|
547
567
|
}
|
|
568
|
+
const resolvedProviderOperationToken =
|
|
569
|
+
row.provider_operation_token === null && input.attempt !== undefined
|
|
570
|
+
? await backfillReservedProviderOperationToken(
|
|
571
|
+
client,
|
|
572
|
+
{
|
|
573
|
+
leaseId,
|
|
574
|
+
orgId,
|
|
575
|
+
runId,
|
|
576
|
+
provider: input.provider,
|
|
577
|
+
attempt: input.attempt,
|
|
578
|
+
environment: input.environment ?? 'production',
|
|
579
|
+
providerOperationToken:
|
|
580
|
+
runtimeSandboxProviderOperationToken(leaseId),
|
|
581
|
+
},
|
|
582
|
+
options,
|
|
583
|
+
)
|
|
584
|
+
: row.provider_operation_token;
|
|
548
585
|
return {
|
|
549
586
|
granted: true,
|
|
550
587
|
leaseId,
|
|
551
|
-
providerOperationToken:
|
|
588
|
+
providerOperationToken: resolvedProviderOperationToken,
|
|
552
589
|
limit,
|
|
553
590
|
activeLeases,
|
|
554
591
|
wouldDefer,
|
|
555
|
-
resumeExistingProviderOperation:
|
|
592
|
+
resumeExistingProviderOperation:
|
|
593
|
+
unsentProviderOperationToken !== undefined &&
|
|
594
|
+
resolvedProviderOperationToken !== unsentProviderOperationToken,
|
|
556
595
|
};
|
|
557
596
|
}
|
|
558
597
|
|
|
@@ -649,6 +688,54 @@ export async function reserveRuntimeSandboxOperation(
|
|
|
649
688
|
);
|
|
650
689
|
}
|
|
651
690
|
|
|
691
|
+
/**
|
|
692
|
+
* Create once per provider admission, outside pooled callback retries. A fresh
|
|
693
|
+
* token identifies this invocation's unsent reservation, while the deterministic
|
|
694
|
+
* operation key still coalesces concurrent admission. A losing invocation must
|
|
695
|
+
* reconcile the winning token, even on 404.
|
|
696
|
+
* The callback becomes unusable on resolution, before provider I/O can start.
|
|
697
|
+
*/
|
|
698
|
+
export function createRuntimeSandboxOperationReservation(
|
|
699
|
+
input: Parameters<typeof reserveRuntimeSandboxOperation>[1],
|
|
700
|
+
options?: Pick<PostgresSchedulerOptions, 'schema'>,
|
|
701
|
+
) {
|
|
702
|
+
if (!Number.isSafeInteger(input.attempt) || input.attempt < 1) {
|
|
703
|
+
throw new Error('attempt must be a positive safe integer.');
|
|
704
|
+
}
|
|
705
|
+
const reservationInput: Parameters<
|
|
706
|
+
typeof reserveRuntimeSandboxCapacityLease
|
|
707
|
+
>[1] = {
|
|
708
|
+
...input,
|
|
709
|
+
leaseId: assertId(input.operationKey, 'operationKey'),
|
|
710
|
+
};
|
|
711
|
+
unsentReservationInputs.set(
|
|
712
|
+
reservationInput,
|
|
713
|
+
runtimeSandboxProviderOperationToken(randomUUID()),
|
|
714
|
+
);
|
|
715
|
+
let inFlight = false;
|
|
716
|
+
let settled = false;
|
|
717
|
+
return async (client: PostgresSchedulerQueryClient) => {
|
|
718
|
+
if (inFlight || settled) {
|
|
719
|
+
throw new Error(
|
|
720
|
+
'Sandbox reservation callback is already in use or settled.',
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
inFlight = true;
|
|
724
|
+
try {
|
|
725
|
+
const reserved = await reserveRuntimeSandboxCapacityLease(
|
|
726
|
+
client,
|
|
727
|
+
reservationInput,
|
|
728
|
+
options,
|
|
729
|
+
);
|
|
730
|
+
settled = true;
|
|
731
|
+
unsentReservationInputs.delete(reservationInput);
|
|
732
|
+
return reserved;
|
|
733
|
+
} finally {
|
|
734
|
+
inFlight = false;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
|
|
652
739
|
export async function bindRuntimeSandboxCapacityLease(
|
|
653
740
|
client: PostgresSchedulerQueryClient,
|
|
654
741
|
input: {
|
|
@@ -242,6 +242,25 @@ function isConcurrencyBackpressureResponse(input: {
|
|
|
242
242
|
);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
function isCredentialDispatchRefusalResponse(input: {
|
|
246
|
+
status: number;
|
|
247
|
+
bodyText: string;
|
|
248
|
+
executionFenced?: boolean;
|
|
249
|
+
}): boolean {
|
|
250
|
+
// Fenced gateway attempts have their own qualified redelivery contract.
|
|
251
|
+
// This exemption is only for the ordinary execute route's released charge.
|
|
252
|
+
if (input.status !== 429 || input.executionFenced === true) return false;
|
|
253
|
+
const body = parseJsonObject(input.bodyText);
|
|
254
|
+
return (
|
|
255
|
+
body?.code === 'RATE_LIMIT' &&
|
|
256
|
+
body.failure_origin === 'deepline_rate_limit' &&
|
|
257
|
+
body.error_category === 'rate_limit' &&
|
|
258
|
+
body.rate_limit_bucket_scope === 'credential_shared' &&
|
|
259
|
+
body.rate_limit_kind === 'requests_per_second' &&
|
|
260
|
+
body.provider_request_dispatched === false
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
245
264
|
function isRuntimeSchedulerCapacityResponse(input: {
|
|
246
265
|
status: number;
|
|
247
266
|
bodyText: string;
|
|
@@ -573,6 +592,7 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
573
592
|
const concurrencyBackpressure = isConcurrencyBackpressureResponse(input);
|
|
574
593
|
const runtimeSchedulerCapacity = isRuntimeSchedulerCapacityResponse(input);
|
|
575
594
|
const providerPacingWait = isProviderPacingWaitResponse(input);
|
|
595
|
+
const credentialDispatchRefusal = isCredentialDispatchRefusalResponse(input);
|
|
576
596
|
const edgeGatewayFailure = isEdgeGatewayFailureResponse(input);
|
|
577
597
|
const executionFenced = input.executionFenced === true;
|
|
578
598
|
const explicitRetryable = readExplicitToolHttpRetryability(input);
|
|
@@ -747,11 +767,12 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
747
767
|
retryDecision.reason !== 'concurrency_backpressure' &&
|
|
748
768
|
retryDecision.reason !== 'runtime_scheduler_capacity' &&
|
|
749
769
|
retryDecision.reason !== 'provider_pacing_wait',
|
|
750
|
-
//
|
|
751
|
-
//
|
|
752
|
-
// classification so the retry boundary can safely pace either response
|
|
753
|
-
// without asking a non-idempotent provider call to be replayed.
|
|
770
|
+
// Only explicit pre-dispatch refusals permit automatic replay of an
|
|
771
|
+
// operation that does not declare ordinary transient retry safety.
|
|
754
772
|
knownNotDispatched:
|
|
755
|
-
runtimeSchedulerCapacity ||
|
|
773
|
+
runtimeSchedulerCapacity ||
|
|
774
|
+
concurrencyBackpressure ||
|
|
775
|
+
providerPacingWait ||
|
|
776
|
+
credentialDispatchRefusal,
|
|
756
777
|
};
|
|
757
778
|
}
|
|
@@ -370,6 +370,12 @@ function isPublicResultSafeTokenKey(
|
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
function maybeRedactString(value: string, options: SecretRedactionOptions) {
|
|
373
|
+
// These names are credentials only on MillionVerifier URLs. Keep unrelated
|
|
374
|
+
// object identifiers named `key` and other providers' query fields intact.
|
|
375
|
+
value = value.replace(
|
|
376
|
+
/https?:\/\/(?:api|bulkapi)\.millionverifier\.com(?=[:/?#]|$)[^\s"'<>]*/gi,
|
|
377
|
+
(url) => url.replace(/([?&](?:api|key)=)[^&#\s"'<>]*/gi, '$1[REDACTED]'),
|
|
378
|
+
);
|
|
373
379
|
if (hasExplicitSecretValue(value, options)) {
|
|
374
380
|
return REDACTED_VALUE;
|
|
375
381
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -3515,7 +3515,7 @@ var SDK_RELEASE = {
|
|
|
3515
3515
|
// getters keep their established compatibility behavior.
|
|
3516
3516
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3517
3517
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3518
|
-
version: "0.3.
|
|
3518
|
+
version: "0.3.283",
|
|
3519
3519
|
updateSummary: "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.",
|
|
3520
3520
|
packageCapabilities: {
|
|
3521
3521
|
updatePreferences: 1
|
package/dist/cli/index.mjs
CHANGED
|
@@ -3510,7 +3510,7 @@ var SDK_RELEASE = {
|
|
|
3510
3510
|
// getters keep their established compatibility behavior.
|
|
3511
3511
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3512
3512
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3513
|
-
version: "0.3.
|
|
3513
|
+
version: "0.3.283",
|
|
3514
3514
|
updateSummary: "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.",
|
|
3515
3515
|
packageCapabilities: {
|
|
3516
3516
|
updatePreferences: 1
|
package/dist/index.js
CHANGED
|
@@ -1328,7 +1328,7 @@ var SDK_RELEASE = {
|
|
|
1328
1328
|
// getters keep their established compatibility behavior.
|
|
1329
1329
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
1330
1330
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
1331
|
-
version: "0.3.
|
|
1331
|
+
version: "0.3.283",
|
|
1332
1332
|
updateSummary: "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.",
|
|
1333
1333
|
packageCapabilities: {
|
|
1334
1334
|
updatePreferences: 1
|
package/dist/index.mjs
CHANGED
|
@@ -1232,7 +1232,7 @@ var SDK_RELEASE = {
|
|
|
1232
1232
|
// getters keep their established compatibility behavior.
|
|
1233
1233
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
1234
1234
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
1235
|
-
version: "0.3.
|
|
1235
|
+
version: "0.3.283",
|
|
1236
1236
|
updateSummary: "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.",
|
|
1237
1237
|
packageCapabilities: {
|
|
1238
1238
|
updatePreferences: 1
|
package/dist/release.d.mts
CHANGED
|
@@ -149,7 +149,7 @@ type SdkRelease = {
|
|
|
149
149
|
supportPolicy: SdkSupportPolicy;
|
|
150
150
|
};
|
|
151
151
|
declare const SDK_RELEASE: {
|
|
152
|
-
readonly version: "0.3.
|
|
152
|
+
readonly version: "0.3.283";
|
|
153
153
|
readonly updateSummary: "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.";
|
|
154
154
|
readonly packageCapabilities: {
|
|
155
155
|
readonly updatePreferences: 1;
|
package/dist/release.d.ts
CHANGED
|
@@ -149,7 +149,7 @@ type SdkRelease = {
|
|
|
149
149
|
supportPolicy: SdkSupportPolicy;
|
|
150
150
|
};
|
|
151
151
|
declare const SDK_RELEASE: {
|
|
152
|
-
readonly version: "0.3.
|
|
152
|
+
readonly version: "0.3.283";
|
|
153
153
|
readonly updateSummary: "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.";
|
|
154
154
|
readonly packageCapabilities: {
|
|
155
155
|
readonly updatePreferences: 1;
|
package/dist/release.js
CHANGED
|
@@ -74,7 +74,7 @@ var SDK_RELEASE = {
|
|
|
74
74
|
// getters keep their established compatibility behavior.
|
|
75
75
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
76
76
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
77
|
-
version: "0.3.
|
|
77
|
+
version: "0.3.283",
|
|
78
78
|
updateSummary: "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.",
|
|
79
79
|
packageCapabilities: {
|
|
80
80
|
updatePreferences: 1
|
package/dist/release.mjs
CHANGED
|
@@ -48,7 +48,7 @@ var SDK_RELEASE = {
|
|
|
48
48
|
// getters keep their established compatibility behavior.
|
|
49
49
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
50
50
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
51
|
-
version: "0.3.
|
|
51
|
+
version: "0.3.283",
|
|
52
52
|
updateSummary: "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.",
|
|
53
53
|
packageCapabilities: {
|
|
54
54
|
updatePreferences: 1
|