deepline 0.3.323 → 0.3.324

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 (112) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +454 -129
  3. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +407 -37
  4. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +18 -4
  5. package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +16 -1
  6. package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +6 -2
  7. package/dist/bundling-sources/shared_libs/play-runtime/play-http-request.ts +1 -1
  8. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/gradient2-concurrency-controller.ts +15 -5
  10. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-capacity-policy.ts +1 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-concurrency-policy.ts +7 -2
  12. package/dist/bundling-sources/shared_libs/play-runtime/provider-row-retry-queue.ts +65 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +2 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/runtime-flow.ts +34 -5
  15. package/dist/bundling-sources/shared_libs/play-runtime/runtime-map-row-feed.ts +116 -15
  16. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-scope.ts +59 -6
  17. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +294 -37
  18. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +73 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +2 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +41 -3
  21. package/dist/cli/{auth-4TYSEEPE.mjs → auth-6KWNR6AI.mjs} +4 -4
  22. package/dist/cli/auth-LKSTC3YV.js +31 -0
  23. package/dist/cli/{billing-HYX3OWXB.js → billing-CLLWHIAT.js} +70 -70
  24. package/dist/cli/{billing-QDER3JLM.mjs → billing-VQ2NUJLB.mjs} +4 -4
  25. package/dist/cli/{chunk-GUPKB4UE.js → chunk-2UQUOUAW.js} +2 -2
  26. package/dist/cli/{chunk-FGD2M25K.mjs → chunk-3ZKAKQYC.mjs} +1 -1
  27. package/dist/cli/{chunk-HJN5KR42.mjs → chunk-5CQ554BM.mjs} +1 -1
  28. package/dist/cli/{chunk-VN433ENM.mjs → chunk-5QZFWY7C.mjs} +2 -2
  29. package/dist/cli/{chunk-2HYJDTQT.mjs → chunk-AKM5JVPG.mjs} +2 -2
  30. package/dist/cli/{chunk-HSZD4VBE.js → chunk-D5YJHGST.js} +4 -4
  31. package/dist/cli/{chunk-CXJND6CJ.mjs → chunk-DO2VA5UP.mjs} +2 -2
  32. package/dist/cli/{chunk-YY6EASRE.mjs → chunk-EHSI2DMO.mjs} +4 -4
  33. package/dist/cli/{chunk-WOG5IKUF.js → chunk-EXKRHZRG.js} +220 -220
  34. package/dist/cli/{chunk-KLIHE4X5.js → chunk-G4F2AIQ5.js} +6 -6
  35. package/dist/cli/{chunk-LXUY7LRE.js → chunk-GMO3NBE2.js} +60 -60
  36. package/dist/cli/{chunk-M2WAAIM4.js → chunk-GROXVGGW.js} +6 -6
  37. package/dist/cli/{chunk-UE55ECMI.mjs → chunk-HHZDWBSQ.mjs} +1 -1
  38. package/dist/cli/{chunk-3OTHDXTV.mjs → chunk-I7RLL7LN.mjs} +1 -1
  39. package/dist/cli/{chunk-BEG62AQW.js → chunk-IXYR65PW.js} +1 -1
  40. package/dist/cli/{chunk-ZF6WOSMW.mjs → chunk-LHJKYMTS.mjs} +1 -1
  41. package/dist/cli/{chunk-UMUH2FVZ.js → chunk-N5E66DP3.js} +6 -6
  42. package/dist/cli/{chunk-OQPG3ND3.js → chunk-NC2ZXG2Y.js} +26 -26
  43. package/dist/cli/{chunk-BC3E7GEW.mjs → chunk-NIOFMRYW.mjs} +5 -5
  44. package/dist/cli/{chunk-3I4KDUKY.mjs → chunk-NQJQ3CLX.mjs} +3 -3
  45. package/dist/cli/{chunk-FNKBVKQX.js → chunk-PAA4FYCF.js} +15 -15
  46. package/dist/cli/{chunk-I7TAO4XH.js → chunk-QM2Z4FQF.js} +6 -6
  47. package/dist/cli/{chunk-W2V624T6.mjs → chunk-TJGJTB5T.mjs} +1 -1
  48. package/dist/cli/{chunk-EWFKMQFO.js → chunk-TZZNI2A4.js} +50 -50
  49. package/dist/cli/{chunk-JGTV5QNG.mjs → chunk-U2RPP53P.mjs} +6 -6
  50. package/dist/cli/{chunk-HUQVA5MR.js → chunk-V5PA6TCT.js} +48 -48
  51. package/dist/cli/{client-CEX7EHUM.mjs → client-VHUKTDAZ.mjs} +3 -3
  52. package/dist/cli/client-WMSTRUGP.js +20 -0
  53. package/dist/cli/{csv-GBPRJ6YU.js → csv-5MZY7HU5.js} +12 -12
  54. package/dist/cli/{csv-LR5KWOIK.mjs → csv-7DA4APB6.mjs} +3 -3
  55. package/dist/cli/{db-4BMCUU34.mjs → db-7UPW3N5U.mjs} +4 -4
  56. package/dist/cli/{db-VFKXAHQA.js → db-ICG3FUVE.js} +20 -20
  57. package/dist/cli/{describe-BYWGWALB.js → describe-VDRY2JCE.js} +15 -15
  58. package/dist/cli/{describe-VTLU5KX5.mjs → describe-ZAPGM5ND.mjs} +8 -8
  59. package/dist/cli/{executions-DU7I6YEU.js → executions-D5GYCY3A.js} +6 -6
  60. package/dist/cli/{executions-NGREVSY6.mjs → executions-TK3C2FAQ.mjs} +4 -4
  61. package/dist/cli/{feedback-PHPAWVHC.js → feedback-PZAZZUXF.js} +6 -6
  62. package/dist/cli/{feedback-GUY2CCGF.mjs → feedback-T4EIP5QR.mjs} +3 -3
  63. package/dist/cli/git-73N6EUZ3.js +23 -0
  64. package/dist/cli/{git-22YV3DFJ.mjs → git-WBFWIVO5.mjs} +4 -4
  65. package/dist/cli/index.js +75 -75
  66. package/dist/cli/index.mjs +31 -31
  67. package/dist/cli/{legacy-noops-YEQDVI6U.mjs → legacy-noops-DUCK7OKN.mjs} +4 -4
  68. package/dist/cli/{legacy-noops-V72GCF5R.js → legacy-noops-LRBJWXRK.js} +7 -7
  69. package/dist/cli/{monitors-MVJBQDQG.js → monitors-7S2SL62G.js} +85 -85
  70. package/dist/cli/{monitors-QOCAMBRV.mjs → monitors-WYH5AQIO.mjs} +4 -4
  71. package/dist/cli/{org-DMIOMJK6.js → org-EQNGGHPV.js} +60 -60
  72. package/dist/cli/{org-A53XV2UQ.mjs → org-XCSMLTUW.mjs} +4 -4
  73. package/dist/cli/{play-73L74CUW.mjs → play-A5ISFBJM.mjs} +7 -7
  74. package/dist/cli/play-CLC2Z5SO.js +152 -0
  75. package/dist/cli/{providers-PEOAZYM6.js → providers-DR2CLD3N.js} +7 -7
  76. package/dist/cli/{providers-6LDD5X5G.mjs → providers-KHTZFQ5I.mjs} +4 -4
  77. package/dist/cli/{quickstart-PXMC66UG.mjs → quickstart-KT4LKORA.mjs} +3 -3
  78. package/dist/cli/{quickstart-33IPDK2I.js → quickstart-NIRWI6YU.js} +5 -5
  79. package/dist/cli/search-DM7IQS5F.js +26 -0
  80. package/dist/cli/{search-FJEJDKPA.mjs → search-WCEXPBAT.mjs} +5 -5
  81. package/dist/cli/{secrets-3SCAG3T7.js → secrets-QSFEZGVE.js} +13 -13
  82. package/dist/cli/{secrets-Q3ZWHE5S.mjs → secrets-TN7DW3QR.mjs} +4 -4
  83. package/dist/cli/{sessions-QJB4LSNZ.js → sessions-LJS6W67T.js} +5 -5
  84. package/dist/cli/{sessions-RY3SCVN7.mjs → sessions-WSE5FN4H.mjs} +4 -4
  85. package/dist/cli/{settings-DLAE6POM.js → settings-FGS3VYJN.js} +74 -74
  86. package/dist/cli/{settings-VTYJKMI6.mjs → settings-Z3V53A6J.mjs} +7 -7
  87. package/dist/cli/{setup-UYQ4V4GP.mjs → setup-GMH4VJS3.mjs} +6 -6
  88. package/dist/cli/{setup-LM2VWSFE.js → setup-ZA234HEY.js} +41 -41
  89. package/dist/cli/skills-6APAQV6W.js +28 -0
  90. package/dist/cli/{skills-2OKHLQF7.mjs → skills-7G2RZMYH.mjs} +4 -4
  91. package/dist/cli/{tools-F5RYANCG.mjs → tools-7EE4BGXO.mjs} +6 -6
  92. package/dist/cli/tools-EPYUFWPB.js +38 -0
  93. package/dist/cli/update-FPVX3XJC.js +28 -0
  94. package/dist/cli/{update-5FDHGSNE.mjs → update-O5QEWS7K.mjs} +6 -6
  95. package/dist/cli/{workflow-TWQH53ZO.mjs → workflow-KE54RUTR.mjs} +5 -5
  96. package/dist/cli/{workflow-EQMKPCUV.js → workflow-M5PAAW3O.js} +15 -15
  97. package/dist/index.js +6 -6
  98. package/dist/index.mjs +3 -3
  99. package/dist/install-integrity.json +75 -74
  100. package/dist/release.d.mts +1 -1
  101. package/dist/release.d.ts +1 -1
  102. package/dist/release.js +2 -2
  103. package/dist/release.mjs +1 -1
  104. package/package.json +1 -1
  105. package/dist/cli/auth-KWPQ4JVV.js +0 -31
  106. package/dist/cli/client-YMQ2QFV7.js +0 -20
  107. package/dist/cli/git-W5NEI2BP.js +0 -23
  108. package/dist/cli/play-NBFCGFVC.js +0 -152
  109. package/dist/cli/search-FPUK2H6Y.js +0 -26
  110. package/dist/cli/skills-5NH6JX5H.js +0 -28
  111. package/dist/cli/tools-ZPGDY3I2.js +0 -38
  112. package/dist/cli/update-W5KBXMKC.js +0 -28
@@ -26,6 +26,7 @@ import {
26
26
  type PacingRule,
27
27
  type ProviderConcurrencyGrant,
28
28
  type ProviderPacingGrant,
29
+ type ProviderPermitValidationOptions,
29
30
  type RateStateBackend,
30
31
  } from './rate-state-backend';
31
32
  import {
@@ -37,6 +38,7 @@ import {
37
38
  InMemoryBudgetStateBackend,
38
39
  type BudgetStateBackend,
39
40
  } from './budget-state-backend';
41
+ import { PROVIDER_CONCURRENCY_SAFETY_LIMIT } from '../provider-capacity/provider-concurrency-policy';
40
42
 
41
43
  export interface WorkLease {
42
44
  /** Free the slot / pacing permit. Idempotent. MUST be called in a finally. */
@@ -68,7 +70,10 @@ export interface ProviderPermit extends WorkLease {
68
70
  */
69
71
  park(): Promise<void>;
70
72
  /** Recheck shared provider state immediately before network dispatch. */
71
- validate?(): Promise<boolean>;
73
+ validate?(
74
+ signal?: AbortSignal,
75
+ options?: ProviderPermitValidationOptions,
76
+ ): Promise<boolean>;
72
77
  }
73
78
 
74
79
  export type BudgetKind = 'toolCall' | 'retry' | 'waterfallStep';
@@ -179,7 +184,11 @@ export interface PlayExecutionGovernor {
179
184
  */
180
185
  acquireProviderPermit(
181
186
  toolId: string,
182
- opts?: { signal?: AbortSignal; yieldProviderHolds?: boolean },
187
+ opts?: {
188
+ signal?: AbortSignal;
189
+ yieldProviderHolds?: boolean;
190
+ retryPriority?: boolean;
191
+ },
183
192
  ): Promise<ProviderPermit>;
184
193
 
185
194
  /**
@@ -444,9 +453,12 @@ export function createPlayExecutionGovernor(
444
453
  gatewayDispatchProof: rateLease.gatewayDispatchProof ?? null,
445
454
  ...(rateLease.validate
446
455
  ? {
447
- validate: async () => {
456
+ validate: async (
457
+ signal?: AbortSignal,
458
+ options?: ProviderPermitValidationOptions,
459
+ ) => {
448
460
  if (settled) return false;
449
- return await rateLease.validate!();
461
+ return await rateLease.validate!(signal, options);
450
462
  },
451
463
  }
452
464
  : {}),
@@ -519,6 +531,7 @@ export function createPlayExecutionGovernor(
519
531
  ruleId: 'default:provider-scope',
520
532
  requestsPerWindow: DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS,
521
533
  windowMs: 1_000,
534
+ maxConcurrency: PROVIDER_CONCURRENCY_SAFETY_LIMIT,
522
535
  }));
523
536
  }
524
537
  providerByTool.set(toolId, pacing.provider);
@@ -661,6 +674,7 @@ export function createPlayExecutionGovernor(
661
674
  rules: pacing.rules,
662
675
  signal: opts?.signal,
663
676
  yieldProviderHolds: opts?.yieldProviderHolds,
677
+ ...(opts?.retryPriority ? { retryPriority: true } : {}),
664
678
  });
665
679
  return providerPermitFrom(
666
680
  pacing.provider,
@@ -17,6 +17,10 @@ export const DEFAULT_ADVISORY_PROVIDER_MAX_RPS = 100;
17
17
  /** Cold-start rate for an unknown provider bucket shared across a fleet key. */
18
18
  export const DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS = 10;
19
19
 
20
+ /** Recover quickly to useful seed capacity while leaving quota headroom. */
21
+ export const DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS =
22
+ DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS * 0.9;
23
+
20
24
  /** Learned-rate floor, independent of the unknown provider's starting guess. */
21
25
  export const DEFAULT_UNKNOWN_PROVIDER_MIN_RPS = 2;
22
26
 
@@ -93,7 +97,10 @@ export interface PacingPermit {
93
97
  */
94
98
  park?(): Promise<void>;
95
99
  /** Recheck the durable provider-scope hold immediately before dispatch. */
96
- validate?(): Promise<boolean>;
100
+ validate?(
101
+ signal?: AbortSignal,
102
+ options?: ProviderPermitValidationOptions,
103
+ ): Promise<boolean>;
97
104
  /** Server-authored context for attributing this request's provider 429. */
98
105
  readonly concurrencyGrant?: ProviderConcurrencyGrant | null;
99
106
  /** Effective shared rate for every app-runtime provider permit. */
@@ -103,9 +110,15 @@ export interface PacingPermit {
103
110
  readonly bucketId: string;
104
111
  readonly rateScopeToken: string;
105
112
  readonly retryAtMsAtGrant: number;
113
+ readonly dispatchValidationSupported?: boolean;
106
114
  } | null;
107
115
  }
108
116
 
117
+ export type ProviderPermitValidationOptions = {
118
+ /** The request carries this permit's signed proof to the execution gateway. */
119
+ dispatchValidationAtGateway?: boolean;
120
+ };
121
+
109
122
  /**
110
123
  * Discriminates the backend so the Governor can adapt its policy to it. Only
111
124
  * `app_runtime_postgres` (the Absurd/Node substrate's DB-backed pacer) is
@@ -137,6 +150,8 @@ export interface RateStateBackend {
137
150
  signal?: AbortSignal;
138
151
  /** Managed Plays hand a shared provider hold to durable row suspension. */
139
152
  yieldProviderHolds?: boolean;
153
+ /** Prefer an already-started logical call over fresh permit waiters. */
154
+ retryPriority?: boolean;
140
155
  }): Promise<PacingPermit>;
141
156
 
142
157
  /**
@@ -503,9 +503,13 @@ export class MapExecutionProgress {
503
503
  });
504
504
  }
505
505
 
506
- resolverStarted(): void {
506
+ get activeResolvers(): number {
507
+ return this.active;
508
+ }
509
+
510
+ resolverStarted(input: { retry?: boolean } = {}): void {
507
511
  this.tracksActivity = true;
508
- this.started += 1;
512
+ if (!input.retry) this.started += 1;
509
513
  this.active += 1;
510
514
  }
511
515
 
@@ -291,7 +291,7 @@ export async function sendPlayHttpRequest(input: {
291
291
  try {
292
292
  signal.throwIfAborted();
293
293
  const valid =
294
- !acquired.validate || (await acquired.validate());
294
+ !acquired.validate || (await acquired.validate(signal));
295
295
  signal.throwIfAborted();
296
296
  if (valid) return acquired;
297
297
  } catch (error) {
@@ -88,6 +88,8 @@ export type PlayRunnerRateStateAcquireResult = {
88
88
  waitMs: number;
89
89
  /** This receiver serves rate_state_validate. Absent on pre-expansion readers. */
90
90
  scopeValidationSupported?: boolean;
91
+ /** This gateway rechecks signed provider permits on integration execute. */
92
+ dispatchValidationSupported?: boolean;
91
93
  /**
92
94
  * Gateway clock used to produce `scheduledAtMs`. Runners translate the
93
95
  * schedule into relative delays so cross-machine clock skew cannot compress
@@ -7,6 +7,8 @@ export type Gradient2ConcurrencyControllerOptions = Readonly<{
7
7
  rttTolerance: number;
8
8
  /** Bounded cold-start probe; one or below preserves ordinary Gradient2. */
9
9
  startupProbeMultiplier?: number;
10
+ /** Reprobe undeclared capacity after an entire healthy completion window. */
11
+ recoveryProbeMultiplier?: number;
10
12
  }>;
11
13
 
12
14
  export type Gradient2Observation = Readonly<{
@@ -63,6 +65,7 @@ export class Gradient2ConcurrencyController {
63
65
  readonly #longWindowSmoothing: number;
64
66
  readonly #rttTolerance: number;
65
67
  readonly #startupProbeMultiplier: number;
68
+ readonly #recoveryProbeMultiplier: number;
66
69
  #limit: number;
67
70
  #shortRttMs: number | null = null;
68
71
  #longRttMs: number | null = null;
@@ -95,6 +98,13 @@ export class Gradient2ConcurrencyController {
95
98
  1,
96
99
  positive(options.startupProbeMultiplier ?? 1, 'Startup probe multiplier'),
97
100
  );
101
+ this.#recoveryProbeMultiplier = Math.max(
102
+ 1,
103
+ positive(
104
+ options.recoveryProbeMultiplier ?? 1,
105
+ 'Recovery probe multiplier',
106
+ ),
107
+ );
98
108
  this.#windowStartedAtLimit = this.#limit;
99
109
  if (checkpoint) {
100
110
  this.#limit = clamp(
@@ -171,13 +181,13 @@ export class Gradient2ConcurrencyController {
171
181
  0.5,
172
182
  1,
173
183
  );
174
- const probeHealthy =
175
- !this.#hasObservedThrottle &&
176
- this.#startupProbeMultiplier > 1 &&
177
- gradient >= 1 - this.#smoothing;
184
+ const probeMultiplier = this.#hasObservedThrottle
185
+ ? this.#recoveryProbeMultiplier
186
+ : this.#startupProbeMultiplier;
187
+ const probeHealthy = probeMultiplier > 1 && gradient >= 1 - this.#smoothing;
178
188
  if (probeHealthy) {
179
189
  this.#limit = clamp(
180
- this.#limit * this.#startupProbeMultiplier,
190
+ this.#limit * probeMultiplier,
181
191
  this.#minimumLimit,
182
192
  this.#maximumLimit,
183
193
  );
@@ -30,6 +30,7 @@ export const PROVIDER_CAPACITY_POLICY = Object.freeze({
30
30
  longWindowSmoothing: 0.03,
31
31
  rttTolerance: 1.25,
32
32
  startupProbeMultiplier: 2,
33
+ unknownProviderRecoveryProbeMultiplier: 2,
33
34
  }),
34
35
  } as const);
35
36
 
@@ -15,16 +15,21 @@ export const PROVIDER_CONCURRENCY_CONTROLLER_TUNING = Object.freeze({
15
15
 
16
16
  /**
17
17
  * The one Gradient2 policy used by Postgres enforcement, shadow evaluation,
18
- * and simulations. Callers choose only the server-authored initial and maximum
19
- * limits; algorithm tuning must not drift by deployment surface.
18
+ * and simulations. Callers choose the server-authored limits and whether the
19
+ * scope is undeclared; algorithm tuning must not drift by deployment surface.
20
20
  */
21
21
  export function providerConcurrencyControllerOptions(input: {
22
22
  initialLimit: number;
23
23
  maximumLimit?: number;
24
+ unknownProvider?: boolean;
24
25
  }): Gradient2ConcurrencyControllerOptions {
25
26
  return Object.freeze({
26
27
  ...PROVIDER_CONCURRENCY_CONTROLLER_TUNING,
27
28
  initialLimit: input.initialLimit,
28
29
  maximumLimit: input.maximumLimit ?? PROVIDER_CONCURRENCY_SAFETY_LIMIT,
30
+ recoveryProbeMultiplier: input.unknownProvider
31
+ ? PROVIDER_CAPACITY_POLICY.concurrency
32
+ .unknownProviderRecoveryProbeMultiplier
33
+ : 1,
29
34
  });
30
35
  }
@@ -0,0 +1,65 @@
1
+ export type ProviderRowRetry = {
2
+ rowIndex: number;
3
+ provider: string;
4
+ retryAtMs: number;
5
+ readyAtMs: number;
6
+ /** Ready work may run, but this row must not wait in an otherwise idle map. */
7
+ residentWaitBudgetExhausted?: boolean;
8
+ };
9
+
10
+ /** Deadlines belong to rows; a long hold must not hide another ready row. */
11
+ export class ProviderRowRetryQueue {
12
+ private readonly rows: ProviderRowRetry[] = [];
13
+
14
+ get size(): number {
15
+ return this.rows.length;
16
+ }
17
+
18
+ peek(): ProviderRowRetry | undefined {
19
+ return this.rows[0];
20
+ }
21
+
22
+ add(row: ProviderRowRetry): void {
23
+ let index = this.rows.length;
24
+ this.rows.push(row);
25
+ while (index > 0) {
26
+ const parentIndex = Math.floor((index - 1) / 2);
27
+ const parent = this.rows[parentIndex]!;
28
+ if (!this.precedes(row, parent)) break;
29
+ this.rows[index] = parent;
30
+ index = parentIndex;
31
+ }
32
+ this.rows[index] = row;
33
+ }
34
+
35
+ takeReady(nowMs: number): ProviderRowRetry | undefined {
36
+ const first = this.peek();
37
+ if (!first || first.readyAtMs > nowMs) return undefined;
38
+ const last = this.rows.pop()!;
39
+ if (this.rows.length === 0) return first;
40
+ let index = 0;
41
+ while (true) {
42
+ const left = index * 2 + 1;
43
+ if (left >= this.rows.length) break;
44
+ const right = left + 1;
45
+ const childIndex =
46
+ right < this.rows.length &&
47
+ this.precedes(this.rows[right]!, this.rows[left]!)
48
+ ? right
49
+ : left;
50
+ const child = this.rows[childIndex]!;
51
+ if (!this.precedes(child, last)) break;
52
+ this.rows[index] = child;
53
+ index = childIndex;
54
+ }
55
+ this.rows[index] = last;
56
+ return first;
57
+ }
58
+
59
+ private precedes(left: ProviderRowRetry, right: ProviderRowRetry): boolean {
60
+ return (
61
+ left.readyAtMs < right.readyAtMs ||
62
+ (left.readyAtMs === right.readyAtMs && left.rowIndex < right.rowIndex)
63
+ );
64
+ }
65
+ }
@@ -770,6 +770,7 @@ export interface RuntimeResourceGovernor {
770
770
  toolId: string;
771
771
  signal?: AbortSignal;
772
772
  yieldProviderHolds?: boolean;
773
+ retryPriority?: boolean;
773
774
  }): Promise<RuntimeProviderPermit>;
774
775
  /**
775
776
  * Non-blocking provider admission from capacity this process already holds.
@@ -1746,6 +1747,7 @@ export function createRuntimeResourceGovernor(input: {
1746
1747
  {
1747
1748
  signal: providerInput.signal,
1748
1749
  yieldProviderHolds: providerInput.yieldProviderHolds,
1750
+ ...(providerInput.retryPriority ? { retryPriority: true } : {}),
1749
1751
  },
1750
1752
  );
1751
1753
  observe({
@@ -8,6 +8,8 @@
8
8
  * inventing a second runtime scheduler.
9
9
  */
10
10
 
11
+ import type { ProviderPermitValidationOptions } from './governor/rate-state-backend';
12
+
11
13
  export type RuntimeFlowLease = {
12
14
  release(): void;
13
15
  };
@@ -86,7 +88,10 @@ export type RuntimeDispatchProviderPermit = RuntimeFlowLease & {
86
88
  cancel(): Promise<void>;
87
89
  park?(): Promise<void>;
88
90
  /** Recheck a shared provider hold at the physical dispatch boundary. */
89
- validate?(): Promise<boolean>;
91
+ validate?(
92
+ signal?: AbortSignal,
93
+ options?: ProviderPermitValidationOptions,
94
+ ): Promise<boolean>;
90
95
  };
91
96
 
92
97
  export type RuntimeDispatchFlowInput = {
@@ -99,6 +104,8 @@ export type RuntimeDispatchFlowInput = {
99
104
  * outbound send. The callback must call the supplied boundary exactly once.
100
105
  */
101
106
  deferProviderPermitValidation?: boolean;
107
+ /** The request forwards its signed permit to the execution gateway. */
108
+ providerDispatchAtGateway?: boolean;
102
109
  /** Emits the request-side transport wait observed by the caller's metrics. */
103
110
  onTransportAdmitted?: (waitMs: number) => void;
104
111
  };
@@ -133,7 +140,15 @@ export type RuntimeDispatchScope = {
133
140
  onPermitInvalidated?: () => void,
134
141
  ) => boolean | Promise<boolean>,
135
142
  ) => Output | Promise<Output>,
136
- beforeRelease?: () => void | Promise<void>,
143
+ /**
144
+ * Persist response feedback while the lease is live. Return
145
+ * `not_dispatched` only for a definite rejection before provider work;
146
+ * this refunds the reservation. Ambiguous outcomes retain release semantics.
147
+ */
148
+ beforeRelease?: () =>
149
+ | void
150
+ | 'not_dispatched'
151
+ | Promise<void | 'not_dispatched'>,
137
152
  ): Promise<Output>;
138
153
  };
139
154
 
@@ -306,7 +321,10 @@ export function createRuntimeDispatchFlow<
306
321
  onPermitInvalidated?: () => void,
307
322
  ) => boolean | Promise<boolean>,
308
323
  ) => RequestOutput | Promise<RequestOutput>,
309
- beforeRelease?: () => void | Promise<void>,
324
+ beforeRelease?: () =>
325
+ | void
326
+ | 'not_dispatched'
327
+ | Promise<void | 'not_dispatched'>,
310
328
  ): Promise<RequestOutput> => {
311
329
  if (!scopeOpen) {
312
330
  return Promise.reject(
@@ -348,7 +366,11 @@ export function createRuntimeDispatchFlow<
348
366
  let replaced = false;
349
367
  while (
350
368
  permit.providerPermit.validate &&
351
- !(await permit.providerPermit.validate())
369
+ !(await (admission.providerDispatchAtGateway
370
+ ? permit.providerPermit.validate(admission.signal, {
371
+ dispatchValidationAtGateway: true,
372
+ })
373
+ : permit.providerPermit.validate(admission.signal)))
352
374
  ) {
353
375
  requestStarted = false;
354
376
  onPermitInvalidated?.();
@@ -412,7 +434,14 @@ export function createRuntimeDispatchFlow<
412
434
  // always release all leases even when feedback persistence
413
435
  // fails.
414
436
  try {
415
- if (requestStarted) await beforeRelease?.();
437
+ if (
438
+ requestStarted &&
439
+ (await beforeRelease?.()) === 'not_dispatched'
440
+ ) {
441
+ // A definite receiver rejection returns the reservation.
442
+ // Ambiguous exchanges retain ordinary release semantics.
443
+ requestStarted = false;
444
+ }
416
445
  } finally {
417
446
  await releasePermit();
418
447
  }
@@ -15,6 +15,7 @@ export type RuntimeMapRowFeedSnapshot = {
15
15
  };
16
16
 
17
17
  export const RUNTIME_MAP_ROW_FEED_MAX_WAIT_MS = 30 * 60 * 1000;
18
+ const MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
18
19
 
19
20
  export class RuntimeMapRowFeedStalledError extends Error {
20
21
  readonly snapshot: RuntimeMapRowFeedSnapshot;
@@ -51,7 +52,11 @@ export class RuntimeMapRowFeed {
51
52
  #closed = false;
52
53
  #failure: unknown = null;
53
54
  #failed = false;
54
- readonly #rowWaiters = new Map<Waiter<boolean>, number>();
55
+ // A null index waits for pool activity/deadlines independently of source
56
+ // closure; an indexed waiter still means "more source rows".
57
+ readonly #rowWaiters = new Map<Waiter<boolean>, number | null>();
58
+ #workerWakeTimer: ReturnType<typeof setTimeout> | null = null;
59
+ #workerWakeAtMs: number | null = null;
55
60
  #drainWaiter: DrainWaiter | null = null;
56
61
  #onAppended: ((fromIndex: number, toIndex: number) => void) | null = null;
57
62
  readonly #maxWaitMs: number;
@@ -89,6 +94,7 @@ export class RuntimeMapRowFeed {
89
94
  close(): void {
90
95
  if (this.#closed || this.#failed) return;
91
96
  this.#closed = true;
97
+ if (!this.#hasWorkerWakeWaiters()) this.#clearWorkerWake();
92
98
  this.#wakeRowWaiters();
93
99
  this.#settleDrainWaiter();
94
100
  }
@@ -97,6 +103,7 @@ export class RuntimeMapRowFeed {
97
103
  if (this.#failed) return;
98
104
  this.#failed = true;
99
105
  this.#failure = error;
106
+ this.#clearWorkerWake();
100
107
  for (const [waiter] of this.#rowWaiters) {
101
108
  clearTimeout(waiter.timer);
102
109
  waiter.reject(error);
@@ -117,6 +124,38 @@ export class RuntimeMapRowFeed {
117
124
  return this.#closed;
118
125
  }
119
126
 
127
+ /** Recheck a provider retry deadline while the source prepares its next page. */
128
+ wakeWorkersAt(wakeAtMs: number): void {
129
+ if (!Number.isFinite(wakeAtMs)) {
130
+ throw new Error('Runtime map worker wake deadline must be finite.');
131
+ }
132
+ if (this.#failed || (this.#closed && !this.#hasWorkerWakeWaiters())) return;
133
+ if (this.#workerWakeAtMs !== null && this.#workerWakeAtMs <= wakeAtMs)
134
+ return;
135
+ this.#clearWorkerWake();
136
+ this.#workerWakeAtMs = wakeAtMs;
137
+ this.#scheduleWorkerWake();
138
+ }
139
+
140
+ #scheduleWorkerWake(): void {
141
+ const wakeAtMs = this.#workerWakeAtMs;
142
+ if (wakeAtMs === null) return;
143
+ const waitMs = Math.min(
144
+ MAX_TIMER_DELAY_MS,
145
+ Math.max(0, wakeAtMs - Date.now()),
146
+ );
147
+ this.#workerWakeTimer = setTimeout(() => {
148
+ this.#workerWakeTimer = null;
149
+ if (Date.now() < wakeAtMs) {
150
+ this.#scheduleWorkerWake();
151
+ return;
152
+ }
153
+ this.#workerWakeAtMs = null;
154
+ this.wakeWorkers();
155
+ }, waitMs);
156
+ this.#workerWakeTimer.unref?.();
157
+ }
158
+
120
159
  markDispatched(count = 1): void {
121
160
  if (!Number.isInteger(count) || count < 0) {
122
161
  throw new Error(
@@ -144,28 +183,77 @@ export class RuntimeMapRowFeed {
144
183
  }
145
184
 
146
185
  /**
147
- * Resolves true once row `index` has been appended, false once the feed is
148
- * closed with no such row. Rejects with the feed's failure.
186
+ * Resolves true once row `index` has been appended or an explicitly scheduled
187
+ * worker wake requests a recheck, false once the feed is closed with no such
188
+ * row. Rejects with the feed's failure.
149
189
  */
150
190
  waitForRows(index: number): Promise<boolean> {
151
191
  if (this.#failed) return Promise.reject(this.#failure);
152
192
  if (index < this.#appended) return Promise.resolve(true);
153
193
  if (this.#closed) return Promise.resolve(false);
194
+ return this.#waitForRowsOrWake(index);
195
+ }
196
+
197
+ /** A retry can become ready while siblings work after the source closes. */
198
+ waitForWorkerWake(wakeAtMs: number): Promise<boolean> {
199
+ if (!Number.isFinite(wakeAtMs)) {
200
+ return Promise.reject(
201
+ new Error('Runtime map worker wake deadline must be finite.'),
202
+ );
203
+ }
204
+ if (this.#failed) return Promise.reject(this.#failure);
205
+ if (wakeAtMs <= Date.now()) return Promise.resolve(true);
206
+ // A known provider hold is bounded by its own deadline, rather than the
207
+ // source's idle diagnostic. Leave indexed source waits on their usual bound.
208
+ const waiting = this.#waitForRowsOrWake(
209
+ null,
210
+ Math.max(this.#maxWaitMs, wakeAtMs - Date.now() + 1),
211
+ );
212
+ this.wakeWorkersAt(wakeAtMs);
213
+ return waiting;
214
+ }
215
+
216
+ /** Recheck retry ownership when the last active resolver returns its lease. */
217
+ wakeWorkers(): void {
218
+ this.#clearWorkerWake();
219
+ for (const [waiter] of this.#rowWaiters) {
220
+ clearTimeout(waiter.timer);
221
+ waiter.resolve(true);
222
+ }
223
+ this.#rowWaiters.clear();
224
+ }
225
+
226
+ #waitForRowsOrWake(
227
+ index: number | null,
228
+ maxWaitMs = this.#maxWaitMs,
229
+ ): Promise<boolean> {
154
230
  return new Promise<boolean>((resolve, reject) => {
155
231
  const startedAt = Date.now();
232
+ const expiresAtMs = startedAt + maxWaitMs;
233
+ const onTimeout = () => {
234
+ const remainingMs = expiresAtMs - Date.now();
235
+ if (remainingMs > 0) {
236
+ waiter.timer = setTimeout(
237
+ onTimeout,
238
+ Math.min(MAX_TIMER_DELAY_MS, remainingMs),
239
+ );
240
+ waiter.timer.unref?.();
241
+ return;
242
+ }
243
+ this.#rowWaiters.delete(waiter);
244
+ if (this.#rowWaiters.size === 0) this.#clearWorkerWake();
245
+ reject(
246
+ new RuntimeMapRowFeedStalledError({
247
+ waiter: 'worker',
248
+ waitMs: Date.now() - startedAt,
249
+ snapshot: this.snapshot(),
250
+ }),
251
+ );
252
+ };
156
253
  const waiter: Waiter<boolean> = {
157
254
  resolve,
158
255
  reject,
159
- timer: setTimeout(() => {
160
- this.#rowWaiters.delete(waiter);
161
- reject(
162
- new RuntimeMapRowFeedStalledError({
163
- waiter: 'worker',
164
- waitMs: Date.now() - startedAt,
165
- snapshot: this.snapshot(),
166
- }),
167
- );
168
- }, this.#maxWaitMs),
256
+ timer: setTimeout(onTimeout, Math.min(MAX_TIMER_DELAY_MS, maxWaitMs)),
169
257
  };
170
258
  waiter.timer.unref?.();
171
259
  this.#rowWaiters.set(waiter, index);
@@ -216,13 +304,26 @@ export class RuntimeMapRowFeed {
216
304
  };
217
305
  }
218
306
 
307
+ #clearWorkerWake(): void {
308
+ if (this.#workerWakeTimer) clearTimeout(this.#workerWakeTimer);
309
+ this.#workerWakeTimer = null;
310
+ this.#workerWakeAtMs = null;
311
+ }
312
+
313
+ #hasWorkerWakeWaiters(): boolean {
314
+ for (const index of this.#rowWaiters.values()) {
315
+ if (index === null) return true;
316
+ }
317
+ return false;
318
+ }
319
+
219
320
  #wakeRowWaiters(): void {
220
321
  for (const [waiter, index] of this.#rowWaiters) {
221
- if (index < this.#appended) {
322
+ if (index !== null && index < this.#appended) {
222
323
  this.#rowWaiters.delete(waiter);
223
324
  clearTimeout(waiter.timer);
224
325
  waiter.resolve(true);
225
- } else if (this.#closed) {
326
+ } else if (this.#closed && index !== null) {
226
327
  this.#rowWaiters.delete(waiter);
227
328
  clearTimeout(waiter.timer);
228
329
  waiter.resolve(false);