deepline 0.3.247 → 0.3.249
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/sdk/src/client.ts +177 -13
- package/dist/bundling-sources/sdk/src/index.ts +8 -0
- package/dist/bundling-sources/sdk/src/plays/input-example.ts +40 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/runs/overview.ts +457 -0
- package/dist/bundling-sources/sdk/src/runs/receipts.ts +23 -0
- package/dist/bundling-sources/sdk/src/tool-calls.ts +21 -1
- package/dist/bundling-sources/sdk/src/types.ts +16 -1
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +19 -1
- package/dist/bundling-sources/shared_libs/play-data-plane/column-names.ts +17 -3
- package/dist/bundling-sources/shared_libs/play-data-plane/sheet-contract.ts +40 -8
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +26 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +144 -14
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +2 -1
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-runtime-config.ts +6 -3
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +26 -21
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +23 -21
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +105 -0
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +8 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +40 -4
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +50 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-lifecycle-policy.ts +3 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +28 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +14 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +56 -16
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-control-transport.ts +184 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +327 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +87 -30
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +24 -13
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +23 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +76 -33
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +90 -35
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +112 -50
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +447 -77
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +22 -4
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +105 -121
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +1648 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-queue-health.ts +16 -19
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +14 -6
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +40 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +5 -2
- package/dist/bundling-sources/shared_libs/plays/dataset-summary.ts +11 -7
- package/dist/bundling-sources/shared_libs/plays/static-pipeline-storage-codec.ts +5 -2
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +11 -2
- package/dist/cli/index.js +3504 -2368
- package/dist/cli/index.mjs +3188 -2052
- package/dist/{compiler-manifest-n8ZtPv2h.d.mts → compiler-manifest-DavmLa8y.d.mts} +1 -0
- package/dist/{compiler-manifest-n8ZtPv2h.d.ts → compiler-manifest-DavmLa8y.d.ts} +1 -0
- package/dist/index.d.mts +97 -5
- package/dist/index.d.ts +97 -5
- package/dist/index.js +135 -15
- package/dist/index.mjs +135 -15
- package/dist/install-integrity.json +9 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/bundle-play-file.mjs +1 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import type { PlayStatus } from '../types';
|
|
2
|
+
import { DATASET_EXECUTION_BUCKETS } from '../../../shared_libs/plays/dataset-summary';
|
|
3
|
+
|
|
4
|
+
type RecordValue = Record<string, unknown>;
|
|
5
|
+
const record = (value: unknown): RecordValue | undefined =>
|
|
6
|
+
value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
7
|
+
? (value as RecordValue)
|
|
8
|
+
: undefined;
|
|
9
|
+
|
|
10
|
+
/** Presentation only: exports and retained evidence keep the original metadata. */
|
|
11
|
+
function excludePreviewMetadata(value: unknown): unknown {
|
|
12
|
+
if (Array.isArray(value)) return value.map(excludePreviewMetadata);
|
|
13
|
+
const fields = record(value);
|
|
14
|
+
if (!fields) return value;
|
|
15
|
+
return Object.fromEntries(
|
|
16
|
+
Object.entries(fields).map(([name, child]) => [
|
|
17
|
+
name,
|
|
18
|
+
name === '_dl_meta'
|
|
19
|
+
? '<excluded from preview>'
|
|
20
|
+
: excludePreviewMetadata(child),
|
|
21
|
+
]),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function lifecycleActions(status: PlayStatus, id: string, state?: string) {
|
|
26
|
+
return {
|
|
27
|
+
...(['started', 'queued', 'running', 'waiting'].includes(state ?? '')
|
|
28
|
+
? { stopRunCmd: `deepline runs stop ${id} --json` }
|
|
29
|
+
: {}),
|
|
30
|
+
...(state === 'failed'
|
|
31
|
+
? {
|
|
32
|
+
getFailedLogsCmd: `deepline runs get ${id} --log-failed --json`,
|
|
33
|
+
...(status.rerunCommand ? { rerunCmd: status.rerunCommand } : {}),
|
|
34
|
+
}
|
|
35
|
+
: {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function withoutProviderSpend(value: unknown): unknown {
|
|
40
|
+
if (Array.isArray(value)) return value.map(withoutProviderSpend);
|
|
41
|
+
const source = record(value);
|
|
42
|
+
if (!source) return value;
|
|
43
|
+
return Object.fromEntries(
|
|
44
|
+
Object.entries(source)
|
|
45
|
+
.filter(
|
|
46
|
+
([key]) => key !== 'providerCostUsd' && key !== 'totalProviderCostUsd',
|
|
47
|
+
)
|
|
48
|
+
.map(([key, child]) => [key, withoutProviderSpend(child)]),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function executionSummary(value: unknown, conflictingPopulation: boolean) {
|
|
53
|
+
const source = record(value);
|
|
54
|
+
if (!source)
|
|
55
|
+
return {
|
|
56
|
+
recordedAttempts: null,
|
|
57
|
+
countConsistency: 'unknown',
|
|
58
|
+
coverage: 'unknown',
|
|
59
|
+
byStatus: {},
|
|
60
|
+
};
|
|
61
|
+
const states: Record<string, { count: number; percent: number }> = {};
|
|
62
|
+
const totals = new Set<number>();
|
|
63
|
+
let sum = 0;
|
|
64
|
+
let valid = true;
|
|
65
|
+
for (const [name, text] of Object.entries(source)) {
|
|
66
|
+
const match =
|
|
67
|
+
typeof text === 'string' && /^(\d+)\/(\d+) \((\d+)%\)$/.exec(text);
|
|
68
|
+
if (!match) {
|
|
69
|
+
valid = false;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const [, numerator, denominator, percentage] = match;
|
|
73
|
+
const count = Number(numerator),
|
|
74
|
+
total = Number(denominator),
|
|
75
|
+
percent = Number(percentage);
|
|
76
|
+
if (
|
|
77
|
+
![count, total, percent].every(Number.isSafeInteger) ||
|
|
78
|
+
count > total ||
|
|
79
|
+
percent !== (total ? Math.round((count * 100) / total) : 0)
|
|
80
|
+
)
|
|
81
|
+
valid = false;
|
|
82
|
+
totals.add(total);
|
|
83
|
+
sum += count;
|
|
84
|
+
if (count !== 0) states[name] = { count, percent };
|
|
85
|
+
}
|
|
86
|
+
const total = totals.size === 1 ? [...totals][0] : null;
|
|
87
|
+
const reconciled =
|
|
88
|
+
valid &&
|
|
89
|
+
total !== null &&
|
|
90
|
+
sum === total &&
|
|
91
|
+
DATASET_EXECUTION_BUCKETS.every((name) => name in source);
|
|
92
|
+
return {
|
|
93
|
+
recordedAttempts: total,
|
|
94
|
+
countConsistency: reconciled ? 'consistent' : 'inconsistent',
|
|
95
|
+
coverage: conflictingPopulation ? 'conflicting' : 'unknown',
|
|
96
|
+
byStatus: states,
|
|
97
|
+
...(!valid ? { unparsed: source } : {}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function datasetSummary(value: unknown, conflictingPopulation: boolean) {
|
|
102
|
+
const summary = record(value);
|
|
103
|
+
if (!summary) return null;
|
|
104
|
+
const columns = record(summary.columnStats);
|
|
105
|
+
const {
|
|
106
|
+
total_rows,
|
|
107
|
+
rowCounts,
|
|
108
|
+
columnStats: _columnStats,
|
|
109
|
+
...other
|
|
110
|
+
} = summary;
|
|
111
|
+
return {
|
|
112
|
+
...other,
|
|
113
|
+
reportedPopulation: {
|
|
114
|
+
totalRows: total_rows ?? record(rowCounts)?.persisted ?? null,
|
|
115
|
+
persistedRows: record(rowCounts)?.persisted ?? null,
|
|
116
|
+
succeededRows: record(rowCounts)?.succeeded ?? null,
|
|
117
|
+
failedRows: record(rowCounts)?.failed ?? null,
|
|
118
|
+
},
|
|
119
|
+
coverage: conflictingPopulation ? 'conflicting' : 'unknown',
|
|
120
|
+
...(columns
|
|
121
|
+
? {
|
|
122
|
+
columnStats: Object.fromEntries(
|
|
123
|
+
Object.entries(columns).map(([name, value]) => {
|
|
124
|
+
const column = record(value);
|
|
125
|
+
const { execution, ...rest } = column ?? {};
|
|
126
|
+
return [
|
|
127
|
+
name,
|
|
128
|
+
column && 'execution' in column
|
|
129
|
+
? {
|
|
130
|
+
...rest,
|
|
131
|
+
executionStatusCounts: executionSummary(
|
|
132
|
+
execution,
|
|
133
|
+
conflictingPopulation,
|
|
134
|
+
),
|
|
135
|
+
}
|
|
136
|
+
: value,
|
|
137
|
+
];
|
|
138
|
+
}),
|
|
139
|
+
),
|
|
140
|
+
}
|
|
141
|
+
: {}),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Action descriptions are affordances, never evidence that an action ran. */
|
|
146
|
+
function actions(value: unknown, names: Record<string, string>) {
|
|
147
|
+
return Object.fromEntries(
|
|
148
|
+
Object.entries(record(value) ?? {}).flatMap(([name, value]) => {
|
|
149
|
+
const action = record(value);
|
|
150
|
+
if (!action || action.deprecated) return [];
|
|
151
|
+
if (!(name in names) || typeof action.command !== 'string') return [];
|
|
152
|
+
return [
|
|
153
|
+
[
|
|
154
|
+
names[name],
|
|
155
|
+
action.kind === 'deepline_run_billing'
|
|
156
|
+
? action.command.replace(
|
|
157
|
+
"jq '.billing'",
|
|
158
|
+
"jq '.diagnostics.billing'",
|
|
159
|
+
)
|
|
160
|
+
: action.command,
|
|
161
|
+
],
|
|
162
|
+
];
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** A read-only overview of recorded run facts, never a complete row export. */
|
|
168
|
+
export function buildRunOverview(status: PlayStatus, fullStatus?: PlayStatus) {
|
|
169
|
+
const pkg = status.package;
|
|
170
|
+
if (!pkg) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
'This server did not return a run package; use runs get --full --json.',
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const outputs = Object.entries(pkg.outputs);
|
|
176
|
+
const resultPackage = fullStatus?.package?.resultState
|
|
177
|
+
? fullStatus.package
|
|
178
|
+
: pkg;
|
|
179
|
+
const warnings = [...(pkg.warnings ?? [])];
|
|
180
|
+
const { outcome: _outcome, ...run } = pkg.run;
|
|
181
|
+
const steps = pkg.steps.map((step) => {
|
|
182
|
+
const timing = record(step.timing);
|
|
183
|
+
const started = timing?.startedAt,
|
|
184
|
+
finished = timing?.completedAt;
|
|
185
|
+
return {
|
|
186
|
+
id: step.id,
|
|
187
|
+
kind: step.kind,
|
|
188
|
+
status: step.status,
|
|
189
|
+
progress: step.progress,
|
|
190
|
+
timing: step.timing,
|
|
191
|
+
...(typeof started === 'number' &&
|
|
192
|
+
typeof finished === 'number' &&
|
|
193
|
+
finished >= started
|
|
194
|
+
? { durationMs: finished - started }
|
|
195
|
+
: {}),
|
|
196
|
+
...(step.error ? { error: step.error } : {}),
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
const catalog = [...(pkg.datasets ?? [])];
|
|
200
|
+
for (const value of Object.values(pkg.outputs)) {
|
|
201
|
+
if (
|
|
202
|
+
value.kind === 'dataset' &&
|
|
203
|
+
typeof value.path === 'string' &&
|
|
204
|
+
!catalog.some((entry) =>
|
|
205
|
+
value.datasetId
|
|
206
|
+
? entry.datasetId === value.datasetId
|
|
207
|
+
: entry.path === value.path,
|
|
208
|
+
)
|
|
209
|
+
) {
|
|
210
|
+
catalog.push({
|
|
211
|
+
kind: 'dataset',
|
|
212
|
+
path: value.path,
|
|
213
|
+
...(typeof value.datasetId === 'string'
|
|
214
|
+
? { datasetId: value.datasetId }
|
|
215
|
+
: {}),
|
|
216
|
+
...(typeof value.rowCount === 'number'
|
|
217
|
+
? { rowCount: value.rowCount }
|
|
218
|
+
: {}),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const uniqueCatalog = catalog.filter(
|
|
223
|
+
(entry, index) =>
|
|
224
|
+
catalog.findIndex((candidate) =>
|
|
225
|
+
entry.datasetId
|
|
226
|
+
? candidate.datasetId === entry.datasetId
|
|
227
|
+
: candidate.path === entry.path && !candidate.datasetId,
|
|
228
|
+
) === index,
|
|
229
|
+
);
|
|
230
|
+
const datasets = uniqueCatalog.map((dataset) => {
|
|
231
|
+
const step = pkg.steps.find((candidate) => {
|
|
232
|
+
const output = record(candidate.output);
|
|
233
|
+
return (
|
|
234
|
+
output &&
|
|
235
|
+
(dataset.datasetId
|
|
236
|
+
? output.datasetId === dataset.datasetId
|
|
237
|
+
: output.path === dataset.path)
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
const stepOutput = record(step?.output);
|
|
241
|
+
const preview = record(stepOutput?.preview) ?? dataset.preview;
|
|
242
|
+
// Also project historical/server previews that still contain debug columns.
|
|
243
|
+
const rows = Array.isArray(preview?.rows)
|
|
244
|
+
? preview.rows.map((row: unknown) => {
|
|
245
|
+
const fields = record(row);
|
|
246
|
+
return fields
|
|
247
|
+
? Object.fromEntries(
|
|
248
|
+
Object.entries(fields)
|
|
249
|
+
.filter(([name]) => !name.startsWith('_'))
|
|
250
|
+
.map(([name, value]) => [
|
|
251
|
+
name,
|
|
252
|
+
excludePreviewMetadata(value),
|
|
253
|
+
]),
|
|
254
|
+
)
|
|
255
|
+
: row;
|
|
256
|
+
})
|
|
257
|
+
: [];
|
|
258
|
+
const total =
|
|
259
|
+
typeof preview?.totalRows === 'number'
|
|
260
|
+
? preview.totalRows
|
|
261
|
+
: dataset.rowCount;
|
|
262
|
+
const named = outputs.filter(
|
|
263
|
+
([, value]) =>
|
|
264
|
+
value.kind === 'dataset' &&
|
|
265
|
+
(dataset.datasetId
|
|
266
|
+
? value.datasetId === dataset.datasetId
|
|
267
|
+
: value.path === dataset.path),
|
|
268
|
+
);
|
|
269
|
+
const summary = record(record(dataset)?.summary ?? stepOutput?.summary);
|
|
270
|
+
const rowCounts = record(summary?.rowCounts);
|
|
271
|
+
const progress = record(step?.progress);
|
|
272
|
+
const countConflict =
|
|
273
|
+
(typeof rowCounts?.persisted === 'number' &&
|
|
274
|
+
typeof dataset.rowCount === 'number' &&
|
|
275
|
+
rowCounts.persisted !== dataset.rowCount) ||
|
|
276
|
+
(typeof rowCounts?.failed === 'number' &&
|
|
277
|
+
typeof progress?.failed === 'number' &&
|
|
278
|
+
rowCounts.failed !== progress.failed);
|
|
279
|
+
if (countConflict)
|
|
280
|
+
warnings.push(
|
|
281
|
+
`Output ${dataset.path}: dataset summary and step/row counts disagree. Column execution counts may cover only a subset; inspect step progress and exported rows.`,
|
|
282
|
+
);
|
|
283
|
+
return {
|
|
284
|
+
datasetId: dataset.datasetId,
|
|
285
|
+
name: dataset.tableNamespace,
|
|
286
|
+
stepId: step?.id ?? named[0]?.[1].stepId,
|
|
287
|
+
rowCount: dataset.rowCount ?? null,
|
|
288
|
+
...(dataset.lifecycle ? { lifecycle: dataset.lifecycle } : {}),
|
|
289
|
+
summary: datasetSummary(summary, countConflict),
|
|
290
|
+
storage: {
|
|
291
|
+
...(dataset.sqlQualifiedTableName?.includes('.')
|
|
292
|
+
? {
|
|
293
|
+
schema: dataset.sqlQualifiedTableName
|
|
294
|
+
.slice(0, dataset.sqlQualifiedTableName.lastIndexOf('.'))
|
|
295
|
+
.replace(/^"(.*)"$/, '$1')
|
|
296
|
+
.replace(/""/g, '"'),
|
|
297
|
+
}
|
|
298
|
+
: {}),
|
|
299
|
+
table: dataset.sqlTableName,
|
|
300
|
+
},
|
|
301
|
+
preview: {
|
|
302
|
+
population: 'source-reported dataset sample; not an export count',
|
|
303
|
+
availability: rows.length
|
|
304
|
+
? 'available'
|
|
305
|
+
: total === 0
|
|
306
|
+
? 'empty'
|
|
307
|
+
: 'unavailable',
|
|
308
|
+
returnedRows: rows.length,
|
|
309
|
+
totalRows: total ?? null,
|
|
310
|
+
partial:
|
|
311
|
+
preview?.truncated === true ||
|
|
312
|
+
total === undefined ||
|
|
313
|
+
rows.length < total,
|
|
314
|
+
rows,
|
|
315
|
+
},
|
|
316
|
+
...(dataset.exportUnavailable
|
|
317
|
+
? { exportUnavailable: dataset.exportUnavailable }
|
|
318
|
+
: {}),
|
|
319
|
+
recovered: dataset.recovered === true,
|
|
320
|
+
};
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
schemaVersion: pkg.schemaVersion,
|
|
324
|
+
kind: 'run_overview',
|
|
325
|
+
...((status.runStatus ?? pkg.runStatus)
|
|
326
|
+
? { runStatus: status.runStatus ?? pkg.runStatus }
|
|
327
|
+
: {}),
|
|
328
|
+
run: {
|
|
329
|
+
...run,
|
|
330
|
+
...(status.revisionId && !run.revisionId
|
|
331
|
+
? { revisionId: status.revisionId }
|
|
332
|
+
: {}),
|
|
333
|
+
},
|
|
334
|
+
steps,
|
|
335
|
+
failures: {
|
|
336
|
+
steps: steps.flatMap((step) => {
|
|
337
|
+
const progress = record(step.progress);
|
|
338
|
+
const failedRows =
|
|
339
|
+
typeof progress?.failed === 'number' ? progress.failed : undefined;
|
|
340
|
+
return step.status === 'failed' ||
|
|
341
|
+
step.error ||
|
|
342
|
+
(failedRows !== undefined && failedRows > 0)
|
|
343
|
+
? [
|
|
344
|
+
{
|
|
345
|
+
stepId: step.id,
|
|
346
|
+
status: step.status,
|
|
347
|
+
failedRows,
|
|
348
|
+
...(step.error ? { message: step.error } : {}),
|
|
349
|
+
},
|
|
350
|
+
]
|
|
351
|
+
: [];
|
|
352
|
+
}),
|
|
353
|
+
},
|
|
354
|
+
errorDetails: {
|
|
355
|
+
coverage: 'unknown',
|
|
356
|
+
reported: record(pkg)?.errors ?? record(status)?.errors ?? [],
|
|
357
|
+
...(pkg.run.error ? { run: pkg.run.error } : {}),
|
|
358
|
+
steps: steps
|
|
359
|
+
.filter((step) => step.error)
|
|
360
|
+
.map((step) => ({ stepId: step.id, message: step.error })),
|
|
361
|
+
...(pkg.next?.logs ? { getMore: 'actions.getLogsCmd' } : {}),
|
|
362
|
+
},
|
|
363
|
+
rowOutcomes: status.rowOutcomes,
|
|
364
|
+
warnings,
|
|
365
|
+
...(status.failedLogs ? { failedLogs: status.failedLogs } : {}),
|
|
366
|
+
datasets,
|
|
367
|
+
...(Object.hasOwn(resultPackage, 'result')
|
|
368
|
+
? { result: resultPackage.result }
|
|
369
|
+
: {}),
|
|
370
|
+
resultState: resultPackage.resultState ?? 'unavailable',
|
|
371
|
+
actions: {
|
|
372
|
+
...lifecycleActions(status, pkg.run.id, pkg.run.status),
|
|
373
|
+
...actions(pkg.next, {
|
|
374
|
+
inspect: 'getRunCmd',
|
|
375
|
+
full: 'getFullRunCmd',
|
|
376
|
+
billing: 'getBillingCmd',
|
|
377
|
+
logs: 'getLogsCmd',
|
|
378
|
+
}),
|
|
379
|
+
...receiptAction(pkg.run.id),
|
|
380
|
+
...(uniqueCatalog.some((dataset) => dataset.actions?.exportCsv)
|
|
381
|
+
? {
|
|
382
|
+
exportDatasetCmds: uniqueCatalog.flatMap((dataset) => {
|
|
383
|
+
const command = dataset.actions?.exportCsv?.command;
|
|
384
|
+
return command && !dataset.exportUnavailable ? [command] : [];
|
|
385
|
+
}),
|
|
386
|
+
}
|
|
387
|
+
: {}),
|
|
388
|
+
...(uniqueCatalog.some((dataset) => dataset.actions?.query)
|
|
389
|
+
? {
|
|
390
|
+
queryTableSqlCmds: uniqueCatalog.flatMap((dataset) =>
|
|
391
|
+
dataset.actions?.query?.command
|
|
392
|
+
? [dataset.actions.query.command]
|
|
393
|
+
: [],
|
|
394
|
+
),
|
|
395
|
+
}
|
|
396
|
+
: {}),
|
|
397
|
+
},
|
|
398
|
+
// Keep the full diagnostic payload except its duplicate presentation package.
|
|
399
|
+
// Do not maintain an allowlist: it silently lost child runs, usage and future
|
|
400
|
+
// diagnostic fields. Full mode also resolves an explicitly full-only result.
|
|
401
|
+
...(fullStatus
|
|
402
|
+
? {
|
|
403
|
+
diagnostics: Object.fromEntries(
|
|
404
|
+
Object.entries(fullStatus)
|
|
405
|
+
.filter(([key]) => key !== 'package')
|
|
406
|
+
.map(([key, value]) => [key, withoutProviderSpend(value)]),
|
|
407
|
+
),
|
|
408
|
+
}
|
|
409
|
+
: {}),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function receiptAction(id: string) {
|
|
414
|
+
const quotedId = `'${id.replace(/'/g, "'\\''")}'`;
|
|
415
|
+
return {
|
|
416
|
+
getReceiptCmd: `deepline runs receipt ${quotedId} --key '<receipt-key>' --json`,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** Same facts and commands as get; launch intentionally omits row content/statistics. */
|
|
421
|
+
export function buildRunNavigation(status: PlayStatus) {
|
|
422
|
+
if (
|
|
423
|
+
!status.package?.run ||
|
|
424
|
+
!status.package.steps ||
|
|
425
|
+
!status.package.outputs
|
|
426
|
+
) {
|
|
427
|
+
const id = status.runId;
|
|
428
|
+
return {
|
|
429
|
+
run: {
|
|
430
|
+
id,
|
|
431
|
+
playName: status.playName ?? status.name,
|
|
432
|
+
status: status.status,
|
|
433
|
+
},
|
|
434
|
+
datasets: [],
|
|
435
|
+
actions: {
|
|
436
|
+
...lifecycleActions(status, id, status.status),
|
|
437
|
+
getRunCmd: `deepline runs get ${id} --json`,
|
|
438
|
+
getFullRunCmd: `deepline runs get ${id} --full --json`,
|
|
439
|
+
getLogsCmd: `deepline runs logs ${id} --limit 100 --json`,
|
|
440
|
+
...receiptAction(id),
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const { run, datasets, actions } = buildRunOverview(status);
|
|
445
|
+
return {
|
|
446
|
+
run,
|
|
447
|
+
datasets: datasets.map(
|
|
448
|
+
({ datasetId, name, storage, exportUnavailable }) => ({
|
|
449
|
+
datasetId,
|
|
450
|
+
name,
|
|
451
|
+
storage,
|
|
452
|
+
...(exportUnavailable ? { exportUnavailable } : {}),
|
|
453
|
+
}),
|
|
454
|
+
),
|
|
455
|
+
actions,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** A retained result, not a new tool execution or a billing receipt. */
|
|
2
|
+
export type RunToolReceiptResult = {
|
|
3
|
+
receiptKey: string;
|
|
4
|
+
status?: string;
|
|
5
|
+
} & (
|
|
6
|
+
| {
|
|
7
|
+
result: {
|
|
8
|
+
status: string;
|
|
9
|
+
job_id?: string;
|
|
10
|
+
toolResponse: Record<string, unknown>;
|
|
11
|
+
targetValues?: Record<string, unknown>;
|
|
12
|
+
listDatasets?: Record<string, unknown>;
|
|
13
|
+
listRows?: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
error?: never;
|
|
16
|
+
}
|
|
17
|
+
| { result?: never; error: { code: string; message: string } }
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export type RunToolReceiptsResponse = {
|
|
21
|
+
runId: string;
|
|
22
|
+
receipts: RunToolReceiptResult[];
|
|
23
|
+
};
|
|
@@ -168,11 +168,25 @@ export type ExecutionByKeyResult = {
|
|
|
168
168
|
*/
|
|
169
169
|
const APIFY_SYNC_DEFAULT_TIMEOUT_MS = 300_000;
|
|
170
170
|
const APIFY_SYNC_RESPONSE_GRACE_MS = 90_000;
|
|
171
|
+
/**
|
|
172
|
+
* JSearch inherits the server HTTP owner's 120s per-attempt deadline and one
|
|
173
|
+
* eligible network retry, with at most 250ms backoff. HTTP errors and timeouts
|
|
174
|
+
* are not retried by this action. Add the existing response-policy 15s grace.
|
|
175
|
+
* This encloses provider transport with usual route headroom, not the entire
|
|
176
|
+
* route: auth, catalog and financial awaits have no shared aggregate deadline.
|
|
177
|
+
* Keep this client-only policy beside the SDK resolver; it changes no provider
|
|
178
|
+
* request, retry, admission or billing behavior.
|
|
179
|
+
*/
|
|
180
|
+
const JSEARCH_SEARCH_CLIENT_TIMEOUT_MS = 2 * 120_000 + 250 + 15_000;
|
|
171
181
|
function resolveToolExecuteTimeoutMs(
|
|
172
182
|
toolId: string,
|
|
173
183
|
input: Record<string, unknown>,
|
|
184
|
+
configuredRequestTimeoutMs?: number,
|
|
174
185
|
): number | undefined {
|
|
175
186
|
const normalized = toolId.trim().toLowerCase();
|
|
187
|
+
if (normalized === 'openwebninja_jsearch_search') {
|
|
188
|
+
return configuredRequestTimeoutMs ?? JSEARCH_SEARCH_CLIENT_TIMEOUT_MS;
|
|
189
|
+
}
|
|
176
190
|
const lookupTimeoutMs = resolveLookupClientTimeoutMs(normalized);
|
|
177
191
|
if (lookupTimeoutMs !== undefined) return lookupTimeoutMs;
|
|
178
192
|
if (normalized === 'apify_run_actor_sync') {
|
|
@@ -304,6 +318,7 @@ export class ToolCalls {
|
|
|
304
318
|
constructor(
|
|
305
319
|
private readonly http: HttpClient,
|
|
306
320
|
private readonly defaultRequestTimeoutMs: number,
|
|
321
|
+
private readonly configuredRequestTimeoutMs?: number,
|
|
307
322
|
) {}
|
|
308
323
|
|
|
309
324
|
async call<TData = unknown, TMeta = Record<string, unknown>>(
|
|
@@ -342,7 +357,12 @@ export class ToolCalls {
|
|
|
342
357
|
await options?.onExecution?.({ idempotencyKey });
|
|
343
358
|
}
|
|
344
359
|
const timeoutMs =
|
|
345
|
-
options?.timeout ??
|
|
360
|
+
options?.timeout ??
|
|
361
|
+
resolveToolExecuteTimeoutMs(
|
|
362
|
+
toolId,
|
|
363
|
+
inputSnapshot,
|
|
364
|
+
this.configuredRequestTimeoutMs,
|
|
365
|
+
);
|
|
346
366
|
if (idempotencyKey !== undefined) {
|
|
347
367
|
await this.lookupExecutionByKey(idempotencyKey, timeoutMs);
|
|
348
368
|
}
|
|
@@ -747,6 +747,14 @@ export interface PlayRunPackage {
|
|
|
747
747
|
schemaVersion: 1;
|
|
748
748
|
/** Package discriminator. */
|
|
749
749
|
kind: 'play_run';
|
|
750
|
+
/** Retained return structure with dataset references, not flattened output previews. */
|
|
751
|
+
result?: unknown;
|
|
752
|
+
/** Full-only values are omitted as a whole, never replaced with partial authored data. */
|
|
753
|
+
resultState?:
|
|
754
|
+
| 'available'
|
|
755
|
+
| 'available_in_full'
|
|
756
|
+
| 'not_returned'
|
|
757
|
+
| 'unavailable';
|
|
750
758
|
/** Ledger position of the detailed snapshot, when provided by this server. */
|
|
751
759
|
source?: { ledgerNextSeq: number | null };
|
|
752
760
|
/** Legacy status evidence returned by older servers. */
|
|
@@ -756,6 +764,8 @@ export interface PlayRunPackage {
|
|
|
756
764
|
/** Run identity, status, timing, and dashboard metadata. */
|
|
757
765
|
run: {
|
|
758
766
|
id: string;
|
|
767
|
+
/** Revision pinned at admission, never the play's current revision. */
|
|
768
|
+
revisionId?: string;
|
|
759
769
|
playName: string;
|
|
760
770
|
status: string;
|
|
761
771
|
dashboardUrl?: string;
|
|
@@ -785,6 +795,8 @@ export interface PlayRunPackage {
|
|
|
785
795
|
kind: 'dataset';
|
|
786
796
|
datasetId?: string;
|
|
787
797
|
path: string;
|
|
798
|
+
/** Other proven catalog/return paths for this same identity. */
|
|
799
|
+
aliases?: string[];
|
|
788
800
|
tableNamespace?: string;
|
|
789
801
|
rowCount?: number;
|
|
790
802
|
/** The materialized dataset count is exact only after this lifecycle completes. */
|
|
@@ -802,6 +814,7 @@ export interface PlayRunPackage {
|
|
|
802
814
|
reason:
|
|
803
815
|
| 'empty_dataset'
|
|
804
816
|
| 'shared_table_namespace'
|
|
817
|
+
| 'ambiguous_selector'
|
|
805
818
|
| 'returned_dataset_not_persisted';
|
|
806
819
|
message: string;
|
|
807
820
|
};
|
|
@@ -1840,7 +1853,9 @@ export interface PlayCheckRecognizedSummary {
|
|
|
1840
1853
|
triggers?: PlayCheckTriggersSummary;
|
|
1841
1854
|
tools?: string[];
|
|
1842
1855
|
/**
|
|
1843
|
-
* Durable datasets the play produces. `
|
|
1856
|
+
* Durable datasets the play produces. `columns` contains statically known
|
|
1857
|
+
* physical SQL column names, not logical step ids or a complete CSV schema.
|
|
1858
|
+
* `undrawnColumns` echoes the authored field identifiers the
|
|
1844
1859
|
* author declared out of an authored `@mermaid` diagram with
|
|
1845
1860
|
* `.run({ undrawnColumns: [...] })`, so an opt-out is visible in check output.
|
|
1846
1861
|
*/
|
|
@@ -323,6 +323,24 @@
|
|
|
323
323
|
"failureAlert": "github_workflow_run",
|
|
324
324
|
"domainReportRequired": true
|
|
325
325
|
},
|
|
326
|
+
{
|
|
327
|
+
"id": "github.runtime-event-retention",
|
|
328
|
+
"name": "Runtime Event Retention",
|
|
329
|
+
"scheduler": "github_actions",
|
|
330
|
+
"cadence": "17 7 * * *",
|
|
331
|
+
"timezone": "UTC",
|
|
332
|
+
"source": ".github/workflows/runtime-event-retention.yml",
|
|
333
|
+
"locator": "schedule:",
|
|
334
|
+
"owner": "runtime",
|
|
335
|
+
"criticality": "operational",
|
|
336
|
+
"purpose": "Expire Preview and production Play runtime search rows at their declared seven- and thirty-day boundaries.",
|
|
337
|
+
"successDefinition": "Both target-verified cleanup jobs finish and prove no rows remain before their fixed cutoff.",
|
|
338
|
+
"failureDefinition": "A database target is ambiguous, a delete batch fails, the backlog does not converge, or either cleanup job times out.",
|
|
339
|
+
"startGraceMs": 600000,
|
|
340
|
+
"runTimeoutMs": 1800000,
|
|
341
|
+
"failureAlert": "github_workflow_run",
|
|
342
|
+
"domainReportRequired": true
|
|
343
|
+
},
|
|
326
344
|
{
|
|
327
345
|
"id": "github.runtime-shape",
|
|
328
346
|
"name": "Twice-Daily Play Runtime Dataset Matrix",
|
|
@@ -377,7 +395,7 @@
|
|
|
377
395
|
"successDefinition": "Due financial work settles or is durably parked with recovery, witnesses are recorded, and backlog advances.",
|
|
378
396
|
"failureDefinition": "A ledger/provider mismatch is unresolved, parked work lacks recovery, or continuation cannot advance.",
|
|
379
397
|
"startGraceMs": 300000,
|
|
380
|
-
"runTimeoutMs":
|
|
398
|
+
"runTimeoutMs": 300000,
|
|
381
399
|
"failureAlert": "vercel_route_wrapper",
|
|
382
400
|
"domainReportRequired": true
|
|
383
401
|
},
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
const POSTGRES_IDENTIFIER_MAX_BYTES = 63;
|
|
2
2
|
const HASH_SUFFIX_LENGTH = 10;
|
|
3
3
|
|
|
4
|
-
function normalizePlayColumnName(
|
|
4
|
+
function normalizePlayColumnName(
|
|
5
|
+
id: string,
|
|
6
|
+
preserveAuthoredPrefix = true,
|
|
7
|
+
): string {
|
|
8
|
+
const prefix = preserveAuthoredPrefix
|
|
9
|
+
? (id.trim().match(/^_+/)?.[0] ?? '')
|
|
10
|
+
: '';
|
|
5
11
|
const normalized = id
|
|
6
12
|
.trim()
|
|
7
13
|
.replace(/\.+/g, '__')
|
|
8
14
|
.replace(/[^A-Za-z0-9_]+/g, '_')
|
|
9
15
|
.replace(/^_+|_+$/g, '')
|
|
10
16
|
.toLowerCase();
|
|
11
|
-
const safe = normalized || 'column'
|
|
17
|
+
const safe = `${prefix}${normalized || 'column'}`;
|
|
12
18
|
return /^[A-Za-z_]/.test(safe) ? safe : `c_${safe}`;
|
|
13
19
|
}
|
|
14
20
|
|
|
@@ -48,10 +54,18 @@ export function sqlSafePlayColumnName(id: string): string {
|
|
|
48
54
|
return `${prefix}_${hash}`;
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
/** Physical debug column only; never use this as a step or checkpoint id. */
|
|
58
|
+
export function sqlSafePlayStepColumnName(
|
|
59
|
+
parent: string,
|
|
60
|
+
step: string,
|
|
61
|
+
): string {
|
|
62
|
+
return sqlSafePlayColumnName(`_${parent}__step__${step}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
51
65
|
export function legacyPostgresTruncatedPlayColumnName(
|
|
52
66
|
id: string,
|
|
53
67
|
): string | null {
|
|
54
|
-
const prefixed = normalizePlayColumnName(id);
|
|
68
|
+
const prefixed = normalizePlayColumnName(id, false);
|
|
55
69
|
if (prefixed.length <= POSTGRES_IDENTIFIER_MAX_BYTES) {
|
|
56
70
|
return null;
|
|
57
71
|
}
|