deepline 0.3.289 → 0.3.291

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 (64) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +8 -6
  2. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  3. package/dist/bundling-sources/sdk/src/runs/overview.ts +16 -6
  4. package/dist/bundling-sources/sdk/src/types.ts +21 -2
  5. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +41 -11
  6. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +317 -63
  7. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -2
  8. package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +1 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +3 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts +1 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +36 -0
  12. package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +19 -1
  13. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +6 -5
  14. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +2 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +12 -1
  16. package/dist/bundling-sources/shared_libs/play-runtime/run-event-catalog.ts +2 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +74 -33
  18. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +73 -3
  19. package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +39 -2
  20. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +72 -9
  21. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +2 -3
  22. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +77 -10
  23. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +1 -1
  24. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
  25. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool-options.ts +145 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +17 -9
  27. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -139
  28. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +19 -5
  29. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +176 -18
  30. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +7 -5
  31. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1404 -478
  32. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +55 -25
  33. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +97 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +34 -15
  35. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +558 -57
  36. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +10 -1
  37. package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +6 -1
  38. package/dist/bundling-sources/shared_libs/play-runtime/test-admission-batching.ts +103 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +22 -5
  40. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +35 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +68 -8
  42. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +2 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +75 -28
  44. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +2 -0
  45. package/dist/cli/index.js +195 -582
  46. package/dist/cli/index.mjs +195 -582
  47. package/dist/{client-FR_u0dxT.d.ts → client-Ct4jmvAg.d.ts} +29 -8
  48. package/dist/{client-phK1Ixcl.d.mts → client-Ct9GKrwu.d.mts} +29 -8
  49. package/dist/{compiler-manifest-9SQtUuKb.d.mts → compiler-manifest-qtTNYvBl.d.mts} +2 -0
  50. package/dist/{compiler-manifest-9SQtUuKb.d.ts → compiler-manifest-qtTNYvBl.d.ts} +2 -0
  51. package/dist/index.d.mts +4 -4
  52. package/dist/index.d.ts +4 -4
  53. package/dist/index.js +14 -5
  54. package/dist/index.mjs +14 -5
  55. package/dist/install-integrity.json +7 -4
  56. package/dist/plays/bundle-play-file.d.mts +2 -2
  57. package/dist/plays/bundle-play-file.d.ts +2 -2
  58. package/dist/plays/run-request.d.mts +2 -2
  59. package/dist/plays/run-request.d.ts +2 -2
  60. package/dist/release.d.mts +1 -1
  61. package/dist/release.d.ts +1 -1
  62. package/dist/release.js +1 -1
  63. package/dist/release.mjs +1 -1
  64. package/package.json +1 -1
@@ -36,7 +36,10 @@ import {
36
36
  DURABLE_INVOCATION_FENCE_ATTESTATION_VALUE,
37
37
  } from '../durable-invocation-fence';
38
38
  import type { PlayRunInputPayload } from '../play-input';
39
- import type { PlayLogContext } from '../log-context';
39
+ import type {
40
+ PlayLogContext,
41
+ PlayLogMetadata,
42
+ } from '../log-context';
40
43
  import { playRunInputObjectView } from '../play-input';
41
44
  import {
42
45
  normalizePlayRunFailure,
@@ -1737,6 +1740,13 @@ async function executeRuntimePlayCode(input: {
1737
1740
  }
1738
1741
 
1739
1742
  const CAPTURED_CONSOLE_MAX_CHARS = 2_000;
1743
+ const CAPTURED_CONSOLE_PREFIX_RESERVE_CHARS = 64;
1744
+ const CAPTURED_CONSOLE_SHORTENED_MARKER =
1745
+ '… [log shortened; save complete values in a dataset and use runs export]';
1746
+ const CAPTURED_CONSOLE_MAX_MESSAGE_CHARS =
1747
+ CAPTURED_CONSOLE_MAX_CHARS - CAPTURED_CONSOLE_PREFIX_RESERVE_CHARS;
1748
+ const CAPTURED_CONSOLE_FORMAT_TRUNCATION =
1749
+ /(\.\.\.|…)\s*\d+\s+more\s+(?:characters?|items?)/i;
1740
1750
 
1741
1751
  function logLevelForCapturedConsoleMethod(
1742
1752
  method: string,
@@ -1763,14 +1773,20 @@ function createCapturedCustomerConsole(
1763
1773
  compact: true,
1764
1774
  depth: 4,
1765
1775
  maxArrayLength: 20,
1766
- maxStringLength: 2_000,
1776
+ maxStringLength: CAPTURED_CONSOLE_MAX_MESSAGE_CHARS,
1767
1777
  },
1768
1778
  ...args,
1769
1779
  );
1770
- const message =
1771
- formatted.length > CAPTURED_CONSOLE_MAX_CHARS
1772
- ? `${formatted.slice(0, CAPTURED_CONSOLE_MAX_CHARS - 1)}…`
1773
- : formatted;
1780
+ const shortened =
1781
+ formatted.length > CAPTURED_CONSOLE_MAX_MESSAGE_CHARS ||
1782
+ CAPTURED_CONSOLE_FORMAT_TRUNCATION.test(formatted);
1783
+ const message = shortened
1784
+ ? `${formatted.slice(
1785
+ 0,
1786
+ CAPTURED_CONSOLE_MAX_MESSAGE_CHARS -
1787
+ CAPTURED_CONSOLE_SHORTENED_MARKER.length,
1788
+ )}${CAPTURED_CONSOLE_SHORTENED_MARKER}`
1789
+ : formatted;
1774
1790
  ctx.log(`[console.${method}] ${message}`, {
1775
1791
  level: logLevelForCapturedConsoleMethod(method),
1776
1792
  });
@@ -1903,6 +1919,14 @@ async function releaseRuntimeLeasesOnAppRunnerSettlement(input: {
1903
1919
  }
1904
1920
  }
1905
1921
 
1922
+ function terminalLogIdsWithPhaseSummary(ctx: {
1923
+ getLogIds(): string[];
1924
+ }): string[] {
1925
+ // Summaries are new occurrences at result construction. Terminal retries
1926
+ // reuse that result and its IDs, including the frozen summary bodies.
1927
+ return [...ctx.getLogIds(), crypto.randomUUID(), crypto.randomUUID()];
1928
+ }
1929
+
1906
1930
  function terminalLogsWithPhaseSummary(ctx: {
1907
1931
  getLogs(): string[];
1908
1932
  getRuntimeResourceSnapshot(): unknown;
@@ -1915,6 +1939,13 @@ function terminalLogsWithPhaseSummary(ctx: {
1915
1939
  ];
1916
1940
  }
1917
1941
 
1942
+ function terminalLogMetadataWithPhaseSummary(ctx: {
1943
+ getLogMetadata(): Array<PlayLogMetadata | null>;
1944
+ }): Array<PlayLogMetadata | null> | undefined {
1945
+ const metadata = ctx.getLogMetadata();
1946
+ return metadata.some(Boolean) ? [...metadata, null, null] : undefined;
1947
+ }
1948
+
1918
1949
  function terminalLogContextsWithPhaseSummary(ctx: {
1919
1950
  getLogContexts(): Array<PlayLogContext | null>;
1920
1951
  }): Array<PlayLogContext | null> | undefined {
@@ -2488,7 +2519,7 @@ async function run(
2488
2519
  });
2489
2520
  }
2490
2521
  : undefined,
2491
- onLog: (line, context) => {
2522
+ onLog: (line, context, entryId, metadata) => {
2492
2523
  const timestampMatch = line.match(/^\[([^\]]+)\]/);
2493
2524
  const timestamp = timestampMatch?.[1];
2494
2525
  const at =
@@ -2501,7 +2532,11 @@ async function run(
2501
2532
  at,
2502
2533
  source: 'play',
2503
2534
  line,
2535
+ ...(config.context.runtimeLogIdentity === 'origin' && entryId
2536
+ ? { entryId }
2537
+ : {}),
2504
2538
  ...(context ? { context } : {}),
2539
+ ...(metadata ? { metadata } : {}),
2505
2540
  },
2506
2541
  options,
2507
2542
  );
@@ -3128,7 +3163,11 @@ async function run(
3128
3163
  error: runtimeLimitExceededCause(runtimeLimitSeconds),
3129
3164
  errors: [runtimeLimitExceededFailure(runtimeLimitSeconds)],
3130
3165
  logs: terminalLogsWithPhaseSummary(ctx),
3166
+ ...(config.context.runtimeLogIdentity === 'origin'
3167
+ ? { logIds: terminalLogIdsWithPhaseSummary(ctx) }
3168
+ : {}),
3131
3169
  logContexts: terminalLogContextsWithPhaseSummary(ctx),
3170
+ logMetadata: terminalLogMetadataWithPhaseSummary(ctx),
3132
3171
  stats: runtimeStats(),
3133
3172
  steps: ctx.getSteps(),
3134
3173
  tableNamespace: null,
@@ -3372,7 +3411,11 @@ async function run(
3372
3411
  : {}),
3373
3412
  outputRowCount,
3374
3413
  logs,
3414
+ ...(config.context.runtimeLogIdentity === 'origin'
3415
+ ? { logIds: terminalLogIdsWithPhaseSummary(ctx) }
3416
+ : {}),
3375
3417
  logContexts: terminalLogContextsWithPhaseSummary(ctx),
3418
+ logMetadata: terminalLogMetadataWithPhaseSummary(ctx),
3376
3419
  ...(runtimeDiagnostics.length > 0 ? { runtimeDiagnostics } : {}),
3377
3420
  ...(rowOutcomes ? { rowOutcomes } : {}),
3378
3421
  stats: runtimeStats(),
@@ -3427,7 +3470,11 @@ async function run(
3427
3470
  status: 'suspended',
3428
3471
  suspension: error.suspension,
3429
3472
  logs: terminalLogsWithPhaseSummary(ctx),
3473
+ ...(config.context.runtimeLogIdentity === 'origin'
3474
+ ? { logIds: terminalLogIdsWithPhaseSummary(ctx) }
3475
+ : {}),
3430
3476
  logContexts: terminalLogContextsWithPhaseSummary(ctx),
3477
+ logMetadata: terminalLogMetadataWithPhaseSummary(ctx),
3431
3478
  stats: runtimeStats(),
3432
3479
  steps: ctx.getSteps(),
3433
3480
  checkpoint: ctx.getCheckpoint(),
@@ -3463,7 +3510,11 @@ async function run(
3463
3510
  error: formatRunnerFailure(error),
3464
3511
  errors: [failure],
3465
3512
  logs: terminalLogsWithPhaseSummary(ctx),
3513
+ ...(config.context.runtimeLogIdentity === 'origin'
3514
+ ? { logIds: terminalLogIdsWithPhaseSummary(ctx) }
3515
+ : {}),
3466
3516
  logContexts: terminalLogContextsWithPhaseSummary(ctx),
3517
+ logMetadata: terminalLogMetadataWithPhaseSummary(ctx),
3467
3518
  ...(rowOutcomes ? { rowOutcomes } : {}),
3468
3519
  stats: runtimeStats(),
3469
3520
  steps: ctx.getSteps(),
@@ -3483,11 +3534,23 @@ export async function executePlayRunner(
3483
3534
  config: PlayRunnerExecutionConfig,
3484
3535
  options?: ExecutePlayRunnerOptions,
3485
3536
  ): Promise<PlayRunnerResult> {
3486
- const result = await run(config, options);
3537
+ const emissionOptions: ExecutePlayRunnerOptions = {
3538
+ ...options,
3539
+ emitEvent: (event) =>
3540
+ emitRunnerEvent(
3541
+ config.context.runtimeLogIdentity === 'origin' &&
3542
+ event.type === 'log' &&
3543
+ !event.entryId
3544
+ ? { ...event, entryId: crypto.randomUUID() }
3545
+ : event,
3546
+ options,
3547
+ ),
3548
+ };
3549
+ const result = await run(config, emissionOptions);
3487
3550
  // Inline-executor mirror of the child terminal drain: the caller's finalize
3488
3551
  // reads the observations table right after this returns, so land in-flight
3489
3552
  // observation posts first (bounded, best-effort, docflow-gated by emptiness).
3490
- await drainPendingDocflowObservationPosts(options);
3553
+ await drainPendingDocflowObservationPosts(emissionOptions);
3491
3554
  return result;
3492
3555
  }
3493
3556
 
@@ -1,4 +1,5 @@
1
1
  import type { PlayRunnerEvent } from '../protocol';
2
+ import { isDurableDirectRunnerLog } from '../log-provenance';
2
3
  import { jsonByteLengthUpTo } from '../output-size-limits';
3
4
  import { RUNTIME_RUNNER_TERMINAL_PROGRESS_MAX_EVENTS } from '../runtime-contract';
4
5
 
@@ -116,9 +117,7 @@ type PendingEvent = {
116
117
  };
117
118
 
118
119
  function isDurableLog(event: PlayRunnerEvent): boolean {
119
- return (
120
- event.type === 'log' && event.source !== 'worker' && event.line.length > 0
121
- );
120
+ return event.type === 'log' && isDurableDirectRunnerLog(event);
122
121
  }
123
122
 
124
123
  export function createRunnerProgressPusher(
@@ -3,7 +3,10 @@ import {
3
3
  type PlayRunnerEvent,
4
4
  type PlayRunnerResult,
5
5
  } from '../protocol';
6
- import type { PlayLogContext } from '../log-context';
6
+ import type {
7
+ PlayLogContext,
8
+ PlayLogMetadata,
9
+ } from '../log-context';
7
10
  import { parseCtxFetchHttpFailureDiagnostic } from '../log-provenance';
8
11
  import {
9
12
  RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES,
@@ -201,9 +204,14 @@ function retainArrayWithinJsonByteLimit<T>(
201
204
  * Retain the normal newest log tail, but reserve space for the canonical
202
205
  * ctx.fetch failure diagnostics first. `runtimeDiagnostics` powers the final
203
206
  * warning, while the matching raw log line is the durable evidence customers
204
- * see through `runs logs --debug` after progress events could not be pushed.
207
+ * see through `runs logs` after progress events could not be pushed.
205
208
  */
206
- function retainTerminalLogTailWithRuntimeDiagnostics(logs: readonly string[]): {
209
+ function retainTerminalLogTailWithRuntimeDiagnostics(
210
+ logs: readonly string[],
211
+ logContexts?: readonly (PlayLogContext | null)[],
212
+ logMetadata?: readonly (PlayLogMetadata | null)[],
213
+ logIds?: readonly string[],
214
+ ): {
207
215
  values: string[];
208
216
  indexes: number[];
209
217
  truncated: boolean;
@@ -215,8 +223,16 @@ function retainTerminalLogTailWithRuntimeDiagnostics(logs: readonly string[]): {
215
223
  // array. Re-serializing the retained 128 KiB tail for every historical
216
224
  // line made large completed runs exhaust their terminal-flush deadline.
217
225
  const separatorBytes = retainedIndexes.size ? 1 : 0;
218
- const measurement = jsonByteLengthUpTo(
226
+ // The aligned arrays cross this same transport. Counting their bytes
227
+ // preserves the existing diagnostic allowance and customer-output headroom.
228
+ const entry = [
219
229
  logs[index]!,
230
+ ...(logContexts ? [logContexts[index] ?? null] : []),
231
+ ...(logMetadata ? [logMetadata[index] ?? null] : []),
232
+ ...(logIds ? [logIds[index]] : []),
233
+ ];
234
+ const measurement = jsonByteLengthUpTo(
235
+ entry.length === 1 ? entry[0] : entry,
220
236
  TERMINAL_TRANSPORT_LOG_TAIL_MAX_BYTES - retainedBytes - separatorBytes,
221
237
  );
222
238
  if (measurement.exceeded) return;
@@ -269,15 +285,30 @@ function appendTerminalDiagnosticsMarker(
269
285
  logs: string[],
270
286
  truncatedFields: string[],
271
287
  logContexts?: Array<PlayLogContext | null>,
272
- ): { logs: string[]; logContexts?: Array<PlayLogContext | null> } {
288
+ logIds?: string[],
289
+ logMetadata?: Array<PlayLogMetadata | null>,
290
+ ): {
291
+ logs: string[];
292
+ logContexts?: Array<PlayLogContext | null>;
293
+ logIds?: string[];
294
+ logMetadata?: Array<PlayLogMetadata | null>;
295
+ } {
273
296
  const marker =
274
297
  `[deepline] terminal diagnostic metadata was bounded (${truncatedFields.join(', ')}); ` +
275
298
  'the authored play output is complete.';
276
299
  const marked = [marker, ...logs];
277
300
  const markedContexts = logContexts ? [null, ...logContexts] : undefined;
301
+ const markedMetadata = logMetadata ? [null, ...logMetadata] : undefined;
302
+ const markedIds = logIds ? [crypto.randomUUID(), ...logIds] : undefined;
303
+ const logPayload = () => ({
304
+ logs: marked,
305
+ ...(markedContexts ? { logContexts: markedContexts } : {}),
306
+ ...(markedMetadata ? { logMetadata: markedMetadata } : {}),
307
+ ...(markedIds ? { logIds: markedIds } : {}),
308
+ });
278
309
  while (
279
310
  marked.length > 1 &&
280
- !fitsJsonByteLimit(marked, TERMINAL_TRANSPORT_LOG_TAIL_MAX_BYTES)
311
+ !fitsJsonByteLimit(logPayload(), TERMINAL_TRANSPORT_LOG_TAIL_MAX_BYTES)
281
312
  ) {
282
313
  // Preserve canonical ctx.fetch evidence when a marker itself needs room.
283
314
  // Other tail lines are diagnostic context, but this line is the durable
@@ -289,15 +320,23 @@ function appendTerminalDiagnosticsMarker(
289
320
  const removeAt = removableIndex === -1 ? 1 : removableIndex;
290
321
  marked.splice(removeAt, 1);
291
322
  markedContexts?.splice(removeAt, 1);
323
+ markedMetadata?.splice(removeAt, 1);
324
+ markedIds?.splice(removeAt, 1);
292
325
  }
293
326
  const logsFit = fitsJsonByteLimit(
294
- marked,
327
+ logPayload(),
295
328
  TERMINAL_TRANSPORT_LOG_TAIL_MAX_BYTES,
296
329
  );
297
330
  const retainedLogs = logsFit ? marked : [marker];
298
331
  const retainedContexts = logsFit ? markedContexts : [null];
299
332
  return {
300
333
  logs: retainedLogs,
334
+ ...(markedMetadata?.some(Boolean)
335
+ ? { logMetadata: logsFit ? markedMetadata : [null] }
336
+ : {}),
337
+ ...(markedIds
338
+ ? { logIds: logsFit ? markedIds : markedIds.slice(0, 1) }
339
+ : {}),
301
340
  ...(retainedContexts?.some(Boolean)
302
341
  ? { logContexts: retainedContexts }
303
342
  : {}),
@@ -313,18 +352,34 @@ function appendTerminalDiagnosticsMarker(
313
352
  function boundSettledTerminalTransportDiagnostics(
314
353
  result: SettledRunnerTerminalTransportResult,
315
354
  ): SettledRunnerTerminalTransportResult {
316
- const logProjection = retainTerminalLogTailWithRuntimeDiagnostics(
317
- result.logs,
318
- );
319
355
  if (
320
356
  result.logContexts !== undefined &&
321
357
  result.logContexts.length !== result.logs.length
322
358
  ) {
323
359
  throw new TypeError('Terminal log contexts must align with result logs.');
324
360
  }
361
+ if (
362
+ result.logMetadata !== undefined &&
363
+ result.logMetadata.length !== result.logs.length
364
+ )
365
+ throw new TypeError('Terminal log metadata must align with logs.');
366
+ const logProjection = retainTerminalLogTailWithRuntimeDiagnostics(
367
+ result.logs,
368
+ result.logContexts,
369
+ result.logMetadata,
370
+ result.logIds,
371
+ );
325
372
  const logContexts = result.logContexts?.length
326
373
  ? logProjection.indexes.map((index) => result.logContexts![index] ?? null)
327
374
  : undefined;
375
+ const logMetadata = result.logMetadata?.length
376
+ ? logProjection.indexes.map((index) => result.logMetadata![index] ?? null)
377
+ : undefined;
378
+ if (result.logIds && result.logIds.length !== result.logs.length)
379
+ throw new TypeError('Terminal log identities must align with result logs.');
380
+ const logIds = result.logIds
381
+ ? logProjection.indexes.map((index) => result.logIds![index]!)
382
+ : undefined;
328
383
  const statsProjection = retainRecordWithinJsonByteLimit(
329
384
  result.stats,
330
385
  TERMINAL_TRANSPORT_STATS_MAX_BYTES,
@@ -368,15 +423,21 @@ function boundSettledTerminalTransportDiagnostics(
368
423
  logProjection.values,
369
424
  truncatedFields,
370
425
  logContexts,
426
+ logIds,
427
+ logMetadata,
371
428
  )
372
429
  : {
373
430
  logs: logProjection.values,
431
+ ...(logIds ? { logIds } : {}),
374
432
  ...(logContexts?.some(Boolean) ? { logContexts } : {}),
433
+ ...(logMetadata?.some(Boolean) ? { logMetadata } : {}),
375
434
  };
376
435
  return {
377
436
  ...result,
378
437
  logs: boundedLogs.logs,
379
438
  logContexts: boundedLogs.logContexts,
439
+ logMetadata: boundedLogs.logMetadata,
440
+ logIds: boundedLogs.logIds,
380
441
  stats: statsProjection.value,
381
442
  steps: stepsProjection.values,
382
443
  ...(result.outputWarnings
@@ -408,16 +469,22 @@ function boundSettledTerminalTransportDiagnostics(
408
469
  logProjection.values,
409
470
  truncatedFields,
410
471
  logContexts,
472
+ logIds,
473
+ logMetadata,
411
474
  )
412
475
  : {
413
476
  logs: logProjection.values,
477
+ ...(logIds ? { logIds } : {}),
414
478
  ...(logContexts?.some(Boolean) ? { logContexts } : {}),
479
+ ...(logMetadata?.some(Boolean) ? { logMetadata } : {}),
415
480
  };
416
481
  return {
417
482
  ...result,
418
483
  error: error.value,
419
484
  logs: boundedLogs.logs,
420
485
  logContexts: boundedLogs.logContexts,
486
+ logMetadata: boundedLogs.logMetadata,
487
+ logIds: boundedLogs.logIds,
421
488
  stats: statsProjection.value,
422
489
  steps: stepsProjection.values,
423
490
  ...(result.errors ? { errors: errorsProjection.values } : {}),
@@ -90,7 +90,7 @@ const DAYTONA_PUBLIC_STATUS_TRANSIENT_RETRY_PATTERN =
90
90
  const DAYTONA_RUNNER_STARTUP_TRANSPORT_RETRY_PATTERN =
91
91
  /\bDaytonaRunnerInitializationError:\s*RUNTIME_SANDBOX_START_FAILED:.*\bScheduler diagnosis:\s*runner_heartbeat_not_observed\..*"schedulerReadinessObserved":false.*"commandFound":true.*"exitCode":null.*"sessionFound":true.*"exitCodeFile":null.*"runnerStartup":\{"phase":"heartbeat_transport","outcome":"network_error","errorCode":"(?:ECONNRESET|ECONNREFUSED|ETIMEDOUT|UND_ERR_CONNECT_TIMEOUT)"/i;
92
92
  const DAYTONA_INFRASTRUCTURE_RETRY_PATTERN =
93
- /\b(?:Request failed with status code (?:408|429|500|502|503|504)|ECONNRESET|ECONNREFUSED|ETIMEDOUT|UND_ERR_CONNECT_TIMEOUT|fetch failed|socket hang up|Daytona sandbox create failed across|Daytona sandbox create did not complete|Daytona readiness baseline)\b/i;
93
+ /\b(?:Request failed with status code (?:408|429|500|502|503|504)|ECONNRESET|ECONNREFUSED|ETIMEDOUT|UND_ERR_CONNECT_TIMEOUT|fetch failed|socket hang up|The socket connection was closed unexpectedly|Daytona sandbox create failed across|Daytona sandbox create did not complete|Daytona readiness baseline)\b/i;
94
94
 
95
95
  type DaytonaExecution = { exitCode: number | null; result: string };
96
96
  export type DaytonaLookupFailure = {
@@ -86,6 +86,8 @@ export type PlaySchedulerSubmitInput = RetainedPipelineCarrier & {
86
86
  * Omitted for legacy launches whose Play Run row was persisted synchronously.
87
87
  */
88
88
  requiresRunCreatedProjection?: boolean;
89
+ /** Admission-owned producer pin. Absent keeps launch-pinned legacy logging. */
90
+ runtimeLogIdentity?: 'origin';
89
91
  /** Optional inline artifact for the Node scheduler. */
90
92
  runtimeArtifact?: unknown;
91
93
  artifactHash: string;
@@ -0,0 +1,145 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import {
3
+ PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS,
4
+ PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES,
5
+ PLAY_ADMISSION_MAX_PARALLELISM,
6
+ } from '../admission-transport-policy';
7
+ import {
8
+ RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS,
9
+ RUNTIME_SCHEDULER_SERVERLESS_IDLE_TIMEOUT_MS,
10
+ resolveWorkflowRuntimePostgresUrl,
11
+ type RuntimeSchedulerPoolOptions,
12
+ } from './postgres-pool';
13
+
14
+ // All queues in one worker share this single process pool. Scheduler queries
15
+ // are short and batched; execution concurrency belongs to Daytona/Fly slots,
16
+ // not to a growing set of database clients. Keeping this at two makes a
17
+ // horizontally scaled fleet predictable against the shared PgBouncer budget.
18
+ export const ABSURD_RUNTIME_POOL_MAX_CLIENTS =
19
+ RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS;
20
+
21
+ /**
22
+ * Vercel Fluid invocations share a process and its scheduler pool. A single
23
+ * client serializes unrelated launches, reads, and critical lease lookups;
24
+ * one held connection can make every other request hit its acquire timeout.
25
+ * Use the shared two-client control-plane budget so another request can make
26
+ * progress while keeping each instance's PgBouncer footprint bounded.
27
+ */
28
+ export const ABSURD_SERVERLESS_POOL_MAX_CLIENTS =
29
+ RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS;
30
+
31
+ /**
32
+ * A Vercel isolate may survive long after a webhook has returned. Keep its
33
+ * scheduler clients only long enough to reuse them for an immediate follow-up,
34
+ * then return idle PgBouncer clients before the source's delayed Workpool
35
+ * recovery is due. The worker fleet keeps the ordinary 30-second idle lifetime.
36
+ */
37
+ export const ABSURD_SERVERLESS_POOL_IDLE_TIMEOUT_MS =
38
+ RUNTIME_SCHEDULER_SERVERLESS_IDLE_TIMEOUT_MS;
39
+
40
+ // Ordinary Vercel requests share the bounded control-plane pool above. The
41
+ // private batch adapter is different: Workpool elects one global up-to-1,024-
42
+ // source collection before entering this context, so one batch request may use
43
+ // its larger bounded scheduler capacity without multiplying it per source.
44
+ // Other Workpool adapters can still overlap during rollout.
45
+ type ServerlessAdmissionPoolContext = {
46
+ maxClients: number;
47
+ maxAcquireQueueDepth: number;
48
+ maxAcquireQueueWaitMs: number;
49
+ };
50
+
51
+ const serverlessAdmissionPoolContext =
52
+ new AsyncLocalStorage<ServerlessAdmissionPoolContext>();
53
+
54
+ export async function withServerlessAdmissionPoolMaxClients<T>(input: {
55
+ maxClients: number;
56
+ maxAcquireQueueDepth?: number;
57
+ maxAcquireQueueWaitMs?: number;
58
+ fn: () => Promise<T>;
59
+ }): Promise<T> {
60
+ if (
61
+ !Number.isInteger(input.maxClients) ||
62
+ input.maxClients < 1 ||
63
+ input.maxClients > PLAY_ADMISSION_MAX_PARALLELISM
64
+ ) {
65
+ throw new Error(
66
+ 'Serverless admission pool capacity is outside the supported range.',
67
+ );
68
+ }
69
+ const maxAcquireQueueDepth =
70
+ input.maxAcquireQueueDepth ?? PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES;
71
+ const maxAcquireQueueWaitMs =
72
+ input.maxAcquireQueueWaitMs ?? PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS;
73
+ if (
74
+ !Number.isSafeInteger(maxAcquireQueueDepth) ||
75
+ maxAcquireQueueDepth < 1 ||
76
+ maxAcquireQueueDepth > PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES
77
+ ) {
78
+ throw new Error(
79
+ 'Serverless admission acquire queue capacity is outside the supported range.',
80
+ );
81
+ }
82
+ if (
83
+ !Number.isSafeInteger(maxAcquireQueueWaitMs) ||
84
+ maxAcquireQueueWaitMs < 5_000 ||
85
+ maxAcquireQueueWaitMs > PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS
86
+ ) {
87
+ throw new Error(
88
+ 'Serverless admission acquire queue wait is outside the supported range.',
89
+ );
90
+ }
91
+ return await serverlessAdmissionPoolContext.run(
92
+ {
93
+ maxClients: input.maxClients,
94
+ maxAcquireQueueDepth,
95
+ maxAcquireQueueWaitMs,
96
+ },
97
+ input.fn,
98
+ );
99
+ }
100
+
101
+ /**
102
+ * The private batch adapter owns this request-local scheduler pool. Reserve
103
+ * one client for executor-token and other critical control-plane traffic; the
104
+ * rest may submit the independently idempotent batch sources in parallel.
105
+ */
106
+ export function serverlessAdmissionSubmitMaxActive(): number | null {
107
+ const context = serverlessAdmissionPoolContext.getStore();
108
+ return context === undefined ? null : Math.max(1, context.maxClients - 1);
109
+ }
110
+
111
+ /**
112
+ * Resolve the runtime scheduler Postgres pool options for the absurd control
113
+ * plane. Loud failure over silent fallback: the absurd worker and scheduler
114
+ * only run under Node where `WORKFLOW_RUNTIME_POSTGRES` is configured.
115
+ */
116
+ export function resolveAbsurdPoolOptions(
117
+ explicitUrl?: string | null,
118
+ ): RuntimeSchedulerPoolOptions {
119
+ const url = explicitUrl?.trim() || resolveWorkflowRuntimePostgresUrl();
120
+ if (!url) {
121
+ throw new Error(
122
+ 'absurd scheduler backend requires WORKFLOW_RUNTIME_POSTGRES (runtime ' +
123
+ 'scheduler Postgres URL). Refusing to fall back to local execution.',
124
+ );
125
+ }
126
+ const serverless = process.env.VERCEL?.trim();
127
+ const context = serverlessAdmissionPoolContext.getStore();
128
+ return {
129
+ url,
130
+ applicationName: 'deepline-absurd-runtime',
131
+ maxClients:
132
+ serverless || context !== undefined
133
+ ? (context?.maxClients ?? ABSURD_SERVERLESS_POOL_MAX_CLIENTS)
134
+ : ABSURD_RUNTIME_POOL_MAX_CLIENTS,
135
+ ...(serverless || context !== undefined
136
+ ? { idleTimeoutMs: ABSURD_SERVERLESS_POOL_IDLE_TIMEOUT_MS }
137
+ : {}),
138
+ ...(context
139
+ ? {
140
+ maxAcquireQueueDepth: context.maxAcquireQueueDepth,
141
+ maxAcquireQueueWaitMs: context.maxAcquireQueueWaitMs,
142
+ }
143
+ : {}),
144
+ };
145
+ }
@@ -3040,8 +3040,14 @@ async function executeAssignedAbsurdAttempt(input: {
3040
3040
  });
3041
3041
  }
3042
3042
 
3043
- // A failed run's settled counts must reach the ledger BEFORE its terminal
3044
- // makes the steps immutable, so they ride the terminal outbox row.
3043
+ // Terminal step errors and failed-map settled counts ride the existing
3044
+ // terminal outbox row. They no longer need a progress write immediately
3045
+ // ahead of the terminal in the same ordered run stream.
3046
+ const observedTerminalProgressEvents =
3047
+ progressReporter.collectTerminalFailureProgressEvents?.() ?? [];
3048
+ if (observedTerminalProgressEvents.length > 0) {
3049
+ progressReporter.markTerminalFailureProgressPersisted?.();
3050
+ }
3045
3051
  const failedTerminalProgressEvents =
3046
3052
  runnerResult.status === 'failed'
3047
3053
  ? await collectFailedTerminalSettledProgressEvents({
@@ -3059,8 +3065,14 @@ async function executeAssignedAbsurdAttempt(input: {
3059
3065
  ...options,
3060
3066
  terminalLogOutbox: input.fromDetachedRunner ? 'ctx_log_tail' : 'omit',
3061
3067
  sandboxCrashDiagnostic,
3062
- ...(failedTerminalProgressEvents.length > 0
3063
- ? { failedTerminalProgressEvents }
3068
+ ...(observedTerminalProgressEvents.length > 0 ||
3069
+ failedTerminalProgressEvents.length > 0
3070
+ ? {
3071
+ terminalProgressEvents: [
3072
+ ...observedTerminalProgressEvents,
3073
+ ...failedTerminalProgressEvents,
3074
+ ],
3075
+ }
3064
3076
  : {}),
3065
3077
  },
3066
3078
  ),
@@ -4463,7 +4475,6 @@ function createAbsurdWorkerTerminalProjectionAdapter(input: {
4463
4475
  );
4464
4476
  });
4465
4477
  };
4466
- let projectionStartedAtMs: number | undefined;
4467
4478
  return createPlayRunAttemptWorkerTerminalProjection({
4468
4479
  async observeFenceLost() {
4469
4480
  // A lost fence means this attempt is dead, which a cancelled run is the
@@ -4479,7 +4490,6 @@ function createAbsurdWorkerTerminalProjectionAdapter(input: {
4479
4490
  });
4480
4491
  },
4481
4492
  async reconcileTerminalTruth() {
4482
- projectionStartedAtMs ??= Date.now();
4483
4493
  // ADR 0016 rule 3: detached Daytona runs may provide no live events, so
4484
4494
  // fold stored docflow/runtime-sheet facts into progress after truth wins.
4485
4495
  await reconcileTerminalTruthIntoReporter({
@@ -4564,9 +4574,7 @@ function createAbsurdWorkerTerminalProjectionAdapter(input: {
4564
4574
  runId: input.runId,
4565
4575
  options: input.options,
4566
4576
  trigger: 'committed_terminal',
4567
- ...(projectionStartedAtMs === undefined
4568
- ? {}
4569
- : { projectionStartedAtMs }),
4577
+ projectionStartedAtMs: Date.now(),
4570
4578
  });
4571
4579
  },
4572
4580
  });