deepline 0.3.288 → 0.3.290

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 (44) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +30 -10
  3. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +108 -12
  4. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +1 -1
  5. package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +1 -0
  6. package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +3 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts +1 -0
  8. package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +17 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +6 -5
  10. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +2 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +7 -0
  12. package/dist/bundling-sources/shared_libs/play-runtime/run-event-catalog.ts +2 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +31 -3
  14. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +23 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +30 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +38 -3
  17. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +2 -3
  18. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +22 -1
  19. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +1 -1
  20. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
  21. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool-options.ts +145 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +1 -5
  23. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -139
  24. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +19 -5
  25. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +37 -15
  26. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +7 -5
  27. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +961 -281
  28. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +55 -25
  29. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +97 -0
  30. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +33 -14
  31. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +452 -43
  32. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +10 -1
  33. package/dist/bundling-sources/shared_libs/play-runtime/test-admission-batching.ts +103 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +75 -28
  35. package/dist/cli/index.js +24 -11
  36. package/dist/cli/index.mjs +24 -11
  37. package/dist/index.js +3 -1
  38. package/dist/index.mjs +3 -1
  39. package/dist/install-integrity.json +3 -0
  40. package/dist/release.d.mts +1 -1
  41. package/dist/release.d.ts +1 -1
  42. package/dist/release.js +1 -1
  43. package/dist/release.mjs +1 -1
  44. package/package.json +1 -1
@@ -11,7 +11,6 @@ import { RUN_EVENT_KIND } from '../run-event-catalog';
11
11
  * absurd-specific wiring (queue, event names, checkpoint boundary shaping).
12
12
  */
13
13
  import { createHash } from 'node:crypto';
14
- import { AsyncLocalStorage } from 'node:async_hooks';
15
14
  import type {
16
15
  PlayCheckpoint,
17
16
  PlayExecutionEvent,
@@ -22,11 +21,6 @@ import type { PlayRunLedgerEvent } from '../run-ledger';
22
21
  import type { PlaySchedulerSubmitInput } from '../scheduler-backend';
23
22
  import type { WorkerRuntimeApiContext } from '../app-runtime-api';
24
23
  import { RUNTIME_CAPACITY_POLICY } from '../runtime-capacity-policy';
25
- import {
26
- PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS,
27
- PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES,
28
- PLAY_ADMISSION_MAX_PARALLELISM,
29
- } from '../admission-transport-policy';
30
24
  import { assertKnownResolvedBoundary } from '../checkpoint-boundaries';
31
25
  import type { Absurd, Log, RetryStrategy } from '@deepline/absurd/sdk';
32
26
  import { isIsolatedRuntimeSchedulerSchema } from '../runtime-scheduler-topology';
@@ -37,8 +31,6 @@ import {
37
31
  } from './postgres';
38
32
  import { createAbsurdForRuntimeScheduler } from './absurd-pool';
39
33
  import {
40
- RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS,
41
- resolveWorkflowRuntimePostgresUrl,
42
34
  withRuntimeSchedulerClient,
43
35
  type RuntimeSchedulerPoolOptions,
44
36
  } from './postgres-pool';
@@ -548,137 +540,14 @@ export type AbsurdRuntimeResult = {
548
540
  error?: string;
549
541
  };
550
542
 
551
- // All queues in one worker share this single process pool. Scheduler queries
552
- // are short and batched; execution concurrency belongs to Daytona/Fly slots,
553
- // not to a growing set of database clients. Keeping this at two makes a
554
- // horizontally scaled fleet predictable against the shared PgBouncer budget.
555
- export const ABSURD_RUNTIME_POOL_MAX_CLIENTS =
556
- RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS;
557
-
558
- /**
559
- * Vercel Fluid invocations share a process and its scheduler pool. A single
560
- * client serializes unrelated launches, reads, and critical lease lookups;
561
- * one held connection can make every other request hit its acquire timeout.
562
- * Use the shared two-client control-plane budget so another request can make
563
- * progress while keeping each instance's PgBouncer footprint bounded.
564
- */
565
- export const ABSURD_SERVERLESS_POOL_MAX_CLIENTS =
566
- RUNTIME_SCHEDULER_CONTROL_PLANE_MAX_CLIENTS;
567
-
568
- /**
569
- * A Vercel isolate may survive long after a webhook has returned. Keep its
570
- * scheduler clients only long enough to reuse them for an immediate follow-up,
571
- * then return idle PgBouncer clients before the source's delayed Workpool
572
- * recovery is due. The worker fleet keeps the ordinary 30-second idle lifetime.
573
- */
574
- export const ABSURD_SERVERLESS_POOL_IDLE_TIMEOUT_MS = 1_000;
575
-
576
- // Ordinary Vercel requests share the bounded control-plane pool above. The
577
- // private batch adapter is different: Workpool elects one global up-to-1,024-
578
- // source collection before entering this context, so one batch request may use
579
- // its larger bounded scheduler capacity without multiplying it per source.
580
- // Other Workpool adapters can still overlap during rollout.
581
- type ServerlessAdmissionPoolContext = {
582
- maxClients: number;
583
- maxAcquireQueueDepth: number;
584
- maxAcquireQueueWaitMs: number;
585
- };
586
-
587
- const serverlessAdmissionPoolContext =
588
- new AsyncLocalStorage<ServerlessAdmissionPoolContext>();
589
-
590
- export async function withServerlessAdmissionPoolMaxClients<T>(input: {
591
- maxClients: number;
592
- maxAcquireQueueDepth?: number;
593
- maxAcquireQueueWaitMs?: number;
594
- fn: () => Promise<T>;
595
- }): Promise<T> {
596
- if (
597
- !Number.isInteger(input.maxClients) ||
598
- input.maxClients < 1 ||
599
- input.maxClients > PLAY_ADMISSION_MAX_PARALLELISM
600
- ) {
601
- throw new Error(
602
- 'Serverless admission pool capacity is outside the supported range.',
603
- );
604
- }
605
- const maxAcquireQueueDepth =
606
- input.maxAcquireQueueDepth ?? PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES;
607
- const maxAcquireQueueWaitMs =
608
- input.maxAcquireQueueWaitMs ?? PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS;
609
- if (
610
- !Number.isSafeInteger(maxAcquireQueueDepth) ||
611
- maxAcquireQueueDepth < 1 ||
612
- maxAcquireQueueDepth > PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES
613
- ) {
614
- throw new Error(
615
- 'Serverless admission acquire queue capacity is outside the supported range.',
616
- );
617
- }
618
- if (
619
- !Number.isSafeInteger(maxAcquireQueueWaitMs) ||
620
- maxAcquireQueueWaitMs < 5_000 ||
621
- maxAcquireQueueWaitMs > PLAY_ADMISSION_MAX_ACQUIRE_QUEUE_WAIT_MS
622
- ) {
623
- throw new Error(
624
- 'Serverless admission acquire queue wait is outside the supported range.',
625
- );
626
- }
627
- return await serverlessAdmissionPoolContext.run(
628
- {
629
- maxClients: input.maxClients,
630
- maxAcquireQueueDepth,
631
- maxAcquireQueueWaitMs,
632
- },
633
- input.fn,
634
- );
635
- }
636
-
637
- /**
638
- * The private batch adapter owns this request-local scheduler pool. Reserve
639
- * one client for executor-token and other critical control-plane traffic; the
640
- * rest may submit the independently idempotent batch sources in parallel.
641
- */
642
- export function serverlessAdmissionSubmitMaxActive(): number | null {
643
- const context = serverlessAdmissionPoolContext.getStore();
644
- return context === undefined ? null : Math.max(1, context.maxClients - 1);
645
- }
646
-
647
- /**
648
- * Resolve the runtime scheduler Postgres pool options for the absurd control
649
- * plane. Loud failure over silent fallback: the absurd worker and scheduler
650
- * only run under Node where `WORKFLOW_RUNTIME_POSTGRES` is configured.
651
- */
652
- export function resolveAbsurdPoolOptions(
653
- explicitUrl?: string | null,
654
- ): RuntimeSchedulerPoolOptions {
655
- const url = explicitUrl?.trim() || resolveWorkflowRuntimePostgresUrl();
656
- if (!url) {
657
- throw new Error(
658
- 'absurd scheduler backend requires WORKFLOW_RUNTIME_POSTGRES (runtime ' +
659
- 'scheduler Postgres URL). Refusing to fall back to local execution.',
660
- );
661
- }
662
- const serverless = process.env.VERCEL?.trim();
663
- const context = serverlessAdmissionPoolContext.getStore();
664
- return {
665
- url,
666
- applicationName: 'deepline-absurd-runtime',
667
- maxClients:
668
- serverless || context !== undefined
669
- ? (context?.maxClients ?? ABSURD_SERVERLESS_POOL_MAX_CLIENTS)
670
- : ABSURD_RUNTIME_POOL_MAX_CLIENTS,
671
- ...(serverless || context !== undefined
672
- ? { idleTimeoutMs: ABSURD_SERVERLESS_POOL_IDLE_TIMEOUT_MS }
673
- : {}),
674
- ...(context
675
- ? {
676
- maxAcquireQueueDepth: context.maxAcquireQueueDepth,
677
- maxAcquireQueueWaitMs: context.maxAcquireQueueWaitMs,
678
- }
679
- : {}),
680
- };
681
- }
543
+ export {
544
+ ABSURD_RUNTIME_POOL_MAX_CLIENTS,
545
+ ABSURD_SERVERLESS_POOL_MAX_CLIENTS,
546
+ ABSURD_SERVERLESS_POOL_IDLE_TIMEOUT_MS,
547
+ withServerlessAdmissionPoolMaxClients,
548
+ serverlessAdmissionSubmitMaxActive,
549
+ resolveAbsurdPoolOptions,
550
+ } from './absurd-pool-options';
682
551
 
683
552
  export function normalizeAbsurdSchedulerOptions(
684
553
  options: { url?: string | null; schema?: string | null } = {},
@@ -22,6 +22,7 @@
22
22
  * connection. Notifications are strictly a wakeup hint — the tables remain
23
23
  * the source of truth and every waiter still reads the DB.
24
24
  */
25
+ import { attachDatabasePool } from '@vercel/functions/db-connections';
25
26
  import { Client, Pool } from 'pg';
26
27
  import {
27
28
  RuntimeSchedulerPgBouncerCapacityError,
@@ -175,6 +176,8 @@ export const RUNTIME_SCHEDULER_POOL_DEFAULTS = {
175
176
  keepAliveIntervalMs: 60_000,
176
177
  } as const;
177
178
 
179
+ export const RUNTIME_SCHEDULER_SERVERLESS_IDLE_TIMEOUT_MS = 1_000;
180
+
178
181
  /**
179
182
  * A control-plane process only needs a few short scheduler transactions at
180
183
  * once. This shared cap keeps horizontally scaled workers and the receipt
@@ -197,8 +200,8 @@ export type RuntimeSchedulerPoolOptions = {
197
200
  maxClients?: number;
198
201
  /**
199
202
  * Serverless launchers must not pin a scarce PgBouncer client between
200
- * requests. Long-lived workers retain the default; Vercel callers opt into
201
- * a short idle lifetime through the shared Absurd pool resolver.
203
+ * requests. Vercel pools share one short idle lifetime; this override only
204
+ * applies to long-lived processes.
202
205
  */
203
206
  idleTimeoutMs?: number;
204
207
  /**
@@ -447,6 +450,7 @@ export class RuntimeSchedulerPoolAdmission {
447
450
 
448
451
  type PoolEntry = {
449
452
  pool: Pool;
453
+ databasePoolAttached?: boolean;
450
454
  maxClients: number;
451
455
  maxAcquireQueueDepth: number;
452
456
  admission: RuntimeSchedulerPoolAdmission;
@@ -723,7 +727,7 @@ function getPoolEntry(options: RuntimeSchedulerPoolOptions): PoolEntry {
723
727
  assertRuntimeSchedulerHotPathUrl(options.url);
724
728
  const maxClients =
725
729
  options.maxClients ?? RUNTIME_SCHEDULER_POOL_DEFAULTS.maxClients;
726
- const idleTimeoutMs =
730
+ const requestedIdleTimeoutMs =
727
731
  options.idleTimeoutMs ?? RUNTIME_SCHEDULER_POOL_DEFAULTS.idleTimeoutMs;
728
732
  const maxAcquireQueueDepth =
729
733
  options.maxAcquireQueueDepth ??
@@ -738,11 +742,17 @@ function getPoolEntry(options: RuntimeSchedulerPoolOptions): PoolEntry {
738
742
  'instead of widening a single process pool.',
739
743
  );
740
744
  }
741
- if (!Number.isSafeInteger(idleTimeoutMs) || idleTimeoutMs < 0) {
745
+ if (
746
+ !Number.isSafeInteger(requestedIdleTimeoutMs) ||
747
+ requestedIdleTimeoutMs < 0
748
+ ) {
742
749
  throw new Error(
743
- `Runtime scheduler pool idleTimeoutMs=${idleTimeoutMs} must be a non-negative integer.`,
750
+ `Runtime scheduler pool idleTimeoutMs=${requestedIdleTimeoutMs} must be a non-negative integer.`,
744
751
  );
745
752
  }
753
+ const idleTimeoutMs = process.env.VERCEL?.trim()
754
+ ? RUNTIME_SCHEDULER_SERVERLESS_IDLE_TIMEOUT_MS
755
+ : requestedIdleTimeoutMs;
746
756
  if (
747
757
  !Number.isSafeInteger(maxAcquireQueueDepth) ||
748
758
  maxAcquireQueueDepth < 1 ||
@@ -1049,6 +1059,10 @@ export async function withRuntimeSchedulerClient<T>(
1049
1059
  let queryCount = 0;
1050
1060
  let queryMs = 0;
1051
1061
  try {
1062
+ if (!entry.databasePoolAttached) {
1063
+ attachDatabasePool(entry.pool);
1064
+ entry.databasePoolAttached = true;
1065
+ }
1052
1066
  throwIfSchedulerAborted(signal);
1053
1067
  const result = await fn({
1054
1068
  query: async (sql, params) => {
@@ -14,6 +14,7 @@ import { RUN_EVENT_KIND } from '../run-event-catalog';
14
14
  * Runtime Sheet row durability belongs to the context-level row writer.
15
15
  */
16
16
  import { PlayStepLifecycleTracker } from '../step-lifecycle-tracker';
17
+ import { isDurableDirectRunnerLog } from '../log-provenance';
17
18
  import {
18
19
  updateRunStatusViaAppRuntime,
19
20
  type RuntimeStatusUpdate,
@@ -268,18 +269,7 @@ function shouldPublishDurableLogEvent(
268
269
  | Pick<PlayRunnerLogEvent, 'source' | 'line'>
269
270
  | { source?: unknown; line?: unknown },
270
271
  ): boolean {
271
- // Daytona host lifecycle logs use source "worker" (sandbox
272
- // create/upload/execute). Keep those in worker logs while still storing
273
- // play-authored ctx.log events in the durable Run Log Stream. The one
274
- // exception is the structured performance marker: it is bounded (one
275
- // terminal phase observation per runner phase), run-scoped, and is the
276
- // canonical black-box regression signal for dispatcher throughput. Do not
277
- // broaden this to arbitrary worker output.
278
- return (
279
- mode !== 'direct_worker' ||
280
- event.source !== 'worker' ||
281
- (typeof event.line === 'string' && event.line.startsWith('[perf-trace] '))
282
- );
272
+ return mode !== 'direct_worker' || isDurableDirectRunnerLog(event);
283
273
  }
284
274
 
285
275
  function shouldPublishTerminalResultLog(
@@ -305,14 +295,25 @@ type TerminalLogReplayPayload = NonNullable<
305
295
  function terminalReplayEntriesForMode(input: {
306
296
  resultLogs: readonly string[];
307
297
  resultLogContexts?: readonly (PlayLogContext | null)[];
298
+ resultLogIds?: readonly string[];
308
299
  durableLogMode: PostgresSchedulerDurableLogMode;
309
- }): Array<{ line: string; context: PlayLogContext | null }> {
300
+ }): Array<{
301
+ line: string;
302
+ context: PlayLogContext | null;
303
+ entryId: string | null;
304
+ }> {
310
305
  if (
311
306
  input.resultLogContexts !== undefined &&
312
307
  input.resultLogContexts.length !== input.resultLogs.length
313
308
  ) {
314
309
  throw new TypeError('Terminal log contexts must align with result logs.');
315
310
  }
311
+ if (
312
+ input.resultLogIds &&
313
+ input.resultLogIds.length !== input.resultLogs.length
314
+ ) {
315
+ throw new TypeError('Terminal log identities must align with result logs.');
316
+ }
316
317
  return input.resultLogs.flatMap((rawLine, index) => {
317
318
  const line = rawLine.trim();
318
319
  if (
@@ -325,6 +326,7 @@ function terminalReplayEntriesForMode(input: {
325
326
  {
326
327
  line,
327
328
  context: normalizePlayLogContext(input.resultLogContexts?.[index]),
329
+ entryId: input.resultLogIds?.[index] ?? null,
328
330
  },
329
331
  ];
330
332
  });
@@ -333,6 +335,7 @@ function terminalReplayEntriesForMode(input: {
333
335
  function terminalReplayPayloadForMode(input: {
334
336
  resultLogs: readonly string[];
335
337
  resultLogContexts?: readonly (PlayLogContext | null)[];
338
+ resultLogIds?: readonly string[];
336
339
  durableLogMode: PostgresSchedulerDurableLogMode;
337
340
  liveLogs: readonly string[];
338
341
  liveLogContexts: readonly (PlayLogContext | null)[];
@@ -351,6 +354,7 @@ function terminalReplayPayloadForMode(input: {
351
354
  let totalCount = entries.length;
352
355
  if (
353
356
  input.durableLogMode === 'direct_worker' &&
357
+ !input.resultLogIds &&
354
358
  !input.replayAllTerminalLogs
355
359
  ) {
356
360
  const observedCounts = new Map<string, number>();
@@ -380,6 +384,9 @@ function terminalReplayPayloadForMode(input: {
380
384
  return {
381
385
  lines,
382
386
  ...(lineContexts.some(Boolean) ? { lineContexts } : {}),
387
+ ...(input.resultLogIds
388
+ ? { lineIds: entries.map((entry) => entry.entryId) }
389
+ : {}),
383
390
  ...(input.replayAllTerminalLogs ? { dedupeMode: 'exact' as const } : {}),
384
391
  ...(typeof input.producerAttempt === 'number'
385
392
  ? { producerAttempt: input.producerAttempt }
@@ -439,6 +446,7 @@ export function createPostgresSchedulerProgressReporter(input: {
439
446
  let nodeProgressSealed = false;
440
447
  let liveLogs: string[] = [];
441
448
  let liveLogContexts: Array<PlayLogContext | null> = [];
449
+ let liveLogIds: Array<string | null> = [];
442
450
  let liveLogsTotalCount = input.initialLiveLogTotalCount ?? 0;
443
451
  let liveLogsDirty = false;
444
452
  let workCounters: RuntimeStatusUpdate['workCounters'];
@@ -541,6 +549,7 @@ export function createPostgresSchedulerProgressReporter(input: {
541
549
  : undefined;
542
550
  const nextLogs = liveLogs.slice();
543
551
  const nextLogContexts = liveLogContexts.slice();
552
+ const nextLogIds = liveLogIds.slice();
544
553
  const nextLogTotalCount = liveLogsTotalCount;
545
554
  const nextDatasetLifecycleEvents = includeProgress
546
555
  ? pendingDatasetLifecycleEvents
@@ -566,7 +575,11 @@ export function createPostgresSchedulerProgressReporter(input: {
566
575
  liveNodeProgress: nodeProgressSealed ? {} : nextProgress,
567
576
  liveLogs: nextLogs,
568
577
  liveLogContexts: nextLogContexts,
578
+ liveLogIds: nextLogIds,
569
579
  liveLogTotalCount: nextLogTotalCount,
580
+ ...(input.initialLiveLogTotalCount !== undefined
581
+ ? { liveLogBatchOffset: nextLogTotalCount - nextLogs.length }
582
+ : {}),
570
583
  liveLogProducerAttempt: producerAttempt,
571
584
  datasetLifecycleEvents: nextDatasetLifecycleEvents,
572
585
  activityObservations: nextActivityObservations,
@@ -667,7 +680,11 @@ export function createPostgresSchedulerProgressReporter(input: {
667
680
  void flush({ force }).catch(() => undefined);
668
681
  };
669
682
 
670
- const appendLiveLog = (line: string, context?: unknown) => {
683
+ const appendLiveLog = (
684
+ line: string,
685
+ context?: unknown,
686
+ entryId?: unknown,
687
+ ) => {
671
688
  const trimmed = line.trim();
672
689
  if (!trimmed) {
673
690
  return;
@@ -675,8 +692,10 @@ export function createPostgresSchedulerProgressReporter(input: {
675
692
  liveLogsTotalCount += 1;
676
693
  liveLogs = [...liveLogs, trimmed];
677
694
  liveLogContexts = [...liveLogContexts, normalizePlayLogContext(context)];
695
+ liveLogIds = [...liveLogIds, typeof entryId === 'string' ? entryId : null];
678
696
  if (liveLogs.length > LIVE_LOG_TAIL_LIMIT) {
679
697
  liveLogs = liveLogs.slice(liveLogs.length - LIVE_LOG_TAIL_LIMIT);
698
+ liveLogIds = liveLogIds.slice(liveLogIds.length - LIVE_LOG_TAIL_LIMIT);
680
699
  liveLogContexts = liveLogContexts.slice(
681
700
  liveLogContexts.length - LIVE_LOG_TAIL_LIMIT,
682
701
  );
@@ -1321,13 +1340,14 @@ export function createPostgresSchedulerProgressReporter(input: {
1321
1340
  line?: unknown;
1322
1341
  source?: unknown;
1323
1342
  context?: unknown;
1343
+ entryId?: unknown;
1324
1344
  };
1325
1345
  const rawLine = logEvent.line;
1326
1346
  if (typeof rawLine !== 'string' || !rawLine) {
1327
1347
  return;
1328
1348
  }
1329
1349
  if (shouldPublishDurableLogEvent(durableLogMode, logEvent)) {
1330
- appendLiveLog(rawLine, logEvent.context);
1350
+ appendLiveLog(rawLine, logEvent.context, logEvent.entryId);
1331
1351
  }
1332
1352
  markMapStartedFromLog(rawLine);
1333
1353
  markMapStartedFromPerfLog(rawLine);
@@ -1629,6 +1649,7 @@ export function createPostgresSchedulerProgressReporter(input: {
1629
1649
  : terminalReplayPayloadForMode({
1630
1650
  resultLogs: result.logs,
1631
1651
  resultLogContexts: result.logContexts,
1652
+ resultLogIds: result.logIds,
1632
1653
  durableLogMode,
1633
1654
  liveLogs,
1634
1655
  liveLogContexts,
@@ -1832,6 +1853,7 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
1832
1853
  liveNodeProgress: finalUpdate.liveNodeProgress,
1833
1854
  liveLogs,
1834
1855
  liveLogContexts: finalUpdate.liveLogContexts,
1856
+ liveLogIds: finalUpdate.liveLogIds,
1835
1857
  liveLogsChannelOffset: Math.max(0, liveLogTotalCount - liveLogs.length),
1836
1858
  liveLogsProducerAttempt:
1837
1859
  finalUpdate.liveLogProducerAttempt ?? input.attempt,
@@ -10,6 +10,7 @@ import type {
10
10
  import {
11
11
  DEFAULT_ADVISORY_PROVIDER_MAX_RPS,
12
12
  DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS,
13
+ DEFAULT_UNKNOWN_PROVIDER_MIN_RPS,
13
14
  PROVIDER_AIMD_COOLDOWN_CAP_MS,
14
15
  type PacingRule,
15
16
  } from '../governor/rate-state-backend';
@@ -431,7 +432,8 @@ function normalizeRules(
431
432
  * per second; `capacity` is one window of burst. The adaptive `[min,max]` range
432
433
  * is derived value-first so the same wire shape drives both declared and
433
434
  * default-pacing buckets:
434
- * - min_rps = min(5/60, max)
435
+ * - min_rps = 2 for the unknown shared-provider scope
436
+ * - min_rps = min(5/60, max) for declared rules
435
437
  * (a lower explicit adaptive maximum remains authoritative)
436
438
  * - max_rps = max(base, 100) (catalogued-rate recommendation)
437
439
  * - max_rps = explicit adaptiveMaxRps when supplied
@@ -454,11 +456,11 @@ function seedForRule(rule: AcquirePacingRule): RateBucketSeed {
454
456
  rule.adaptiveMaxRps != null && rule.adaptiveMaxRps > 0
455
457
  ? Math.max(rule.adaptiveMaxRps, base)
456
458
  : Math.max(base, DEFAULT_ADVISORY_PROVIDER_MAX_RPS);
457
- // Undeclared providers keep their existing 10-RPS fallback. Other provider
458
- // scopes use the five-RPM floor, capped by an explicit adaptive maximum.
459
+ // The unknown-provider seed is a guess, not a quota floor. Declared provider
460
+ // scopes keep the five-RPM floor, capped by an explicit adaptive maximum.
459
461
  const minRps =
460
462
  rule.ruleId === 'default:provider-scope'
461
- ? base
463
+ ? DEFAULT_UNKNOWN_PROVIDER_MIN_RPS
462
464
  : Math.min(MIN_ADAPTIVE_RPS_FLOOR, maxRps);
463
465
  return {
464
466
  rule_id: rule.ruleId,
@@ -472,7 +474,7 @@ function seedForRule(rule: AcquirePacingRule): RateBucketSeed {
472
474
 
473
475
  function minimumRateRpsForRules(rules: readonly AcquirePacingRule[]): number {
474
476
  if (rules.some((rule) => rule.ruleId === 'default:provider-scope')) {
475
- return DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS;
477
+ return DEFAULT_UNKNOWN_PROVIDER_MIN_RPS;
476
478
  }
477
479
  const explicitAdaptiveMaxima = rules.flatMap((rule) => {
478
480
  if (rule.adaptiveMaxRps == null || rule.adaptiveMaxRps <= 0) return [];