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.
Files changed (67) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +177 -13
  2. package/dist/bundling-sources/sdk/src/index.ts +8 -0
  3. package/dist/bundling-sources/sdk/src/plays/input-example.ts +40 -0
  4. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  5. package/dist/bundling-sources/sdk/src/runs/overview.ts +457 -0
  6. package/dist/bundling-sources/sdk/src/runs/receipts.ts +23 -0
  7. package/dist/bundling-sources/sdk/src/tool-calls.ts +21 -1
  8. package/dist/bundling-sources/sdk/src/types.ts +16 -1
  9. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +19 -1
  10. package/dist/bundling-sources/shared_libs/play-data-plane/column-names.ts +17 -3
  11. package/dist/bundling-sources/shared_libs/play-data-plane/sheet-contract.ts +40 -8
  12. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +26 -1
  13. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +144 -14
  14. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +2 -1
  15. package/dist/bundling-sources/shared_libs/play-runtime/daytona-runtime-config.ts +6 -3
  16. package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +26 -21
  17. package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +23 -21
  18. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +17 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +105 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +8 -1
  21. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +1 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +1 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +9 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +40 -4
  25. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +50 -5
  26. package/dist/bundling-sources/shared_libs/play-runtime/run-lifecycle-policy.ts +3 -1
  27. package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +28 -1
  28. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +14 -1
  29. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +56 -16
  30. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-control-transport.ts +184 -0
  31. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +327 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +87 -30
  33. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +24 -13
  34. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +23 -6
  35. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +76 -33
  36. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +90 -35
  37. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +16 -0
  38. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +112 -50
  39. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +447 -77
  40. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +22 -4
  41. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +105 -121
  42. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +1648 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-queue-health.ts +16 -19
  44. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +14 -6
  45. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +16 -0
  46. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +40 -0
  47. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +5 -2
  48. package/dist/bundling-sources/shared_libs/plays/dataset-summary.ts +11 -7
  49. package/dist/bundling-sources/shared_libs/plays/static-pipeline-storage-codec.ts +5 -2
  50. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +11 -2
  51. package/dist/cli/index.js +3504 -2368
  52. package/dist/cli/index.mjs +3188 -2052
  53. package/dist/{compiler-manifest-n8ZtPv2h.d.mts → compiler-manifest-DavmLa8y.d.mts} +1 -0
  54. package/dist/{compiler-manifest-n8ZtPv2h.d.ts → compiler-manifest-DavmLa8y.d.ts} +1 -0
  55. package/dist/index.d.mts +97 -5
  56. package/dist/index.d.ts +97 -5
  57. package/dist/index.js +135 -15
  58. package/dist/index.mjs +135 -15
  59. package/dist/install-integrity.json +9 -2
  60. package/dist/plays/bundle-play-file.d.mts +2 -2
  61. package/dist/plays/bundle-play-file.d.ts +2 -2
  62. package/dist/plays/bundle-play-file.mjs +1 -1
  63. package/dist/release.d.mts +1 -1
  64. package/dist/release.d.ts +1 -1
  65. package/dist/release.js +1 -1
  66. package/dist/release.mjs +1 -1
  67. 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 ?? resolveToolExecuteTimeoutMs(toolId, inputSnapshot);
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. `undrawnColumns` echoes the columns the
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": 1800000,
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(id: string): string {
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
  }