deepline 0.3.209 → 0.3.211
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/play-runtime/context.ts +28 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +95 -19
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +3 -3
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +11 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +17 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +303 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +38 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-scope.ts +10 -6
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +37 -13
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +7 -2
- 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.211',
|
|
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: {
|
|
@@ -122,6 +122,7 @@ import {
|
|
|
122
122
|
import {
|
|
123
123
|
AllAdmittedRowsFailedError,
|
|
124
124
|
isProviderExhaustedFailure,
|
|
125
|
+
ProviderAdmissionWaitTimeoutError,
|
|
125
126
|
ProviderExhaustedError,
|
|
126
127
|
} from './run-failure';
|
|
127
128
|
import { PROVIDER_EXHAUSTED_MAX_WAIT_MS } from './governor/app-runtime-rate-state-backend';
|
|
@@ -18682,6 +18683,30 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18682
18683
|
networkScope: null,
|
|
18683
18684
|
},
|
|
18684
18685
|
);
|
|
18686
|
+
const providerAdmissionTimeoutError = (
|
|
18687
|
+
error: ProviderAdmissionWaitTimeoutError,
|
|
18688
|
+
) =>
|
|
18689
|
+
createToolHttpError(
|
|
18690
|
+
toolErrorSchemaVersion,
|
|
18691
|
+
error.message,
|
|
18692
|
+
null,
|
|
18693
|
+
503,
|
|
18694
|
+
'repairable',
|
|
18695
|
+
{
|
|
18696
|
+
toolId,
|
|
18697
|
+
provider: error.provider,
|
|
18698
|
+
operation: toolId,
|
|
18699
|
+
code: error.code,
|
|
18700
|
+
origin: 'deepline',
|
|
18701
|
+
category: 'rate_limit',
|
|
18702
|
+
retryable: true,
|
|
18703
|
+
statusCode: 503,
|
|
18704
|
+
requestId: null,
|
|
18705
|
+
retryAfterMs: null,
|
|
18706
|
+
networkKind: null,
|
|
18707
|
+
networkScope: null,
|
|
18708
|
+
},
|
|
18709
|
+
);
|
|
18685
18710
|
const observeProviderWait = (retryAtMs: number) => {
|
|
18686
18711
|
const startedAtMsByScope =
|
|
18687
18712
|
(this.checkpoint.providerNoProgressStartedAtMsByScope ??= {});
|
|
@@ -19934,6 +19959,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19934
19959
|
if (heartbeatFailure) {
|
|
19935
19960
|
throw heartbeatFailure;
|
|
19936
19961
|
}
|
|
19962
|
+
if (error instanceof ProviderAdmissionWaitTimeoutError) {
|
|
19963
|
+
throw providerAdmissionTimeoutError(error);
|
|
19964
|
+
}
|
|
19937
19965
|
if (error instanceof ProviderExhaustedError) {
|
|
19938
19966
|
const retryAtMs = Date.parse(error.retryAt);
|
|
19939
19967
|
observeProviderWait(
|
package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type RateStateBackendKind,
|
|
21
21
|
} from './rate-state-backend';
|
|
22
22
|
import {
|
|
23
|
+
ProviderAdmissionWaitTimeoutError,
|
|
23
24
|
ProviderExhaustedError,
|
|
24
25
|
providerNameFromBucketId,
|
|
25
26
|
} from '../run-failure';
|
|
@@ -36,12 +37,18 @@ import type { PlayRunnerRateStateValidateResult } from '../protocol';
|
|
|
36
37
|
*/
|
|
37
38
|
export const PROVIDER_EXHAUSTED_MAX_WAIT_MS = 300_000;
|
|
38
39
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* starter parked here for twenty minutes with its core idle.
|
|
40
|
+
* Max total duration of a cached rate-state hold before it is yielded or its
|
|
41
|
+
* stale local no-progress warning is cleared. This is distinct from the shorter
|
|
42
|
+
* local permit-admission budget below.
|
|
43
43
|
*/
|
|
44
44
|
export const RATE_ACQUIRE_MAX_WAIT_MS = 300_000;
|
|
45
|
+
/**
|
|
46
|
+
* Total time one acquirer may spend waiting for a locally paced permit. This
|
|
47
|
+
* covers both empty-block refills and future-scheduled tickets. A spent budget
|
|
48
|
+
* is a repairable Deepline admission error, not another retry loop or a
|
|
49
|
+
* provider-owned Retry-After hold.
|
|
50
|
+
*/
|
|
51
|
+
export const PROVIDER_ADMISSION_WAIT_BUDGET_MS = 120_000;
|
|
45
52
|
const PROVIDER_NO_PROGRESS_WARNING_MS = 180_000;
|
|
46
53
|
|
|
47
54
|
/**
|
|
@@ -91,6 +98,31 @@ export const APP_RUNTIME_RATE_STATE_LEASE_BLOCK_SIZE = 8;
|
|
|
91
98
|
const LEASE_BLOCK_MIN_TTL_MS = 250;
|
|
92
99
|
const LEASE_BLOCK_MAX_TTL_MS = 5_000;
|
|
93
100
|
const MAX_ACQUIRE_SLEEP_MS = 5_000;
|
|
101
|
+
|
|
102
|
+
function abortReason(signal: AbortSignal): Error {
|
|
103
|
+
return signal.reason instanceof Error
|
|
104
|
+
? signal.reason
|
|
105
|
+
: new Error('Rate-state acquire aborted.');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function sleepWithAbort(ms: number, signal?: AbortSignal): Promise<void> {
|
|
109
|
+
if (signal?.aborted) return Promise.reject(abortReason(signal));
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
const cleanup = () => signal?.removeEventListener('abort', onAbort);
|
|
112
|
+
const timer = setTimeout(() => {
|
|
113
|
+
cleanup();
|
|
114
|
+
resolve();
|
|
115
|
+
}, ms);
|
|
116
|
+
const onAbort = () => {
|
|
117
|
+
clearTimeout(timer);
|
|
118
|
+
cleanup();
|
|
119
|
+
reject(abortReason(signal!));
|
|
120
|
+
};
|
|
121
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
122
|
+
if (signal?.aborted) onAbort();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
94
126
|
/**
|
|
95
127
|
* Horizon used to size the next lease block from the rate actually in force.
|
|
96
128
|
* Kept short so a block stays inside its own TTL and a stalled runner can never
|
|
@@ -156,7 +188,7 @@ interface PendingRelease {
|
|
|
156
188
|
interface Options {
|
|
157
189
|
schedulerSchema?: string | null;
|
|
158
190
|
now?: () => number;
|
|
159
|
-
sleep?: (ms: number) => Promise<void>;
|
|
191
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
160
192
|
onStoreFailure?: (info: { bucketId: string; error: string }) => void;
|
|
161
193
|
/** Override the transport (tests inject a counting/faulting port). */
|
|
162
194
|
port?: AppRuntimeRatePort;
|
|
@@ -177,7 +209,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
177
209
|
private readonly port: AppRuntimeRatePort;
|
|
178
210
|
private readonly schedulerSchema: string | null;
|
|
179
211
|
private readonly now: () => number;
|
|
180
|
-
private readonly sleep: (ms: number) => Promise<void>;
|
|
212
|
+
private readonly sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
181
213
|
private readonly jitter: () => number;
|
|
182
214
|
private readonly onStoreFailure: (info: {
|
|
183
215
|
bucketId: string;
|
|
@@ -238,9 +270,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
238
270
|
this.port = options.port ?? contextRatePort(context);
|
|
239
271
|
this.schedulerSchema = options.schedulerSchema?.trim() || null;
|
|
240
272
|
this.now = options.now ?? (() => Date.now());
|
|
241
|
-
this.sleep =
|
|
242
|
-
options.sleep ??
|
|
243
|
-
((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
273
|
+
this.sleep = options.sleep ?? sleepWithAbort;
|
|
244
274
|
this.jitter = options.jitter ?? Math.random;
|
|
245
275
|
this.onStoreFailure =
|
|
246
276
|
options.onStoreFailure ??
|
|
@@ -412,20 +442,47 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
412
442
|
const localPermit = this.drawFromBlock(bucketId, laneKey);
|
|
413
443
|
if (localPermit) {
|
|
414
444
|
const waitMs = localPermit.scheduledAtMs - this.now();
|
|
415
|
-
|
|
445
|
+
const elapsedMs = Math.max(0, this.now() - acquireStartedAt);
|
|
446
|
+
const remainingWaitMs = Math.max(
|
|
447
|
+
0,
|
|
448
|
+
PROVIDER_ADMISSION_WAIT_BUDGET_MS - elapsedMs,
|
|
449
|
+
);
|
|
450
|
+
if (
|
|
451
|
+
elapsedMs >= PROVIDER_ADMISSION_WAIT_BUDGET_MS ||
|
|
452
|
+
waitMs > remainingWaitMs
|
|
453
|
+
) {
|
|
454
|
+
await this.cancelUndispatchedPermit(localPermit);
|
|
455
|
+
throw new ProviderAdmissionWaitTimeoutError({
|
|
456
|
+
provider: providerNameFromBucketId(bucketId),
|
|
457
|
+
waitBudgetMs: PROVIDER_ADMISSION_WAIT_BUDGET_MS,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
if (waitMs > 0) await this.sleep(waitMs, signal);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
await this.cancelUndispatchedPermit(localPermit);
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
416
466
|
if (signal?.aborted) {
|
|
417
|
-
|
|
418
|
-
throw signal
|
|
419
|
-
|
|
420
|
-
|
|
467
|
+
await this.cancelUndispatchedPermit(localPermit);
|
|
468
|
+
throw abortReason(signal);
|
|
469
|
+
}
|
|
470
|
+
if (
|
|
471
|
+
this.now() - acquireStartedAt >= PROVIDER_ADMISSION_WAIT_BUDGET_MS
|
|
472
|
+
) {
|
|
473
|
+
await this.cancelUndispatchedPermit(localPermit);
|
|
474
|
+
throw new ProviderAdmissionWaitTimeoutError({
|
|
475
|
+
provider: providerNameFromBucketId(bucketId),
|
|
476
|
+
waitBudgetMs: PROVIDER_ADMISSION_WAIT_BUDGET_MS,
|
|
477
|
+
});
|
|
421
478
|
}
|
|
422
479
|
return localPermit.permit;
|
|
423
480
|
}
|
|
424
481
|
const waitedMs = this.now() - acquireStartedAt;
|
|
425
|
-
if (waitedMs >=
|
|
426
|
-
throw new
|
|
482
|
+
if (waitedMs >= PROVIDER_ADMISSION_WAIT_BUDGET_MS) {
|
|
483
|
+
throw new ProviderAdmissionWaitTimeoutError({
|
|
427
484
|
provider: providerNameFromBucketId(bucketId),
|
|
428
|
-
|
|
485
|
+
waitBudgetMs: PROVIDER_ADMISSION_WAIT_BUDGET_MS,
|
|
429
486
|
});
|
|
430
487
|
}
|
|
431
488
|
// Count this acquirer as waiting on the refill so the single-flight refill
|
|
@@ -1389,7 +1446,11 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
1389
1446
|
private drawFromBlock(
|
|
1390
1447
|
bucketId: string,
|
|
1391
1448
|
laneKey: string,
|
|
1392
|
-
): {
|
|
1449
|
+
): {
|
|
1450
|
+
permit: PacingPermit;
|
|
1451
|
+
scheduledAtMs: number;
|
|
1452
|
+
reservationId: string | null;
|
|
1453
|
+
} | null {
|
|
1393
1454
|
const block = this.liveBlock(bucketId, laneKey);
|
|
1394
1455
|
if (!block) return null;
|
|
1395
1456
|
// An emptied block stays until it expires: the next refill merges into it,
|
|
@@ -1399,9 +1460,24 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
1399
1460
|
return {
|
|
1400
1461
|
permit: this.permitFor(bucketId, laneKey, block, scheduled),
|
|
1401
1462
|
scheduledAtMs: scheduled[0],
|
|
1463
|
+
reservationId: scheduled[2],
|
|
1402
1464
|
};
|
|
1403
1465
|
}
|
|
1404
1466
|
|
|
1467
|
+
private async cancelUndispatchedPermit(input: {
|
|
1468
|
+
permit: PacingPermit;
|
|
1469
|
+
reservationId: string | null;
|
|
1470
|
+
}): Promise<void> {
|
|
1471
|
+
if (input.reservationId) {
|
|
1472
|
+
await input.permit.cancel();
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
// Older gateways may grant a permit without a refundable reservation id.
|
|
1476
|
+
// Still release any concurrency lease; losing one rate token is safer than
|
|
1477
|
+
// manufacturing an immediate permit during a rolling deploy.
|
|
1478
|
+
input.permit.release();
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1405
1481
|
private drainBlocksForBucket(
|
|
1406
1482
|
bucketId: string,
|
|
1407
1483
|
beforeRetryAtMs?: number,
|
|
@@ -1546,7 +1622,7 @@ function withAdaptiveMaxRps(rules: readonly PacingRule[]): PacingRule[] {
|
|
|
1546
1622
|
// This one shared default rule is synthesized for unknown providers whose
|
|
1547
1623
|
// credential bucket spans operations. Keep the generic policy unchanged
|
|
1548
1624
|
// for local/coordinator backends, but let Postgres start a new provider
|
|
1549
|
-
// scope
|
|
1625
|
+
// scope at 10 RPS before learning its sustainable rate.
|
|
1550
1626
|
const seed =
|
|
1551
1627
|
rule.ruleId === 'default:provider-scope'
|
|
1552
1628
|
? {
|
|
@@ -511,9 +511,9 @@ export function createPlayExecutionGovernor(
|
|
|
511
511
|
if (!declaredPacing) {
|
|
512
512
|
// bucketId is the authenticated quota scope. Unknown tools sharing a
|
|
513
513
|
// credential must not each mint an independent copy of the default rate.
|
|
514
|
-
// Start its one shared lane at
|
|
515
|
-
// ramps on sustained success and reacts to
|
|
516
|
-
// tells us the quota is lower.
|
|
514
|
+
// Start its one shared lane at 10 RPS, matching the generic fallback.
|
|
515
|
+
// The DB-owned AIMD ramps on sustained success and reacts to
|
|
516
|
+
// Retry-After if the provider tells us the quota is lower.
|
|
517
517
|
pacing.rules = pacing.rules.map((rule) => ({
|
|
518
518
|
...rule,
|
|
519
519
|
ruleId: 'default:provider-scope',
|
|
@@ -189,7 +189,7 @@ export const SHARED_EXECUTION_POLICY: ResolvedExecutionPolicy = {
|
|
|
189
189
|
},
|
|
190
190
|
pacing: {
|
|
191
191
|
// Keep the generic fallback rate. The shared Postgres backend separately
|
|
192
|
-
// cold-starts unknown provider scopes at
|
|
192
|
+
// cold-starts unknown provider scopes at 10 RPS.
|
|
193
193
|
// Declared providers (rate-limit-definitions.ts) keep their own rates.
|
|
194
194
|
defaultProviderRequestsPerSecond: 10,
|
|
195
195
|
// This is the scheduler's resident-call envelope, not an RPS target.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
export const DEFAULT_ADVISORY_PROVIDER_MAX_RPS = 100;
|
|
16
16
|
|
|
17
17
|
/** Cold-start rate for an unknown provider bucket shared across a fleet key. */
|
|
18
|
-
export const DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS =
|
|
18
|
+
export const DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS = 10;
|
|
19
19
|
|
|
20
20
|
/** AIMD state cooldown cap; provider Retry-After holds remain uncapped. */
|
|
21
21
|
export const PROVIDER_AIMD_COOLDOWN_CAP_MS = 45 * 60 * 1_000;
|
|
@@ -240,6 +240,27 @@ export function providerExhaustedMessage(input: {
|
|
|
240
240
|
);
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
/**
|
|
244
|
+
* The local rate governor could not offer a provider permit inside its bounded
|
|
245
|
+
* admission window. No provider request was dispatched, so this is a Deepline
|
|
246
|
+
* pacing failure rather than a provider Retry-After hold.
|
|
247
|
+
*/
|
|
248
|
+
export class ProviderAdmissionWaitTimeoutError extends Error {
|
|
249
|
+
readonly code = 'PROVIDER_ADMISSION_TIMEOUT';
|
|
250
|
+
readonly provider: string;
|
|
251
|
+
readonly waitBudgetMs: number;
|
|
252
|
+
|
|
253
|
+
constructor(input: { provider: string; waitBudgetMs: number }) {
|
|
254
|
+
const minutes = Math.max(1, Math.ceil(input.waitBudgetMs / 60_000));
|
|
255
|
+
super(
|
|
256
|
+
`PROVIDER_ADMISSION_TIMEOUT: Deepline could not grant a provider permit for ${input.provider} within its ${minutes}-minute wait budget. No provider request was sent.`,
|
|
257
|
+
);
|
|
258
|
+
this.name = 'ProviderAdmissionWaitTimeoutError';
|
|
259
|
+
this.provider = input.provider;
|
|
260
|
+
this.waitBudgetMs = input.waitBudgetMs;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
243
264
|
/**
|
|
244
265
|
* Control signal from the DB-authoritative pacer when a provider hold exceeds
|
|
245
266
|
* the in-process wait threshold. The next provider call is not dispatched.
|
|
@@ -42,6 +42,7 @@ import type {
|
|
|
42
42
|
import {
|
|
43
43
|
buildDaytonaRuntimeWatchdogSource,
|
|
44
44
|
PLAY_RUNNER_UV_THREADPOOL_SIZE,
|
|
45
|
+
RUNNER_MEMORY_CGROUP_ROOT,
|
|
45
46
|
RUNTIME_SANDBOX_MEMORY_ABORT_REASON,
|
|
46
47
|
} from './daytona-runtime-watchdog';
|
|
47
48
|
|
|
@@ -72,6 +73,9 @@ export type StagedDaytonaPayload = {
|
|
|
72
73
|
command: string;
|
|
73
74
|
outputPath: string;
|
|
74
75
|
exitCodePath: string;
|
|
76
|
+
oomKillBaselinePath: string;
|
|
77
|
+
runnerPidPath: string;
|
|
78
|
+
runtimeStartedPath: string;
|
|
75
79
|
runtimeCompletedPath: string;
|
|
76
80
|
terminationDiagnosticPath: string;
|
|
77
81
|
progressEventPath: string;
|
|
@@ -656,6 +660,7 @@ export async function stageRunnerPayload(input: {
|
|
|
656
660
|
const crashPusherLogPath = `${workDir}/deepline-play-crash-terminal-${randomUUID()}.log`;
|
|
657
661
|
const exitCodePath = `${workDir}/deepline-play-exit-${randomUUID()}.txt`;
|
|
658
662
|
const oomKillBaselinePath = `${workDir}/deepline-play-oom-kill-${randomUUID()}.txt`;
|
|
663
|
+
const runnerPidPath = `${workDir}/deepline-play-runner-pid-${randomUUID()}.txt`;
|
|
659
664
|
const runtimeStartedPath = `${workDir}/deepline-play-runtime-started-${randomUUID()}.json`;
|
|
660
665
|
const runtimeCompletedPath = `${workDir}/deepline-play-runtime-completed-${randomUUID()}.json`;
|
|
661
666
|
const runtimeLimitMarkerPath = `${workDir}/deepline-play-runtime-limit-${randomUUID()}.txt`;
|
|
@@ -691,6 +696,8 @@ export async function stageRunnerPayload(input: {
|
|
|
691
696
|
shellQuote(terminationDiagnosticPath),
|
|
692
697
|
shellQuote(memoryLedgerPath),
|
|
693
698
|
shellQuote(memoryPressurePath),
|
|
699
|
+
shellQuote(RUNNER_MEMORY_CGROUP_ROOT),
|
|
700
|
+
shellQuote(runnerPidPath),
|
|
694
701
|
].join(' ');
|
|
695
702
|
const runnerCommand = `${nodeMaterializePayloadCommand({
|
|
696
703
|
envelopePath,
|
|
@@ -708,13 +715,16 @@ export async function stageRunnerPayload(input: {
|
|
|
708
715
|
// Its own retry diagnostics are kept in a separate log: appending them to
|
|
709
716
|
// `outputPath` after a near-gateway-sized result could hide that result from
|
|
710
717
|
// a later bounded-tail recovery read.
|
|
711
|
-
const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(crashPusherLogPath)} ${shellQuote(exitCodePath)} ${shellQuote(oomKillBaselinePath)} ${shellQuote(runtimeStartedPath)} ${shellQuote(runtimeCompletedPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(terminationDiagnosticPath)} ${shellQuote(memoryLedgerPath)} ${shellQuote(`${memoryLedgerPath}.tmp`)} ${shellQuote(memoryPressurePath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)} ${shellQuote(startupDiagnosticPath)}; ( awk '$1 == "oom_kill" { print $2 }' /sys/fs/cgroup/memory.events 2>/dev/null || true ) > ${shellQuote(oomKillBaselinePath)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; ${runtimeReliabilityEnv}node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(oomKillBaselinePath)} /sys/fs/cgroup/memory.events ${shellQuote(terminationDiagnosticPath)} > ${shellQuote(crashPusherLogPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
|
|
718
|
+
const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(crashPusherLogPath)} ${shellQuote(exitCodePath)} ${shellQuote(oomKillBaselinePath)} ${shellQuote(runnerPidPath)} ${shellQuote(runtimeStartedPath)} ${shellQuote(runtimeCompletedPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(terminationDiagnosticPath)} ${shellQuote(memoryLedgerPath)} ${shellQuote(`${memoryLedgerPath}.tmp`)} ${shellQuote(memoryPressurePath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)} ${shellQuote(startupDiagnosticPath)}; ( awk '$1 == "oom_kill" { print $2 }' /sys/fs/cgroup/memory.events 2>/dev/null || true ) > ${shellQuote(oomKillBaselinePath)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; ${runtimeReliabilityEnv}node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(oomKillBaselinePath)} /sys/fs/cgroup/memory.events ${shellQuote(terminationDiagnosticPath)} > ${shellQuote(crashPusherLogPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
|
|
712
719
|
|
|
713
720
|
return {
|
|
714
721
|
workDir: input.workDir,
|
|
715
722
|
command,
|
|
716
723
|
outputPath,
|
|
717
724
|
exitCodePath,
|
|
725
|
+
oomKillBaselinePath,
|
|
726
|
+
runnerPidPath,
|
|
727
|
+
runtimeStartedPath,
|
|
718
728
|
runtimeCompletedPath,
|
|
719
729
|
terminationDiagnosticPath,
|
|
720
730
|
progressEventPath,
|
|
@@ -126,7 +126,7 @@ export const RUNTIME_SANDBOX_MEMORY_ABORT_REASON =
|
|
|
126
126
|
* 1 runnerPath, 2 configPath, 3 runtimeStartedPath, 4 runtimeCompletedPath,
|
|
127
127
|
* 5 limitMs, 6 startupLimitMs, 7 terminalGraceMs, 8 runtimeLimitMarkerPath,
|
|
128
128
|
* 9 terminationDiagnosticPath, 10 memoryLedgerPath, 11 memoryPressurePath,
|
|
129
|
-
* 12 cgroupRoot.
|
|
129
|
+
* 12 cgroupRoot, 13 runnerPidPath.
|
|
130
130
|
*/
|
|
131
131
|
export function buildDaytonaRuntimeWatchdogSource(): string {
|
|
132
132
|
return `
|
|
@@ -144,6 +144,7 @@ const terminationDiagnosticPath = process.argv[9] || '';
|
|
|
144
144
|
const memoryLedgerPath = process.argv[10] || '';
|
|
145
145
|
const memoryPressurePath = process.argv[11] || '';
|
|
146
146
|
const cgroupRoot = process.argv[12] || ${JSON.stringify(RUNNER_MEMORY_CGROUP_ROOT)};
|
|
147
|
+
const runnerPidPath = process.argv[13] || '';
|
|
147
148
|
const runtimeLimitSeconds = Math.max(1, Math.ceil(limitMs / 1_000));
|
|
148
149
|
const child = spawn(
|
|
149
150
|
process.execPath,
|
|
@@ -161,6 +162,21 @@ const child = spawn(
|
|
|
161
162
|
stdio: 'inherit',
|
|
162
163
|
},
|
|
163
164
|
);
|
|
165
|
+
if (runnerPidPath && Number.isSafeInteger(child.pid)) {
|
|
166
|
+
try {
|
|
167
|
+
const rawStat = fs.readFileSync('/proc/' + child.pid + '/stat', 'utf8');
|
|
168
|
+
const close = rawStat.lastIndexOf(')');
|
|
169
|
+
const fields = close >= 0 ? rawStat.slice(close + 1).trim().split(/\\s+/) : [];
|
|
170
|
+
const startTimeTicks = Number.parseInt(fields[19], 10);
|
|
171
|
+
if (Number.isSafeInteger(startTimeTicks) && startTimeTicks >= 0) {
|
|
172
|
+
fs.writeFileSync(
|
|
173
|
+
runnerPidPath,
|
|
174
|
+
JSON.stringify({ pid: child.pid, startTimeTicks }),
|
|
175
|
+
{ flag: 'wx' },
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
} catch {}
|
|
179
|
+
}
|
|
164
180
|
let timedOut = false;
|
|
165
181
|
let startupTimedOut = false;
|
|
166
182
|
let terminalFlushTimedOut = false;
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
} from './daytona-session-execution';
|
|
47
47
|
import { RUNTIME_RELIABILITY_POLICY } from '../../runtime-reliability-policy';
|
|
48
48
|
import { daytonaSandboxStartAdmission } from './daytona-start-admission';
|
|
49
|
+
import { RUNNER_MEMORY_CGROUP_ROOT } from './daytona-runtime-watchdog';
|
|
49
50
|
|
|
50
51
|
const DAYTONA_COMMAND_RECOVERY_TIMEOUT_MS =
|
|
51
52
|
RUNTIME_RELIABILITY_POLICY.sandbox.daytonaCommandRecoveryTimeoutMs;
|
|
@@ -94,13 +95,37 @@ export type DaytonaCrashDiagnostic = {
|
|
|
94
95
|
exitCodeFile: number | null;
|
|
95
96
|
sessionExitCode: number | null;
|
|
96
97
|
outputBytes: number | null;
|
|
98
|
+
outputFileAvailable?: boolean | null;
|
|
99
|
+
exitCodeFileAvailable?: boolean | null;
|
|
100
|
+
terminationFileAvailable?: boolean | null;
|
|
101
|
+
runtimeStarted?: boolean | null;
|
|
102
|
+
runtimeCompleted?: boolean | null;
|
|
97
103
|
tailBytes: number;
|
|
98
104
|
tailTruncated: boolean;
|
|
99
105
|
signals: string[];
|
|
100
106
|
unclassifiedLineCount: number;
|
|
101
107
|
termination: DaytonaTerminationDiagnostic | null;
|
|
108
|
+
resourceSnapshot?: DaytonaCrashResourceSnapshot | null;
|
|
102
109
|
collectionFailure: DaytonaLookupFailure | null;
|
|
103
110
|
};
|
|
111
|
+
export type DaytonaCrashResourceSnapshot = {
|
|
112
|
+
memorySource: 'cgroup_v1' | 'cgroup_v1_nested' | 'cgroup_v2' | null;
|
|
113
|
+
memoryUsageBytes: number | null;
|
|
114
|
+
memoryLimitBytes: number | null;
|
|
115
|
+
memoryUsageRatio: number | null;
|
|
116
|
+
memoryOomCount: number | null;
|
|
117
|
+
memoryOomKillCount: number | null;
|
|
118
|
+
memoryOomKillDelta: number | null;
|
|
119
|
+
memoryOomGroupKillCount: number | null;
|
|
120
|
+
memoryHighCount: number | null;
|
|
121
|
+
memoryMaxCount: number | null;
|
|
122
|
+
cgroupCpuUsageUsecDelta: number | null;
|
|
123
|
+
cgroupCpuSampleWindowMs: number | null;
|
|
124
|
+
runnerPresent: boolean | null;
|
|
125
|
+
runnerState: string | null;
|
|
126
|
+
runnerRssPages: number | null;
|
|
127
|
+
runnerCpuTicksDelta: number | null;
|
|
128
|
+
};
|
|
104
129
|
export type DaytonaTerminationDiagnostic = {
|
|
105
130
|
schemaVersion: 1;
|
|
106
131
|
reason:
|
|
@@ -185,6 +210,56 @@ function parseDaytonaTerminationDiagnostic(
|
|
|
185
210
|
}
|
|
186
211
|
return diagnostic;
|
|
187
212
|
}
|
|
213
|
+
|
|
214
|
+
export function parseDaytonaCrashResourceSnapshot(
|
|
215
|
+
value: unknown,
|
|
216
|
+
): DaytonaCrashResourceSnapshot | null {
|
|
217
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
218
|
+
const candidate = value as Record<string, unknown>;
|
|
219
|
+
const nonNegativeNumber = (key: string): number | null => {
|
|
220
|
+
const value = candidate[key];
|
|
221
|
+
return typeof value === 'number' &&
|
|
222
|
+
Number.isSafeInteger(value) &&
|
|
223
|
+
value >= 0
|
|
224
|
+
? value
|
|
225
|
+
: null;
|
|
226
|
+
};
|
|
227
|
+
const ratio = candidate.memoryUsageRatio;
|
|
228
|
+
const source = candidate.memorySource;
|
|
229
|
+
return {
|
|
230
|
+
memorySource:
|
|
231
|
+
source === 'cgroup_v1' ||
|
|
232
|
+
source === 'cgroup_v1_nested' ||
|
|
233
|
+
source === 'cgroup_v2'
|
|
234
|
+
? source
|
|
235
|
+
: null,
|
|
236
|
+
memoryUsageBytes: nonNegativeNumber('memoryUsageBytes'),
|
|
237
|
+
memoryLimitBytes: nonNegativeNumber('memoryLimitBytes'),
|
|
238
|
+
memoryUsageRatio:
|
|
239
|
+
typeof ratio === 'number' && Number.isFinite(ratio) && ratio >= 0
|
|
240
|
+
? ratio
|
|
241
|
+
: null,
|
|
242
|
+
memoryOomCount: nonNegativeNumber('memoryOomCount'),
|
|
243
|
+
memoryOomKillCount: nonNegativeNumber('memoryOomKillCount'),
|
|
244
|
+
memoryOomKillDelta: nonNegativeNumber('memoryOomKillDelta'),
|
|
245
|
+
memoryOomGroupKillCount: nonNegativeNumber('memoryOomGroupKillCount'),
|
|
246
|
+
memoryHighCount: nonNegativeNumber('memoryHighCount'),
|
|
247
|
+
memoryMaxCount: nonNegativeNumber('memoryMaxCount'),
|
|
248
|
+
cgroupCpuUsageUsecDelta: nonNegativeNumber('cgroupCpuUsageUsecDelta'),
|
|
249
|
+
cgroupCpuSampleWindowMs: nonNegativeNumber('cgroupCpuSampleWindowMs'),
|
|
250
|
+
runnerPresent:
|
|
251
|
+
typeof candidate.runnerPresent === 'boolean'
|
|
252
|
+
? candidate.runnerPresent
|
|
253
|
+
: null,
|
|
254
|
+
runnerState:
|
|
255
|
+
typeof candidate.runnerState === 'string' &&
|
|
256
|
+
/^[A-Za-z]$/.test(candidate.runnerState)
|
|
257
|
+
? candidate.runnerState
|
|
258
|
+
: null,
|
|
259
|
+
runnerRssPages: nonNegativeNumber('runnerRssPages'),
|
|
260
|
+
runnerCpuTicksDelta: nonNegativeNumber('runnerCpuTicksDelta'),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
188
263
|
type DaytonaUploadAttemptTiming = {
|
|
189
264
|
attempt: number;
|
|
190
265
|
sandboxId: string;
|
|
@@ -368,6 +443,189 @@ export function extractDaytonaCrashDiagnosticSignals(outputTail: string): {
|
|
|
368
443
|
return { signals, unclassifiedLineCount };
|
|
369
444
|
}
|
|
370
445
|
|
|
446
|
+
export function buildDaytonaCrashDiagnosticSnapshotSource(): string {
|
|
447
|
+
return String.raw`
|
|
448
|
+
const fs = require('node:fs');
|
|
449
|
+
(async () => {
|
|
450
|
+
const output = process.argv[1];
|
|
451
|
+
const exit = process.argv[2];
|
|
452
|
+
const max = Number(process.argv[3]);
|
|
453
|
+
const terminationPath = process.argv[4] || '';
|
|
454
|
+
const startedPath = process.argv[5] || '';
|
|
455
|
+
const completedPath = process.argv[6] || '';
|
|
456
|
+
const pidPath = process.argv[7] || '';
|
|
457
|
+
const oomBaselinePath = process.argv[8] || '';
|
|
458
|
+
const cgroupRoot = process.argv[9] || '/sys/fs/cgroup';
|
|
459
|
+
const read = (path) => {
|
|
460
|
+
try { return fs.readFileSync(path, 'utf8'); } catch { return null; }
|
|
461
|
+
};
|
|
462
|
+
const exists = (path) => {
|
|
463
|
+
try { return fs.statSync(path).isFile(); } catch { return false; }
|
|
464
|
+
};
|
|
465
|
+
let outputStat = null;
|
|
466
|
+
try { outputStat = fs.statSync(output); } catch {}
|
|
467
|
+
let tail = Buffer.alloc(0);
|
|
468
|
+
if (outputStat) {
|
|
469
|
+
try {
|
|
470
|
+
const start = Math.max(0, outputStat.size - max);
|
|
471
|
+
const fd = fs.openSync(output, 'r');
|
|
472
|
+
tail = Buffer.alloc(outputStat.size - start);
|
|
473
|
+
fs.readSync(fd, tail, 0, tail.length, start);
|
|
474
|
+
fs.closeSync(fd);
|
|
475
|
+
} catch {}
|
|
476
|
+
}
|
|
477
|
+
const exitRaw = read(exit);
|
|
478
|
+
let exitCode = null;
|
|
479
|
+
if (exitRaw !== null) {
|
|
480
|
+
const value = Number.parseInt(exitRaw.trim(), 10);
|
|
481
|
+
if (Number.isFinite(value)) exitCode = value;
|
|
482
|
+
}
|
|
483
|
+
let termination = null;
|
|
484
|
+
try {
|
|
485
|
+
const value = JSON.parse(read(terminationPath) || 'null');
|
|
486
|
+
if (value && typeof value === 'object') termination = value;
|
|
487
|
+
} catch {}
|
|
488
|
+
const memoryPaths = [
|
|
489
|
+
['cgroup_v2', cgroupRoot + '/memory.current', cgroupRoot + '/memory.max'],
|
|
490
|
+
['cgroup_v1', cgroupRoot + '/memory.usage_in_bytes', cgroupRoot + '/memory.limit_in_bytes'],
|
|
491
|
+
['cgroup_v1_nested', cgroupRoot + '/memory/memory.usage_in_bytes', cgroupRoot + '/memory/memory.limit_in_bytes'],
|
|
492
|
+
];
|
|
493
|
+
const readNumber = (path) => {
|
|
494
|
+
const raw = read(path);
|
|
495
|
+
if (raw === null || !raw.trim() || raw.trim() === 'max') return null;
|
|
496
|
+
const value = Number.parseInt(raw.trim(), 10);
|
|
497
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
498
|
+
};
|
|
499
|
+
const readMemory = () => {
|
|
500
|
+
for (const [source, usagePath, limitPath] of memoryPaths) {
|
|
501
|
+
const usage = readNumber(usagePath);
|
|
502
|
+
const limit = readNumber(limitPath);
|
|
503
|
+
if (usage !== null && limit !== null && limit > 0 && limit < 2 ** 50) {
|
|
504
|
+
return {
|
|
505
|
+
memorySource: source,
|
|
506
|
+
memoryUsageBytes: usage,
|
|
507
|
+
memoryLimitBytes: limit,
|
|
508
|
+
memoryUsageRatio: usage / limit,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
memorySource: null,
|
|
514
|
+
memoryUsageBytes: null,
|
|
515
|
+
memoryLimitBytes: null,
|
|
516
|
+
memoryUsageRatio: null,
|
|
517
|
+
};
|
|
518
|
+
};
|
|
519
|
+
const readEvents = () => {
|
|
520
|
+
const result = {};
|
|
521
|
+
for (const line of (read(cgroupRoot + '/memory.events') || '').split(/\r?\n/)) {
|
|
522
|
+
const match = line.match(/^(oom|oom_kill|oom_group_kill|high|max)\s+(\d+)$/);
|
|
523
|
+
if (match) result[match[1]] = Number.parseInt(match[2], 10);
|
|
524
|
+
}
|
|
525
|
+
return result;
|
|
526
|
+
};
|
|
527
|
+
const readCpu = () => {
|
|
528
|
+
const line = (read(cgroupRoot + '/cpu.stat') || '')
|
|
529
|
+
.split(/\r?\n/)
|
|
530
|
+
.find((entry) => /^usage_usec\s+\d+$/.test(entry));
|
|
531
|
+
return line ? Number.parseInt(line.split(/\s+/)[1], 10) : null;
|
|
532
|
+
};
|
|
533
|
+
const readRunner = () => {
|
|
534
|
+
let identity = null;
|
|
535
|
+
try { identity = JSON.parse(read(pidPath) || 'null'); } catch {}
|
|
536
|
+
const pid = identity && identity.pid;
|
|
537
|
+
const expectedStartTimeTicks = identity && identity.startTimeTicks;
|
|
538
|
+
if (
|
|
539
|
+
!Number.isSafeInteger(pid) ||
|
|
540
|
+
pid < 1 ||
|
|
541
|
+
!Number.isSafeInteger(expectedStartTimeTicks) ||
|
|
542
|
+
expectedStartTimeTicks < 0
|
|
543
|
+
) return null;
|
|
544
|
+
const rawStat = read('/proc/' + pid + '/stat');
|
|
545
|
+
if (rawStat === null) return { present: false };
|
|
546
|
+
const close = rawStat.lastIndexOf(')');
|
|
547
|
+
if (close < 0) return { present: true };
|
|
548
|
+
const fields = rawStat.slice(close + 1).trim().split(/\s+/);
|
|
549
|
+
const startTimeTicks = Number.parseInt(fields[19], 10);
|
|
550
|
+
if (startTimeTicks !== expectedStartTimeTicks) return null;
|
|
551
|
+
const userTicks = Number.parseInt(fields[11], 10);
|
|
552
|
+
const systemTicks = Number.parseInt(fields[12], 10);
|
|
553
|
+
const rssRaw = (read('/proc/' + pid + '/statm') || '').trim().split(/\s+/)[1];
|
|
554
|
+
const rssPages = Number.parseInt(rssRaw || '', 10);
|
|
555
|
+
return {
|
|
556
|
+
present: true,
|
|
557
|
+
state: fields[0] || null,
|
|
558
|
+
cpuTicks:
|
|
559
|
+
Number.isFinite(userTicks) && Number.isFinite(systemTicks)
|
|
560
|
+
? userTicks + systemTicks
|
|
561
|
+
: null,
|
|
562
|
+
rssPages: Number.isSafeInteger(rssPages) && rssPages >= 0 ? rssPages : null,
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
const beforeCpu = readCpu();
|
|
566
|
+
const beforeRunner = readRunner();
|
|
567
|
+
const sampleStarted = Date.now();
|
|
568
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
569
|
+
const afterCpu = readCpu();
|
|
570
|
+
const afterRunner = readRunner();
|
|
571
|
+
const sampleWindowMs = Date.now() - sampleStarted;
|
|
572
|
+
const events = readEvents();
|
|
573
|
+
const baseline = readNumber(oomBaselinePath);
|
|
574
|
+
const resourceSnapshot = {
|
|
575
|
+
...readMemory(),
|
|
576
|
+
memoryOomCount: Number.isSafeInteger(events.oom) ? events.oom : null,
|
|
577
|
+
memoryOomKillCount: Number.isSafeInteger(events.oom_kill) ? events.oom_kill : null,
|
|
578
|
+
memoryOomKillDelta:
|
|
579
|
+
Number.isSafeInteger(events.oom_kill) &&
|
|
580
|
+
baseline !== null &&
|
|
581
|
+
events.oom_kill >= baseline
|
|
582
|
+
? events.oom_kill - baseline
|
|
583
|
+
: null,
|
|
584
|
+
memoryOomGroupKillCount:
|
|
585
|
+
Number.isSafeInteger(events.oom_group_kill) ? events.oom_group_kill : null,
|
|
586
|
+
memoryHighCount: Number.isSafeInteger(events.high) ? events.high : null,
|
|
587
|
+
memoryMaxCount: Number.isSafeInteger(events.max) ? events.max : null,
|
|
588
|
+
cgroupCpuUsageUsecDelta:
|
|
589
|
+
Number.isSafeInteger(beforeCpu) &&
|
|
590
|
+
Number.isSafeInteger(afterCpu) &&
|
|
591
|
+
afterCpu >= beforeCpu
|
|
592
|
+
? afterCpu - beforeCpu
|
|
593
|
+
: null,
|
|
594
|
+
cgroupCpuSampleWindowMs: sampleWindowMs,
|
|
595
|
+
runnerPresent: afterRunner ? afterRunner.present : null,
|
|
596
|
+
runnerState:
|
|
597
|
+
afterRunner && typeof afterRunner.state === 'string'
|
|
598
|
+
? afterRunner.state
|
|
599
|
+
: null,
|
|
600
|
+
runnerRssPages:
|
|
601
|
+
afterRunner && Number.isSafeInteger(afterRunner.rssPages)
|
|
602
|
+
? afterRunner.rssPages
|
|
603
|
+
: null,
|
|
604
|
+
runnerCpuTicksDelta:
|
|
605
|
+
beforeRunner &&
|
|
606
|
+
afterRunner &&
|
|
607
|
+
Number.isSafeInteger(beforeRunner.cpuTicks) &&
|
|
608
|
+
Number.isSafeInteger(afterRunner.cpuTicks) &&
|
|
609
|
+
afterRunner.cpuTicks >= beforeRunner.cpuTicks
|
|
610
|
+
? afterRunner.cpuTicks - beforeRunner.cpuTicks
|
|
611
|
+
: null,
|
|
612
|
+
};
|
|
613
|
+
process.stdout.write(JSON.stringify({
|
|
614
|
+
outputBytes: outputStat ? outputStat.size : null,
|
|
615
|
+
outputFileAvailable: Boolean(outputStat),
|
|
616
|
+
exitCodeFileAvailable: exitRaw !== null,
|
|
617
|
+
tail: tail.toString('base64'),
|
|
618
|
+
exitCode,
|
|
619
|
+
terminationFileAvailable: exists(terminationPath),
|
|
620
|
+
termination,
|
|
621
|
+
runtimeStarted: exists(startedPath),
|
|
622
|
+
runtimeCompleted: exists(completedPath),
|
|
623
|
+
resourceSnapshot,
|
|
624
|
+
}));
|
|
625
|
+
})().catch(() => { process.exitCode = 1; });
|
|
626
|
+
`;
|
|
627
|
+
}
|
|
628
|
+
|
|
371
629
|
/**
|
|
372
630
|
* Read bounded crash evidence while the failed sandbox still exists. This is
|
|
373
631
|
* operator telemetry only: it never becomes a run result or customer log.
|
|
@@ -378,6 +636,10 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
378
636
|
cmdId: string;
|
|
379
637
|
outputPath: string;
|
|
380
638
|
exitCodePath: string;
|
|
639
|
+
oomKillBaselinePath?: string;
|
|
640
|
+
runnerPidPath?: string;
|
|
641
|
+
runtimeStartedPath?: string;
|
|
642
|
+
runtimeCompletedPath?: string;
|
|
381
643
|
terminationDiagnosticPath?: string;
|
|
382
644
|
expectedOrganizationId?: string | null;
|
|
383
645
|
}): Promise<DaytonaCrashDiagnostic> {
|
|
@@ -389,11 +651,17 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
389
651
|
exitCodeFile: null,
|
|
390
652
|
sessionExitCode: null,
|
|
391
653
|
outputBytes: null,
|
|
654
|
+
outputFileAvailable: null,
|
|
655
|
+
exitCodeFileAvailable: null,
|
|
656
|
+
terminationFileAvailable: null,
|
|
657
|
+
runtimeStarted: null,
|
|
658
|
+
runtimeCompleted: null,
|
|
392
659
|
tailBytes: 0,
|
|
393
660
|
tailTruncated: false,
|
|
394
661
|
signals: [],
|
|
395
662
|
unclassifiedLineCount: 0,
|
|
396
663
|
termination: null,
|
|
664
|
+
resourceSnapshot: null,
|
|
397
665
|
collectionFailure,
|
|
398
666
|
});
|
|
399
667
|
try {
|
|
@@ -419,10 +687,10 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
419
687
|
};
|
|
420
688
|
}
|
|
421
689
|
|
|
422
|
-
const script =
|
|
690
|
+
const script = buildDaytonaCrashDiagnosticSnapshotSource();
|
|
423
691
|
const [snapshotResult, sessionResult] = await Promise.allSettled([
|
|
424
692
|
sandbox.process.executeCommand(
|
|
425
|
-
`node -e ${shellQuoteDaytonaDiagnostic(script)} ${shellQuoteDaytonaDiagnostic(input.outputPath)} ${shellQuoteDaytonaDiagnostic(input.exitCodePath)} ${DAYTONA_CRASH_DIAGNOSTIC_TAIL_MAX_BYTES} ${shellQuoteDaytonaDiagnostic(input.terminationDiagnosticPath ?? '')}`,
|
|
693
|
+
`node -e ${shellQuoteDaytonaDiagnostic(script)} ${shellQuoteDaytonaDiagnostic(input.outputPath)} ${shellQuoteDaytonaDiagnostic(input.exitCodePath)} ${DAYTONA_CRASH_DIAGNOSTIC_TAIL_MAX_BYTES} ${shellQuoteDaytonaDiagnostic(input.terminationDiagnosticPath ?? '')} ${shellQuoteDaytonaDiagnostic(input.runtimeStartedPath ?? '')} ${shellQuoteDaytonaDiagnostic(input.runtimeCompletedPath ?? '')} ${shellQuoteDaytonaDiagnostic(input.runnerPidPath ?? '')} ${shellQuoteDaytonaDiagnostic(input.oomKillBaselinePath ?? '')} ${shellQuoteDaytonaDiagnostic(RUNNER_MEMORY_CGROUP_ROOT)}`,
|
|
426
694
|
undefined,
|
|
427
695
|
{},
|
|
428
696
|
8,
|
|
@@ -434,9 +702,15 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
434
702
|
}
|
|
435
703
|
const snapshot = JSON.parse(snapshotResult.value.result) as {
|
|
436
704
|
outputBytes?: unknown;
|
|
705
|
+
outputFileAvailable?: unknown;
|
|
706
|
+
exitCodeFileAvailable?: unknown;
|
|
707
|
+
terminationFileAvailable?: unknown;
|
|
708
|
+
runtimeStarted?: unknown;
|
|
709
|
+
runtimeCompleted?: unknown;
|
|
437
710
|
tail?: unknown;
|
|
438
711
|
exitCode?: unknown;
|
|
439
712
|
termination?: unknown;
|
|
713
|
+
resourceSnapshot?: unknown;
|
|
440
714
|
};
|
|
441
715
|
const tailBuffer =
|
|
442
716
|
typeof snapshot.tail === 'string'
|
|
@@ -456,6 +730,9 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
456
730
|
? sessionResult.value.exitCode
|
|
457
731
|
: null;
|
|
458
732
|
const termination = parseDaytonaTerminationDiagnostic(snapshot.termination);
|
|
733
|
+
const resourceSnapshot = parseDaytonaCrashResourceSnapshot(
|
|
734
|
+
snapshot.resourceSnapshot,
|
|
735
|
+
);
|
|
459
736
|
return {
|
|
460
737
|
schemaVersion: 1,
|
|
461
738
|
collectionStatus: 'collected',
|
|
@@ -466,6 +743,26 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
466
743
|
: null,
|
|
467
744
|
sessionExitCode,
|
|
468
745
|
outputBytes,
|
|
746
|
+
outputFileAvailable:
|
|
747
|
+
typeof snapshot.outputFileAvailable === 'boolean'
|
|
748
|
+
? snapshot.outputFileAvailable
|
|
749
|
+
: null,
|
|
750
|
+
exitCodeFileAvailable:
|
|
751
|
+
typeof snapshot.exitCodeFileAvailable === 'boolean'
|
|
752
|
+
? snapshot.exitCodeFileAvailable
|
|
753
|
+
: null,
|
|
754
|
+
terminationFileAvailable:
|
|
755
|
+
typeof snapshot.terminationFileAvailable === 'boolean'
|
|
756
|
+
? snapshot.terminationFileAvailable
|
|
757
|
+
: null,
|
|
758
|
+
runtimeStarted:
|
|
759
|
+
typeof snapshot.runtimeStarted === 'boolean'
|
|
760
|
+
? snapshot.runtimeStarted
|
|
761
|
+
: null,
|
|
762
|
+
runtimeCompleted:
|
|
763
|
+
typeof snapshot.runtimeCompleted === 'boolean'
|
|
764
|
+
? snapshot.runtimeCompleted
|
|
765
|
+
: null,
|
|
469
766
|
tailBytes: tailBuffer.byteLength,
|
|
470
767
|
tailTruncated:
|
|
471
768
|
outputBytes !== null &&
|
|
@@ -473,6 +770,7 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
473
770
|
signals,
|
|
474
771
|
unclassifiedLineCount,
|
|
475
772
|
termination,
|
|
773
|
+
resourceSnapshot,
|
|
476
774
|
collectionFailure:
|
|
477
775
|
sessionResult.status === 'rejected'
|
|
478
776
|
? describeDaytonaLookupFailure(sessionResult.reason)
|
|
@@ -1418,6 +1716,9 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1418
1716
|
cmdId: start.cmdId,
|
|
1419
1717
|
outputPath: stagedPayload.outputPath,
|
|
1420
1718
|
exitCodePath: stagedPayload.exitCodePath,
|
|
1719
|
+
oomKillBaselinePath: stagedPayload.oomKillBaselinePath,
|
|
1720
|
+
runnerPidPath: stagedPayload.runnerPidPath,
|
|
1721
|
+
runtimeStartedPath: stagedPayload.runtimeStartedPath,
|
|
1421
1722
|
runtimeCompletedPath: stagedPayload.runtimeCompletedPath,
|
|
1422
1723
|
terminationDiagnosticPath:
|
|
1423
1724
|
stagedPayload.terminationDiagnosticPath,
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts
CHANGED
|
@@ -834,6 +834,10 @@ function buildDefaultDeps(absurd: Absurd): AbsurdRuntimeWorkerDeps {
|
|
|
834
834
|
cmdId: request.cmdId,
|
|
835
835
|
outputPath: request.outputPath,
|
|
836
836
|
exitCodePath: request.exitCodePath,
|
|
837
|
+
oomKillBaselinePath: request.oomKillBaselinePath,
|
|
838
|
+
runnerPidPath: request.runnerPidPath,
|
|
839
|
+
runtimeStartedPath: request.runtimeStartedPath,
|
|
840
|
+
runtimeCompletedPath: request.runtimeCompletedPath,
|
|
837
841
|
terminationDiagnosticPath: request.terminationDiagnosticPath,
|
|
838
842
|
expectedOrganizationId: routingDomain,
|
|
839
843
|
}),
|
|
@@ -4057,6 +4061,18 @@ function sandboxCrashDiagnosticRequest(input: {
|
|
|
4057
4061
|
cmdId: suspension.cmdId,
|
|
4058
4062
|
outputPath: suspension.outputPath,
|
|
4059
4063
|
exitCodePath: suspension.exitCodePath,
|
|
4064
|
+
...(suspension.oomKillBaselinePath
|
|
4065
|
+
? { oomKillBaselinePath: suspension.oomKillBaselinePath }
|
|
4066
|
+
: {}),
|
|
4067
|
+
...(suspension.runnerPidPath
|
|
4068
|
+
? { runnerPidPath: suspension.runnerPidPath }
|
|
4069
|
+
: {}),
|
|
4070
|
+
...(suspension.runtimeStartedPath
|
|
4071
|
+
? { runtimeStartedPath: suspension.runtimeStartedPath }
|
|
4072
|
+
: {}),
|
|
4073
|
+
...(suspension.runtimeCompletedPath
|
|
4074
|
+
? { runtimeCompletedPath: suspension.runtimeCompletedPath }
|
|
4075
|
+
: {}),
|
|
4060
4076
|
...(suspension.terminationDiagnosticPath
|
|
4061
4077
|
? { terminationDiagnosticPath: suspension.terminationDiagnosticPath }
|
|
4062
4078
|
: {}),
|
|
@@ -4371,7 +4387,7 @@ async function resolveDetachedRunnerOutcome(input: {
|
|
|
4371
4387
|
runnerAttempt: suspension.runnerAttempt,
|
|
4372
4388
|
sandboxId: suspension.sandboxId,
|
|
4373
4389
|
exitCode: inspection.exitCode,
|
|
4374
|
-
diagnosis: inspection.diagnosis?.stage,
|
|
4390
|
+
diagnosis: inspection.diagnosis?.stage ?? 'unknown',
|
|
4375
4391
|
resultStatus: inspection.result.status,
|
|
4376
4392
|
}),
|
|
4377
4393
|
);
|
|
@@ -4385,7 +4401,7 @@ async function resolveDetachedRunnerOutcome(input: {
|
|
|
4385
4401
|
runnerAttempt: suspension.runnerAttempt,
|
|
4386
4402
|
sandboxId: suspension.sandboxId,
|
|
4387
4403
|
exitCode: inspection.exitCode,
|
|
4388
|
-
diagnosis: inspection.diagnosis?.stage,
|
|
4404
|
+
diagnosis: inspection.diagnosis?.stage ?? 'unknown',
|
|
4389
4405
|
resultStatus: inspection.result?.status ?? null,
|
|
4390
4406
|
}),
|
|
4391
4407
|
);
|
|
@@ -6172,6 +6188,20 @@ function parseSandboxCrashDiagnosticRequest(
|
|
|
6172
6188
|
) {
|
|
6173
6189
|
return null;
|
|
6174
6190
|
}
|
|
6191
|
+
for (const key of [
|
|
6192
|
+
'oomKillBaselinePath',
|
|
6193
|
+
'runnerPidPath',
|
|
6194
|
+
'runtimeStartedPath',
|
|
6195
|
+
'runtimeCompletedPath',
|
|
6196
|
+
'terminationDiagnosticPath',
|
|
6197
|
+
] as const) {
|
|
6198
|
+
if (
|
|
6199
|
+
candidate[key] !== undefined &&
|
|
6200
|
+
(typeof candidate[key] !== 'string' || !(candidate[key] as string).trim())
|
|
6201
|
+
) {
|
|
6202
|
+
return null;
|
|
6203
|
+
}
|
|
6204
|
+
}
|
|
6175
6205
|
return candidate as PostgresSchedulerSandboxCrashDiagnosticRequest;
|
|
6176
6206
|
}
|
|
6177
6207
|
|
|
@@ -6184,11 +6214,17 @@ function unavailableSandboxCrashDiagnostic(
|
|
|
6184
6214
|
exitCodeFile: null,
|
|
6185
6215
|
sessionExitCode: null,
|
|
6186
6216
|
outputBytes: null,
|
|
6217
|
+
outputFileAvailable: null,
|
|
6218
|
+
exitCodeFileAvailable: null,
|
|
6219
|
+
terminationFileAvailable: null,
|
|
6220
|
+
runtimeStarted: null,
|
|
6221
|
+
runtimeCompleted: null,
|
|
6187
6222
|
tailBytes: 0,
|
|
6188
6223
|
tailTruncated: false,
|
|
6189
6224
|
signals: [],
|
|
6190
6225
|
unclassifiedLineCount: 0,
|
|
6191
6226
|
termination: null,
|
|
6227
|
+
resourceSnapshot: null,
|
|
6192
6228
|
collectionFailure: {
|
|
6193
6229
|
errorName: 'CrashDiagnosticUnavailable',
|
|
6194
6230
|
errorCode: null,
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-scope.ts
CHANGED
|
@@ -14,18 +14,22 @@ const AIMD_ADDITIVE_STEP_FRACTION = 0.25;
|
|
|
14
14
|
export function acquireRateScopeSql(
|
|
15
15
|
table: string,
|
|
16
16
|
successesParameter: string,
|
|
17
|
+
minimumRateSql = 'NULL::float8',
|
|
17
18
|
): string {
|
|
19
|
+
const refillRate = `CASE WHEN ${minimumRateSql} IS NULL
|
|
20
|
+
THEN h.refill_per_sec
|
|
21
|
+
ELSE GREATEST(h.refill_per_sec, ${minimumRateSql}) END`;
|
|
18
22
|
return `provider_hold_rows AS (
|
|
19
23
|
SELECT * FROM ${table} WHERE bucket_id = $1 FOR UPDATE
|
|
20
24
|
), provider_hold AS (
|
|
21
25
|
SELECT h.*,
|
|
22
26
|
LEAST(
|
|
23
|
-
GREATEST(CASE WHEN h.seen_backpressure THEN 1 ELSE h.capacity END,
|
|
24
|
-
h.tokens + GREATEST(0, nv.now_ms - h.refilled_at_ms) *
|
|
27
|
+
GREATEST(CASE WHEN h.seen_backpressure THEN 1 ELSE h.capacity END, ${refillRate}),
|
|
28
|
+
h.tokens + GREATEST(0, nv.now_ms - h.refilled_at_ms) * ${refillRate} / 1000.0
|
|
25
29
|
) AS tokens_after_refill,
|
|
26
30
|
${successesParameter}::int = 0
|
|
27
31
|
AND nv.now_ms - GREATEST(h.refilled_at_ms, h.next_dispatch_at_ms, h.retry_at_ms)
|
|
28
|
-
> GREATEST(1000, 2000.0 /
|
|
32
|
+
> GREATEST(1000, 2000.0 / ${refillRate}) AS observation_was_idle,
|
|
29
33
|
CASE WHEN ${successesParameter}::int > 0
|
|
30
34
|
AND h.rate_observation_started_at_ms IS NOT NULL
|
|
31
35
|
AND nv.now_ms - h.last_ramp_at_ms >= CASE WHEN h.seen_backpressure
|
|
@@ -33,9 +37,9 @@ export function acquireRateScopeSql(
|
|
|
33
37
|
ELSE h.rate_recovery_interval_ms END
|
|
34
38
|
AND h.retry_at_ms <= nv.now_ms
|
|
35
39
|
THEN LEAST(h.max_rps, CASE WHEN h.seen_backpressure
|
|
36
|
-
THEN
|
|
37
|
-
ELSE
|
|
38
|
-
ELSE
|
|
40
|
+
THEN ${refillRate} * (1 + ${AIMD_ADDITIVE_STEP_FRACTION})
|
|
41
|
+
ELSE ${refillRate} * 2 END)
|
|
42
|
+
ELSE ${refillRate} END AS next_refill_per_sec,
|
|
39
43
|
CASE WHEN ${successesParameter}::int > 0
|
|
40
44
|
AND h.rate_observation_started_at_ms IS NOT NULL
|
|
41
45
|
AND nv.now_ms - h.last_ramp_at_ms >= CASE WHEN h.seen_backpressure
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts
CHANGED
|
@@ -390,9 +390,9 @@ function normalizeRules(
|
|
|
390
390
|
return {
|
|
391
391
|
ruleId: rule.ruleId,
|
|
392
392
|
// Rates are fractional; only dispatched requests must be whole.
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
393
|
+
// Normalize every runner to the canonical 10-RPS seed for an unknown
|
|
394
|
+
// shared credential scope. The shared row's AIMD state can still be
|
|
395
|
+
// lower after backpressure and learns upward from successful calls.
|
|
396
396
|
requestsPerWindow: isUnknownProviderScope
|
|
397
397
|
? DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS
|
|
398
398
|
: rule.requestsPerWindow,
|
|
@@ -446,7 +446,13 @@ function seedForRule(rule: AcquirePacingRule): RateBucketSeed {
|
|
|
446
446
|
// Preserve its fractional refill rate, with dispatch times resolved in ms.
|
|
447
447
|
const capacity = Math.max(1, rule.requestsPerWindow);
|
|
448
448
|
const base = (rule.requestsPerWindow / rule.windowMs) * 1000;
|
|
449
|
-
|
|
449
|
+
// Undeclared providers use the shared fallback rate as their operating
|
|
450
|
+
// floor. Explicit provider Retry-After deadlines still block dispatch; a
|
|
451
|
+
// prior learned AIMD rate must not leave later runs paced below the fallback.
|
|
452
|
+
const minRps =
|
|
453
|
+
rule.ruleId === 'default:provider-scope'
|
|
454
|
+
? base
|
|
455
|
+
: Math.min(base, MIN_ADAPTIVE_RPS_FLOOR);
|
|
450
456
|
const maxRps =
|
|
451
457
|
rule.adaptiveMaxRps != null && rule.adaptiveMaxRps > 0
|
|
452
458
|
? Math.max(rule.adaptiveMaxRps, base)
|
|
@@ -461,6 +467,12 @@ function seedForRule(rule: AcquirePacingRule): RateBucketSeed {
|
|
|
461
467
|
};
|
|
462
468
|
}
|
|
463
469
|
|
|
470
|
+
function minimumRateSqlForRules(rules: readonly AcquirePacingRule[]): string {
|
|
471
|
+
return rules.some((rule) => rule.ruleId === 'default:provider-scope')
|
|
472
|
+
? `${DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS}::float8`
|
|
473
|
+
: 'NULL::float8';
|
|
474
|
+
}
|
|
475
|
+
|
|
464
476
|
function positiveInt(value: number, label: string): number {
|
|
465
477
|
if (!Number.isFinite(value) || value <= 0) {
|
|
466
478
|
throw new Error(`${label} must be a positive finite number.`);
|
|
@@ -743,11 +755,10 @@ async function seedBucketRows(
|
|
|
743
755
|
ON CONFLICT (bucket_id) DO UPDATE SET
|
|
744
756
|
retry_at_ms = GREATEST(hold.retry_at_ms, EXCLUDED.retry_at_ms),
|
|
745
757
|
refill_per_sec = CASE
|
|
746
|
-
--
|
|
747
|
-
--
|
|
748
|
-
--
|
|
749
|
-
--
|
|
750
|
-
-- evidence, and never erase a provider Retry-After hold.
|
|
758
|
+
-- If this row never moved from its seed, adopt the canonical
|
|
759
|
+
-- shared-scope seed on the first acquire after upgrade. Preserve
|
|
760
|
+
-- any rate with actual ramp or throttle evidence, and never erase
|
|
761
|
+
-- a provider Retry-After hold.
|
|
751
762
|
WHEN EXISTS (
|
|
752
763
|
SELECT 1 FROM seed_config
|
|
753
764
|
WHERE rule_id = 'default:provider-scope'
|
|
@@ -1295,7 +1306,11 @@ async function acquirePureRate(
|
|
|
1295
1306
|
adaptive_max_configured boolean
|
|
1296
1307
|
)
|
|
1297
1308
|
),
|
|
1298
|
-
${acquireRateScopeSql(
|
|
1309
|
+
${acquireRateScopeSql(
|
|
1310
|
+
scopeHoldTableName(input),
|
|
1311
|
+
'$5',
|
|
1312
|
+
minimumRateSqlForRules(rules),
|
|
1313
|
+
)},
|
|
1299
1314
|
reclaim_reservations AS (
|
|
1300
1315
|
DELETE FROM ${reservationTableName(input)}
|
|
1301
1316
|
WHERE bucket_id = $1
|
|
@@ -1735,7 +1750,11 @@ async function acquireWithConcurrency(
|
|
|
1735
1750
|
FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint
|
|
1736
1751
|
) AS now_ms
|
|
1737
1752
|
),
|
|
1738
|
-
${acquireRateScopeSql(
|
|
1753
|
+
${acquireRateScopeSql(
|
|
1754
|
+
scopeHoldTableName(input),
|
|
1755
|
+
'$10',
|
|
1756
|
+
minimumRateSqlForRules(rules),
|
|
1757
|
+
)},
|
|
1739
1758
|
reclaim_reservations AS (
|
|
1740
1759
|
DELETE FROM ${reservationTableName(input)}
|
|
1741
1760
|
WHERE bucket_id = $1
|
|
@@ -3015,6 +3034,11 @@ async function penalizeRateStateStatements(
|
|
|
3015
3034
|
return emptyRateStatePenaltyTelemetry();
|
|
3016
3035
|
}
|
|
3017
3036
|
const rules = normalizeRules((input.rules ?? []) as AcquirePacingRule[]);
|
|
3037
|
+
const minimumRateRps = rules.some(
|
|
3038
|
+
(rule) => rule.ruleId === 'default:provider-scope',
|
|
3039
|
+
)
|
|
3040
|
+
? DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS
|
|
3041
|
+
: MIN_ADAPTIVE_RPS_FLOOR;
|
|
3018
3042
|
await applyConcurrencyObservations(
|
|
3019
3043
|
client,
|
|
3020
3044
|
input,
|
|
@@ -3091,7 +3115,7 @@ async function penalizeRateStateStatements(
|
|
|
3091
3115
|
FROM held h CROSS JOIN now_value nv
|
|
3092
3116
|
), throttle_ceiling AS (
|
|
3093
3117
|
SELECT e.*,
|
|
3094
|
-
GREATEST(${
|
|
3118
|
+
GREATEST(${minimumRateRps},
|
|
3095
3119
|
CASE WHEN e.seen_backpressure AND e.rate_successes = 0 THEN
|
|
3096
3120
|
CASE WHEN $5::bigint IS NOT NULL
|
|
3097
3121
|
-- A fresh provider deadline is not a throughput measurement.
|
|
@@ -3106,7 +3130,7 @@ async function penalizeRateStateStatements(
|
|
|
3106
3130
|
FROM evidence e
|
|
3107
3131
|
), learned AS (
|
|
3108
3132
|
SELECT e.*,
|
|
3109
|
-
CASE WHEN e.new_episode THEN GREATEST(${
|
|
3133
|
+
CASE WHEN e.new_episode THEN GREATEST(${minimumRateRps}, LEAST(
|
|
3110
3134
|
e.next_ceiling_rps,
|
|
3111
3135
|
GREATEST(1, e.rate_successes) * 1000.0 / e.cycle_ms
|
|
3112
3136
|
)) ELSE e.refill_before END AS refill_after
|
|
@@ -392,6 +392,10 @@ export type PostgresSchedulerSandboxCrashDiagnosticRequest = {
|
|
|
392
392
|
cmdId: string;
|
|
393
393
|
outputPath: string;
|
|
394
394
|
exitCodePath: string;
|
|
395
|
+
oomKillBaselinePath?: string;
|
|
396
|
+
runnerPidPath?: string;
|
|
397
|
+
runtimeStartedPath?: string;
|
|
398
|
+
runtimeCompletedPath?: string;
|
|
395
399
|
terminationDiagnosticPath?: string;
|
|
396
400
|
};
|
|
397
401
|
|
|
@@ -68,6 +68,10 @@ export type PlayExecutionSuspension =
|
|
|
68
68
|
* timeout-wake salvage verification. */
|
|
69
69
|
outputPath: string;
|
|
70
70
|
exitCodePath: string;
|
|
71
|
+
/** Bounded crash evidence paths for sandbox state at liveness loss. */
|
|
72
|
+
oomKillBaselinePath?: string;
|
|
73
|
+
runnerPidPath?: string;
|
|
74
|
+
runtimeStartedPath?: string;
|
|
71
75
|
/** Exact customer-code completion fence inside the Daytona sandbox. */
|
|
72
76
|
runtimeCompletedPath?: string;
|
|
73
77
|
/** Bounded watchdog-owned cause marker for crash-only diagnostics. */
|
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
type ToolExecutionErrorSchemaVersion,
|
|
9
9
|
} from '../plays/tool-execution-error';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// Give an explicitly replay-safe transient 5xx one additional bounded retry.
|
|
12
|
+
// Preview gateway load runs can overlap brief Convex billing-plane 503s; this
|
|
13
|
+
// adds a second recovery chance without replaying operations that did not opt
|
|
14
|
+
// into the transient HTTP safety contract.
|
|
15
|
+
export const TOOL_EXECUTE_TRANSIENT_HTTP_MAX_ATTEMPTS = 3;
|
|
16
|
+
const TOOL_EXECUTE_CUSTOMER_DB_STORAGE_UNAVAILABLE_MAX_ATTEMPTS = 2;
|
|
12
17
|
// A provider may need to finish the original request before it can replay the
|
|
13
18
|
// result for this key. With no Retry-After signal, these two polls give the
|
|
14
19
|
// original request ten seconds to finish without repeatedly hammering it.
|
|
@@ -344,7 +349,7 @@ function decideToolExecuteHttpRetry(input: {
|
|
|
344
349
|
if (input.customerDbStorageUnavailable) {
|
|
345
350
|
return {
|
|
346
351
|
retryable: true,
|
|
347
|
-
attemptCap:
|
|
352
|
+
attemptCap: TOOL_EXECUTE_CUSTOMER_DB_STORAGE_UNAVAILABLE_MAX_ATTEMPTS,
|
|
348
353
|
reason: 'customer_db_storage_unavailable',
|
|
349
354
|
};
|
|
350
355
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -3051,7 +3051,7 @@ var SDK_RELEASE = {
|
|
|
3051
3051
|
// getters keep their established compatibility behavior.
|
|
3052
3052
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3053
3053
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3054
|
-
version: "0.3.
|
|
3054
|
+
version: "0.3.211",
|
|
3055
3055
|
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.",
|
|
3056
3056
|
packageCapabilities: {
|
|
3057
3057
|
updatePreferences: 1
|
package/dist/cli/index.mjs
CHANGED
|
@@ -3046,7 +3046,7 @@ var SDK_RELEASE = {
|
|
|
3046
3046
|
// getters keep their established compatibility behavior.
|
|
3047
3047
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3048
3048
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3049
|
-
version: "0.3.
|
|
3049
|
+
version: "0.3.211",
|
|
3050
3050
|
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.",
|
|
3051
3051
|
packageCapabilities: {
|
|
3052
3052
|
updatePreferences: 1
|
package/dist/index.js
CHANGED
|
@@ -864,7 +864,7 @@ var SDK_RELEASE = {
|
|
|
864
864
|
// getters keep their established compatibility behavior.
|
|
865
865
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
866
866
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
867
|
-
version: "0.3.
|
|
867
|
+
version: "0.3.211",
|
|
868
868
|
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.",
|
|
869
869
|
packageCapabilities: {
|
|
870
870
|
updatePreferences: 1
|
package/dist/index.mjs
CHANGED
|
@@ -768,7 +768,7 @@ var SDK_RELEASE = {
|
|
|
768
768
|
// getters keep their established compatibility behavior.
|
|
769
769
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
770
770
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
771
|
-
version: "0.3.
|
|
771
|
+
version: "0.3.211",
|
|
772
772
|
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.",
|
|
773
773
|
packageCapabilities: {
|
|
774
774
|
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.211";
|
|
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.211";
|
|
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.211",
|
|
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.211",
|
|
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
|