@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.
- package/dist/src/config/config.d.ts +20 -0
- package/dist/src/config/config.js +143 -23
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/configBaseCore.d.ts +6 -2
- package/dist/src/config/configBaseCore.js +8 -0
- package/dist/src/config/configBaseCore.js.map +1 -1
- package/dist/src/config/configConstructor.d.ts +2 -0
- package/dist/src/config/configConstructor.js +9 -1
- package/dist/src/config/configConstructor.js.map +1 -1
- package/dist/src/config/configTestHarness.d.ts +8 -6
- package/dist/src/config/configTestHarness.js +10 -4
- package/dist/src/config/configTestHarness.js.map +1 -1
- package/dist/src/config/configTypes.d.ts +7 -2
- package/dist/src/config/configTypes.js +2 -0
- package/dist/src/config/configTypes.js.map +1 -1
- package/dist/src/config/liveTrustTransitionLifecycle.d.ts +35 -0
- package/dist/src/config/liveTrustTransitionLifecycle.js +180 -0
- package/dist/src/config/liveTrustTransitionLifecycle.js.map +1 -0
- package/dist/src/config/schedulerSingleton.d.ts +2 -2
- package/dist/src/config/schedulerSingleton.js.map +1 -1
- package/dist/src/core/compression/types.d.ts +48 -1
- package/dist/src/core/compression/types.js +41 -0
- package/dist/src/core/compression/types.js.map +1 -1
- package/dist/src/core/model-limits.json +96 -0
- package/dist/src/core/model-limits.schema.d.ts +123 -0
- package/dist/src/core/model-limits.schema.js +75 -0
- package/dist/src/core/model-limits.schema.js.map +1 -0
- package/dist/src/core/tokenLimits.d.ts +6 -8
- package/dist/src/core/tokenLimits.js +56 -132
- package/dist/src/core/tokenLimits.js.map +1 -1
- package/dist/src/core/turn.d.ts +20 -3
- package/dist/src/core/turn.js +20 -3
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/hooks/hookRegistry.d.ts +1 -1
- package/dist/src/hooks/hookRegistry.js +3 -1
- package/dist/src/hooks/hookRegistry.js.map +1 -1
- package/dist/src/hooks/hookRunner.d.ts +6 -0
- package/dist/src/hooks/hookRunner.js +22 -6
- package/dist/src/hooks/hookRunner.js.map +1 -1
- package/dist/src/hooks/hookSystem.d.ts +10 -1
- package/dist/src/hooks/hookSystem.js +69 -12
- package/dist/src/hooks/hookSystem.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/llm-types/geminiContent.d.ts +3 -0
- package/dist/src/policy/config.d.ts +4 -2
- package/dist/src/policy/config.js +22 -18
- package/dist/src/policy/config.js.map +1 -1
- package/dist/src/recording/ReplayEngine.js +39 -2
- package/dist/src/recording/ReplayEngine.js.map +1 -1
- package/dist/src/recording/SessionDiscovery.d.ts +8 -0
- package/dist/src/recording/SessionDiscovery.js +88 -3
- package/dist/src/recording/SessionDiscovery.js.map +1 -1
- package/dist/src/recording/SessionLockManager.d.ts +3 -0
- package/dist/src/recording/SessionLockManager.js +9 -3
- package/dist/src/recording/SessionLockManager.js.map +1 -1
- package/dist/src/recording/SessionRecordingService.d.ts +14 -1
- package/dist/src/recording/SessionRecordingService.js +27 -3
- package/dist/src/recording/SessionRecordingService.js.map +1 -1
- package/dist/src/recording/index.d.ts +4 -3
- package/dist/src/recording/index.js +4 -3
- package/dist/src/recording/index.js.map +1 -1
- package/dist/src/recording/replay-test-helpers.d.ts +5 -0
- package/dist/src/recording/replay-test-helpers.js +13 -0
- package/dist/src/recording/replay-test-helpers.js.map +1 -1
- package/dist/src/recording/resumeNotFoundMessages.d.ts +56 -0
- package/dist/src/recording/resumeNotFoundMessages.js +64 -0
- package/dist/src/recording/resumeNotFoundMessages.js.map +1 -0
- package/dist/src/recording/resumeSession.js +3 -2
- package/dist/src/recording/resumeSession.js.map +1 -1
- package/dist/src/recording/sessionManagement.d.ts +3 -0
- package/dist/src/recording/sessionManagement.js +38 -19
- package/dist/src/recording/sessionManagement.js.map +1 -1
- package/dist/src/recording/types.d.ts +42 -3
- package/dist/src/recording/types.js +1 -1
- package/dist/src/recording/types.js.map +1 -1
- package/dist/src/runtime/AgentRuntimeLoader.js +2 -1
- package/dist/src/runtime/AgentRuntimeLoader.js.map +1 -1
- package/dist/src/scheduler/liveOutput.d.ts +10 -19
- package/dist/src/scheduler/liveOutput.js +21 -21
- package/dist/src/scheduler/liveOutput.js.map +1 -1
- package/dist/src/scheduler/types.d.ts +2 -2
- package/dist/src/services/history/ContentConverters.d.ts +4 -0
- package/dist/src/services/history/ContentConverters.js +77 -59
- package/dist/src/services/history/ContentConverters.js.map +1 -1
- package/dist/src/services/history/HistoryService.d.ts +13 -1
- package/dist/src/services/history/HistoryService.js +71 -0
- package/dist/src/services/history/HistoryService.js.map +1 -1
- package/dist/src/services/history/IContent.d.ts +10 -0
- package/dist/src/services/history/IContent.js.map +1 -1
- package/dist/src/skills/skillManager.d.ts +11 -1
- package/dist/src/skills/skillManager.js +21 -1
- package/dist/src/skills/skillManager.js.map +1 -1
- package/dist/src/test-utils/mock-tool.d.ts +3 -3
- package/dist/src/test-utils/mock-tool.js.map +1 -1
- package/dist/src/test-utils/tools.d.ts +2 -2
- package/dist/src/test-utils/tools.js.map +1 -1
- package/dist/src/tools-adapters/CoreShellToolHostAdapter.js +1 -6
- package/dist/src/tools-adapters/CoreShellToolHostAdapter.js.map +1 -1
- package/dist/src/utils/browser-profile-discovery.d.ts +40 -0
- package/dist/src/utils/browser-profile-discovery.js +251 -0
- package/dist/src/utils/browser-profile-discovery.js.map +1 -0
- package/dist/src/utils/env.d.ts +12 -0
- package/dist/src/utils/env.js +15 -0
- package/dist/src/utils/env.js.map +1 -0
- package/dist/src/utils/events.d.ts +10 -1
- package/dist/src/utils/events.js +22 -0
- package/dist/src/utils/events.js.map +1 -1
- package/dist/src/utils/fileUtils.js +8 -1
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/retry.js +3 -4
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/secure-browser-launcher.d.ts +35 -1
- package/dist/src/utils/secure-browser-launcher.js +341 -1
- package/dist/src/utils/secure-browser-launcher.js.map +1 -1
- package/dist/src/utils/terminalSerializer.d.ts +12 -0
- package/dist/src/utils/terminalSerializer.js.map +1 -1
- package/dist/src/utils/thoughtUtils.d.ts +2 -0
- package/dist/src/utils/thoughtUtils.js.map +1 -1
- package/package.json +13 -9
- package/src/config/config.ts +161 -22
- package/src/config/configBaseCore.ts +10 -2
- package/src/config/configConstructor.ts +11 -0
- package/src/config/configTestHarness.ts +10 -4
- package/src/config/configTypes.ts +6 -1
- package/src/config/liveTrustTransitionLifecycle.ts +250 -0
- package/src/config/schedulerSingleton.ts +2 -5
- package/src/core/compression/types.ts +81 -1
- package/src/core/legacy-model-limits.expected.txt +96 -0
- package/src/core/model-limits.json +96 -0
- package/src/core/model-limits.schema.ts +87 -0
- package/src/core/tokenLimits.ts +73 -147
- package/src/core/turn.ts +20 -3
- package/src/hooks/hookRegistry.ts +3 -1
- package/src/hooks/hookRunner.ts +24 -7
- package/src/hooks/hookSystem.ts +73 -13
- package/src/index.ts +1 -0
- package/src/llm-types/geminiContent.ts +3 -0
- package/src/policy/config.ts +32 -19
- package/src/recording/ReplayEngine.ts +51 -2
- package/src/recording/SessionDiscovery.ts +99 -4
- package/src/recording/SessionLockManager.ts +10 -3
- package/src/recording/SessionRecordingService.ts +36 -3
- package/src/recording/index.ts +19 -2
- package/src/recording/replay-test-helpers.ts +14 -0
- package/src/recording/resumeNotFoundMessages.ts +75 -0
- package/src/recording/resumeSession.ts +3 -1
- package/src/recording/sessionManagement.ts +51 -21
- package/src/recording/types.ts +44 -2
- package/src/runtime/AgentRuntimeLoader.ts +3 -1
- package/src/scheduler/liveOutput.ts +25 -22
- package/src/scheduler/types.ts +5 -2
- package/src/services/history/ContentConverters.ts +91 -68
- package/src/services/history/HistoryService.ts +92 -1
- package/src/services/history/IContent.ts +13 -0
- package/src/skills/skillManager.ts +26 -1
- package/src/test-utils/mock-tool.ts +4 -3
- package/src/test-utils/tools.ts +4 -3
- package/src/tools-adapters/CoreShellToolHostAdapter.ts +1 -7
- package/src/utils/browser-profile-discovery.ts +358 -0
- package/src/utils/env.ts +15 -0
- package/src/utils/events.ts +31 -0
- package/src/utils/fileUtils.ts +11 -1
- package/src/utils/retry.ts +3 -4
- package/src/utils/secure-browser-launcher.ts +500 -2
- package/src/utils/terminalSerializer.ts +9 -0
- 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 {
|
|
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<
|
|
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>;
|