deepline 0.1.200 → 0.1.202
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +49 -13
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +25 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +139 -124
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +17 -2
- package/dist/bundling-sources/shared_libs/play-runtime/bounded-dispatch.ts +52 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +152 -23
- package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +121 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/adaptive-admission.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-budget-state-backend.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +86 -19
- package/dist/bundling-sources/shared_libs/play-runtime/governor/block-reserving-budget-state-backend.ts +141 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/budget-state-backend.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +129 -41
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +40 -2
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-completion-sink.ts +92 -33
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +13 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +122 -120
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +10 -8
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +140 -32
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +327 -31
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +21 -0
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +32 -10
- package/dist/cli/index.js +198 -76
- package/dist/cli/index.mjs +204 -82
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/package.json +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/adaptive-tool-dispatcher.ts +0 -355
|
@@ -115,7 +115,11 @@ type RuntimeApiContext = {
|
|
|
115
115
|
runId?: string | null;
|
|
116
116
|
userEmail?: string | null;
|
|
117
117
|
integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
|
|
118
|
-
dbSessionStrategy?:
|
|
118
|
+
dbSessionStrategy?:
|
|
119
|
+
| 'preloaded'
|
|
120
|
+
| 'sandbox_public_key'
|
|
121
|
+
| 'gateway_only'
|
|
122
|
+
| null;
|
|
119
123
|
preloadedDbSessions?: PreloadedRuntimeDbSession[] | null;
|
|
120
124
|
vercelProtectionBypassToken?: string | null;
|
|
121
125
|
runtimeTestFaultHeader?: string | null;
|
|
@@ -443,9 +447,33 @@ type RuntimeApiActionRequest =
|
|
|
443
447
|
action: 'repair_runtime_storage_grants';
|
|
444
448
|
playName: string;
|
|
445
449
|
runId?: string | null;
|
|
450
|
+
}
|
|
451
|
+
| {
|
|
452
|
+
action: 'runtime_sheet_start';
|
|
453
|
+
input: Parameters<typeof startRuntimeSheetDataset>[1];
|
|
454
|
+
}
|
|
455
|
+
| {
|
|
456
|
+
action: 'runtime_sheet_complete_map_rows';
|
|
457
|
+
input: Parameters<typeof completeRuntimeMapRows>[1];
|
|
458
|
+
}
|
|
459
|
+
| {
|
|
460
|
+
action: 'runtime_sheet_read_rows';
|
|
461
|
+
input: Parameters<typeof readRuntimeSheetDatasetRows>[1];
|
|
462
|
+
}
|
|
463
|
+
| {
|
|
464
|
+
action: 'runtime_sheet_read_row_keys';
|
|
465
|
+
input: Parameters<typeof readRuntimeSheetDatasetRowKeys>[1];
|
|
466
|
+
}
|
|
467
|
+
| {
|
|
468
|
+
action: 'runtime_sheet_release_attempt';
|
|
469
|
+
input: Parameters<typeof releaseRuntimeSheetAttempt>[1];
|
|
470
|
+
}
|
|
471
|
+
| {
|
|
472
|
+
action: 'runtime_receipts_release_attempt';
|
|
473
|
+
input: Parameters<typeof releaseRuntimeWorkReceipts>[1];
|
|
446
474
|
};
|
|
447
475
|
|
|
448
|
-
type RuntimeApiRowRecord = MapRowOutcome & {
|
|
476
|
+
export type RuntimeApiRowRecord = MapRowOutcome & {
|
|
449
477
|
inputIndex?: number | null;
|
|
450
478
|
};
|
|
451
479
|
|
|
@@ -614,8 +642,8 @@ function runtimeApiMaxAttempts(
|
|
|
614
642
|
return runtimeApiRetryDelaysForAction(action).length + 1;
|
|
615
643
|
}
|
|
616
644
|
|
|
617
|
-
function isTransientRuntimeApiErrorMessage(message: string): boolean {
|
|
618
|
-
return /timeout exceeded when trying to connect|timed out|fetch failed|ECONNRESET|ECONNREFUSED|UND_ERR_CONNECT_TIMEOUT|requested endpoint could not be found, or you don't have access/i.test(
|
|
645
|
+
export function isTransientRuntimeApiErrorMessage(message: string): boolean {
|
|
646
|
+
return /timeout exceeded when trying to connect|timed out|fetch failed|ECONNRESET|ECONNREFUSED|UND_ERR_CONNECT_TIMEOUT|requested endpoint could not be found, or you don't have access|RUNTIME_SCHEDULER_SATURATED|Runtime scheduler DB pool is saturated|Runtime scheduler DB circuit breaker is open/i.test(
|
|
619
647
|
message,
|
|
620
648
|
);
|
|
621
649
|
}
|
|
@@ -726,6 +754,75 @@ async function isRuntimeSheetSchemaReady(
|
|
|
726
754
|
}
|
|
727
755
|
}
|
|
728
756
|
|
|
757
|
+
/**
|
|
758
|
+
* Report exactly what the scoped runtime role can see for the sheet table, from
|
|
759
|
+
* the same connection the readiness probe uses. When ensure_sheet reports the
|
|
760
|
+
* table as provisioned but the runner probe still returns not-ready, the cause
|
|
761
|
+
* is almost always that the runtime role lacks a privilege on the freshly
|
|
762
|
+
* created table (so information_schema.columns hides the attempt columns) or a
|
|
763
|
+
* schema/table-name mismatch. This turns the previously blind "still not ready"
|
|
764
|
+
* error into an actionable one that names the concrete missing piece.
|
|
765
|
+
*/
|
|
766
|
+
async function readRuntimeSheetSchemaDiagnostic(
|
|
767
|
+
session: RuntimePostgresSession,
|
|
768
|
+
): Promise<Record<string, unknown>> {
|
|
769
|
+
const query = async (
|
|
770
|
+
client: RuntimeQueryClient,
|
|
771
|
+
): Promise<Record<string, unknown>> => {
|
|
772
|
+
const visibleColumns = await client.query<{ column_name: string }>(
|
|
773
|
+
`
|
|
774
|
+
SELECT column_name
|
|
775
|
+
FROM information_schema.columns
|
|
776
|
+
WHERE table_schema = $1
|
|
777
|
+
AND table_name = $2
|
|
778
|
+
ORDER BY ordinal_position
|
|
779
|
+
`,
|
|
780
|
+
[session.postgres.schema, session.postgres.sheetTable],
|
|
781
|
+
);
|
|
782
|
+
const privileges = await client.query<Record<string, unknown>>(
|
|
783
|
+
`
|
|
784
|
+
SELECT
|
|
785
|
+
current_user AS probe_role,
|
|
786
|
+
to_regclass($3) IS NOT NULL AS regclass_visible,
|
|
787
|
+
has_table_privilege(current_user, $3, 'SELECT') AS can_select,
|
|
788
|
+
has_table_privilege(current_user, $3, 'INSERT') AS can_insert,
|
|
789
|
+
has_table_privilege(current_user, $3, 'UPDATE') AS can_update
|
|
790
|
+
`,
|
|
791
|
+
[
|
|
792
|
+
session.postgres.schema,
|
|
793
|
+
session.postgres.sheetTable,
|
|
794
|
+
`${quoteIdentifier(session.postgres.schema)}.${quoteIdentifier(session.postgres.sheetTable)}`,
|
|
795
|
+
],
|
|
796
|
+
);
|
|
797
|
+
const visible = new Set(visibleColumns.rows.map((row) => row.column_name));
|
|
798
|
+
return {
|
|
799
|
+
schema: session.postgres.schema,
|
|
800
|
+
table: session.postgres.sheetTable,
|
|
801
|
+
probeRole: privileges.rows[0]?.probe_role ?? null,
|
|
802
|
+
regclassVisible: privileges.rows[0]?.regclass_visible ?? null,
|
|
803
|
+
canSelect: privileges.rows[0]?.can_select ?? null,
|
|
804
|
+
canInsert: privileges.rows[0]?.can_insert ?? null,
|
|
805
|
+
canUpdate: privileges.rows[0]?.can_update ?? null,
|
|
806
|
+
visibleColumnCount: visible.size,
|
|
807
|
+
missingAttemptColumns: RUNTIME_SHEET_ATTEMPT_COLUMNS.filter(
|
|
808
|
+
(column) => !visible.has(column),
|
|
809
|
+
),
|
|
810
|
+
};
|
|
811
|
+
};
|
|
812
|
+
try {
|
|
813
|
+
if (isRuntimeOneShotQueryFactoryRegistered()) {
|
|
814
|
+
return await withRuntimeOneShotPostgres(session, query);
|
|
815
|
+
}
|
|
816
|
+
return await withRuntimePostgres(session, query);
|
|
817
|
+
} catch (error) {
|
|
818
|
+
return {
|
|
819
|
+
schema: session.postgres.schema,
|
|
820
|
+
table: session.postgres.sheetTable,
|
|
821
|
+
diagnosticError: error instanceof Error ? error.message : String(error),
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
729
826
|
async function waitForRuntimeSheetSchemaReadyAfterEnsure(
|
|
730
827
|
session: RuntimePostgresSession,
|
|
731
828
|
input: {
|
|
@@ -839,7 +936,7 @@ async function ensureRuntimeSheetForPreloadedSession(
|
|
|
839
936
|
phase: 'ensure_sheet_for_preloaded_session',
|
|
840
937
|
ms: Date.now() - ensureStartedAt,
|
|
841
938
|
});
|
|
842
|
-
|
|
939
|
+
let readyAfterEnsure = await waitForRuntimeSheetSchemaReadyAfterEnsure(
|
|
843
940
|
input.session,
|
|
844
941
|
{
|
|
845
942
|
sheetContract: input.sheetContract,
|
|
@@ -847,8 +944,39 @@ async function ensureRuntimeSheetForPreloadedSession(
|
|
|
847
944
|
},
|
|
848
945
|
);
|
|
849
946
|
if (!readyAfterEnsure) {
|
|
947
|
+
// ensure_sheet provisions the sheet as the tenant admin role, but the
|
|
948
|
+
// preloaded session probes as the scoped runtime role. When the tenant
|
|
949
|
+
// role contract was only partially applied (e.g. role-membership grants
|
|
950
|
+
// skipped on a data plane where the owner cannot administer roles), the
|
|
951
|
+
// runtime role never inherits access to the freshly created table, so its
|
|
952
|
+
// information_schema view hides the attempt columns and the probe stays
|
|
953
|
+
// not-ready even though the table exists. Re-apply the storage grants
|
|
954
|
+
// through the same self-heal the pre-ensure probe uses, then re-check
|
|
955
|
+
// once. This is a grant repair, not a blind retry: if the runtime role
|
|
956
|
+
// still cannot see the sheet, we fail loudly below with the concrete
|
|
957
|
+
// runtime-role view.
|
|
958
|
+
const repairStartedAt = Date.now();
|
|
959
|
+
await repairRuntimeStorageGrants(context, {
|
|
960
|
+
playName: context.playName,
|
|
961
|
+
});
|
|
962
|
+
input.timings?.push({
|
|
963
|
+
phase: 'repair_storage_grants_after_preloaded_session_ensure',
|
|
964
|
+
ms: Date.now() - repairStartedAt,
|
|
965
|
+
retried: true,
|
|
966
|
+
});
|
|
967
|
+
readyAfterEnsure = await waitForRuntimeSheetSchemaReadyAfterEnsure(
|
|
968
|
+
input.session,
|
|
969
|
+
{
|
|
970
|
+
sheetContract: input.sheetContract,
|
|
971
|
+
timings: input.timings,
|
|
972
|
+
},
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
if (!readyAfterEnsure) {
|
|
976
|
+
const diagnostic = await readRuntimeSheetSchemaDiagnostic(input.session);
|
|
850
977
|
throw new Error(
|
|
851
|
-
`Runtime sheet schema for ctx.dataset("${input.tableNamespace}") is still not ready after ensure_sheet
|
|
978
|
+
`Runtime sheet schema for ctx.dataset("${input.tableNamespace}") is still not ready after ensure_sheet. ` +
|
|
979
|
+
`Runtime-role view: ${JSON.stringify(diagnostic)}`,
|
|
852
980
|
);
|
|
853
981
|
}
|
|
854
982
|
})();
|
|
@@ -4158,6 +4286,36 @@ export async function markRuntimeWorkReceiptsQueued(
|
|
|
4158
4286
|
);
|
|
4159
4287
|
}
|
|
4160
4288
|
|
|
4289
|
+
/**
|
|
4290
|
+
* Replay-idempotency fence for a receipt whose active complete/fail UPDATE
|
|
4291
|
+
* matched 0 rows. This fires when the durable write committed but the HTTP
|
|
4292
|
+
* response was lost and the SAME caller retries: the row is already terminal,
|
|
4293
|
+
* so the active-owner predicate (which requires status IN (queued, running))
|
|
4294
|
+
* can never match again. We surface the already-terminal row as success ONLY
|
|
4295
|
+
* when its run identity matches the retrying caller, so the retry does not
|
|
4296
|
+
* spuriously fail a receipt whose provider spend and durable write both
|
|
4297
|
+
* succeeded.
|
|
4298
|
+
*
|
|
4299
|
+
* The predicate mirrors the run-identity half of {@link
|
|
4300
|
+
* workReceiptActiveOwnerPredicateSql}: `COALESCE(lease_owner_run_id, run_id)`
|
|
4301
|
+
* must equal the caller's run id. complete/fail null the lease-owner and
|
|
4302
|
+
* attempt columns as part of the terminal write, so run id is the only durable
|
|
4303
|
+
* ownership marker left on a terminal row — a different run (the stale-owner
|
|
4304
|
+
* rejection case) never matches and still resolves to null exactly as before.
|
|
4305
|
+
* This is replay-idempotency only; it never lets a foreign run read or claim a
|
|
4306
|
+
* receipt it did not complete.
|
|
4307
|
+
*/
|
|
4308
|
+
function workReceiptTerminalReplayPredicateSql(input: {
|
|
4309
|
+
receiptTable: string;
|
|
4310
|
+
terminalStatusSql: string;
|
|
4311
|
+
ownerRunIdSql: string;
|
|
4312
|
+
}): string {
|
|
4313
|
+
const table = input.receiptTable;
|
|
4314
|
+
return `${table}.status = ${input.terminalStatusSql}::smallint
|
|
4315
|
+
/* replay-idempotency: same-run retry after a lost response; run identity is the only durable owner marker on a terminal row */
|
|
4316
|
+
AND COALESCE(${table}.lease_owner_run_id, ${table}.run_id) = ${input.ownerRunIdSql}`;
|
|
4317
|
+
}
|
|
4318
|
+
|
|
4161
4319
|
export async function completeRuntimeWorkReceipt(
|
|
4162
4320
|
context: RuntimeApiContext,
|
|
4163
4321
|
input: {
|
|
@@ -4240,8 +4398,31 @@ export async function completeRuntimeWorkReceipt(
|
|
|
4240
4398
|
lease_owner_attempt,
|
|
4241
4399
|
lease_expires_at,
|
|
4242
4400
|
updated_at
|
|
4401
|
+
),
|
|
4402
|
+
replay AS (
|
|
4403
|
+
SELECT convert_from(k, 'UTF8') AS k,
|
|
4404
|
+
status,
|
|
4405
|
+
output,
|
|
4406
|
+
error,
|
|
4407
|
+
failure_kind,
|
|
4408
|
+
run_id,
|
|
4409
|
+
lease_id,
|
|
4410
|
+
lease_owner_run_id,
|
|
4411
|
+
lease_owner_attempt,
|
|
4412
|
+
lease_expires_at,
|
|
4413
|
+
updated_at
|
|
4414
|
+
FROM ${workReceiptTable(session)}
|
|
4415
|
+
WHERE k = decode($1, 'hex')
|
|
4416
|
+
AND NOT EXISTS (SELECT 1 FROM completed)
|
|
4417
|
+
AND ${workReceiptTerminalReplayPredicateSql({
|
|
4418
|
+
receiptTable: workReceiptTable(session),
|
|
4419
|
+
terminalStatusSql: '$2',
|
|
4420
|
+
ownerRunIdSql: '$4',
|
|
4421
|
+
})}
|
|
4243
4422
|
)
|
|
4244
4423
|
SELECT k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at FROM completed
|
|
4424
|
+
UNION ALL
|
|
4425
|
+
SELECT k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at FROM replay
|
|
4245
4426
|
`,
|
|
4246
4427
|
[
|
|
4247
4428
|
workReceiptKeyHex(input.key),
|
|
@@ -4378,21 +4559,49 @@ export async function completeRuntimeWorkReceipts(
|
|
|
4378
4559
|
target.updated_at,
|
|
4379
4560
|
inputs.ord
|
|
4380
4561
|
),
|
|
4562
|
+
replay AS (
|
|
4563
|
+
SELECT target.k,
|
|
4564
|
+
target.status,
|
|
4565
|
+
target.output,
|
|
4566
|
+
target.error,
|
|
4567
|
+
target.failure_kind,
|
|
4568
|
+
target.run_id,
|
|
4569
|
+
target.lease_id,
|
|
4570
|
+
target.lease_owner_run_id,
|
|
4571
|
+
target.lease_owner_attempt,
|
|
4572
|
+
target.lease_expires_at,
|
|
4573
|
+
target.updated_at,
|
|
4574
|
+
inputs.ord
|
|
4575
|
+
FROM inputs
|
|
4576
|
+
JOIN ${workReceiptTable(session)} AS target
|
|
4577
|
+
ON target.k = decode(inputs.key_hex, 'hex')
|
|
4578
|
+
WHERE NOT EXISTS (SELECT 1 FROM completed WHERE completed.ord = inputs.ord)
|
|
4579
|
+
AND ${workReceiptTerminalReplayPredicateSql({
|
|
4580
|
+
receiptTable: 'target',
|
|
4581
|
+
terminalStatusSql: '$6',
|
|
4582
|
+
ownerRunIdSql: 'inputs.run_id',
|
|
4583
|
+
})}
|
|
4584
|
+
),
|
|
4585
|
+
resolved AS (
|
|
4586
|
+
SELECT ord, k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at FROM completed
|
|
4587
|
+
UNION ALL
|
|
4588
|
+
SELECT ord, k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at FROM replay
|
|
4589
|
+
),
|
|
4381
4590
|
returned AS (
|
|
4382
|
-
SELECT
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4591
|
+
SELECT resolved.k,
|
|
4592
|
+
resolved.status,
|
|
4593
|
+
resolved.output,
|
|
4594
|
+
resolved.error,
|
|
4595
|
+
resolved.failure_kind,
|
|
4596
|
+
resolved.run_id,
|
|
4597
|
+
resolved.lease_id,
|
|
4598
|
+
resolved.lease_owner_run_id,
|
|
4599
|
+
resolved.lease_owner_attempt,
|
|
4600
|
+
resolved.lease_expires_at,
|
|
4601
|
+
resolved.updated_at,
|
|
4393
4602
|
inputs.ord
|
|
4394
4603
|
FROM inputs
|
|
4395
|
-
LEFT JOIN
|
|
4604
|
+
LEFT JOIN resolved ON resolved.ord = inputs.ord
|
|
4396
4605
|
)
|
|
4397
4606
|
SELECT convert_from(returned.k, 'UTF8') AS k,
|
|
4398
4607
|
returned.status,
|
|
@@ -4484,8 +4693,31 @@ export async function failRuntimeWorkReceipt(
|
|
|
4484
4693
|
lease_owner_attempt,
|
|
4485
4694
|
lease_expires_at,
|
|
4486
4695
|
updated_at
|
|
4696
|
+
),
|
|
4697
|
+
replay AS (
|
|
4698
|
+
SELECT convert_from(k, 'UTF8') AS k,
|
|
4699
|
+
status,
|
|
4700
|
+
output,
|
|
4701
|
+
error,
|
|
4702
|
+
failure_kind,
|
|
4703
|
+
run_id,
|
|
4704
|
+
lease_id,
|
|
4705
|
+
lease_owner_run_id,
|
|
4706
|
+
lease_owner_attempt,
|
|
4707
|
+
lease_expires_at,
|
|
4708
|
+
updated_at
|
|
4709
|
+
FROM ${workReceiptTable(session)}
|
|
4710
|
+
WHERE k = decode($1, 'hex')
|
|
4711
|
+
AND NOT EXISTS (SELECT 1 FROM failed)
|
|
4712
|
+
AND ${workReceiptTerminalReplayPredicateSql({
|
|
4713
|
+
receiptTable: workReceiptTable(session),
|
|
4714
|
+
terminalStatusSql: '$2',
|
|
4715
|
+
ownerRunIdSql: '$4',
|
|
4716
|
+
})}
|
|
4487
4717
|
)
|
|
4488
4718
|
SELECT k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at FROM failed
|
|
4719
|
+
UNION ALL
|
|
4720
|
+
SELECT k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at FROM replay
|
|
4489
4721
|
`,
|
|
4490
4722
|
[
|
|
4491
4723
|
workReceiptKeyHex(input.key),
|
|
@@ -4649,21 +4881,49 @@ export async function failRuntimeWorkReceipts(
|
|
|
4649
4881
|
target.updated_at,
|
|
4650
4882
|
inputs.ord
|
|
4651
4883
|
),
|
|
4884
|
+
replay AS (
|
|
4885
|
+
SELECT target.k,
|
|
4886
|
+
target.status,
|
|
4887
|
+
target.output,
|
|
4888
|
+
target.error,
|
|
4889
|
+
target.failure_kind,
|
|
4890
|
+
target.run_id,
|
|
4891
|
+
target.lease_id,
|
|
4892
|
+
target.lease_owner_run_id,
|
|
4893
|
+
target.lease_owner_attempt,
|
|
4894
|
+
target.lease_expires_at,
|
|
4895
|
+
target.updated_at,
|
|
4896
|
+
inputs.ord
|
|
4897
|
+
FROM inputs
|
|
4898
|
+
JOIN ${workReceiptTable(session)} AS target
|
|
4899
|
+
ON target.k = decode(inputs.key_hex, 'hex')
|
|
4900
|
+
WHERE NOT EXISTS (SELECT 1 FROM failed WHERE failed.ord = inputs.ord)
|
|
4901
|
+
AND ${workReceiptTerminalReplayPredicateSql({
|
|
4902
|
+
receiptTable: 'target',
|
|
4903
|
+
terminalStatusSql: '$7',
|
|
4904
|
+
ownerRunIdSql: 'inputs.run_id',
|
|
4905
|
+
})}
|
|
4906
|
+
),
|
|
4907
|
+
resolved AS (
|
|
4908
|
+
SELECT ord, k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at FROM failed
|
|
4909
|
+
UNION ALL
|
|
4910
|
+
SELECT ord, k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at FROM replay
|
|
4911
|
+
),
|
|
4652
4912
|
returned AS (
|
|
4653
|
-
SELECT
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4913
|
+
SELECT resolved.k,
|
|
4914
|
+
resolved.status,
|
|
4915
|
+
resolved.output,
|
|
4916
|
+
resolved.error,
|
|
4917
|
+
resolved.failure_kind,
|
|
4918
|
+
resolved.run_id,
|
|
4919
|
+
resolved.lease_id,
|
|
4920
|
+
resolved.lease_owner_run_id,
|
|
4921
|
+
resolved.lease_owner_attempt,
|
|
4922
|
+
resolved.lease_expires_at,
|
|
4923
|
+
resolved.updated_at,
|
|
4664
4924
|
inputs.ord
|
|
4665
4925
|
FROM inputs
|
|
4666
|
-
LEFT JOIN
|
|
4926
|
+
LEFT JOIN resolved ON resolved.ord = inputs.ord
|
|
4667
4927
|
)
|
|
4668
4928
|
SELECT convert_from(returned.k, 'UTF8') AS k,
|
|
4669
4929
|
returned.status,
|
|
@@ -4892,6 +5152,12 @@ export async function startRuntimeSheetDataset(
|
|
|
4892
5152
|
force?: boolean;
|
|
4893
5153
|
},
|
|
4894
5154
|
): Promise<PrepareRuntimeSheetResult> {
|
|
5155
|
+
if (context.dbSessionStrategy === 'gateway_only') {
|
|
5156
|
+
return await postRuntimeApi<PrepareRuntimeSheetResult>(context, {
|
|
5157
|
+
action: 'runtime_sheet_start',
|
|
5158
|
+
input,
|
|
5159
|
+
});
|
|
5160
|
+
}
|
|
4895
5161
|
const totalStartedAt = Date.now();
|
|
4896
5162
|
const timings: RuntimeSheetTiming[] = [];
|
|
4897
5163
|
const playName = context.playName?.trim() || input.playName;
|
|
@@ -5283,6 +5549,12 @@ export async function releaseRuntimeSheetAttempt(
|
|
|
5283
5549
|
attemptSeq?: number | null;
|
|
5284
5550
|
},
|
|
5285
5551
|
): Promise<RuntimeSheetAttemptReleaseResult> {
|
|
5552
|
+
if (context.dbSessionStrategy === 'gateway_only') {
|
|
5553
|
+
return await postRuntimeApi<RuntimeSheetAttemptReleaseResult>(context, {
|
|
5554
|
+
action: 'runtime_sheet_release_attempt',
|
|
5555
|
+
input,
|
|
5556
|
+
});
|
|
5557
|
+
}
|
|
5286
5558
|
const playName = context.playName?.trim() || input.playName;
|
|
5287
5559
|
if (!playName) {
|
|
5288
5560
|
throw new Error('Runtime sheet attempt release requires a playName.');
|
|
@@ -5371,6 +5643,12 @@ export async function releaseRuntimeWorkReceipts(
|
|
|
5371
5643
|
runAttempt?: number | null;
|
|
5372
5644
|
},
|
|
5373
5645
|
): Promise<RuntimeWorkReceiptReleaseResult> {
|
|
5646
|
+
if (context.dbSessionStrategy === 'gateway_only') {
|
|
5647
|
+
return await postRuntimeApi<RuntimeWorkReceiptReleaseResult>(context, {
|
|
5648
|
+
action: 'runtime_receipts_release_attempt',
|
|
5649
|
+
input,
|
|
5650
|
+
});
|
|
5651
|
+
}
|
|
5374
5652
|
const runId = input.runId.trim();
|
|
5375
5653
|
if (!runId) {
|
|
5376
5654
|
return { released: 0, releasedKeys: [] };
|
|
@@ -5423,7 +5701,7 @@ type CompleteRuntimeMapRowChunksInput = {
|
|
|
5423
5701
|
outputFields: string[];
|
|
5424
5702
|
};
|
|
5425
5703
|
|
|
5426
|
-
type RuntimeMapRowsWriteResult = {
|
|
5704
|
+
export type RuntimeMapRowsWriteResult = {
|
|
5427
5705
|
updated: number;
|
|
5428
5706
|
fencedKeys: string[];
|
|
5429
5707
|
};
|
|
@@ -6344,6 +6622,12 @@ export async function completeRuntimeMapRows(
|
|
|
6344
6622
|
forceFailedRows?: boolean;
|
|
6345
6623
|
},
|
|
6346
6624
|
): Promise<RuntimeMapRowsWriteResult> {
|
|
6625
|
+
if (context.dbSessionStrategy === 'gateway_only') {
|
|
6626
|
+
return await postRuntimeApi<RuntimeMapRowsWriteResult>(context, {
|
|
6627
|
+
action: 'runtime_sheet_complete_map_rows',
|
|
6628
|
+
input,
|
|
6629
|
+
});
|
|
6630
|
+
}
|
|
6347
6631
|
if (input.rows.length === 0) {
|
|
6348
6632
|
return { updated: 0, fencedKeys: [] };
|
|
6349
6633
|
}
|
|
@@ -6498,6 +6782,12 @@ export async function readRuntimeSheetDatasetRows(
|
|
|
6498
6782
|
offset: number;
|
|
6499
6783
|
},
|
|
6500
6784
|
): Promise<{ rows: Record<string, unknown>[]; limit: number; offset: number }> {
|
|
6785
|
+
if (context.dbSessionStrategy === 'gateway_only') {
|
|
6786
|
+
return await postRuntimeApi(context, {
|
|
6787
|
+
action: 'runtime_sheet_read_rows',
|
|
6788
|
+
input,
|
|
6789
|
+
});
|
|
6790
|
+
}
|
|
6501
6791
|
const limit = Math.max(
|
|
6502
6792
|
1,
|
|
6503
6793
|
Math.min(DIRECT_POSTGRES_BATCH_SIZE, Math.floor(input.limit)),
|
|
@@ -6540,6 +6830,12 @@ export async function readRuntimeSheetDatasetRowKeys(
|
|
|
6540
6830
|
rowMode?: 'output' | 'all' | 'terminalAnyRun';
|
|
6541
6831
|
},
|
|
6542
6832
|
): Promise<{ keys: string[] }> {
|
|
6833
|
+
if (context.dbSessionStrategy === 'gateway_only') {
|
|
6834
|
+
return await postRuntimeApi(context, {
|
|
6835
|
+
action: 'runtime_sheet_read_row_keys',
|
|
6836
|
+
input,
|
|
6837
|
+
});
|
|
6838
|
+
}
|
|
6543
6839
|
const keys = [
|
|
6544
6840
|
...new Set(
|
|
6545
6841
|
input.keys.map((key) => key.trim()).filter((key) => key.length > 0),
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-scheduler topology predicate shared by the app-side lane helpers
|
|
3
|
+
* (`src/lib/plays/scheduler-backends/absurd-shared.ts`) and the runner
|
|
4
|
+
* backends (`runner-backends/backends/daytona-lifecycle.ts`).
|
|
5
|
+
*
|
|
6
|
+
* ONE distinction drives several guards: a run whose scheduler schema is the
|
|
7
|
+
* prod default rides the production topology (base queue names, customer code,
|
|
8
|
+
* production egress policy), while any other schema is an ISOLATED topology —
|
|
9
|
+
* a per-PR preview fleet or a per-worktree dev fleet running synthetic
|
|
10
|
+
* internal test plays. Keeping the predicate here (dependency-free) lets
|
|
11
|
+
* shared_libs code use it without importing the app-side scheduler stack.
|
|
12
|
+
*/
|
|
13
|
+
export const PROD_DEFAULT_RUNTIME_SCHEDULER_SCHEMA = 'runtime_scheduler';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* True when the scheduler schema names an isolated (preview CI / worktree)
|
|
17
|
+
* topology. Null/empty/prod-default => false (production topology). Callers
|
|
18
|
+
* that gate SECURITY policy on this must treat `false` as the fail-closed
|
|
19
|
+
* production posture — an absent schema is production, never "unknown".
|
|
20
|
+
*/
|
|
21
|
+
export function isIsolatedRuntimeSchedulerSchema(
|
|
22
|
+
schedulerSchema: string | null | undefined,
|
|
23
|
+
): boolean {
|
|
24
|
+
const schema = schedulerSchema?.trim();
|
|
25
|
+
return Boolean(schema) && schema !== PROD_DEFAULT_RUNTIME_SCHEDULER_SCHEMA;
|
|
26
|
+
}
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
import type { ExecutionPlan } from './execution-plan';
|
|
19
19
|
import type { PlayRuntimeManifestMap } from '../plays/compiler-manifest';
|
|
20
20
|
import type { PreloadedRuntimeDbSession } from './db-session';
|
|
21
|
+
import type { RuntimeAuthorityDescriptor } from './execution-capabilities';
|
|
21
22
|
import type { PlayRunnerRuntimeTiming } from './protocol';
|
|
22
23
|
import type { RuntimeTestPolicyOverrides } from './test-runtime-seams';
|
|
23
24
|
import type { PlayRunInputPayload } from './play-input';
|
|
@@ -123,9 +124,21 @@ export type PlaySchedulerSubmitInput = {
|
|
|
123
124
|
childPlayManifests?: PlayRuntimeManifestMap | null;
|
|
124
125
|
playCallGovernance?: PlayCallGovernanceSnapshot | null;
|
|
125
126
|
preloadedDbSessions?: PreloadedRuntimeDbSession[] | null;
|
|
127
|
+
/**
|
|
128
|
+
* Secret that decrypts `preloadedDbSessions` postgres URLs. Equals the run's
|
|
129
|
+
* original submit-time executor (authority) token that AES-GCM sealed the
|
|
130
|
+
* sessions. Held separately from `executorToken` because durable schedulers
|
|
131
|
+
* re-mint a per-attempt `executorToken` (adding `run_attempt`/capabilities
|
|
132
|
+
* claims) for API auth; that re-minted string is NOT valid decryption key
|
|
133
|
+
* material for sessions sealed under the original token. The worker/runner use
|
|
134
|
+
* this field for DB-session unwrap and the re-minted `executorToken` for auth.
|
|
135
|
+
*/
|
|
136
|
+
preloadedDbSessionUnwrapKey?: string | null;
|
|
126
137
|
/** Optional immutable Worker module source for local Dynamic Worker loading. */
|
|
127
138
|
dynamicWorkerCode?: string | null;
|
|
128
139
|
executorToken: string;
|
|
140
|
+
/** Immutable facts used by durable schedulers to mint a fresh leg token. */
|
|
141
|
+
runtimeAuthority: RuntimeAuthorityDescriptor;
|
|
129
142
|
/**
|
|
130
143
|
* Public app origin used by Workers to call Deepline runtime API routes.
|
|
131
144
|
* When omitted, legacy schedulers fall back to baseUrl.
|
|
@@ -151,6 +164,14 @@ export type PlaySchedulerSubmitInput = {
|
|
|
151
164
|
coordinatorInternalToken?: string | null;
|
|
152
165
|
/** Runtime deploy generation/version that owns this run, when known. */
|
|
153
166
|
runtimeDeployVersion?: string | null;
|
|
167
|
+
/**
|
|
168
|
+
* Absurd release lane that owns this run for its whole life (children, parks,
|
|
169
|
+
* wakes, retries). Stamped at launch by the absurd scheduler backend and
|
|
170
|
+
* persisted inside launch_json. The worker derives the run's queue from this,
|
|
171
|
+
* never from process-local config. Absent on pre-release launch rows, in which
|
|
172
|
+
* case the worker falls back to the dev-collapse id (today's queue).
|
|
173
|
+
*/
|
|
174
|
+
absurdReleaseId?: string | null;
|
|
154
175
|
/** Request-scoped Vercel Deployment Protection bypass for preview runtime callbacks. */
|
|
155
176
|
vercelProtectionBypassToken?: string | null;
|
|
156
177
|
/** Request-scoped, dev-only runtime fault injection header for black-box durability tests. */
|
|
@@ -4,6 +4,7 @@ const PLAY_DATASET_BRAND = Symbol.for('deepline.play.dataset');
|
|
|
4
4
|
const NODE_INSPECT_CUSTOM = Symbol.for('nodejs.util.inspect.custom');
|
|
5
5
|
const DEFAULT_MATERIALIZE_LIMIT = 10_000;
|
|
6
6
|
export const PLAY_DATASET_EXECUTION_PAGE_ROWS = 1_000;
|
|
7
|
+
export const PLAY_DATASET_EXECUTION_PAGE_BYTES = 64 * 1024 * 1024;
|
|
7
8
|
|
|
8
9
|
export type PlayDatasetKind = 'csv' | 'map';
|
|
9
10
|
|
|
@@ -545,6 +546,8 @@ export type MaterializePlayDatasetInputOptions<T> = {
|
|
|
545
546
|
|
|
546
547
|
export type IteratePlayDatasetInputPagesOptions<T> = {
|
|
547
548
|
pageSize?: number;
|
|
549
|
+
maxPageBytes?: number;
|
|
550
|
+
estimateRowBytes?: (row: T) => number;
|
|
548
551
|
onRow?: (row: T, index: number) => void;
|
|
549
552
|
};
|
|
550
553
|
|
|
@@ -560,21 +563,40 @@ export async function* iteratePlayDatasetInputPages<T>(
|
|
|
560
563
|
options?: IteratePlayDatasetInputPagesOptions<T>,
|
|
561
564
|
): AsyncIterable<{ rows: T[]; offset: number }> {
|
|
562
565
|
const pageSize = normalizeDatasetExecutionPageSize(options?.pageSize);
|
|
566
|
+
const maxPageBytes = Math.max(
|
|
567
|
+
1,
|
|
568
|
+
Math.floor(options?.maxPageBytes ?? PLAY_DATASET_EXECUTION_PAGE_BYTES),
|
|
569
|
+
);
|
|
570
|
+
const estimateRowBytes =
|
|
571
|
+
options?.estimateRowBytes ??
|
|
572
|
+
((row: T) => {
|
|
573
|
+
const serialized = JSON.stringify(row);
|
|
574
|
+
return serialized === undefined ? 0 : serialized.length;
|
|
575
|
+
});
|
|
563
576
|
let page: T[] = [];
|
|
577
|
+
let pageBytes = 0;
|
|
564
578
|
let offset = 0;
|
|
565
579
|
let index = 0;
|
|
566
|
-
const pushRow = (row: T): { rows: T[]; offset: number }
|
|
580
|
+
const pushRow = (row: T): Array<{ rows: T[]; offset: number }> => {
|
|
567
581
|
options?.onRow?.(row, index);
|
|
568
582
|
index += 1;
|
|
583
|
+
const rowBytes = Math.max(0, Math.ceil(estimateRowBytes(row)));
|
|
584
|
+
const ready: Array<{ rows: T[]; offset: number }> = [];
|
|
585
|
+
if (page.length > 0 && pageBytes + rowBytes > maxPageBytes) {
|
|
586
|
+
ready.push({ rows: page, offset });
|
|
587
|
+
offset += page.length;
|
|
588
|
+
page = [];
|
|
589
|
+
pageBytes = 0;
|
|
590
|
+
}
|
|
569
591
|
page.push(row);
|
|
570
|
-
|
|
571
|
-
|
|
592
|
+
pageBytes += rowBytes;
|
|
593
|
+
if (page.length >= pageSize || pageBytes >= maxPageBytes) {
|
|
594
|
+
ready.push({ rows: page, offset });
|
|
595
|
+
offset += page.length;
|
|
596
|
+
page = [];
|
|
597
|
+
pageBytes = 0;
|
|
572
598
|
}
|
|
573
|
-
|
|
574
|
-
const fullOffset = offset;
|
|
575
|
-
page = [];
|
|
576
|
-
offset += fullPage.length;
|
|
577
|
-
return { rows: fullPage, offset: fullOffset };
|
|
599
|
+
return ready;
|
|
578
600
|
};
|
|
579
601
|
|
|
580
602
|
const flush = (): { rows: T[]; offset: number } | null => {
|
|
@@ -582,13 +604,13 @@ export async function* iteratePlayDatasetInputPages<T>(
|
|
|
582
604
|
const finalPage = page;
|
|
583
605
|
const finalOffset = offset;
|
|
584
606
|
page = [];
|
|
607
|
+
pageBytes = 0;
|
|
585
608
|
offset += finalPage.length;
|
|
586
609
|
return { rows: finalPage, offset: finalOffset };
|
|
587
610
|
};
|
|
588
611
|
|
|
589
612
|
const emitRow = async function* (row: T) {
|
|
590
|
-
const ready
|
|
591
|
-
if (ready) yield ready;
|
|
613
|
+
for (const ready of pushRow(row)) yield ready;
|
|
592
614
|
};
|
|
593
615
|
|
|
594
616
|
if (isPlayDataset(input)) {
|