@vybestack/llxprt-code-core 0.10.0 → 0.11.0-nightly.260728.ffaa8d5d7

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 (168) hide show
  1. package/dist/src/config/config.d.ts +20 -0
  2. package/dist/src/config/config.js +143 -23
  3. package/dist/src/config/config.js.map +1 -1
  4. package/dist/src/config/configBaseCore.d.ts +6 -2
  5. package/dist/src/config/configBaseCore.js +8 -0
  6. package/dist/src/config/configBaseCore.js.map +1 -1
  7. package/dist/src/config/configConstructor.d.ts +2 -0
  8. package/dist/src/config/configConstructor.js +9 -1
  9. package/dist/src/config/configConstructor.js.map +1 -1
  10. package/dist/src/config/configTestHarness.d.ts +8 -6
  11. package/dist/src/config/configTestHarness.js +10 -4
  12. package/dist/src/config/configTestHarness.js.map +1 -1
  13. package/dist/src/config/configTypes.d.ts +7 -2
  14. package/dist/src/config/configTypes.js +2 -0
  15. package/dist/src/config/configTypes.js.map +1 -1
  16. package/dist/src/config/liveTrustTransitionLifecycle.d.ts +35 -0
  17. package/dist/src/config/liveTrustTransitionLifecycle.js +180 -0
  18. package/dist/src/config/liveTrustTransitionLifecycle.js.map +1 -0
  19. package/dist/src/config/schedulerSingleton.d.ts +2 -2
  20. package/dist/src/config/schedulerSingleton.js.map +1 -1
  21. package/dist/src/core/compression/types.d.ts +48 -1
  22. package/dist/src/core/compression/types.js +41 -0
  23. package/dist/src/core/compression/types.js.map +1 -1
  24. package/dist/src/core/model-limits.json +96 -0
  25. package/dist/src/core/model-limits.schema.d.ts +123 -0
  26. package/dist/src/core/model-limits.schema.js +75 -0
  27. package/dist/src/core/model-limits.schema.js.map +1 -0
  28. package/dist/src/core/tokenLimits.d.ts +6 -8
  29. package/dist/src/core/tokenLimits.js +56 -132
  30. package/dist/src/core/tokenLimits.js.map +1 -1
  31. package/dist/src/core/turn.d.ts +20 -3
  32. package/dist/src/core/turn.js +20 -3
  33. package/dist/src/core/turn.js.map +1 -1
  34. package/dist/src/hooks/hookRegistry.d.ts +1 -1
  35. package/dist/src/hooks/hookRegistry.js +3 -1
  36. package/dist/src/hooks/hookRegistry.js.map +1 -1
  37. package/dist/src/hooks/hookRunner.d.ts +6 -0
  38. package/dist/src/hooks/hookRunner.js +22 -6
  39. package/dist/src/hooks/hookRunner.js.map +1 -1
  40. package/dist/src/hooks/hookSystem.d.ts +10 -1
  41. package/dist/src/hooks/hookSystem.js +69 -12
  42. package/dist/src/hooks/hookSystem.js.map +1 -1
  43. package/dist/src/index.d.ts +1 -0
  44. package/dist/src/index.js +1 -0
  45. package/dist/src/index.js.map +1 -1
  46. package/dist/src/llm-types/geminiContent.d.ts +3 -0
  47. package/dist/src/policy/config.d.ts +4 -2
  48. package/dist/src/policy/config.js +22 -18
  49. package/dist/src/policy/config.js.map +1 -1
  50. package/dist/src/recording/ReplayEngine.js +39 -2
  51. package/dist/src/recording/ReplayEngine.js.map +1 -1
  52. package/dist/src/recording/SessionDiscovery.d.ts +8 -0
  53. package/dist/src/recording/SessionDiscovery.js +88 -3
  54. package/dist/src/recording/SessionDiscovery.js.map +1 -1
  55. package/dist/src/recording/SessionLockManager.d.ts +3 -0
  56. package/dist/src/recording/SessionLockManager.js +9 -3
  57. package/dist/src/recording/SessionLockManager.js.map +1 -1
  58. package/dist/src/recording/SessionRecordingService.d.ts +14 -1
  59. package/dist/src/recording/SessionRecordingService.js +27 -3
  60. package/dist/src/recording/SessionRecordingService.js.map +1 -1
  61. package/dist/src/recording/index.d.ts +4 -3
  62. package/dist/src/recording/index.js +4 -3
  63. package/dist/src/recording/index.js.map +1 -1
  64. package/dist/src/recording/replay-test-helpers.d.ts +5 -0
  65. package/dist/src/recording/replay-test-helpers.js +13 -0
  66. package/dist/src/recording/replay-test-helpers.js.map +1 -1
  67. package/dist/src/recording/resumeNotFoundMessages.d.ts +56 -0
  68. package/dist/src/recording/resumeNotFoundMessages.js +64 -0
  69. package/dist/src/recording/resumeNotFoundMessages.js.map +1 -0
  70. package/dist/src/recording/resumeSession.js +3 -2
  71. package/dist/src/recording/resumeSession.js.map +1 -1
  72. package/dist/src/recording/sessionManagement.d.ts +3 -0
  73. package/dist/src/recording/sessionManagement.js +38 -19
  74. package/dist/src/recording/sessionManagement.js.map +1 -1
  75. package/dist/src/recording/types.d.ts +42 -3
  76. package/dist/src/recording/types.js +1 -1
  77. package/dist/src/recording/types.js.map +1 -1
  78. package/dist/src/runtime/AgentRuntimeLoader.js +2 -1
  79. package/dist/src/runtime/AgentRuntimeLoader.js.map +1 -1
  80. package/dist/src/scheduler/liveOutput.d.ts +10 -19
  81. package/dist/src/scheduler/liveOutput.js +21 -21
  82. package/dist/src/scheduler/liveOutput.js.map +1 -1
  83. package/dist/src/scheduler/types.d.ts +2 -2
  84. package/dist/src/services/history/ContentConverters.d.ts +4 -0
  85. package/dist/src/services/history/ContentConverters.js +77 -59
  86. package/dist/src/services/history/ContentConverters.js.map +1 -1
  87. package/dist/src/services/history/HistoryService.d.ts +13 -1
  88. package/dist/src/services/history/HistoryService.js +71 -0
  89. package/dist/src/services/history/HistoryService.js.map +1 -1
  90. package/dist/src/services/history/IContent.d.ts +10 -0
  91. package/dist/src/services/history/IContent.js.map +1 -1
  92. package/dist/src/skills/skillManager.d.ts +11 -1
  93. package/dist/src/skills/skillManager.js +21 -1
  94. package/dist/src/skills/skillManager.js.map +1 -1
  95. package/dist/src/test-utils/mock-tool.d.ts +3 -3
  96. package/dist/src/test-utils/mock-tool.js.map +1 -1
  97. package/dist/src/test-utils/tools.d.ts +2 -2
  98. package/dist/src/test-utils/tools.js.map +1 -1
  99. package/dist/src/tools-adapters/CoreShellToolHostAdapter.js +1 -6
  100. package/dist/src/tools-adapters/CoreShellToolHostAdapter.js.map +1 -1
  101. package/dist/src/utils/browser-profile-discovery.d.ts +40 -0
  102. package/dist/src/utils/browser-profile-discovery.js +251 -0
  103. package/dist/src/utils/browser-profile-discovery.js.map +1 -0
  104. package/dist/src/utils/env.d.ts +12 -0
  105. package/dist/src/utils/env.js +15 -0
  106. package/dist/src/utils/env.js.map +1 -0
  107. package/dist/src/utils/events.d.ts +10 -1
  108. package/dist/src/utils/events.js +22 -0
  109. package/dist/src/utils/events.js.map +1 -1
  110. package/dist/src/utils/fileUtils.js +8 -1
  111. package/dist/src/utils/fileUtils.js.map +1 -1
  112. package/dist/src/utils/retry.js +3 -4
  113. package/dist/src/utils/retry.js.map +1 -1
  114. package/dist/src/utils/secure-browser-launcher.d.ts +35 -1
  115. package/dist/src/utils/secure-browser-launcher.js +341 -1
  116. package/dist/src/utils/secure-browser-launcher.js.map +1 -1
  117. package/dist/src/utils/terminalSerializer.d.ts +12 -0
  118. package/dist/src/utils/terminalSerializer.js.map +1 -1
  119. package/dist/src/utils/thoughtUtils.d.ts +2 -0
  120. package/dist/src/utils/thoughtUtils.js.map +1 -1
  121. package/package.json +13 -9
  122. package/src/config/config.ts +161 -22
  123. package/src/config/configBaseCore.ts +10 -2
  124. package/src/config/configConstructor.ts +11 -0
  125. package/src/config/configTestHarness.ts +10 -4
  126. package/src/config/configTypes.ts +6 -1
  127. package/src/config/liveTrustTransitionLifecycle.ts +250 -0
  128. package/src/config/schedulerSingleton.ts +2 -5
  129. package/src/core/compression/types.ts +81 -1
  130. package/src/core/legacy-model-limits.expected.txt +96 -0
  131. package/src/core/model-limits.json +96 -0
  132. package/src/core/model-limits.schema.ts +87 -0
  133. package/src/core/tokenLimits.ts +73 -147
  134. package/src/core/turn.ts +20 -3
  135. package/src/hooks/hookRegistry.ts +3 -1
  136. package/src/hooks/hookRunner.ts +24 -7
  137. package/src/hooks/hookSystem.ts +73 -13
  138. package/src/index.ts +1 -0
  139. package/src/llm-types/geminiContent.ts +3 -0
  140. package/src/policy/config.ts +32 -19
  141. package/src/recording/ReplayEngine.ts +51 -2
  142. package/src/recording/SessionDiscovery.ts +99 -4
  143. package/src/recording/SessionLockManager.ts +10 -3
  144. package/src/recording/SessionRecordingService.ts +36 -3
  145. package/src/recording/index.ts +19 -2
  146. package/src/recording/replay-test-helpers.ts +14 -0
  147. package/src/recording/resumeNotFoundMessages.ts +75 -0
  148. package/src/recording/resumeSession.ts +3 -1
  149. package/src/recording/sessionManagement.ts +51 -21
  150. package/src/recording/types.ts +44 -2
  151. package/src/runtime/AgentRuntimeLoader.ts +3 -1
  152. package/src/scheduler/liveOutput.ts +25 -22
  153. package/src/scheduler/types.ts +5 -2
  154. package/src/services/history/ContentConverters.ts +91 -68
  155. package/src/services/history/HistoryService.ts +92 -1
  156. package/src/services/history/IContent.ts +13 -0
  157. package/src/skills/skillManager.ts +26 -1
  158. package/src/test-utils/mock-tool.ts +4 -3
  159. package/src/test-utils/tools.ts +4 -3
  160. package/src/tools-adapters/CoreShellToolHostAdapter.ts +1 -7
  161. package/src/utils/browser-profile-discovery.ts +358 -0
  162. package/src/utils/env.ts +15 -0
  163. package/src/utils/events.ts +31 -0
  164. package/src/utils/fileUtils.ts +11 -1
  165. package/src/utils/retry.ts +3 -4
  166. package/src/utils/secure-browser-launcher.ts +500 -2
  167. package/src/utils/terminalSerializer.ts +9 -0
  168. package/src/utils/thoughtUtils.ts +2 -0
@@ -0,0 +1,250 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { DebugLogger } from '../debug/DebugLogger.js';
8
+ import { getErrorMessage } from '../utils/errors.js';
9
+
10
+ const HOOK_INITIALIZATION_TIMEOUT_MS = 30_000;
11
+
12
+ export interface LiveTrustTransitionDependencies {
13
+ downgradeApprovalMode(): void;
14
+ removeTrustedPolicyRules(): void;
15
+ updateTrustPolicy(trusted: boolean): void;
16
+ transitionMcp(trusted: boolean): Promise<void> | undefined;
17
+ initializeHooks(signal: AbortSignal): Promise<void> | undefined;
18
+ emitTrustChanged(trusted: boolean): void;
19
+ }
20
+
21
+ interface TransitionFailure {
22
+ readonly sequence: number;
23
+ readonly error: unknown;
24
+ }
25
+
26
+ export class LiveTrustTransitionLifecycle {
27
+ private static readonly logger = new DebugLogger(
28
+ 'llxprt:config:live-trust-transition',
29
+ );
30
+
31
+ private transitionChain: Promise<void> = Promise.resolve();
32
+ private failures: readonly TransitionFailure[] = [];
33
+ private readonly settlementReports = new Map<number, Promise<void>>();
34
+ private transitionSequence = 0;
35
+ private readonly abortController = new AbortController();
36
+ private disposing = false;
37
+
38
+ constructor(private readonly dependencies: LiveTrustTransitionDependencies) {}
39
+
40
+ apply(trusted: boolean): Promise<void> {
41
+ if (this.disposing) {
42
+ return Promise.resolve();
43
+ }
44
+ const sequence = ++this.transitionSequence;
45
+ if (!trusted) {
46
+ this.runSynchronousStep(
47
+ () => this.dependencies.downgradeApprovalMode(),
48
+ sequence,
49
+ );
50
+ }
51
+ this.runSynchronousStep(
52
+ () => this.dependencies.removeTrustedPolicyRules(),
53
+ sequence,
54
+ );
55
+ this.runSynchronousStep(
56
+ () => this.dependencies.updateTrustPolicy(trusted),
57
+ sequence,
58
+ );
59
+ this.enqueue(trusted, sequence);
60
+ this.runSynchronousStep(
61
+ () => this.dependencies.emitTrustChanged(trusted),
62
+ sequence,
63
+ );
64
+ const settlement = this.reportTransition(sequence, this.transitionChain);
65
+ void settlement.catch(() => undefined);
66
+ return settlement;
67
+ }
68
+
69
+ private runSynchronousStep(step: () => void, sequence: number): void {
70
+ try {
71
+ step();
72
+ } catch (error) {
73
+ LiveTrustTransitionLifecycle.logger.error(
74
+ `Trust step failed: ${getErrorMessage(error)}`,
75
+ );
76
+ this.retainFailures([error], sequence);
77
+ }
78
+ }
79
+
80
+ private enqueue(trusted: boolean, sequence: number): void {
81
+ if (this.disposing) {
82
+ return;
83
+ }
84
+ this.transitionChain = this.transitionChain
85
+ .catch((error: unknown) => {
86
+ LiveTrustTransitionLifecycle.logger.error(
87
+ `Unexpected trust transition chain failure: ${getErrorMessage(error)}`,
88
+ );
89
+ this.retainFailures([error], sequence);
90
+ })
91
+ .then(() => this.runTransition(trusted, sequence));
92
+ }
93
+
94
+ private async runTransition(
95
+ trusted: boolean,
96
+ sequence: number,
97
+ ): Promise<void> {
98
+ if (this.disposing) {
99
+ return;
100
+ }
101
+ const failures: unknown[] = [];
102
+ await this.collectMcpFailure(trusted, failures);
103
+ await this.collectHookFailure(failures);
104
+ this.retainFailures(failures, sequence);
105
+ }
106
+
107
+ private async collectMcpFailure(
108
+ trusted: boolean,
109
+ failures: unknown[],
110
+ ): Promise<void> {
111
+ try {
112
+ await this.dependencies.transitionMcp(trusted);
113
+ } catch (error) {
114
+ LiveTrustTransitionLifecycle.logger.error(
115
+ `Error during trust transition side-effects: ${getErrorMessage(error)}`,
116
+ );
117
+ failures.push(error);
118
+ }
119
+ }
120
+
121
+ private async collectHookFailure(failures: unknown[]): Promise<void> {
122
+ if (this.disposing) {
123
+ return;
124
+ }
125
+ try {
126
+ await waitForHookInitialization(
127
+ (signal) => this.dependencies.initializeHooks(signal),
128
+ this.abortController.signal,
129
+ );
130
+ } catch (error) {
131
+ const initializationWasCancelled =
132
+ this.abortController.signal.aborted &&
133
+ error instanceof DOMException &&
134
+ error.name === 'AbortError';
135
+ if (initializationWasCancelled) {
136
+ return;
137
+ }
138
+ LiveTrustTransitionLifecycle.logger.error(
139
+ `Error re-initializing hooks during trust transition: ${getErrorMessage(error)}`,
140
+ );
141
+ failures.push(error);
142
+ }
143
+ }
144
+
145
+ private retainFailures(failures: readonly unknown[], sequence: number): void {
146
+ this.failures = [
147
+ ...this.failures,
148
+ ...failures.map((error) => ({ sequence, error })),
149
+ ];
150
+ }
151
+
152
+ private async reportTransition(
153
+ sequence: number,
154
+ transition: Promise<void>,
155
+ ): Promise<void> {
156
+ await transition;
157
+ const failures = this.failures
158
+ .filter((failure) => failure.sequence === sequence)
159
+ .map((failure) => failure.error);
160
+ if (failures.length === 1) {
161
+ throw failures[0];
162
+ }
163
+ if (failures.length > 1) {
164
+ throw new AggregateError(failures, 'Trust transition failed');
165
+ }
166
+ }
167
+
168
+ async whenSettled(): Promise<void> {
169
+ const snapshot = this.transitionSequence;
170
+ const existingReport = this.settlementReports.get(snapshot);
171
+ if (existingReport !== undefined) {
172
+ await existingReport;
173
+ return;
174
+ }
175
+ const report = this.reportSnapshot(snapshot, this.transitionChain);
176
+ this.settlementReports.set(snapshot, report);
177
+ try {
178
+ await report;
179
+ } finally {
180
+ if (this.settlementReports.get(snapshot) === report) {
181
+ this.settlementReports.delete(snapshot);
182
+ }
183
+ }
184
+ }
185
+
186
+ private async reportSnapshot(
187
+ snapshot: number,
188
+ transition: Promise<void>,
189
+ ): Promise<void> {
190
+ await transition;
191
+ const failures = this.failures
192
+ .filter((failure) => failure.sequence <= snapshot)
193
+ .map((failure) => failure.error);
194
+ this.failures = this.failures.filter(
195
+ (failure) => failure.sequence > snapshot,
196
+ );
197
+ if (failures.length === 1) {
198
+ throw failures[0];
199
+ }
200
+ if (failures.length > 1) {
201
+ throw new AggregateError(failures, 'Trust transition failed');
202
+ }
203
+ }
204
+
205
+ beginDisposal(): void {
206
+ this.disposing = true;
207
+ this.abortController.abort();
208
+ }
209
+ }
210
+
211
+ function abortError(message: string): DOMException {
212
+ return new DOMException(message, 'AbortError');
213
+ }
214
+
215
+ function waitForHookInitialization(
216
+ initialize: (signal: AbortSignal) => Promise<void> | undefined,
217
+ lifecycleSignal: AbortSignal,
218
+ ): Promise<void> {
219
+ if (lifecycleSignal.aborted) {
220
+ return Promise.reject(abortError('Hook initialization was cancelled'));
221
+ }
222
+ const operationController = new AbortController();
223
+ return new Promise<void>((resolve, reject) => {
224
+ let settled = false;
225
+ const finish = (settle: () => void): void => {
226
+ if (settled) {
227
+ return;
228
+ }
229
+ settled = true;
230
+ clearTimeout(timeoutId);
231
+ lifecycleSignal.removeEventListener('abort', onAbort);
232
+ settle();
233
+ };
234
+ const onAbort = () => {
235
+ operationController.abort();
236
+ finish(() => reject(abortError('Hook initialization was cancelled')));
237
+ };
238
+ const timeoutId = setTimeout(() => {
239
+ operationController.abort();
240
+ finish(() => reject(new Error('Hook initialization timed out')));
241
+ }, HOOK_INITIALIZATION_TIMEOUT_MS);
242
+ lifecycleSignal.addEventListener('abort', onAbort, { once: true });
243
+ Promise.resolve()
244
+ .then(() => initialize(operationController.signal))
245
+ .then(
246
+ () => finish(resolve),
247
+ (error: unknown) => finish(() => reject(error)),
248
+ );
249
+ });
250
+ }
@@ -18,16 +18,13 @@ import type {
18
18
  import type { MessageBus } from '../confirmation-bus/message-bus.js';
19
19
  import type { ToolRegistry } from '@vybestack/llxprt-code-tools';
20
20
  import type { EditorType } from '../utils/editor.js';
21
- import type { AnsiOutput } from '../utils/terminalSerializer.js';
21
+ import type { LiveOutputUpdate } from '../utils/terminalSerializer.js';
22
22
  import { DebugLogger } from '../debug/DebugLogger.js';
23
23
 
24
24
  const debugLog = new DebugLogger('llxprt:schedulerSingleton');
25
25
 
26
26
  export interface SchedulerCallbacks {
27
- outputUpdateHandler?: (
28
- toolCallId: string,
29
- outputChunk: string | AnsiOutput,
30
- ) => void;
27
+ outputUpdateHandler?: (toolCallId: string, update: LiveOutputUpdate) => void;
31
28
  onAllToolCallsComplete?: (
32
29
  completedToolCalls: CompletedToolCall[],
33
30
  ) => Promise<void>;
@@ -9,6 +9,7 @@
9
9
  * @plan PLAN-20260211-HIGHDENSITY.P03
10
10
  * @plan PLAN-20260211-HIGHDENSITY.P05
11
11
  * @plan PLAN-20260218-COMPRESSION-RETRY.P01
12
+ * @plan PLAN-20260727-ISSUE2602
12
13
  * @requirement REQ-CS-001.1, REQ-CS-001.4, REQ-CS-001.5, REQ-CS-001.6, REQ-CS-010.3
13
14
  * @requirement REQ-HD-001.1, REQ-HD-001.2, REQ-HD-001.5, REQ-HD-001.8, REQ-HD-001.9, REQ-HD-004.1
14
15
  * @requirement REQ-CR-001, REQ-CR-002
@@ -167,6 +168,50 @@ export interface CompressionContext {
167
168
  readonly config?: Config;
168
169
  }
169
170
 
171
+ /**
172
+ * Well-known reason codes for a structural compression no-op.
173
+ *
174
+ * A structural no-op occurs when the strategy's splitting logic
175
+ * determines it cannot produce a valid compressed candidate without
176
+ * violating its own invariants (e.g. a four-message minimum middle, or
177
+ * tool-call boundary integrity). This is distinct from a transient
178
+ * LLM/provider failure (which is retryable) and from an empty summary
179
+ * (which is a permanent error).
180
+ *
181
+ * @plan PLAN-20260727-ISSUE2602
182
+ */
183
+ export const STRUCTURAL_NOOP_REASONS = [
184
+ /**
185
+ * The middle/compressible region had fewer than the strategy's minimum
186
+ * compressible messages after the initial split. E.g. MiddleOutStrategy
187
+ * when `toCompress.length < MINIMUM_MIDDLE_MESSAGES`, OneShotStrategy
188
+ * when `toCompress.length < MINIMUM_COMPRESS_MESSAGES`.
189
+ */
190
+ 'too-few-compressible',
191
+ /**
192
+ * The compressible region was shrunk below the strategy's minimum by a
193
+ * later preservation step (last-user-prompt preservation, tool-boundary
194
+ * adjustment, or density pruning), so the split no longer has enough
195
+ * messages to summarize safely.
196
+ */
197
+ 'shrunk-below-minimum',
198
+ /** The input history was empty. */
199
+ 'empty-history',
200
+ /**
201
+ * The input was already at or below the target size, so no truncation
202
+ * or summarization is structurally possible (used by TopDownTruncation).
203
+ */
204
+ 'already-under-target',
205
+ /**
206
+ * A density-optimization or tail-covering pass determined the whole
207
+ * history is covered by the preserved tail / no removable unit remains
208
+ * (used by HighDensity / tail-cover paths).
209
+ */
210
+ 'tail-covers-all',
211
+ ] as const;
212
+
213
+ export type StructuralNoopReason = (typeof STRUCTURAL_NOOP_REASONS)[number];
214
+
170
215
  /**
171
216
  * @plan PLAN-20260211-HIGHDENSITY.P03
172
217
  * @requirement REQ-HD-001.1, REQ-HD-001.2
@@ -176,10 +221,19 @@ export interface CompressionStrategy {
176
221
  readonly name: CompressionStrategyName;
177
222
  readonly requiresLLM: boolean;
178
223
  readonly trigger: StrategyTrigger;
179
- compress(context: CompressionContext): Promise<CompressionResult>;
224
+ compress(context: CompressionContext): Promise<StrategyCompressionResult>;
180
225
  optimize?(history: readonly IContent[], config: DensityConfig): DensityResult;
181
226
  }
182
227
 
228
+ /**
229
+ * @deprecated Superseded by {@link StrategyCompressionResult}.
230
+ * CompressionStrategy.compress now returns the discriminated union; this
231
+ * interface is retained only as the shape of the 'applied' variant's payload
232
+ * and for backward-compatible consumers. New code should use
233
+ * StrategyCompressionResult.
234
+ *
235
+ * @plan PLAN-20260727-ISSUE2602
236
+ */
183
237
  export interface CompressionResult {
184
238
  newHistory: IContent[];
185
239
  metadata: CompressionResultMetadata;
@@ -197,6 +251,32 @@ export interface CompressionResultMetadata {
197
251
  usage?: UsageStats;
198
252
  }
199
253
 
254
+ /**
255
+ * Discriminated outcome returned by {@link CompressionStrategy.compress}.
256
+ *
257
+ * @plan PLAN-20260727-ISSUE2602
258
+ *
259
+ * - `applied`: the strategy produced a real compressed candidate. `newHistory`
260
+ * may legitimately differ from the input by only a small amount, but it is
261
+ * a genuine compression result, not an unchanged pass-through.
262
+ * - `noop`: the strategy made a *structural* determination that it could not
263
+ * produce a valid compressed candidate and returned the history unchanged.
264
+ * Callers must NOT treat this as a successful compression; it is the basis
265
+ * for {@link PerformCompressionResult.NOOP} and for middle-out→one-shot
266
+ * routing.
267
+ */
268
+ export type StrategyCompressionResult =
269
+ | {
270
+ kind: 'applied';
271
+ newHistory: IContent[];
272
+ metadata: CompressionResultMetadata;
273
+ }
274
+ | {
275
+ kind: 'noop';
276
+ reason: StructuralNoopReason;
277
+ metadata: CompressionResultMetadata;
278
+ };
279
+
200
280
  // ---------------------------------------------------------------------------
201
281
  // Errors
202
282
  // ---------------------------------------------------------------------------
@@ -0,0 +1,96 @@
1
+ {
2
+ "defaultLimit": 200000,
3
+ "exactLimits": {
4
+ "glm-5.2": 200000,
5
+ "glm-5.1": 200000,
6
+ "glm-5": 200000,
7
+ "glm-4.6": 128000,
8
+ "glm-4.5": 128000,
9
+ "glm-4": 128000,
10
+ "gemini-1.5-pro": 2097152,
11
+ "gemini-1.5-flash": 1048576,
12
+ "gemini-2.5-pro-preview-05-06": 1048576,
13
+ "gemini-2.5-pro-preview-06-05": 1048576,
14
+ "gemini-2.5-pro": 1048576,
15
+ "gemini-2.5-flash-preview-05-20": 1048576,
16
+ "gemini-2.5-flash": 1048576,
17
+ "gemini-2.5-flash-lite": 1048576,
18
+ "gemini-2.0-flash": 1048576,
19
+ "gemini-2.0-flash-preview-image-generation": 32000,
20
+ "claude-opus-5": 200000,
21
+ "claude-opus-5-latest": 200000,
22
+ "claude-opus-4-8": 200000,
23
+ "claude-opus-4-7": 200000,
24
+ "claude-opus-4-6": 200000,
25
+ "claude-opus-4-latest": 200000,
26
+ "claude-sonnet-4-6": 200000,
27
+ "claude-sonnet-4-latest": 400000,
28
+ "claude-sonnet-5": 200000,
29
+ "claude-sonnet-5-latest": 200000,
30
+ "claude-fable-5": 200000,
31
+ "claude-fable-5-latest": 200000,
32
+ "claude-3-7-opus-20250115": 300000,
33
+ "claude-3-7-sonnet-20250115": 300000,
34
+ "claude-3-opus-20240229": 200000,
35
+ "claude-3-sonnet-20240229": 200000,
36
+ "claude-3-haiku-20240307": 200000,
37
+ "claude-3.5-sonnet-20240620": 200000,
38
+ "claude-3.5-sonnet-20241022": 200000,
39
+ "claude-3.5-haiku-20241022": 200000,
40
+ "claude-3-5-sonnet-20241022": 200000,
41
+ "claude-3-5-haiku-20241022": 200000
42
+ },
43
+ "prefixLimits": [
44
+ { "prefix": "gpt-4.1", "limit": 1000000 },
45
+ { "prefix": "gpt-3.5-turbo", "limit": 16385 },
46
+ { "prefix": "gemini-2.5-pro", "limit": 1048576 },
47
+ { "prefix": "gemini-2.5-flash", "limit": 1048576 },
48
+ { "prefix": "gemini-2.0-flash", "limit": 1048576 },
49
+ { "prefix": "glm-5", "limit": 200000 },
50
+ { "prefix": "glm-4", "limit": 128000 }
51
+ ],
52
+ "orderedRules": [
53
+ {
54
+ "type": "substring",
55
+ "substring": "codex-spark",
56
+ "limit": 131072
57
+ },
58
+ {
59
+ "type": "substringOrProviderPrefix",
60
+ "substring": "codex",
61
+ "providerPrefix": "codex",
62
+ "limit": 262144
63
+ },
64
+ {
65
+ "type": "prefixGroup",
66
+ "prefixes": ["o3-pro", "o3-mini", "o1-mini", "o3", "o1"],
67
+ "limit": 200000
68
+ },
69
+ {
70
+ "type": "prefixGroup",
71
+ "prefixes": [
72
+ "o4-mini",
73
+ "gpt-4o-mini",
74
+ "gpt-4o-realtime",
75
+ "gpt-4o",
76
+ "gpt-4-turbo"
77
+ ],
78
+ "limit": 128000
79
+ },
80
+ {
81
+ "type": "substringCaseInsensitive",
82
+ "substring": "claude-sonnet-5",
83
+ "limit": 200000
84
+ },
85
+ {
86
+ "type": "substringCaseInsensitive",
87
+ "substring": "claude-opus-5",
88
+ "limit": 200000
89
+ },
90
+ {
91
+ "type": "substringCaseInsensitive",
92
+ "substring": "claude-fable-5",
93
+ "limit": 200000
94
+ }
95
+ ]
96
+ }
@@ -0,0 +1,96 @@
1
+ {
2
+ "defaultLimit": 200000,
3
+ "exactLimits": {
4
+ "glm-5.2": 200000,
5
+ "glm-5.1": 200000,
6
+ "glm-5": 200000,
7
+ "glm-4.6": 128000,
8
+ "glm-4.5": 128000,
9
+ "glm-4": 128000,
10
+ "gemini-1.5-pro": 2097152,
11
+ "gemini-1.5-flash": 1048576,
12
+ "gemini-2.5-pro-preview-05-06": 1048576,
13
+ "gemini-2.5-pro-preview-06-05": 1048576,
14
+ "gemini-2.5-pro": 1048576,
15
+ "gemini-2.5-flash-preview-05-20": 1048576,
16
+ "gemini-2.5-flash": 1048576,
17
+ "gemini-2.5-flash-lite": 1048576,
18
+ "gemini-2.0-flash": 1048576,
19
+ "gemini-2.0-flash-preview-image-generation": 32000,
20
+ "claude-opus-5": 200000,
21
+ "claude-opus-5-latest": 200000,
22
+ "claude-opus-4-8": 200000,
23
+ "claude-opus-4-7": 200000,
24
+ "claude-opus-4-6": 200000,
25
+ "claude-opus-4-latest": 200000,
26
+ "claude-sonnet-4-6": 200000,
27
+ "claude-sonnet-4-latest": 400000,
28
+ "claude-sonnet-5": 200000,
29
+ "claude-sonnet-5-latest": 200000,
30
+ "claude-fable-5": 200000,
31
+ "claude-fable-5-latest": 200000,
32
+ "claude-3-7-opus-20250115": 300000,
33
+ "claude-3-7-sonnet-20250115": 300000,
34
+ "claude-3-opus-20240229": 200000,
35
+ "claude-3-sonnet-20240229": 200000,
36
+ "claude-3-haiku-20240307": 200000,
37
+ "claude-3.5-sonnet-20240620": 200000,
38
+ "claude-3.5-sonnet-20241022": 200000,
39
+ "claude-3.5-haiku-20241022": 200000,
40
+ "claude-3-5-sonnet-20241022": 200000,
41
+ "claude-3-5-haiku-20241022": 200000
42
+ },
43
+ "prefixLimits": [
44
+ { "prefix": "gpt-4.1", "limit": 1000000 },
45
+ { "prefix": "gpt-3.5-turbo", "limit": 16385 },
46
+ { "prefix": "gemini-2.5-pro", "limit": 1048576 },
47
+ { "prefix": "gemini-2.5-flash", "limit": 1048576 },
48
+ { "prefix": "gemini-2.0-flash", "limit": 1048576 },
49
+ { "prefix": "glm-5", "limit": 200000 },
50
+ { "prefix": "glm-4", "limit": 128000 }
51
+ ],
52
+ "orderedRules": [
53
+ {
54
+ "type": "substring",
55
+ "substring": "codex-spark",
56
+ "limit": 131072
57
+ },
58
+ {
59
+ "type": "substringOrProviderPrefix",
60
+ "substring": "codex",
61
+ "providerPrefix": "codex",
62
+ "limit": 262144
63
+ },
64
+ {
65
+ "type": "prefixGroup",
66
+ "prefixes": ["o3-pro", "o3-mini", "o1-mini", "o3", "o1"],
67
+ "limit": 200000
68
+ },
69
+ {
70
+ "type": "prefixGroup",
71
+ "prefixes": [
72
+ "o4-mini",
73
+ "gpt-4o-mini",
74
+ "gpt-4o-realtime",
75
+ "gpt-4o",
76
+ "gpt-4-turbo"
77
+ ],
78
+ "limit": 128000
79
+ },
80
+ {
81
+ "type": "substringCaseInsensitive",
82
+ "substring": "claude-sonnet-5",
83
+ "limit": 200000
84
+ },
85
+ {
86
+ "type": "substringCaseInsensitive",
87
+ "substring": "claude-opus-5",
88
+ "limit": 200000
89
+ },
90
+ {
91
+ "type": "substringCaseInsensitive",
92
+ "substring": "claude-fable-5",
93
+ "limit": 200000
94
+ }
95
+ ]
96
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Vybestack LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { z } from 'zod';
8
+
9
+ /**
10
+ * Zod schema for the data-driven model-limits catalog, validated once at
11
+ * module init. See model-limits.json for the data.
12
+ *
13
+ * Hardening guarantees enforced here:
14
+ * - All match strings (substrings, provider prefixes, prefix-group entries)
15
+ * must be non-empty so a malformed entry can never silently match every
16
+ * model.
17
+ * - Prefix groups must contain at least one entry.
18
+ * - Every limit is a positive integer.
19
+ * - The catalog is a strict object: unknown top-level keys are rejected so
20
+ * typos like "orderedRule" vs "orderedRules" surface immediately.
21
+ */
22
+
23
+ const PositiveLimit = z.number().int().positive();
24
+
25
+ /** Non-empty string without leading or trailing whitespace. */
26
+ const NonEmptyString = z
27
+ .string()
28
+ .refine((value) => value.length > 0 && value === value.trim());
29
+
30
+ const SubstringRuleSchema = z
31
+ .object({
32
+ type: z.literal('substring'),
33
+ substring: NonEmptyString,
34
+ limit: PositiveLimit,
35
+ })
36
+ .strict();
37
+
38
+ const SubstringOrProviderPrefixRuleSchema = z
39
+ .object({
40
+ type: z.literal('substringOrProviderPrefix'),
41
+ substring: NonEmptyString,
42
+ providerPrefix: NonEmptyString,
43
+ limit: PositiveLimit,
44
+ })
45
+ .strict();
46
+
47
+ const PrefixGroupRuleSchema = z
48
+ .object({
49
+ type: z.literal('prefixGroup'),
50
+ prefixes: z.array(NonEmptyString).min(1),
51
+ limit: PositiveLimit,
52
+ })
53
+ .strict();
54
+
55
+ /** Case-insensitive substring rule; runtime comparison normalizes both sides. */
56
+ const SubstringCaseInsensitiveRuleSchema = z
57
+ .object({
58
+ type: z.literal('substringCaseInsensitive'),
59
+ substring: NonEmptyString,
60
+ limit: PositiveLimit,
61
+ })
62
+ .strict();
63
+
64
+ const OrderedRuleSchema = z.discriminatedUnion('type', [
65
+ SubstringRuleSchema,
66
+ SubstringOrProviderPrefixRuleSchema,
67
+ PrefixGroupRuleSchema,
68
+ SubstringCaseInsensitiveRuleSchema,
69
+ ]);
70
+
71
+ const PrefixLimitSchema = z
72
+ .object({
73
+ prefix: NonEmptyString,
74
+ limit: PositiveLimit,
75
+ })
76
+ .strict();
77
+
78
+ export const ModelLimitsCatalogSchema = z
79
+ .object({
80
+ defaultLimit: PositiveLimit,
81
+ exactLimits: z.record(NonEmptyString, PositiveLimit),
82
+ prefixLimits: z.array(PrefixLimitSchema),
83
+ orderedRules: z.array(OrderedRuleSchema),
84
+ })
85
+ .strict();
86
+
87
+ export type ModelLimitsCatalog = z.infer<typeof ModelLimitsCatalogSchema>;