deepline 0.1.237 → 0.1.239
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 +3 -0
- package/dist/bundling-sources/sdk/src/client.ts +72 -8
- package/dist/bundling-sources/sdk/src/http.ts +1 -0
- package/dist/bundling-sources/sdk/src/index.ts +4 -0
- package/dist/bundling-sources/sdk/src/release.ts +6 -4
- package/dist/bundling-sources/sdk/src/types.ts +63 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +16 -9
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +5 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +3 -18
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +14 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +42 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +9 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +5 -5
- package/dist/cli/index.js +387 -58
- package/dist/cli/index.mjs +387 -58
- package/dist/index.d.mts +107 -6
- package/dist/index.d.ts +107 -6
- package/dist/index.js +58 -12
- package/dist/index.mjs +58 -12
- package/package.json +1 -1
|
@@ -365,6 +365,9 @@ function datasetAvailableLog(
|
|
|
365
365
|
dataset: ReturnType<typeof runtimeDatasetIdentity>,
|
|
366
366
|
count: number,
|
|
367
367
|
): string {
|
|
368
|
+
if (count === 0) {
|
|
369
|
+
return `Dataset ${dataset.path} available: 0 persisted rows; export unavailable: empty dataset`;
|
|
370
|
+
}
|
|
368
371
|
return `Dataset ${dataset.path} available: ${count} persisted rows; export: deepline runs export ${runId} --dataset ${dataset.path} --out ${dataset.tableNamespace}.csv`;
|
|
369
372
|
}
|
|
370
373
|
|
|
@@ -405,6 +405,7 @@ export type PlaySheetRow = {
|
|
|
405
405
|
/** Runtime-sheet rows and aggregate progress for one dataset/table namespace. */
|
|
406
406
|
export type PlaySheetRowsResult = {
|
|
407
407
|
rows: PlaySheetRow[];
|
|
408
|
+
scope?: { kind: 'database' } | { kind: 'run'; runId: string };
|
|
408
409
|
summary?: {
|
|
409
410
|
stats?: {
|
|
410
411
|
total?: number;
|
|
@@ -470,6 +471,15 @@ export type RunsNamespace = {
|
|
|
470
471
|
stopAll: (options?: { reason?: string }) => Promise<StopAllPlayRunsResult>;
|
|
471
472
|
};
|
|
472
473
|
|
|
474
|
+
/** Current mutable customer database namespace exposed as `client.db`. */
|
|
475
|
+
export type DbNamespace = {
|
|
476
|
+
/** Run a bounded SQL query against the current customer data plane. */
|
|
477
|
+
query: (input: {
|
|
478
|
+
sql: string;
|
|
479
|
+
maxRows?: number;
|
|
480
|
+
}) => Promise<CustomerDbQueryResult>;
|
|
481
|
+
};
|
|
482
|
+
|
|
473
483
|
/** One credit grant pool reported by the billing subscription status endpoint. */
|
|
474
484
|
export type BillingCreditPool = {
|
|
475
485
|
pool: string;
|
|
@@ -1077,6 +1087,8 @@ export class DeeplineClient {
|
|
|
1077
1087
|
private readonly config: ResolvedConfig;
|
|
1078
1088
|
/** Canonical run lifecycle namespace backed by `/api/v2/runs`. */
|
|
1079
1089
|
readonly runs: RunsNamespace;
|
|
1090
|
+
/** Current mutable customer database namespace backed by `/api/v2/db/query`. */
|
|
1091
|
+
readonly db: DbNamespace;
|
|
1080
1092
|
/** Billing namespace: subscription status/cancel and invoice history. */
|
|
1081
1093
|
readonly billing: BillingNamespace;
|
|
1082
1094
|
|
|
@@ -1101,6 +1113,9 @@ export class DeeplineClient {
|
|
|
1101
1113
|
stop: (runId, options) => this.stopRun(runId, options),
|
|
1102
1114
|
stopAll: (options) => this.stopAllRuns(options),
|
|
1103
1115
|
};
|
|
1116
|
+
this.db = {
|
|
1117
|
+
query: (input) => this.queryDb(input),
|
|
1118
|
+
};
|
|
1104
1119
|
this.billing = {
|
|
1105
1120
|
topUp: (options) => this.topUpBillingBalance(options),
|
|
1106
1121
|
plans: () => this.getBillingPlans(),
|
|
@@ -1460,20 +1475,36 @@ export class DeeplineClient {
|
|
|
1460
1475
|
return this.executeTool<TData, TMeta>(toolId, input, options);
|
|
1461
1476
|
}
|
|
1462
1477
|
|
|
1478
|
+
private async queryDb(input: {
|
|
1479
|
+
sql: string;
|
|
1480
|
+
maxRows?: number;
|
|
1481
|
+
}): Promise<CustomerDbQueryResult> {
|
|
1482
|
+
const result = await this.http.post<CustomerDbQueryResult>(
|
|
1483
|
+
'/api/v2/db/query',
|
|
1484
|
+
{
|
|
1485
|
+
sql: input.sql,
|
|
1486
|
+
...(input.maxRows ? { max_rows: input.maxRows } : {}),
|
|
1487
|
+
},
|
|
1488
|
+
);
|
|
1489
|
+
return {
|
|
1490
|
+
...result,
|
|
1491
|
+
scope: { kind: 'database', mutability: 'current' },
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1463
1495
|
/**
|
|
1464
|
-
* Run a bounded SQL query against the customer
|
|
1496
|
+
* Run a bounded SQL query against the current mutable customer database.
|
|
1497
|
+
*
|
|
1498
|
+
* This query is not scoped to one play run. Use `client.runs` export actions
|
|
1499
|
+
* when the caller needs the rows produced by a specific run.
|
|
1465
1500
|
*
|
|
1466
|
-
* Use
|
|
1467
|
-
* workspace scoping and row limits.
|
|
1501
|
+
* @deprecated Use {@link DeeplineClient.db} `.query(...)`.
|
|
1468
1502
|
*/
|
|
1469
1503
|
async queryCustomerDb(input: {
|
|
1470
1504
|
sql: string;
|
|
1471
1505
|
maxRows?: number;
|
|
1472
1506
|
}): Promise<CustomerDbQueryResult> {
|
|
1473
|
-
return this.
|
|
1474
|
-
sql: input.sql,
|
|
1475
|
-
...(input.maxRows ? { max_rows: input.maxRows } : {}),
|
|
1476
|
-
});
|
|
1507
|
+
return this.db.query(input);
|
|
1477
1508
|
}
|
|
1478
1509
|
|
|
1479
1510
|
/**
|
|
@@ -3000,9 +3031,42 @@ export class DeeplineClient {
|
|
|
3000
3031
|
if (input.rowMode === 'all') {
|
|
3001
3032
|
params.set('rowMode', 'all');
|
|
3002
3033
|
}
|
|
3003
|
-
|
|
3034
|
+
const result = await this.http.get<PlaySheetRowsResult>(
|
|
3004
3035
|
`/api/v2/plays/${encodeURIComponent(input.playName)}/sheet?${params.toString()}`,
|
|
3005
3036
|
);
|
|
3037
|
+
const requestedRunId = input.runId?.trim() || '';
|
|
3038
|
+
if (requestedRunId) {
|
|
3039
|
+
const confirmedRunId =
|
|
3040
|
+
result.scope?.kind === 'run' ? result.scope.runId.trim() : '';
|
|
3041
|
+
const foreignRowRunIds = [
|
|
3042
|
+
...new Set(
|
|
3043
|
+
result.rows
|
|
3044
|
+
.map((row) =>
|
|
3045
|
+
typeof row.runId === 'string' ? row.runId.trim() : '',
|
|
3046
|
+
)
|
|
3047
|
+
.filter((runId) => runId && runId !== requestedRunId),
|
|
3048
|
+
),
|
|
3049
|
+
];
|
|
3050
|
+
if (
|
|
3051
|
+
result.scope?.kind !== 'run' ||
|
|
3052
|
+
confirmedRunId !== requestedRunId ||
|
|
3053
|
+
foreignRowRunIds.length > 0
|
|
3054
|
+
) {
|
|
3055
|
+
throw new DeeplineError(
|
|
3056
|
+
`Run-scoped dataset export was not confirmed for ${requestedRunId}; no rows were returned to the caller. Update Deepline, then retry the same export command. Do not rerun the play.`,
|
|
3057
|
+
undefined,
|
|
3058
|
+
'RUN_EXPORT_SCOPE_MISMATCH',
|
|
3059
|
+
{
|
|
3060
|
+
requestedRunId,
|
|
3061
|
+
confirmedScope: result.scope ?? null,
|
|
3062
|
+
foreignRowRunIds,
|
|
3063
|
+
playName: input.playName,
|
|
3064
|
+
tableNamespace: input.tableNamespace,
|
|
3065
|
+
},
|
|
3066
|
+
);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
return result;
|
|
3006
3070
|
}
|
|
3007
3071
|
|
|
3008
3072
|
/**
|
|
@@ -218,6 +218,7 @@ export class HttpClient {
|
|
|
218
218
|
'X-Deepline-CLI-Version': SDK_VERSION,
|
|
219
219
|
'X-Deepline-SDK-Version': SDK_VERSION,
|
|
220
220
|
'X-Deepline-API-Contract': SDK_API_CONTRACT,
|
|
221
|
+
'X-Deepline-Run-Result-Shape': 'canonical',
|
|
221
222
|
...extra,
|
|
222
223
|
};
|
|
223
224
|
const skillsVersion = this.readSkillsVersionHeader();
|
|
@@ -62,6 +62,7 @@ export type {
|
|
|
62
62
|
BillingInvoiceEntry,
|
|
63
63
|
BillingInvoicesResult,
|
|
64
64
|
BillingNamespace,
|
|
65
|
+
DbNamespace,
|
|
65
66
|
BillingPaymentMethodSummary,
|
|
66
67
|
BillingSubscriptionCancelResult,
|
|
67
68
|
BillingSubscriptionStatus,
|
|
@@ -141,6 +142,9 @@ export type {
|
|
|
141
142
|
LiveEventEnvelope,
|
|
142
143
|
PlayLiveEvent,
|
|
143
144
|
PlayRunResult,
|
|
145
|
+
PlayRunActionPackage,
|
|
146
|
+
PlayRunDatasetActions,
|
|
147
|
+
PlayRunPackage,
|
|
144
148
|
PlayRunStart,
|
|
145
149
|
ClearPlayHistoryRequest,
|
|
146
150
|
ClearPlayHistoryResult,
|
|
@@ -106,20 +106,22 @@ export const SDK_RELEASE = {
|
|
|
106
106
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
107
107
|
// fields shipped in 0.1.153.
|
|
108
108
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
109
|
+
// 0.1.230 propagates enrich extractor failures as failed rows rather than
|
|
110
|
+
// silently materializing them as unmatched results.
|
|
109
111
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
110
112
|
// automatically without blocking their current command.
|
|
111
|
-
version: '0.1.
|
|
113
|
+
version: '0.1.239',
|
|
112
114
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
113
115
|
supportPolicy: {
|
|
114
|
-
latest: '0.1.
|
|
116
|
+
latest: '0.1.239',
|
|
115
117
|
minimumSupported: '0.1.53',
|
|
116
118
|
deprecatedBelow: '0.1.219',
|
|
117
119
|
commandMinimumSupported: [
|
|
118
120
|
{
|
|
119
121
|
command: 'enrich',
|
|
120
|
-
minimumSupported: '0.1.
|
|
122
|
+
minimumSupported: '0.1.238',
|
|
121
123
|
reason:
|
|
122
|
-
'Older SDK CLI enrich generated stale play source; SDK CLI enrich 0.1.153 generated removed StepOptions fields in play source; SDK CLI enrich before 0.1.175 could silently succeed on the empty-waterfall bug when a requested waterfall returned no values for selected rows.',
|
|
124
|
+
'Older SDK CLI enrich generated stale play source; SDK CLI enrich 0.1.153 generated removed StepOptions fields in play source; SDK CLI enrich before 0.1.175 could silently succeed on the empty-waterfall bug when a requested waterfall returned no values for selected rows; SDK CLI enrich before 0.1.230 could convert extractor failures into successful unmatched results; SDK CLI enrich before 0.1.238 could drop a nested provider email match during generic pick("email") extraction and materialize it as NO_MATCH.',
|
|
123
125
|
},
|
|
124
126
|
{
|
|
125
127
|
command: 'plays',
|
|
@@ -72,12 +72,14 @@ export interface CustomerDbColumn {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* Result returned by {@link DeeplineClient.
|
|
75
|
+
* Result returned by {@link DeeplineClient.db.query}.
|
|
76
76
|
*
|
|
77
77
|
* Rows are intentionally untyped because the schema depends on the caller's SQL
|
|
78
78
|
* query and selected customer tables.
|
|
79
79
|
*/
|
|
80
80
|
export interface CustomerDbQueryResult {
|
|
81
|
+
/** This query reads the current mutable customer database, not one run snapshot. */
|
|
82
|
+
scope?: { kind: 'database'; mutability: 'current' };
|
|
81
83
|
/** Database command executed by the query endpoint. */
|
|
82
84
|
command: string;
|
|
83
85
|
/** Total affected row count when reported by the database. */
|
|
@@ -443,6 +445,19 @@ export type PlayRunActionPackage =
|
|
|
443
445
|
| {
|
|
444
446
|
kind: 'deepline_run_inspect';
|
|
445
447
|
runId: string;
|
|
448
|
+
command?: string;
|
|
449
|
+
api: { method: 'GET'; path: string };
|
|
450
|
+
}
|
|
451
|
+
| {
|
|
452
|
+
kind: 'deepline_run_full';
|
|
453
|
+
runId: string;
|
|
454
|
+
command?: string;
|
|
455
|
+
api: { method: 'GET'; path: string };
|
|
456
|
+
}
|
|
457
|
+
| {
|
|
458
|
+
kind: 'deepline_run_billing';
|
|
459
|
+
runId: string;
|
|
460
|
+
command?: string;
|
|
446
461
|
api: { method: 'GET'; path: string };
|
|
447
462
|
}
|
|
448
463
|
| {
|
|
@@ -453,6 +468,16 @@ export type PlayRunActionPackage =
|
|
|
453
468
|
sqlQualifiedTableName?: string;
|
|
454
469
|
sql: string;
|
|
455
470
|
maxRows: number;
|
|
471
|
+
command?: string;
|
|
472
|
+
scope?:
|
|
473
|
+
| { kind: 'database'; mutability: 'current' }
|
|
474
|
+
| { kind: 'run'; runId: string };
|
|
475
|
+
note?: string;
|
|
476
|
+
deprecated?: {
|
|
477
|
+
replacement: 'queryCurrentTable';
|
|
478
|
+
removeAfter: string;
|
|
479
|
+
reason: string;
|
|
480
|
+
};
|
|
456
481
|
api: {
|
|
457
482
|
method: 'POST';
|
|
458
483
|
path: '/api/v2/db/query';
|
|
@@ -463,6 +488,14 @@ export type PlayRunActionPackage =
|
|
|
463
488
|
runId: string;
|
|
464
489
|
datasetPath: string;
|
|
465
490
|
format: 'csv';
|
|
491
|
+
command?: string;
|
|
492
|
+
scope?: { kind: 'run'; runId: string };
|
|
493
|
+
note?: string;
|
|
494
|
+
deprecated?: {
|
|
495
|
+
replacement: 'datasets[].actions.exportCsv';
|
|
496
|
+
removeAfter: string;
|
|
497
|
+
reason: string;
|
|
498
|
+
};
|
|
466
499
|
}
|
|
467
500
|
| {
|
|
468
501
|
kind: 'deepline_run_logs';
|
|
@@ -473,6 +506,26 @@ export type PlayRunActionPackage =
|
|
|
473
506
|
api: { method: 'GET'; path: string };
|
|
474
507
|
};
|
|
475
508
|
|
|
509
|
+
type PlayRunDbQueryAction = Extract<
|
|
510
|
+
PlayRunActionPackage,
|
|
511
|
+
{ kind: 'deepline_db_query' }
|
|
512
|
+
>;
|
|
513
|
+
type PlayRunExportAction = Extract<
|
|
514
|
+
PlayRunActionPackage,
|
|
515
|
+
{ kind: 'deepline_run_export' }
|
|
516
|
+
>;
|
|
517
|
+
|
|
518
|
+
export type PlayRunDatasetActions = {
|
|
519
|
+
/** @deprecated Use queryCurrentTable or exportCsv. Removed after 2026-08-18. */
|
|
520
|
+
query?: PlayRunDbQueryAction & { scope?: { kind: 'run'; runId: string } };
|
|
521
|
+
queryCurrentTable?: PlayRunDbQueryAction & {
|
|
522
|
+
scope?: { kind: 'database'; mutability: 'current' };
|
|
523
|
+
};
|
|
524
|
+
exportCsv?: PlayRunExportAction & {
|
|
525
|
+
scope?: { kind: 'run'; runId: string };
|
|
526
|
+
};
|
|
527
|
+
};
|
|
528
|
+
|
|
476
529
|
/**
|
|
477
530
|
* Compact canonical package for an inspected play run.
|
|
478
531
|
*
|
|
@@ -508,9 +561,15 @@ export interface PlayRunPackage {
|
|
|
508
561
|
path: string;
|
|
509
562
|
tableNamespace?: string;
|
|
510
563
|
rowCount?: number;
|
|
564
|
+
sqlTableName?: string;
|
|
565
|
+
sqlQualifiedTableName?: string;
|
|
511
566
|
recovered?: true;
|
|
567
|
+
exportUnavailable?: {
|
|
568
|
+
reason: 'empty_dataset' | 'shared_table_namespace';
|
|
569
|
+
message: string;
|
|
570
|
+
};
|
|
512
571
|
preview?: Record<string, unknown>;
|
|
513
|
-
actions?:
|
|
572
|
+
actions?: PlayRunDatasetActions;
|
|
514
573
|
}>;
|
|
515
574
|
/** Small retained tail of customer and runtime logs; fetch the full stream through `runs.logs`. */
|
|
516
575
|
logs?: {
|
|
@@ -522,6 +581,8 @@ export interface PlayRunPackage {
|
|
|
522
581
|
/** Follow-up actions a caller can perform against the run. */
|
|
523
582
|
next?: {
|
|
524
583
|
inspect?: PlayRunActionPackage;
|
|
584
|
+
full?: PlayRunActionPackage;
|
|
585
|
+
billing?: PlayRunActionPackage;
|
|
525
586
|
export?: PlayRunActionPackage;
|
|
526
587
|
query?: PlayRunActionPackage;
|
|
527
588
|
logs?: PlayRunActionPackage;
|
|
@@ -1202,7 +1202,6 @@ export class PlayContextImpl {
|
|
|
1202
1202
|
}> = [];
|
|
1203
1203
|
private processedRowCount = 0;
|
|
1204
1204
|
private sleepBoundaryIndex = 0;
|
|
1205
|
-
private fetchCallIndex = 0;
|
|
1206
1205
|
private readonly secretRedactor: SecretRedactionContext =
|
|
1207
1206
|
createSecretRedactionContext();
|
|
1208
1207
|
private mapInvocationIndex = 0;
|
|
@@ -5908,7 +5907,10 @@ export class PlayContextImpl {
|
|
|
5908
5907
|
if (!this.toolBatchQueuedAtByKey.has(batchKey)) {
|
|
5909
5908
|
this.toolBatchQueuedAtByKey.set(batchKey, queuedAt);
|
|
5910
5909
|
}
|
|
5911
|
-
const
|
|
5910
|
+
const coalesceWindowMs =
|
|
5911
|
+
this.#options.toolBatchCoalesceWindowMs ??
|
|
5912
|
+
TOOL_BATCH_COALESCE_WINDOW_MS;
|
|
5913
|
+
const deadline = queuedAt + coalesceWindowMs;
|
|
5912
5914
|
const maxBatchSize = batchMaxSizes.get(batchKey) ?? 1;
|
|
5913
5915
|
if (count >= maxBatchSize || deadline <= nowMs) {
|
|
5914
5916
|
readyBatchKeys.add(batchKey);
|
|
@@ -6818,11 +6820,15 @@ export class PlayContextImpl {
|
|
|
6818
6820
|
options?: FetchOptions,
|
|
6819
6821
|
): Promise<PlayFetchResponse> {
|
|
6820
6822
|
const normalizedKey = this.normalizeContextKey(key, 'fetch');
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6823
|
+
const rowStore = rowContext.getStore();
|
|
6824
|
+
const rowFetchScope = rowStore
|
|
6825
|
+
? {
|
|
6826
|
+
tableNamespace: rowStore.tableNamespace ?? null,
|
|
6827
|
+
rowKey: rowStore.rowKey ?? null,
|
|
6828
|
+
rowId: rowStore.rowKey ? null : rowStore.rowId,
|
|
6829
|
+
fieldName: rowStore.fieldName ?? null,
|
|
6830
|
+
}
|
|
6831
|
+
: null;
|
|
6826
6832
|
|
|
6827
6833
|
if (valueContainsSecret(input) || valueContainsSecret(init.body)) {
|
|
6828
6834
|
throw new Error(
|
|
@@ -6858,6 +6864,7 @@ export class PlayContextImpl {
|
|
|
6858
6864
|
...normalizeFetchHeaders(init.headers),
|
|
6859
6865
|
...secretHeaderMarkers,
|
|
6860
6866
|
},
|
|
6867
|
+
row: rowFetchScope,
|
|
6861
6868
|
}),
|
|
6862
6869
|
),
|
|
6863
6870
|
staleAfterSeconds: options?.staleAfterSeconds,
|
|
@@ -6872,7 +6879,7 @@ export class PlayContextImpl {
|
|
|
6872
6879
|
const fetchInit = { ...init, headers };
|
|
6873
6880
|
delete fetchInit.auth;
|
|
6874
6881
|
const boundaryId = this.durableBoundaryId(
|
|
6875
|
-
`fetch-${
|
|
6882
|
+
`fetch-${stableDigest(
|
|
6876
6883
|
stableStringify({
|
|
6877
6884
|
url,
|
|
6878
6885
|
method,
|
|
@@ -6881,10 +6888,10 @@ export class PlayContextImpl {
|
|
|
6881
6888
|
...secretHeaderMarkers,
|
|
6882
6889
|
},
|
|
6883
6890
|
body: typeof init.body === 'string' ? init.body : null,
|
|
6891
|
+
row: rowFetchScope,
|
|
6884
6892
|
}),
|
|
6885
6893
|
)}`,
|
|
6886
6894
|
);
|
|
6887
|
-
this.fetchCallIndex += 1;
|
|
6888
6895
|
|
|
6889
6896
|
const existing = this.checkpoint.resolvedBoundaries?.[boundaryId];
|
|
6890
6897
|
if (existing?.kind === 'fetch' && 'output' in existing) {
|
|
@@ -680,6 +680,11 @@ export interface ContextOptions {
|
|
|
680
680
|
getBatchOperationStrategy?: (
|
|
681
681
|
operation: string,
|
|
682
682
|
) => AnyBatchOperationStrategy | null;
|
|
683
|
+
// How long batchable calls wait for same-key row continuations before
|
|
684
|
+
// dispatching, measured from the first queued item. Defaults to 5ms in
|
|
685
|
+
// production; tests override it to a large value to make coalescing
|
|
686
|
+
// deterministic instead of racing wall-clock milliseconds on slow CI.
|
|
687
|
+
toolBatchCoalesceWindowMs?: number;
|
|
683
688
|
queryCustomerDb?: CustomerDbQueryHandler;
|
|
684
689
|
executeStructuredPlayDefinition?: (input: {
|
|
685
690
|
definition: PlayStructuredDefinition;
|
|
@@ -127,14 +127,13 @@ export const PLAY_LATENCY_SUMMARY_COLUMNS: PlayLatencyPhaseDefinition[] = [
|
|
|
127
127
|
phase: 'server.start_route_runtime_data_plane_ready',
|
|
128
128
|
source: 'server',
|
|
129
129
|
description:
|
|
130
|
-
'Start route Runtime Sheet Data Plane readiness check
|
|
130
|
+
'Start route read-only Runtime Sheet Data Plane readiness check.',
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
header: 'ready_plane',
|
|
134
|
-
phase: 'server.
|
|
134
|
+
phase: 'server.runtime_data_plane_read_plane',
|
|
135
135
|
source: 'server',
|
|
136
|
-
description:
|
|
137
|
-
'Runtime Sheet readiness control-plane record lookup or first-workspace provisioning.',
|
|
136
|
+
description: 'Runtime Sheet readiness control-plane record lookup.',
|
|
138
137
|
},
|
|
139
138
|
{
|
|
140
139
|
header: 'ready_markers',
|
|
@@ -143,20 +142,6 @@ export const PLAY_LATENCY_SUMMARY_COLUMNS: PlayLatencyPhaseDefinition[] = [
|
|
|
143
142
|
description:
|
|
144
143
|
'Single pooled tenant-Postgres read of the storage-contract fingerprint and runtime migration version.',
|
|
145
144
|
},
|
|
146
|
-
{
|
|
147
|
-
header: 'ready_repair',
|
|
148
|
-
phase: 'server.runtime_data_plane_repair_contract',
|
|
149
|
-
source: 'server',
|
|
150
|
-
description:
|
|
151
|
-
'Exceptional stale storage-contract repair; absent from the healthy path.',
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
header: 'ready_migrate',
|
|
155
|
-
phase: 'server.runtime_data_plane_migrate',
|
|
156
|
-
source: 'server',
|
|
157
|
-
description:
|
|
158
|
-
'Exceptional stale Runtime Sheet migration; absent from the healthy path.',
|
|
159
|
-
},
|
|
160
145
|
{
|
|
161
146
|
header: 'route_wait',
|
|
162
147
|
phase: 'server.start_route_wait_completion',
|
|
@@ -224,6 +224,20 @@ export type PlayRunnerRuntimeTiming = {
|
|
|
224
224
|
daytonaExecuteMs?: number;
|
|
225
225
|
};
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Why replay checkpoint state is present or absent on the detached-runner
|
|
229
|
+
* terminal wire. This describes checkpoint transport only; it never classifies
|
|
230
|
+
* the customer's returned output as oversized.
|
|
231
|
+
*/
|
|
232
|
+
export const RUNNER_TERMINAL_CHECKPOINT_DISPOSITION = {
|
|
233
|
+
OMITTED_TERMINAL_REPLAY: 'omitted_terminal_replay',
|
|
234
|
+
INCLUDED_RESUME_REQUIRED: 'included_resume_required',
|
|
235
|
+
ABSENT: 'absent',
|
|
236
|
+
} as const;
|
|
237
|
+
|
|
238
|
+
export type RunnerTerminalCheckpointDisposition =
|
|
239
|
+
(typeof RUNNER_TERMINAL_CHECKPOINT_DISPOSITION)[keyof typeof RUNNER_TERMINAL_CHECKPOINT_DISPOSITION];
|
|
240
|
+
|
|
227
241
|
export type PlayRunnerResult =
|
|
228
242
|
| {
|
|
229
243
|
status: 'completed';
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { gzipSync } from 'node:zlib';
|
|
4
|
-
import
|
|
4
|
+
import {
|
|
5
|
+
RUNNER_TERMINAL_CHECKPOINT_DISPOSITION,
|
|
6
|
+
type PlayRunnerExecutionConfig,
|
|
7
|
+
} from '@shared_libs/play-runtime/protocol';
|
|
5
8
|
import {
|
|
6
9
|
PLAY_RUNTIME_CONTRACT,
|
|
7
10
|
PLAY_RUNTIME_CONTRACT_HEADER,
|
|
@@ -126,6 +129,39 @@ function readResultFromOutput() {
|
|
|
126
129
|
return null;
|
|
127
130
|
}
|
|
128
131
|
|
|
132
|
+
function terminalResultForTransport(result) {
|
|
133
|
+
if (
|
|
134
|
+
result &&
|
|
135
|
+
${JSON.stringify(Object.values(RUNNER_TERMINAL_CHECKPOINT_DISPOSITION))}.includes(
|
|
136
|
+
result.checkpointDisposition,
|
|
137
|
+
)
|
|
138
|
+
) {
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
if (result && result.status === 'suspended') {
|
|
142
|
+
return {
|
|
143
|
+
...result,
|
|
144
|
+
checkpointDisposition: '${RUNNER_TERMINAL_CHECKPOINT_DISPOSITION.INCLUDED_RESUME_REQUIRED}',
|
|
145
|
+
checkpointBytes: Buffer.byteLength(JSON.stringify(result.checkpoint)),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const checkpoint = result && result.checkpoint;
|
|
149
|
+
const terminal = { ...(result || {}) };
|
|
150
|
+
delete terminal.checkpoint;
|
|
151
|
+
if (checkpoint === null || checkpoint === undefined) {
|
|
152
|
+
return {
|
|
153
|
+
...terminal,
|
|
154
|
+
checkpointDisposition: '${RUNNER_TERMINAL_CHECKPOINT_DISPOSITION.ABSENT}',
|
|
155
|
+
checkpointBytes: 0,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
...terminal,
|
|
160
|
+
checkpointDisposition: '${RUNNER_TERMINAL_CHECKPOINT_DISPOSITION.OMITTED_TERMINAL_REPLAY}',
|
|
161
|
+
checkpointBytes: Buffer.byteLength(JSON.stringify(checkpoint)),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
129
165
|
async function main() {
|
|
130
166
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
131
167
|
const context = (config && config.context) || {};
|
|
@@ -136,7 +172,8 @@ async function main() {
|
|
|
136
172
|
console.log('[crash-terminal] no push config; skipping');
|
|
137
173
|
return;
|
|
138
174
|
}
|
|
139
|
-
const
|
|
175
|
+
const parsedResult = readResultFromOutput();
|
|
176
|
+
const result = terminalResultForTransport(parsedResult || {
|
|
140
177
|
status: 'failed',
|
|
141
178
|
error:
|
|
142
179
|
'Daytona play runner exited with code ' +
|
|
@@ -146,9 +183,10 @@ async function main() {
|
|
|
146
183
|
logs: [],
|
|
147
184
|
stats: {},
|
|
148
185
|
steps: [],
|
|
149
|
-
|
|
186
|
+
checkpointDisposition: '${RUNNER_TERMINAL_CHECKPOINT_DISPOSITION.ABSENT}',
|
|
187
|
+
checkpointBytes: 0,
|
|
150
188
|
tableNamespace: null,
|
|
151
|
-
};
|
|
189
|
+
});
|
|
152
190
|
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
153
191
|
try {
|
|
154
192
|
const controller = new AbortController();
|
|
@@ -112,7 +112,10 @@ export type DetachedDaytonaCommandStart = {
|
|
|
112
112
|
cmdId: string;
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
// The marker now proves artifact materialization plus an accepted gateway
|
|
116
|
+
// heartbeat. Keep the handoff bounded, while allowing a cold Fly gateway and
|
|
117
|
+
// one timed-out heartbeat request to recover before the worker rejects it.
|
|
118
|
+
export const DAYTONA_RUNNER_READY_TIMEOUT_MS = 60_000;
|
|
116
119
|
const DAYTONA_RUNNER_READY_POLL_MS = 250;
|
|
117
120
|
|
|
118
121
|
export class DaytonaRunnerInitializationError extends Error {
|
|
@@ -150,9 +153,10 @@ export async function startDetachedDaytonaCommand(input: {
|
|
|
150
153
|
|
|
151
154
|
/**
|
|
152
155
|
* A detached command id only proves that Daytona accepted the request. Before
|
|
153
|
-
* the worker parks, require a marker written
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
+
* the worker parks, require a marker written only after the initialized runner
|
|
157
|
+
* materializes its artifact and the receipt gateway accepts its first
|
|
158
|
+
* heartbeat. This closes the unobservable handoff where a sandbox or command
|
|
159
|
+
* disappears after executeSessionCommand but before durable runner liveness.
|
|
156
160
|
*/
|
|
157
161
|
export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
158
162
|
sandbox: DaytonaSandbox;
|
|
@@ -200,6 +204,6 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
|
200
204
|
const oomIndicated =
|
|
201
205
|
diagnostic.exitCode === 137 || diagnostic.exitCodeFile === 137;
|
|
202
206
|
throw new DaytonaRunnerInitializationError(
|
|
203
|
-
`RUNTIME_SANDBOX_START_FAILED: Daytona accepted detached command ${input.cmdId} in sandbox ${input.sandbox.id}, but the play runner did not
|
|
207
|
+
`RUNTIME_SANDBOX_START_FAILED: Daytona accepted detached command ${input.cmdId} in sandbox ${input.sandbox.id}, but the play runner did not materialize and establish receipt-gateway liveness within ${timeoutMs}ms. Marker diagnosis: ${markerDiagnosis}. Daytona diagnosis: ${JSON.stringify(diagnostic)}. OOM is ${oomIndicated ? 'indicated by exit code 137' : 'not confirmed by the available Daytona evidence'}. The play was stopped and was not retried.`,
|
|
204
208
|
);
|
|
205
209
|
}
|
|
@@ -785,11 +785,11 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
785
785
|
});
|
|
786
786
|
// Push execution (B2-final, park-and-wake): start the runner command
|
|
787
787
|
// DETACHED via a Daytona session and return a `detached_runner`
|
|
788
|
-
// suspension after the runner writes its one-time ready marker.
|
|
789
|
-
// marker
|
|
790
|
-
//
|
|
791
|
-
// the worker parks (releasing its
|
|
792
|
-
//
|
|
788
|
+
// suspension only after the runner writes its one-time ready marker.
|
|
789
|
+
// The marker proves artifact materialization and an accepted gateway
|
|
790
|
+
// heartbeat: a Daytona cmdId alone does not prove durable liveness.
|
|
791
|
+
// After that bounded startup check, the worker parks (releasing its
|
|
792
|
+
// claim and slot) and wakes on terminal push or the ceiling timeout.
|
|
793
793
|
const sessionId = `deepline-play-${randomUUID()}`;
|
|
794
794
|
let start: Awaited<ReturnType<typeof startDetachedDaytonaCommand>>;
|
|
795
795
|
try {
|