deepline 0.1.238 → 0.1.240
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 +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +63 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +43 -16
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +13 -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/run-ledger.ts +12 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +47 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +38 -8
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +46 -5
- package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +5 -1
- package/dist/cli/index.js +376 -49
- package/dist/cli/index.mjs +376 -49
- package/dist/index.d.mts +107 -6
- package/dist/index.d.ts +107 -6
- package/dist/index.js +55 -11
- package/dist/index.mjs +55 -11
- 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,
|
|
@@ -110,10 +110,10 @@ export const SDK_RELEASE = {
|
|
|
110
110
|
// silently materializing them as unmatched results.
|
|
111
111
|
// 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
|
|
112
112
|
// automatically without blocking their current command.
|
|
113
|
-
version: '0.1.
|
|
113
|
+
version: '0.1.240',
|
|
114
114
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
115
115
|
supportPolicy: {
|
|
116
|
-
latest: '0.1.
|
|
116
|
+
latest: '0.1.240',
|
|
117
117
|
minimumSupported: '0.1.53',
|
|
118
118
|
deprecatedBelow: '0.1.219',
|
|
119
119
|
commandMinimumSupported: [
|
|
@@ -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;
|
|
@@ -5853,6 +5852,10 @@ export class PlayContextImpl {
|
|
|
5853
5852
|
for (const wake of waiters) wake();
|
|
5854
5853
|
}
|
|
5855
5854
|
|
|
5855
|
+
private toolBatchDispatcherNowMs(): number {
|
|
5856
|
+
return this.#options.toolBatchDispatcherClock?.nowMs() ?? Date.now();
|
|
5857
|
+
}
|
|
5858
|
+
|
|
5856
5859
|
private enqueueToolCall(request: ToolCallRequest): void {
|
|
5857
5860
|
if (this.toolDispatcherFailure != null) {
|
|
5858
5861
|
const resolver = this.toolCallResolvers.get(request.callId);
|
|
@@ -5865,7 +5868,10 @@ export class PlayContextImpl {
|
|
|
5865
5868
|
this.toolCallQueue.push(request);
|
|
5866
5869
|
const batchKey = this.toolBatchCoalesceKey(request);
|
|
5867
5870
|
if (batchKey && !this.toolBatchQueuedAtByKey.has(batchKey)) {
|
|
5868
|
-
this.toolBatchQueuedAtByKey.set(
|
|
5871
|
+
this.toolBatchQueuedAtByKey.set(
|
|
5872
|
+
batchKey,
|
|
5873
|
+
this.toolBatchDispatcherNowMs(),
|
|
5874
|
+
);
|
|
5869
5875
|
}
|
|
5870
5876
|
this.wakeToolDispatcher();
|
|
5871
5877
|
}
|
|
@@ -5908,7 +5914,10 @@ export class PlayContextImpl {
|
|
|
5908
5914
|
if (!this.toolBatchQueuedAtByKey.has(batchKey)) {
|
|
5909
5915
|
this.toolBatchQueuedAtByKey.set(batchKey, queuedAt);
|
|
5910
5916
|
}
|
|
5911
|
-
const
|
|
5917
|
+
const coalesceWindowMs =
|
|
5918
|
+
this.#options.toolBatchCoalesceWindowMs ??
|
|
5919
|
+
TOOL_BATCH_COALESCE_WINDOW_MS;
|
|
5920
|
+
const deadline = queuedAt + coalesceWindowMs;
|
|
5912
5921
|
const maxBatchSize = batchMaxSizes.get(batchKey) ?? 1;
|
|
5913
5922
|
if (count >= maxBatchSize || deadline <= nowMs) {
|
|
5914
5923
|
readyBatchKeys.add(batchKey);
|
|
@@ -5939,15 +5948,26 @@ export class PlayContextImpl {
|
|
|
5939
5948
|
|
|
5940
5949
|
private waitForToolDispatcherWake(deadlineMs: number | null): Promise<void> {
|
|
5941
5950
|
return new Promise((resolve) => {
|
|
5942
|
-
let
|
|
5951
|
+
let cancelDeadline: (() => void) | null = null;
|
|
5943
5952
|
const wake = () => {
|
|
5944
|
-
|
|
5953
|
+
cancelDeadline?.();
|
|
5954
|
+
cancelDeadline = null;
|
|
5945
5955
|
this.toolDispatcherWakeWaiters.delete(wake);
|
|
5946
5956
|
resolve();
|
|
5947
5957
|
};
|
|
5948
5958
|
this.toolDispatcherWakeWaiters.add(wake);
|
|
5949
5959
|
if (deadlineMs != null) {
|
|
5950
|
-
|
|
5960
|
+
const delayMs = Math.max(
|
|
5961
|
+
0,
|
|
5962
|
+
deadlineMs - this.toolBatchDispatcherNowMs(),
|
|
5963
|
+
);
|
|
5964
|
+
const clock = this.#options.toolBatchDispatcherClock;
|
|
5965
|
+
if (clock) {
|
|
5966
|
+
cancelDeadline = clock.schedule(delayMs, wake);
|
|
5967
|
+
} else {
|
|
5968
|
+
const timer = setTimeout(wake, delayMs);
|
|
5969
|
+
cancelDeadline = () => clearTimeout(timer);
|
|
5970
|
+
}
|
|
5951
5971
|
}
|
|
5952
5972
|
});
|
|
5953
5973
|
}
|
|
@@ -5975,7 +5995,9 @@ export class PlayContextImpl {
|
|
|
5975
5995
|
throw this.toolDispatcherFailure;
|
|
5976
5996
|
}
|
|
5977
5997
|
|
|
5978
|
-
const dispatchable = this.takeDispatchableToolCalls(
|
|
5998
|
+
const dispatchable = this.takeDispatchableToolCalls(
|
|
5999
|
+
this.toolBatchDispatcherNowMs(),
|
|
6000
|
+
);
|
|
5979
6001
|
if (dispatchable.requests.length > 0) {
|
|
5980
6002
|
pass += 1;
|
|
5981
6003
|
this.log(` Batch pass ${pass}`);
|
|
@@ -6686,8 +6708,8 @@ export class PlayContextImpl {
|
|
|
6686
6708
|
rowScopeKey ? `@${rowScopeKey}` : ''
|
|
6687
6709
|
}`;
|
|
6688
6710
|
if (rowScope) {
|
|
6689
|
-
const namespaces =
|
|
6690
|
-
|
|
6711
|
+
const namespaces = (rowScope.inlineChildInvocationNamespaces ??=
|
|
6712
|
+
new Set<string>());
|
|
6691
6713
|
namespaces.add(namespace);
|
|
6692
6714
|
if (namespaces.size > MAX_INLINE_CHILD_INVOCATIONS_PER_ROW) {
|
|
6693
6715
|
throw new Error(
|
|
@@ -6818,11 +6840,15 @@ export class PlayContextImpl {
|
|
|
6818
6840
|
options?: FetchOptions,
|
|
6819
6841
|
): Promise<PlayFetchResponse> {
|
|
6820
6842
|
const normalizedKey = this.normalizeContextKey(key, 'fetch');
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6843
|
+
const rowStore = rowContext.getStore();
|
|
6844
|
+
const rowFetchScope = rowStore
|
|
6845
|
+
? {
|
|
6846
|
+
tableNamespace: rowStore.tableNamespace ?? null,
|
|
6847
|
+
rowKey: rowStore.rowKey ?? null,
|
|
6848
|
+
rowId: rowStore.rowKey ? null : rowStore.rowId,
|
|
6849
|
+
fieldName: rowStore.fieldName ?? null,
|
|
6850
|
+
}
|
|
6851
|
+
: null;
|
|
6826
6852
|
|
|
6827
6853
|
if (valueContainsSecret(input) || valueContainsSecret(init.body)) {
|
|
6828
6854
|
throw new Error(
|
|
@@ -6858,6 +6884,7 @@ export class PlayContextImpl {
|
|
|
6858
6884
|
...normalizeFetchHeaders(init.headers),
|
|
6859
6885
|
...secretHeaderMarkers,
|
|
6860
6886
|
},
|
|
6887
|
+
row: rowFetchScope,
|
|
6861
6888
|
}),
|
|
6862
6889
|
),
|
|
6863
6890
|
staleAfterSeconds: options?.staleAfterSeconds,
|
|
@@ -6872,7 +6899,7 @@ export class PlayContextImpl {
|
|
|
6872
6899
|
const fetchInit = { ...init, headers };
|
|
6873
6900
|
delete fetchInit.auth;
|
|
6874
6901
|
const boundaryId = this.durableBoundaryId(
|
|
6875
|
-
`fetch-${
|
|
6902
|
+
`fetch-${stableDigest(
|
|
6876
6903
|
stableStringify({
|
|
6877
6904
|
url,
|
|
6878
6905
|
method,
|
|
@@ -6881,10 +6908,10 @@ export class PlayContextImpl {
|
|
|
6881
6908
|
...secretHeaderMarkers,
|
|
6882
6909
|
},
|
|
6883
6910
|
body: typeof init.body === 'string' ? init.body : null,
|
|
6911
|
+
row: rowFetchScope,
|
|
6884
6912
|
}),
|
|
6885
6913
|
)}`,
|
|
6886
6914
|
);
|
|
6887
|
-
this.fetchCallIndex += 1;
|
|
6888
6915
|
|
|
6889
6916
|
const existing = this.checkpoint.resolvedBoundaries?.[boundaryId];
|
|
6890
6917
|
if (existing?.kind === 'fetch' && 'output' in existing) {
|
|
@@ -680,6 +680,19 @@ export interface ContextOptions {
|
|
|
680
680
|
getBatchOperationStrategy?: (
|
|
681
681
|
operation: string,
|
|
682
682
|
) => AnyBatchOperationStrategy | null;
|
|
683
|
+
/**
|
|
684
|
+
* Internal deterministic clock for fixed-window dispatcher tests. Runtime
|
|
685
|
+
* adapters omit this and use the system clock.
|
|
686
|
+
*/
|
|
687
|
+
toolBatchDispatcherClock?: {
|
|
688
|
+
nowMs: () => number;
|
|
689
|
+
schedule: (delayMs: number, wake: () => void) => () => void;
|
|
690
|
+
};
|
|
691
|
+
// How long batchable calls wait for same-key row continuations before
|
|
692
|
+
// dispatching, measured from the first queued item. Defaults to 5ms in
|
|
693
|
+
// production; tests override it to a large value to make coalescing
|
|
694
|
+
// deterministic instead of racing wall-clock milliseconds on slow CI.
|
|
695
|
+
toolBatchCoalesceWindowMs?: number;
|
|
683
696
|
queryCustomerDb?: CustomerDbQueryHandler;
|
|
684
697
|
executeStructuredPlayDefinition?: (input: {
|
|
685
698
|
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';
|
|
@@ -1055,7 +1055,12 @@ export function reducePlayRunLedgerEvent(
|
|
|
1055
1055
|
if (ignoreNonTerminalAfterStickyTerminal) {
|
|
1056
1056
|
return withTiming(base);
|
|
1057
1057
|
}
|
|
1058
|
-
|
|
1058
|
+
// Scheduler wait/resume events own the active lifecycle once a run is
|
|
1059
|
+
// parked. Worker registration can arrive later through an independent
|
|
1060
|
+
// ledger append lane; replaying that older `run.created` fact must not
|
|
1061
|
+
// make an externally-waiting run look active again.
|
|
1062
|
+
const createdStatus =
|
|
1063
|
+
base.status === 'waiting' ? 'waiting' : (event.status ?? base.status);
|
|
1059
1064
|
return withTiming({
|
|
1060
1065
|
...base,
|
|
1061
1066
|
playName: event.playName ?? base.playName ?? null,
|
|
@@ -1075,7 +1080,9 @@ export function reducePlayRunLedgerEvent(
|
|
|
1075
1080
|
playName: event.playName ?? base.playName ?? null,
|
|
1076
1081
|
status: isTerminalPlayRunLedgerStatus(base.status)
|
|
1077
1082
|
? base.status
|
|
1078
|
-
: '
|
|
1083
|
+
: base.status === 'waiting'
|
|
1084
|
+
? 'waiting'
|
|
1085
|
+
: 'running',
|
|
1079
1086
|
startedAt: base.startedAt ?? occurredAt,
|
|
1080
1087
|
});
|
|
1081
1088
|
case 'run.waiting':
|
|
@@ -1200,7 +1207,9 @@ export function reducePlayRunLedgerEvent(
|
|
|
1200
1207
|
...base,
|
|
1201
1208
|
status: isTerminalPlayRunLedgerStatus(base.status)
|
|
1202
1209
|
? base.status
|
|
1203
|
-
: '
|
|
1210
|
+
: base.status === 'waiting'
|
|
1211
|
+
? 'waiting'
|
|
1212
|
+
: 'running',
|
|
1204
1213
|
startedAt: base.startedAt ?? occurredAt,
|
|
1205
1214
|
orderedStepIds: appendOrderedStepId(base, event.stepId),
|
|
1206
1215
|
stepsById: { ...base.stepsById, [event.stepId]: nextStep },
|
|
@@ -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,
|
|
@@ -29,6 +32,7 @@ export type StagedDaytonaPayload = {
|
|
|
29
32
|
workDir: string;
|
|
30
33
|
command: string;
|
|
31
34
|
readyPath: string;
|
|
35
|
+
startAckPath: string;
|
|
32
36
|
outputPath: string;
|
|
33
37
|
exitCodePath: string;
|
|
34
38
|
progressEventPath: string;
|
|
@@ -126,6 +130,39 @@ function readResultFromOutput() {
|
|
|
126
130
|
return null;
|
|
127
131
|
}
|
|
128
132
|
|
|
133
|
+
function terminalResultForTransport(result) {
|
|
134
|
+
if (
|
|
135
|
+
result &&
|
|
136
|
+
${JSON.stringify(Object.values(RUNNER_TERMINAL_CHECKPOINT_DISPOSITION))}.includes(
|
|
137
|
+
result.checkpointDisposition,
|
|
138
|
+
)
|
|
139
|
+
) {
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
if (result && result.status === 'suspended') {
|
|
143
|
+
return {
|
|
144
|
+
...result,
|
|
145
|
+
checkpointDisposition: '${RUNNER_TERMINAL_CHECKPOINT_DISPOSITION.INCLUDED_RESUME_REQUIRED}',
|
|
146
|
+
checkpointBytes: Buffer.byteLength(JSON.stringify(result.checkpoint)),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const checkpoint = result && result.checkpoint;
|
|
150
|
+
const terminal = { ...(result || {}) };
|
|
151
|
+
delete terminal.checkpoint;
|
|
152
|
+
if (checkpoint === null || checkpoint === undefined) {
|
|
153
|
+
return {
|
|
154
|
+
...terminal,
|
|
155
|
+
checkpointDisposition: '${RUNNER_TERMINAL_CHECKPOINT_DISPOSITION.ABSENT}',
|
|
156
|
+
checkpointBytes: 0,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
...terminal,
|
|
161
|
+
checkpointDisposition: '${RUNNER_TERMINAL_CHECKPOINT_DISPOSITION.OMITTED_TERMINAL_REPLAY}',
|
|
162
|
+
checkpointBytes: Buffer.byteLength(JSON.stringify(checkpoint)),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
129
166
|
async function main() {
|
|
130
167
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
131
168
|
const context = (config && config.context) || {};
|
|
@@ -136,7 +173,8 @@ async function main() {
|
|
|
136
173
|
console.log('[crash-terminal] no push config; skipping');
|
|
137
174
|
return;
|
|
138
175
|
}
|
|
139
|
-
const
|
|
176
|
+
const parsedResult = readResultFromOutput();
|
|
177
|
+
const result = terminalResultForTransport(parsedResult || {
|
|
140
178
|
status: 'failed',
|
|
141
179
|
error:
|
|
142
180
|
'Daytona play runner exited with code ' +
|
|
@@ -146,9 +184,10 @@ async function main() {
|
|
|
146
184
|
logs: [],
|
|
147
185
|
stats: {},
|
|
148
186
|
steps: [],
|
|
149
|
-
|
|
187
|
+
checkpointDisposition: '${RUNNER_TERMINAL_CHECKPOINT_DISPOSITION.ABSENT}',
|
|
188
|
+
checkpointBytes: 0,
|
|
150
189
|
tableNamespace: null,
|
|
151
|
-
};
|
|
190
|
+
});
|
|
152
191
|
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
153
192
|
try {
|
|
154
193
|
const controller = new AbortController();
|
|
@@ -351,6 +390,7 @@ export async function stageDaytonaRunnerPayload(input: {
|
|
|
351
390
|
const outputPath = `${workDir}/deepline-play-output-${randomUUID()}.log`;
|
|
352
391
|
const exitCodePath = `${workDir}/deepline-play-exit-${randomUUID()}.txt`;
|
|
353
392
|
const readyPath = `${workDir}/deepline-play-ready-${randomUUID()}.json`;
|
|
393
|
+
const startAckPath = `${workDir}/deepline-play-start-ack-${randomUUID()}.json`;
|
|
354
394
|
const progressEventPath = `${workDir}/deepline-play-progress-${randomUUID()}.jsonl`;
|
|
355
395
|
const runnerTraceEnv =
|
|
356
396
|
process.env.DEEPLINE_RUNTIME_RECEIPT_TRACE === '1'
|
|
@@ -363,18 +403,19 @@ export async function stageDaytonaRunnerPayload(input: {
|
|
|
363
403
|
artifactCodePath,
|
|
364
404
|
artifactSourceMapPath,
|
|
365
405
|
crashPusherPath,
|
|
366
|
-
})} && DEEPLINE_PLAY_RUNNER_READY_PATH=${shellQuote(readyPath)} DEEPLINE_PLAY_RUNNER_PROGRESS_EVENT_PATH=${shellQuote(progressEventPath)} DEEPLINE_PLAY_RUNNER_STDOUT_EVENT_MODE=compact ${runnerTraceEnv}node ${shellQuote(runnerPath)} ${shellQuote(configPath)}`;
|
|
406
|
+
})} && DEEPLINE_PLAY_RUNNER_READY_PATH=${shellQuote(readyPath)} DEEPLINE_PLAY_RUNNER_START_ACK_PATH=${shellQuote(startAckPath)} DEEPLINE_PLAY_RUNNER_PROGRESS_EVENT_PATH=${shellQuote(progressEventPath)} DEEPLINE_PLAY_RUNNER_STDOUT_EVENT_MODE=compact ${runnerTraceEnv}node ${shellQuote(runnerPath)} ${shellQuote(configPath)}`;
|
|
367
407
|
// Crash-containment epilogue: runs UNCONDITIONALLY after the runner exits and
|
|
368
408
|
// pushes the parsed (or synthesized) terminal to the gateway so the parked
|
|
369
409
|
// worker wakes within seconds of ANY runner death — process.exit abuse, OOM
|
|
370
410
|
// SIGKILL, or a crash before the in-process push. Idempotent against a
|
|
371
411
|
// successful in-runner push (terminals are first-write-wins).
|
|
372
|
-
const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(exitCodePath)} ${shellQuote(readyPath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} >> ${shellQuote(outputPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
|
|
412
|
+
const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(exitCodePath)} ${shellQuote(readyPath)} ${shellQuote(startAckPath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} >> ${shellQuote(outputPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
|
|
373
413
|
|
|
374
414
|
return {
|
|
375
415
|
workDir: input.workDir,
|
|
376
416
|
command,
|
|
377
417
|
readyPath,
|
|
418
|
+
startAckPath,
|
|
378
419
|
outputPath,
|
|
379
420
|
exitCodePath,
|
|
380
421
|
progressEventPath,
|