deepline 0.1.211 → 0.1.212
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 +150 -11
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/customer-console.ts +23 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +1 -1
- package/dist/bundling-sources/sdk/src/client.ts +118 -3
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +253 -30
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +22 -20
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +12 -0
- package/dist/bundling-sources/shared_libs/play-runtime/dataset-id.ts +10 -4
- package/dist/bundling-sources/shared_libs/play-runtime/db-session.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +3 -5
- package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +14 -2
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +4 -2
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +21 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +128 -6
- package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +37 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +19 -10
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +63 -7
- package/dist/bundling-sources/shared_libs/play-runtime/secret-capability.ts +18 -4
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +29 -9
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +22 -1
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +79 -1
- package/dist/cli/index.js +637 -229
- package/dist/cli/index.mjs +637 -229
- package/dist/index.d.mts +76 -6
- package/dist/index.d.ts +76 -6
- package/dist/index.js +225 -43
- package/dist/index.mjs +225 -43
- package/dist/plays/bundle-play-file.mjs +65 -4
- package/package.json +1 -1
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
} from './child-play-await';
|
|
69
69
|
import { submitChildPlayThroughCoordinator } from './child-play-submit';
|
|
70
70
|
import { isRetryableWorkerRuntimeApiError } from './runtime-api-retry';
|
|
71
|
+
import { formatCustomerConsoleValue } from './runtime/customer-console';
|
|
71
72
|
import {
|
|
72
73
|
attachToolResultListDataset,
|
|
73
74
|
createToolExecuteResult,
|
|
@@ -116,6 +117,7 @@ import {
|
|
|
116
117
|
isQueryResultDatasetTool,
|
|
117
118
|
} from '../../../shared_libs/play-runtime/query-result-dataset';
|
|
118
119
|
import { DEDUPE_DUPLICATE_KEY_SAMPLE_CAP } from '../../../shared_libs/play-runtime/map-row-identity';
|
|
120
|
+
import { createRuntimeDatasetId } from '../../../shared_libs/play-runtime/dataset-id';
|
|
119
121
|
import {
|
|
120
122
|
getTopLevelPipelineSubsteps,
|
|
121
123
|
getCompiledPipelineSubsteps,
|
|
@@ -300,7 +302,7 @@ import type {
|
|
|
300
302
|
LiveNodeProgressMap,
|
|
301
303
|
LiveNodeProgressSnapshot,
|
|
302
304
|
} from './runtime/live-progress';
|
|
303
|
-
import { extractErrorBilling } from './runtime/tool-http-errors';
|
|
305
|
+
import { extractErrorBilling, ToolHttpError } from './runtime/tool-http-errors';
|
|
304
306
|
import {
|
|
305
307
|
WorkflowAbortError,
|
|
306
308
|
isAbortLikeError,
|
|
@@ -324,6 +326,55 @@ import type { PlayRunInputPayload } from '../../../shared_libs/play-runtime/sche
|
|
|
324
326
|
// @ts-ignore - virtual module substituted by esbuild plugin at bundle time.
|
|
325
327
|
import playFn from 'deepline-play-entry';
|
|
326
328
|
|
|
329
|
+
const customerConsoleStorage = new AsyncLocalStorage<
|
|
330
|
+
(message: string) => void
|
|
331
|
+
>();
|
|
332
|
+
|
|
333
|
+
type CustomerConsoleGlobal = typeof globalThis & {
|
|
334
|
+
__deeplineCaptureCustomerConsole?: (
|
|
335
|
+
method: string,
|
|
336
|
+
args: unknown[],
|
|
337
|
+
) => unknown;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
(globalThis as CustomerConsoleGlobal).__deeplineCaptureCustomerConsole = (
|
|
341
|
+
method,
|
|
342
|
+
args,
|
|
343
|
+
) => {
|
|
344
|
+
const log = customerConsoleStorage.getStore();
|
|
345
|
+
if (log) {
|
|
346
|
+
assertNoSecretTaint(args, `console.${method}`);
|
|
347
|
+
const message = args.map(formatCustomerConsoleValue).join(' ');
|
|
348
|
+
log(`[console.${method}] ${message}`.slice(0, 2_000));
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
function runCustomerPlay(
|
|
353
|
+
ctx: unknown,
|
|
354
|
+
input: PlayRunInputPayload,
|
|
355
|
+
): Promise<unknown> {
|
|
356
|
+
return customerConsoleStorage.run(
|
|
357
|
+
(ctx as { log(message: string): void }).log,
|
|
358
|
+
() => (playFn as typeof runCustomerPlay)(ctx, input),
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function runtimeDatasetIdentity(playName: string, tableNamespace: string) {
|
|
363
|
+
return {
|
|
364
|
+
datasetId: createRuntimeDatasetId(playName, tableNamespace),
|
|
365
|
+
path: `datasets.${tableNamespace}`,
|
|
366
|
+
tableNamespace,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function datasetAvailableLog(
|
|
371
|
+
runId: string,
|
|
372
|
+
dataset: ReturnType<typeof runtimeDatasetIdentity>,
|
|
373
|
+
count: number,
|
|
374
|
+
): string {
|
|
375
|
+
return `Dataset ${dataset.path} available: ${count} persisted rows; export: deepline runs export ${runId} --dataset ${dataset.path} --out ${dataset.tableNamespace}.csv`;
|
|
376
|
+
}
|
|
377
|
+
|
|
327
378
|
type RunRequest = {
|
|
328
379
|
runId: string;
|
|
329
380
|
callbackUrl: string;
|
|
@@ -999,6 +1050,17 @@ type WorkerCtxCallbacks = {
|
|
|
999
1050
|
}) => void | Promise<void>;
|
|
1000
1051
|
onMapStarted?: (nodeId: string, at?: number) => void;
|
|
1001
1052
|
onMapCompleted?: (nodeId: string, at?: number) => void;
|
|
1053
|
+
onDatasetLifecycle?: (event: {
|
|
1054
|
+
datasetId: string;
|
|
1055
|
+
path: string;
|
|
1056
|
+
tableNamespace: string;
|
|
1057
|
+
phase: 'registered' | 'available' | 'failed';
|
|
1058
|
+
persistedRows?: number;
|
|
1059
|
+
succeededRows?: number;
|
|
1060
|
+
failedRows?: number;
|
|
1061
|
+
complete?: boolean;
|
|
1062
|
+
at: number;
|
|
1063
|
+
}) => void | Promise<void>;
|
|
1002
1064
|
onToolCalled?: (toolId: string, at?: number) => void;
|
|
1003
1065
|
onToolFailed?: (toolId: string, at?: number) => void;
|
|
1004
1066
|
};
|
|
@@ -2073,6 +2135,7 @@ async function callToolDirect(
|
|
|
2073
2135
|
while (true) {
|
|
2074
2136
|
requestAttempt += 1;
|
|
2075
2137
|
let res: Response;
|
|
2138
|
+
const providerCallStartedAt = nowMs();
|
|
2076
2139
|
try {
|
|
2077
2140
|
const runtimeApiTimeout = resolveToolRuntimeApiTimeout({
|
|
2078
2141
|
requestInput: input,
|
|
@@ -2161,8 +2224,11 @@ async function callToolDirect(
|
|
|
2161
2224
|
} catch (error) {
|
|
2162
2225
|
transportAttempt += 1;
|
|
2163
2226
|
const message = error instanceof Error ? error.message : String(error);
|
|
2164
|
-
lastError = new
|
|
2227
|
+
lastError = new ToolHttpError(
|
|
2165
2228
|
`Tool ${toolId} transport failed calling ${path} for run ${req.runId} on attempt ${transportAttempt}/${TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS}: ${message}`,
|
|
2229
|
+
null,
|
|
2230
|
+
0,
|
|
2231
|
+
'repairable',
|
|
2166
2232
|
);
|
|
2167
2233
|
if (
|
|
2168
2234
|
transportAttempt >= TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS ||
|
|
@@ -2212,6 +2278,7 @@ async function callToolDirect(
|
|
|
2212
2278
|
bodyText: text,
|
|
2213
2279
|
retryAfterHeader: res.headers.get('retry-after'),
|
|
2214
2280
|
transientHttpRetrySafe,
|
|
2281
|
+
providerLatencyMs: nowMs() - providerCallStartedAt,
|
|
2215
2282
|
});
|
|
2216
2283
|
lastError = failure.error;
|
|
2217
2284
|
if (failure.backpressureDelayMs !== null) {
|
|
@@ -4865,6 +4932,7 @@ function createMinimalWorkerCtx(
|
|
|
4865
4932
|
};
|
|
4866
4933
|
|
|
4867
4934
|
let totalRowsWritten = 0;
|
|
4935
|
+
let datasetLifecycleRegistered = false;
|
|
4868
4936
|
|
|
4869
4937
|
const volatileWorkflowChunkRows = new Map<
|
|
4870
4938
|
number,
|
|
@@ -4915,6 +4983,22 @@ function createMinimalWorkerCtx(
|
|
|
4915
4983
|
budgetMeter: workBudgetMeter,
|
|
4916
4984
|
force: !!req.force,
|
|
4917
4985
|
});
|
|
4986
|
+
if (!datasetLifecycleRegistered) {
|
|
4987
|
+
datasetLifecycleRegistered = true;
|
|
4988
|
+
const dataset = runtimeDatasetIdentity(req.playName, name);
|
|
4989
|
+
await callbacks?.onDatasetLifecycle?.({
|
|
4990
|
+
...dataset,
|
|
4991
|
+
phase: 'registered',
|
|
4992
|
+
complete: false,
|
|
4993
|
+
at: nowMs(),
|
|
4994
|
+
});
|
|
4995
|
+
emitEvent({
|
|
4996
|
+
type: 'log',
|
|
4997
|
+
level: 'info',
|
|
4998
|
+
message: `Dataset ${dataset.path} registered`,
|
|
4999
|
+
ts: nowMs(),
|
|
5000
|
+
});
|
|
5001
|
+
}
|
|
4918
5002
|
const activeRowAttempt = {
|
|
4919
5003
|
attemptId: prepared.attemptId ?? rowAttempt.attemptId,
|
|
4920
5004
|
attemptOwnerRunId:
|
|
@@ -6261,6 +6345,23 @@ function createMinimalWorkerCtx(
|
|
|
6261
6345
|
finalizedRowsWritten,
|
|
6262
6346
|
),
|
|
6263
6347
|
});
|
|
6348
|
+
const dataset = runtimeDatasetIdentity(req.playName, name);
|
|
6349
|
+
const persistedRows = finalizedRowsWritten + totalRowsFailed;
|
|
6350
|
+
await callbacks?.onDatasetLifecycle?.({
|
|
6351
|
+
...dataset,
|
|
6352
|
+
phase: 'available',
|
|
6353
|
+
persistedRows,
|
|
6354
|
+
succeededRows: finalizedRowsWritten,
|
|
6355
|
+
failedRows: totalRowsFailed,
|
|
6356
|
+
complete: true,
|
|
6357
|
+
at: completedAt,
|
|
6358
|
+
});
|
|
6359
|
+
emitEvent({
|
|
6360
|
+
type: 'log',
|
|
6361
|
+
level: totalRowsFailed > 0 ? 'warn' : 'info',
|
|
6362
|
+
message: datasetAvailableLog(req.runId, dataset, persistedRows),
|
|
6363
|
+
ts: completedAt,
|
|
6364
|
+
});
|
|
6264
6365
|
callbacks?.onMapCompleted?.(mapNodeId, completedAt);
|
|
6265
6366
|
emitEvent({
|
|
6266
6367
|
type: 'log',
|
|
@@ -6534,7 +6635,8 @@ function createMinimalWorkerCtx(
|
|
|
6534
6635
|
if (chunkResult.fatalError) {
|
|
6535
6636
|
throw new Error(
|
|
6536
6637
|
`ctx.dataset("${name}") stopped after a runtime persistence failure ` +
|
|
6537
|
-
`outside the retryable chunk step.
|
|
6638
|
+
`outside the retryable chunk step. Circuit breaker prevented queued row ` +
|
|
6639
|
+
`dispatch. Provider calls already executed for ` +
|
|
6538
6640
|
`${chunkResult.rowsExecuted} row(s), so the chunk was not retried. ` +
|
|
6539
6641
|
`Fix the runtime persistence cause and re-run to resume. ` +
|
|
6540
6642
|
`First error: ${chunkResult.fatalError}`,
|
|
@@ -7607,9 +7709,7 @@ async function handleRun(request: Request, env: WorkerEnv): Promise<Response> {
|
|
|
7607
7709
|
captureHarnessBinding(env);
|
|
7608
7710
|
await probeHarnessOnce(env, runPrefix);
|
|
7609
7711
|
const ctx = createMinimalWorkerCtx(req, emit, env);
|
|
7610
|
-
const result = await (
|
|
7611
|
-
playFn as (ctx: unknown, input: PlayRunInputPayload) => Promise<unknown>
|
|
7612
|
-
)(ctx, req.runtimeInput);
|
|
7712
|
+
const result = await runCustomerPlay(ctx, req.runtimeInput);
|
|
7613
7713
|
emit({
|
|
7614
7714
|
type: 'result',
|
|
7615
7715
|
result,
|
|
@@ -8023,6 +8123,17 @@ async function executeRunRequest(
|
|
|
8023
8123
|
},
|
|
8024
8124
|
onMapStarted: (nodeId, at) => stepLifecycle?.onMapStarted(nodeId, at),
|
|
8025
8125
|
onMapCompleted: (nodeId, at) => stepLifecycle?.onMapCompleted(nodeId, at),
|
|
8126
|
+
onDatasetLifecycle: async (event) => {
|
|
8127
|
+
const { at, ...dataset } = event;
|
|
8128
|
+
pendingLedgerEvents.push({
|
|
8129
|
+
...dataset,
|
|
8130
|
+
type: 'dataset.lifecycle',
|
|
8131
|
+
runId: req.runId,
|
|
8132
|
+
source: 'worker',
|
|
8133
|
+
occurredAt: at,
|
|
8134
|
+
});
|
|
8135
|
+
await flushLedgerEvents(event.complete === true);
|
|
8136
|
+
},
|
|
8026
8137
|
onToolCalled: (toolId, at) => stepLifecycle?.onToolCalled(toolId, at),
|
|
8027
8138
|
onToolFailed: (toolId, at) => stepLifecycle?.onToolFailed(toolId, at),
|
|
8028
8139
|
};
|
|
@@ -8089,9 +8200,7 @@ async function executeRunRequest(
|
|
|
8089
8200
|
: null;
|
|
8090
8201
|
try {
|
|
8091
8202
|
const playStartedAt = nowMs();
|
|
8092
|
-
const result = await (
|
|
8093
|
-
playFn as (ctx: unknown, input: PlayRunInputPayload) => Promise<unknown>
|
|
8094
|
-
)(ctx, req.runtimeInput);
|
|
8203
|
+
const result = await runCustomerPlay(ctx, req.runtimeInput);
|
|
8095
8204
|
recordRunnerPerfTrace({
|
|
8096
8205
|
req,
|
|
8097
8206
|
phase: 'runner.play_function',
|
|
@@ -8135,7 +8244,33 @@ async function executeRunRequest(
|
|
|
8135
8244
|
ms: nowMs() - ledgerFlushWaitStartedAt,
|
|
8136
8245
|
});
|
|
8137
8246
|
const resultDatasetStartedAt = nowMs();
|
|
8138
|
-
await persistResultDatasets(
|
|
8247
|
+
const persistedResultCounts = await persistResultDatasets(
|
|
8248
|
+
req,
|
|
8249
|
+
promoted.handles,
|
|
8250
|
+
serializedResult,
|
|
8251
|
+
);
|
|
8252
|
+
for (const dataset of promoted.handles) {
|
|
8253
|
+
if (dataset.datasetKind === 'map') continue;
|
|
8254
|
+
const identity = runtimeDatasetIdentity(
|
|
8255
|
+
req.playName,
|
|
8256
|
+
dataset.tableNamespace,
|
|
8257
|
+
);
|
|
8258
|
+
const count = persistedResultCounts.get(dataset.tableNamespace) ?? 0;
|
|
8259
|
+
pendingLedgerEvents.push({
|
|
8260
|
+
...identity,
|
|
8261
|
+
type: 'dataset.lifecycle',
|
|
8262
|
+
runId: req.runId,
|
|
8263
|
+
source: 'worker',
|
|
8264
|
+
occurredAt: nowMs(),
|
|
8265
|
+
phase: 'available',
|
|
8266
|
+
persistedRows: count,
|
|
8267
|
+
succeededRows: count,
|
|
8268
|
+
failedRows: 0,
|
|
8269
|
+
complete: true,
|
|
8270
|
+
});
|
|
8271
|
+
appendRunLogLine(datasetAvailableLog(req.runId, identity, count));
|
|
8272
|
+
}
|
|
8273
|
+
await flushLedgerEvents(true);
|
|
8139
8274
|
resultDatasetsPersisted = true;
|
|
8140
8275
|
recordRunnerPerfTrace({
|
|
8141
8276
|
req,
|
|
@@ -8599,8 +8734,9 @@ async function persistResultDatasets(
|
|
|
8599
8734
|
req: RunRequest,
|
|
8600
8735
|
resultDatasets: ProjectedResultDatasetHandle[],
|
|
8601
8736
|
serializedResult: unknown,
|
|
8602
|
-
): Promise<
|
|
8737
|
+
): Promise<Map<string, number>> {
|
|
8603
8738
|
const persistedNamespaces = new Set<string>();
|
|
8739
|
+
const persistedCounts = new Map<string, number>();
|
|
8604
8740
|
for (const dataset of resultDatasets) {
|
|
8605
8741
|
if (dataset.datasetKind === 'map') continue;
|
|
8606
8742
|
const handle = dataset.handle as WorkerDatasetHandle<
|
|
@@ -8639,6 +8775,7 @@ async function persistResultDatasets(
|
|
|
8639
8775
|
inputOffset += chunk.length;
|
|
8640
8776
|
}
|
|
8641
8777
|
persistedNamespaces.add(dataset.tableNamespace);
|
|
8778
|
+
persistedCounts.set(dataset.tableNamespace, inputOffset);
|
|
8642
8779
|
}
|
|
8643
8780
|
|
|
8644
8781
|
const datasets = collectDatasetEnvelopes(serializedResult);
|
|
@@ -8672,7 +8809,9 @@ async function persistResultDatasets(
|
|
|
8672
8809
|
attemptLeaseTtlMs: runtimeSheetAttemptLeaseTtlMs(req),
|
|
8673
8810
|
userEmail: req.userEmail,
|
|
8674
8811
|
});
|
|
8812
|
+
persistedCounts.set(dataset.tableNamespace, dataset.rows.length);
|
|
8675
8813
|
}
|
|
8814
|
+
return persistedCounts;
|
|
8676
8815
|
}
|
|
8677
8816
|
|
|
8678
8817
|
const RESULT_DATASET_PERSIST_CHUNK_ROWS = 5_000;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function formatCustomerConsoleValue(value: unknown): string {
|
|
2
|
+
if (typeof value === 'string') return value;
|
|
3
|
+
try {
|
|
4
|
+
if (value instanceof Error) return value.stack ?? value.message;
|
|
5
|
+
if (value && typeof value === 'object') {
|
|
6
|
+
const seen = new WeakSet<object>();
|
|
7
|
+
const serialized = JSON.stringify(value, (_key, nested) => {
|
|
8
|
+
if (typeof nested === 'symbol' || typeof nested === 'bigint') {
|
|
9
|
+
return nested.toString();
|
|
10
|
+
}
|
|
11
|
+
if (nested && typeof nested === 'object') {
|
|
12
|
+
if (seen.has(nested)) return '[Circular]';
|
|
13
|
+
seen.add(nested);
|
|
14
|
+
}
|
|
15
|
+
return nested;
|
|
16
|
+
});
|
|
17
|
+
if (serialized !== undefined) return serialized;
|
|
18
|
+
}
|
|
19
|
+
return String(value);
|
|
20
|
+
} catch {
|
|
21
|
+
return '[Unprintable]';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -229,7 +229,7 @@ type WorkerToolCompletionResult =
|
|
|
229
229
|
const WORKER_TOOL_BATCH_GRACE_MS = 250;
|
|
230
230
|
const COMPLETE_RECEIPT_RETRY_BACKOFFS_MS = [1_000, 3_000, 9_000] as const;
|
|
231
231
|
const COMPLETE_RECEIPT_RETRY_MIN_HEADROOM_MS = 10_000;
|
|
232
|
-
export const WORKER_TOOL_COMPLETE_RECEIPTS_MAX_BATCH_BYTES =
|
|
232
|
+
export const WORKER_TOOL_COMPLETE_RECEIPTS_MAX_BATCH_BYTES = 32 * 1024 * 1024;
|
|
233
233
|
export const WORKER_TOOL_COMPLETE_RECEIPTS_MAX_BATCH_MEMBERS = 500;
|
|
234
234
|
export const WORKER_TOOL_RECEIPT_RECOVERY_MAX_CONCURRENT_READS = 25;
|
|
235
235
|
|
|
@@ -282,9 +282,23 @@ export type RunsListOptions = {
|
|
|
282
282
|
limit?: number;
|
|
283
283
|
};
|
|
284
284
|
|
|
285
|
+
/** Options for `client.runs.get(...)`. */
|
|
286
|
+
export type RunsGetOptions = {
|
|
287
|
+
/** Return the raw status payload instead of the compact package. */
|
|
288
|
+
full?: boolean;
|
|
289
|
+
/** Attach a bounded end-of-stream window for a failed run. */
|
|
290
|
+
failedLogs?: boolean;
|
|
291
|
+
/** Requested failed-log lines to attach (default 20, hard-capped at 20). */
|
|
292
|
+
failedLogLimit?: number;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const RUNS_FAILED_LOG_LIMIT = 20;
|
|
296
|
+
|
|
285
297
|
/** Streaming options for `client.runs.tail(...)`. */
|
|
286
298
|
export type RunsTailOptions = {
|
|
287
299
|
signal?: AbortSignal;
|
|
300
|
+
/** Observe each canonical live event while `tail` waits for terminal state. */
|
|
301
|
+
onEvent?: (event: PlayLiveEvent) => void;
|
|
288
302
|
/**
|
|
289
303
|
* Called before each stream reconnect. Server stream windows are finite, so
|
|
290
304
|
* long runs reconnect with backoff until a terminal status is observed.
|
|
@@ -308,6 +322,8 @@ export type RunsLogsOptions = {
|
|
|
308
322
|
limit?: number;
|
|
309
323
|
/** Fetch every stored log line, paginating to the full totalCount. */
|
|
310
324
|
all?: boolean;
|
|
325
|
+
/** Select the bounded failure view; truncated runs degrade explicitly. */
|
|
326
|
+
failed?: boolean;
|
|
311
327
|
};
|
|
312
328
|
|
|
313
329
|
/** Persisted log response for one play run. */
|
|
@@ -320,6 +336,14 @@ export type RunsLogsResult = {
|
|
|
320
336
|
truncated: boolean;
|
|
321
337
|
hasMore: boolean;
|
|
322
338
|
entries: string[];
|
|
339
|
+
/** Selected public log view. */
|
|
340
|
+
view?: 'tail' | 'failed' | 'all';
|
|
341
|
+
/** Whether entries are terminal context or the last pre-truncation lines. */
|
|
342
|
+
association?: 'terminal_failure_window' | 'retained_before_truncation';
|
|
343
|
+
/** Loud explanation when the terminal failure window was not retained. */
|
|
344
|
+
warning?: string;
|
|
345
|
+
/** Exact follow-up command when the selected view is degraded. */
|
|
346
|
+
next?: { logs: string };
|
|
323
347
|
/**
|
|
324
348
|
* True when the run crossed the Run Log Stream retention cap: `totalCount`
|
|
325
349
|
* keeps counting, but stored line bodies end at a loud truncation marker.
|
|
@@ -342,6 +366,10 @@ type RunLogsPageResponse = {
|
|
|
342
366
|
lastSeq: number | null;
|
|
343
367
|
hasMore: boolean;
|
|
344
368
|
nextAfterSeq: number | null;
|
|
369
|
+
view?: 'failed';
|
|
370
|
+
association?: 'terminal_failure_window' | 'retained_before_truncation';
|
|
371
|
+
warning?: string;
|
|
372
|
+
next?: { logs: string };
|
|
345
373
|
};
|
|
346
374
|
|
|
347
375
|
/** One persisted runtime-sheet row returned by `client.runs.exportDatasetRows(...)`. */
|
|
@@ -395,7 +423,7 @@ export type PlaySecretMetadata = {
|
|
|
395
423
|
*/
|
|
396
424
|
export type RunsNamespace = {
|
|
397
425
|
/** Get current run status by public run id. */
|
|
398
|
-
get: (runId: string, options?:
|
|
426
|
+
get: (runId: string, options?: RunsGetOptions) => Promise<PlayStatus>;
|
|
399
427
|
/** List runs for one play, optionally filtered by status. */
|
|
400
428
|
list: (options: RunsListOptions) => Promise<PlayRunListItem[]>;
|
|
401
429
|
/** Stream run events and return the latest/terminal run status. */
|
|
@@ -2469,7 +2497,7 @@ export class DeeplineClient {
|
|
|
2469
2497
|
*/
|
|
2470
2498
|
async getRunStatus(
|
|
2471
2499
|
runId: string,
|
|
2472
|
-
options?:
|
|
2500
|
+
options?: RunsGetOptions,
|
|
2473
2501
|
): Promise<PlayStatus> {
|
|
2474
2502
|
const params = new URLSearchParams();
|
|
2475
2503
|
if (options?.full === true) {
|
|
@@ -2479,7 +2507,55 @@ export class DeeplineClient {
|
|
|
2479
2507
|
const response = await this.http.get<Record<string, unknown>>(
|
|
2480
2508
|
`/api/v2/runs/${encodeURIComponent(runId)}${query}`,
|
|
2481
2509
|
);
|
|
2482
|
-
|
|
2510
|
+
const status = normalizePlayStatus(response);
|
|
2511
|
+
if (options?.failedLogs !== true || status.status !== 'failed') {
|
|
2512
|
+
return status;
|
|
2513
|
+
}
|
|
2514
|
+
const requestedFailedLogLimit =
|
|
2515
|
+
typeof options.failedLogLimit === 'number' &&
|
|
2516
|
+
Number.isFinite(options.failedLogLimit)
|
|
2517
|
+
? Math.max(1, Math.floor(options.failedLogLimit))
|
|
2518
|
+
: RUNS_FAILED_LOG_LIMIT;
|
|
2519
|
+
let failedLogs: RunsLogsResult;
|
|
2520
|
+
let failedLogsLoaded = true;
|
|
2521
|
+
try {
|
|
2522
|
+
failedLogs = await this.getRunLogs(runId, {
|
|
2523
|
+
failed: true,
|
|
2524
|
+
limit: Math.min(RUNS_FAILED_LOG_LIMIT, requestedFailedLogLimit),
|
|
2525
|
+
});
|
|
2526
|
+
} catch (error) {
|
|
2527
|
+
failedLogsLoaded = false;
|
|
2528
|
+
const retryCommand = `deepline runs get ${runId} --log-failed --json`;
|
|
2529
|
+
const reason =
|
|
2530
|
+
error instanceof Error && error.message.trim()
|
|
2531
|
+
? ` (${error.message.trim().slice(0, 500)})`
|
|
2532
|
+
: '';
|
|
2533
|
+
failedLogs = {
|
|
2534
|
+
runId,
|
|
2535
|
+
totalCount: 0,
|
|
2536
|
+
returnedCount: 0,
|
|
2537
|
+
firstSequence: null,
|
|
2538
|
+
lastSequence: null,
|
|
2539
|
+
truncated: false,
|
|
2540
|
+
hasMore: false,
|
|
2541
|
+
entries: [],
|
|
2542
|
+
view: 'failed',
|
|
2543
|
+
warning: `Failed logs could not be loaded${reason}. The run status and persisted datasets are still available.`,
|
|
2544
|
+
next: { logs: retryCommand },
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
return {
|
|
2548
|
+
...status,
|
|
2549
|
+
failedLogs: {
|
|
2550
|
+
...failedLogs,
|
|
2551
|
+
view: 'failed' as const,
|
|
2552
|
+
...(failedLogsLoaded
|
|
2553
|
+
? {
|
|
2554
|
+
association: failedLogs.association ?? 'terminal_failure_window',
|
|
2555
|
+
}
|
|
2556
|
+
: {}),
|
|
2557
|
+
},
|
|
2558
|
+
};
|
|
2483
2559
|
}
|
|
2484
2560
|
|
|
2485
2561
|
/**
|
|
@@ -2657,6 +2733,7 @@ export class DeeplineClient {
|
|
|
2657
2733
|
onNotice: options?.onNotice,
|
|
2658
2734
|
fallback: 'none',
|
|
2659
2735
|
})) {
|
|
2736
|
+
options?.onEvent?.(event);
|
|
2660
2737
|
const status = updatePlayLiveStatusState(state, event);
|
|
2661
2738
|
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
2662
2739
|
continue;
|
|
@@ -2725,6 +2802,7 @@ export class DeeplineClient {
|
|
|
2725
2802
|
mode: 'cli',
|
|
2726
2803
|
signal: options?.signal,
|
|
2727
2804
|
})) {
|
|
2805
|
+
options?.onEvent?.(event);
|
|
2728
2806
|
sawEvent = true;
|
|
2729
2807
|
const status = updatePlayLiveStatusState(state, event);
|
|
2730
2808
|
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
@@ -2786,6 +2864,42 @@ export class DeeplineClient {
|
|
|
2786
2864
|
runId: string,
|
|
2787
2865
|
options?: RunsLogsOptions,
|
|
2788
2866
|
): Promise<RunsLogsResult> {
|
|
2867
|
+
if (options?.all === true && options.failed === true) {
|
|
2868
|
+
throw new DeeplineError(
|
|
2869
|
+
'runs.logs cannot combine all and failed views.',
|
|
2870
|
+
undefined,
|
|
2871
|
+
'INVALID_RUN_LOG_VIEW',
|
|
2872
|
+
);
|
|
2873
|
+
}
|
|
2874
|
+
if (options?.failed === true) {
|
|
2875
|
+
const requestedLimit =
|
|
2876
|
+
typeof options.limit === 'number' &&
|
|
2877
|
+
Number.isFinite(options.limit) &&
|
|
2878
|
+
options.limit > 0
|
|
2879
|
+
? Math.trunc(options.limit)
|
|
2880
|
+
: RUNS_FAILED_LOG_LIMIT;
|
|
2881
|
+
const failedLimit = Math.min(RUNS_FAILED_LOG_LIMIT, requestedLimit);
|
|
2882
|
+
const page = await this.http.get<RunLogsPageResponse>(
|
|
2883
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/logs?view=failed&limit=${failedLimit}`,
|
|
2884
|
+
);
|
|
2885
|
+
return {
|
|
2886
|
+
runId: page.runId,
|
|
2887
|
+
totalCount: page.totalLogCount,
|
|
2888
|
+
returnedCount: page.entries.length,
|
|
2889
|
+
firstSequence: page.firstSeq,
|
|
2890
|
+
lastSequence: page.lastSeq,
|
|
2891
|
+
// The failed view is a complete designed window, not a pageable slice.
|
|
2892
|
+
// `truncated` means retention loss here; association/warning explain it.
|
|
2893
|
+
truncated: page.logsTruncated === true,
|
|
2894
|
+
hasMore: false,
|
|
2895
|
+
entries: page.entries.map((entry) => entry.line),
|
|
2896
|
+
view: page.view ?? 'failed',
|
|
2897
|
+
association: page.association ?? 'terminal_failure_window',
|
|
2898
|
+
...(page.warning ? { warning: page.warning } : {}),
|
|
2899
|
+
...(page.next ? { next: page.next } : {}),
|
|
2900
|
+
...(page.logsTruncated ? { logsTruncated: true } : {}),
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2789
2903
|
const limit = options?.all
|
|
2790
2904
|
? Number.MAX_SAFE_INTEGER
|
|
2791
2905
|
: typeof options?.limit === 'number' &&
|
|
@@ -2830,6 +2944,7 @@ export class DeeplineClient {
|
|
|
2830
2944
|
truncated: entries.length < probe.totalLogCount,
|
|
2831
2945
|
hasMore: lastSequence !== null && lastSequence < lastStoredSeq,
|
|
2832
2946
|
entries: entries.map((entry) => entry.line),
|
|
2947
|
+
view: options?.all ? 'all' : 'tail',
|
|
2833
2948
|
...(probe.logsTruncated ? { logsTruncated: true } : {}),
|
|
2834
2949
|
};
|
|
2835
2950
|
}
|
|
@@ -39,7 +39,7 @@ export type {
|
|
|
39
39
|
|
|
40
40
|
export { extractDefinedPlayName } from '../../../shared_libs/plays/bundling/index.js';
|
|
41
41
|
|
|
42
|
-
const PLAY_BUNDLE_CACHE_VERSION =
|
|
42
|
+
const PLAY_BUNDLE_CACHE_VERSION = 31;
|
|
43
43
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
44
44
|
const SDK_PACKAGE_ROOT = resolve(MODULE_DIR, '..', '..');
|
|
45
45
|
const SOURCE_REPO_ROOT = resolve(SDK_PACKAGE_ROOT, '..');
|
|
@@ -106,10 +106,10 @@ 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
|
-
version: '0.1.
|
|
109
|
+
version: '0.1.212',
|
|
110
110
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
111
111
|
supportPolicy: {
|
|
112
|
-
latest: '0.1.
|
|
112
|
+
latest: '0.1.212',
|
|
113
113
|
minimumSupported: '0.1.53',
|
|
114
114
|
deprecatedBelow: '0.1.53',
|
|
115
115
|
commandMinimumSupported: [
|
|
@@ -463,6 +463,14 @@ export type PlayRunActionPackage =
|
|
|
463
463
|
runId: string;
|
|
464
464
|
datasetPath: string;
|
|
465
465
|
format: 'csv';
|
|
466
|
+
}
|
|
467
|
+
| {
|
|
468
|
+
kind: 'deepline_run_logs';
|
|
469
|
+
runId: string;
|
|
470
|
+
view: 'failed' | 'tail';
|
|
471
|
+
limit: number;
|
|
472
|
+
command: string;
|
|
473
|
+
api: { method: 'GET'; path: string };
|
|
466
474
|
};
|
|
467
475
|
|
|
468
476
|
/**
|
|
@@ -493,11 +501,30 @@ export interface PlayRunPackage {
|
|
|
493
501
|
steps: Array<Record<string, unknown>>;
|
|
494
502
|
/** Named output summaries, including dataset handles and scalar outputs. */
|
|
495
503
|
outputs: Record<string, Record<string, unknown>>;
|
|
504
|
+
/** Every durable Dataset Handle explicitly registered by this run. */
|
|
505
|
+
datasets?: Array<{
|
|
506
|
+
kind: 'dataset';
|
|
507
|
+
datasetId?: string;
|
|
508
|
+
path: string;
|
|
509
|
+
tableNamespace?: string;
|
|
510
|
+
rowCount?: number;
|
|
511
|
+
recovered?: true;
|
|
512
|
+
preview?: Record<string, unknown>;
|
|
513
|
+
actions?: Record<string, PlayRunActionPackage>;
|
|
514
|
+
}>;
|
|
515
|
+
/** Small retained tail of customer and runtime logs; fetch the full stream through `runs.logs`. */
|
|
516
|
+
logs?: {
|
|
517
|
+
tail: string[];
|
|
518
|
+
totalCount: number;
|
|
519
|
+
returnedCount: number;
|
|
520
|
+
truncated?: boolean;
|
|
521
|
+
};
|
|
496
522
|
/** Follow-up actions a caller can perform against the run. */
|
|
497
523
|
next?: {
|
|
498
524
|
inspect?: PlayRunActionPackage;
|
|
499
525
|
export?: PlayRunActionPackage;
|
|
500
526
|
query?: PlayRunActionPackage;
|
|
527
|
+
logs?: PlayRunActionPackage;
|
|
501
528
|
};
|
|
502
529
|
}
|
|
503
530
|
|
|
@@ -524,6 +551,8 @@ export interface PlayStatus {
|
|
|
524
551
|
apiVersion?: number;
|
|
525
552
|
/** Saved play name for this run, when available. */
|
|
526
553
|
name?: string;
|
|
554
|
+
/** Exact saved revision launched for this run, when applicable. */
|
|
555
|
+
revisionId?: string;
|
|
527
556
|
/** Alias for `name` used by run/result APIs. */
|
|
528
557
|
playName?: string;
|
|
529
558
|
/** Dashboard URL for inspecting the play and its run output in the app. */
|
|
@@ -564,6 +593,24 @@ export interface PlayStatus {
|
|
|
564
593
|
} | null;
|
|
565
594
|
/** Structured follow-up actions for inspect/query/export. */
|
|
566
595
|
next?: PlayRunPackage['next'] | Record<string, unknown>;
|
|
596
|
+
/** Bounded terminal-failure log window requested by `runs.get`. */
|
|
597
|
+
failedLogs?: {
|
|
598
|
+
runId: string;
|
|
599
|
+
totalCount: number;
|
|
600
|
+
returnedCount: number;
|
|
601
|
+
firstSequence: number | null;
|
|
602
|
+
lastSequence: number | null;
|
|
603
|
+
truncated: boolean;
|
|
604
|
+
hasMore: boolean;
|
|
605
|
+
entries: string[];
|
|
606
|
+
view?: 'failed';
|
|
607
|
+
association?: 'terminal_failure_window' | 'retained_before_truncation';
|
|
608
|
+
warning?: string;
|
|
609
|
+
next?: { logs: string };
|
|
610
|
+
logsTruncated?: boolean;
|
|
611
|
+
};
|
|
612
|
+
/** Exact ordinary `plays run` command that can rerun a failed execution. */
|
|
613
|
+
rerunCommand?: string;
|
|
567
614
|
}
|
|
568
615
|
|
|
569
616
|
export type LiveEventScope = 'play' | 'agent';
|