@xbzb404/dsh-context-compression-selector-runtime 0.0.0-stage → 0.2.1-rc.3
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/LICENSE +21 -0
- package/README.md +13 -3
- package/README.zh.md +13 -0
- package/THIRD_PARTY_NOTICES.md +36 -0
- package/assets/deepseek-v4/LICENSE.DeepSeek-V4-Pro.txt +21 -0
- package/assets/deepseek-v4/manifest.json +22 -0
- package/assets/deepseek-v4/tokenizer.json +267359 -0
- package/assets/deepseek-v4/tokenizer_config.json +34 -0
- package/assets/deepseek-v4.1-flash/LICENSE.DeepSeek-V4.1-Flash.txt +21 -0
- package/assets/deepseek-v4.1-flash/manifest.json +24 -0
- package/assets/deepseek-v4.1-flash/tokenizer.json +267359 -0
- package/assets/deepseek-v4.1-flash/tokenizer_config.json +34 -0
- package/lib/index.d.ts +581 -0
- package/lib/index.js +3871 -0
- package/lib/invariant.d.ts +10 -0
- package/lib/invariant.js +70 -0
- package/lib/tail-trim.js +142 -0
- package/package.json +67 -4
package/lib/index.js
ADDED
|
@@ -0,0 +1,3871 @@
|
|
|
1
|
+
import { a as validatePublishedTailTrim, i as tailTrimStub, n as tailTrimMessage, o as sessionEvents, r as tailTrimRef, t as parseTailTrimRef } from "./tail-trim.js";
|
|
2
|
+
import { Service } from "@deepseek-ai/cordis";
|
|
3
|
+
import z from "@deepseek-ai/schemastery";
|
|
4
|
+
import { freezeMessage } from "@deepseek-ai/dsh-llm";
|
|
5
|
+
import { SessionSeq, deriveEventMessage } from "@deepseek-ai/dsh-session";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { Tokenizer } from "@huggingface/tokenizers";
|
|
9
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
10
|
+
//#region src/deepseek-v4-tokenizer.ts
|
|
11
|
+
/** Offline DeepSeek tokenizers backed by pinned official Hugging Face assets. */
|
|
12
|
+
const TOKENIZER_ID = "deepseek-ai/DeepSeek-V4-Pro";
|
|
13
|
+
const TOKENIZER_REVISION = "0e1a0e5e52aea73055f50fef6f2423db370265b6";
|
|
14
|
+
const TOKENIZER_SHA256 = "8f9f37ca37fdc4f5fd36d5cf4d3b0e8392edb4e894fd10cc0d70b4957c8633cf";
|
|
15
|
+
const CONFIG_SHA256 = "6ac8c8dc065ed118161d02dd532749ae3f52c243deac27872134fae2f50d8547";
|
|
16
|
+
const V41_FLASH_TOKENIZER_ID = "deepseek-ai/DeepSeek-V4.1-Flash";
|
|
17
|
+
const V41_FLASH_TOKENIZER_REVISION = "dba1be0a40aa45a94ad051997016db3960a90277";
|
|
18
|
+
const V41_FLASH_TOKENIZER_SHA256 = "c90dfa01249db1be4245780a052ede752e1361c612ac6d08e2bdada7d599476b";
|
|
19
|
+
const V41_FLASH_CONFIG_SHA256 = "6ac8c8dc065ed118161d02dd532749ae3f52c243deac27872134fae2f50d8547";
|
|
20
|
+
/** Auditable origin and compatibility mapping for the bundled V4 Pro tokenizer. */
|
|
21
|
+
const DEEPSEEK_V4_TOKENIZER_ARTIFACT = Object.freeze({
|
|
22
|
+
repository: TOKENIZER_ID,
|
|
23
|
+
revision: TOKENIZER_REVISION,
|
|
24
|
+
license: "MIT",
|
|
25
|
+
tokenizerSha256: TOKENIZER_SHA256,
|
|
26
|
+
tokenizerConfigSha256: CONFIG_SHA256,
|
|
27
|
+
modelIds: Object.freeze(["deepseek-v4-pro"])
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Auditable origin and compatibility mapping for the bundled V4.1-Flash
|
|
31
|
+
* tokenizer.
|
|
32
|
+
*
|
|
33
|
+
* `deepseek-flash` is the default DeepSeek API model and is served by
|
|
34
|
+
* DeepSeek-V4.1-Flash; `deepseek-v4-flash` and `deepseek-v4-flash-vision-exp`
|
|
35
|
+
* are compatibility aliases routed to the same served model. All three ids
|
|
36
|
+
* therefore share ONE artifact, and this artifact is deliberately never treated
|
|
37
|
+
* as an alias of the V4-Pro tokenizer: the vocabularies differ (the V4.1
|
|
38
|
+
* vocabulary knows `<|deepseek_image|>` as a single token while the V4-Pro
|
|
39
|
+
* vocabulary does not, and vice versa for `<|image|>`).
|
|
40
|
+
*/
|
|
41
|
+
const DEEPSEEK_V41_FLASH_TOKENIZER_ARTIFACT = Object.freeze({
|
|
42
|
+
repository: V41_FLASH_TOKENIZER_ID,
|
|
43
|
+
revision: V41_FLASH_TOKENIZER_REVISION,
|
|
44
|
+
license: "MIT",
|
|
45
|
+
tokenizerSha256: V41_FLASH_TOKENIZER_SHA256,
|
|
46
|
+
tokenizerConfigSha256: V41_FLASH_CONFIG_SHA256,
|
|
47
|
+
modelIds: Object.freeze([
|
|
48
|
+
"deepseek-flash",
|
|
49
|
+
"deepseek-v4-flash",
|
|
50
|
+
"deepseek-v4-flash-vision-exp"
|
|
51
|
+
])
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Every bundled artifact is registered with independent asset roots, integrity
|
|
55
|
+
* manifests, and cache entries: one corrupted artifact must never disable the
|
|
56
|
+
* tokenizer serving the other model family.
|
|
57
|
+
*/
|
|
58
|
+
const ARTIFACTS = Object.freeze([Object.freeze({
|
|
59
|
+
origin: DEEPSEEK_V4_TOKENIZER_ARTIFACT,
|
|
60
|
+
assetRoot: new URL("../assets/deepseek-v4/", import.meta.url),
|
|
61
|
+
integrity: Object.freeze({
|
|
62
|
+
tokenizer: Object.freeze({
|
|
63
|
+
bytes: 6367146,
|
|
64
|
+
sha256: TOKENIZER_SHA256
|
|
65
|
+
}),
|
|
66
|
+
config: Object.freeze({
|
|
67
|
+
bytes: 801,
|
|
68
|
+
sha256: CONFIG_SHA256
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
}), Object.freeze({
|
|
72
|
+
origin: DEEPSEEK_V41_FLASH_TOKENIZER_ARTIFACT,
|
|
73
|
+
assetRoot: new URL("../assets/deepseek-v4.1-flash/", import.meta.url),
|
|
74
|
+
integrity: Object.freeze({
|
|
75
|
+
tokenizer: Object.freeze({
|
|
76
|
+
bytes: 6367257,
|
|
77
|
+
sha256: V41_FLASH_TOKENIZER_SHA256
|
|
78
|
+
}),
|
|
79
|
+
config: Object.freeze({
|
|
80
|
+
bytes: 801,
|
|
81
|
+
sha256: V41_FLASH_CONFIG_SHA256
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
})]);
|
|
85
|
+
const registryCache = /* @__PURE__ */ new Map();
|
|
86
|
+
function artifactForModel(modelId) {
|
|
87
|
+
return ARTIFACTS.find((artifact) => artifact.origin.modelIds.includes(modelId));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the shared offline tokenizer for one compatible API model.
|
|
91
|
+
* Unknown models and a cached asset/runtime failure return `undefined`; callers
|
|
92
|
+
* must report unavailable instead of manufacturing a character estimate.
|
|
93
|
+
* @param modelId - exact DeepSeek API wire model id.
|
|
94
|
+
* @returns the shared verified tokenizer, or undefined when unsupported/unavailable.
|
|
95
|
+
*/
|
|
96
|
+
function deepSeekV4TokenizerForModel(modelId) {
|
|
97
|
+
const artifact = artifactForModel(modelId);
|
|
98
|
+
if (artifact === void 0) return void 0;
|
|
99
|
+
const cached = registryCache.get(artifact.origin);
|
|
100
|
+
if (cached !== void 0) return cached.tokenizer;
|
|
101
|
+
let entry;
|
|
102
|
+
try {
|
|
103
|
+
entry = { tokenizer: createDeepSeekV4TokenizerFromAssets(artifact.assetRoot, artifact.integrity, artifact.origin) };
|
|
104
|
+
} catch (error) {
|
|
105
|
+
entry = { failure: error instanceof Error ? error.message : String(error) };
|
|
106
|
+
}
|
|
107
|
+
registryCache.set(artifact.origin, entry);
|
|
108
|
+
return entry.tokenizer;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Build a tokenizer from one local asset directory after byte/hash validation.
|
|
112
|
+
* This provider-private seam exists so tests can prove every failure branch
|
|
113
|
+
* without mutating the committed artifact.
|
|
114
|
+
* @param assetRoot - local URL containing tokenizer.json and tokenizer_config.json.
|
|
115
|
+
* @param integrity - expected byte length and SHA-256 for both files.
|
|
116
|
+
* @param origin - auditable identity recorded on every returned count.
|
|
117
|
+
* @returns a synchronous exact text counter.
|
|
118
|
+
* @internal
|
|
119
|
+
*/
|
|
120
|
+
function createDeepSeekV4TokenizerFromAssets(assetRoot, integrity, origin = DEEPSEEK_V4_TOKENIZER_ARTIFACT) {
|
|
121
|
+
const tokenizerJson = readVerifiedJson(assetRoot, "tokenizer.json", integrity.tokenizer);
|
|
122
|
+
const tokenizerConfig = readVerifiedJson(assetRoot, "tokenizer_config.json", integrity.config);
|
|
123
|
+
const runtime = new Tokenizer(tokenizerJson, tokenizerConfig);
|
|
124
|
+
return Object.freeze({ countText(text) {
|
|
125
|
+
const tokens = runtime.encode(text, { add_special_tokens: false }).ids.length;
|
|
126
|
+
return Object.freeze({
|
|
127
|
+
kind: "exact-tokenizer",
|
|
128
|
+
tokens,
|
|
129
|
+
tokenizerId: origin.repository,
|
|
130
|
+
tokenizerRevision: origin.revision
|
|
131
|
+
});
|
|
132
|
+
} });
|
|
133
|
+
}
|
|
134
|
+
function readVerifiedJson(assetRoot, name, descriptor) {
|
|
135
|
+
const bytes = readFileSync(new URL(name, assetRoot));
|
|
136
|
+
if (bytes.byteLength !== descriptor.bytes) throw new Error(`DeepSeek tokenizer asset ${name} has ${String(bytes.byteLength)} bytes; expected ${String(descriptor.bytes)}`);
|
|
137
|
+
if (createHash("sha256").update(bytes).digest("hex") !== descriptor.sha256) throw new Error(`DeepSeek tokenizer asset ${name} failed SHA-256 verification`);
|
|
138
|
+
const parsed = JSON.parse(bytes.toString("utf8"));
|
|
139
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error(`DeepSeek tokenizer asset ${name} must contain a JSON object`);
|
|
140
|
+
return parsed;
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/deepseek-route.ts
|
|
144
|
+
/**
|
|
145
|
+
* Provider-route identity for the DeepSeek surfaces this runtime may measure.
|
|
146
|
+
*
|
|
147
|
+
* Two independent gates ask the same question: the exact-tokenizer binding in
|
|
148
|
+
* `measurement.ts` (without an exact same-revision count no lossy rewrite is
|
|
149
|
+
* allowed) and the published price catalog in `deepseek-official-pricing.ts`
|
|
150
|
+
* (without a priced route the conservative Adaptive gate cannot authorize
|
|
151
|
+
* anything). They must answer identically, so the answer lives here once.
|
|
152
|
+
*/
|
|
153
|
+
/**
|
|
154
|
+
* Provider ids whose DeepSeek V4 requests are served and billed by DeepSeek.
|
|
155
|
+
*
|
|
156
|
+
* `deepseek` is the API-key route and `deepseek-official` is the same surface
|
|
157
|
+
* when a deployment names it explicitly.
|
|
158
|
+
*
|
|
159
|
+
* `deepseek-account` is the signed-in account route added by
|
|
160
|
+
* `@deepseek-ai/dsh-llm-deepseek-account`: it registers through the shared
|
|
161
|
+
* `registerDeepSeekProvider` seam with the same model catalog, discovers the
|
|
162
|
+
* same model ids, and addresses the same public DeepSeek API surface — it
|
|
163
|
+
* authenticates with an account token (`x-dsh-auth-token`) instead of an API
|
|
164
|
+
* key. Refusing it therefore refused exact measurement and pricing on a route
|
|
165
|
+
* that is served by the very same V4 tokenizer artifacts and billed on the very
|
|
166
|
+
* same published price page, which silently turned every rewrite off there.
|
|
167
|
+
*
|
|
168
|
+
* Anything else — a gateway, a compatible proxy, an unrelated vendor — stays
|
|
169
|
+
* fail-closed.
|
|
170
|
+
*/
|
|
171
|
+
const DEEPSEEK_BILLED_PROVIDER_IDS = Object.freeze([
|
|
172
|
+
"deepseek",
|
|
173
|
+
"deepseek-official",
|
|
174
|
+
"deepseek-account"
|
|
175
|
+
]);
|
|
176
|
+
/**
|
|
177
|
+
* Whether one provider id belongs to the measured-and-priced DeepSeek routes.
|
|
178
|
+
* @param provider - provider id from a durable request header, or undefined.
|
|
179
|
+
* @returns `true` only for the DeepSeek-served routes above.
|
|
180
|
+
*/
|
|
181
|
+
function isDeepSeekBilledProvider(provider) {
|
|
182
|
+
return provider !== void 0 && DEEPSEEK_BILLED_PROVIDER_IDS.includes(provider);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Base-url classes addressed by the published DeepSeek price page.
|
|
186
|
+
*
|
|
187
|
+
* `official-public` is the public API host. `account-official` is the
|
|
188
|
+
* signed-in account surface, billed by the same account and unreachable with an
|
|
189
|
+
* API key. Any other class — a self-hosted gateway, an HMAC-compatible proxy —
|
|
190
|
+
* is not covered by the published prices and stays unpriced.
|
|
191
|
+
*/
|
|
192
|
+
const DEEPSEEK_OFFICIAL_BASE_URL_CLASSES = Object.freeze(["official-public", "account-official"]);
|
|
193
|
+
/**
|
|
194
|
+
* Whether one base-url class is covered by the published DeepSeek prices.
|
|
195
|
+
* @param baseUrlClass - endpoint class observed for the routed request.
|
|
196
|
+
* @returns `true` only for the two official DeepSeek surfaces.
|
|
197
|
+
*/
|
|
198
|
+
function isDeepSeekOfficialBaseUrlClass(baseUrlClass) {
|
|
199
|
+
return DEEPSEEK_OFFICIAL_BASE_URL_CLASSES.includes(baseUrlClass);
|
|
200
|
+
}
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/deepseek-v4-vision-tokens.ts
|
|
203
|
+
/**
|
|
204
|
+
* Exact DeepSeek V4.1-Flash image-token arithmetic.
|
|
205
|
+
*
|
|
206
|
+
* Every rule in this module is a line-by-line port of the official
|
|
207
|
+
* `inference/image_processor.py` published by `deepseek-ai/DeepSeek-V4.1-Flash`
|
|
208
|
+
* at the pinned immutable revision recorded below. The golden fixtures in
|
|
209
|
+
* `tests/fixtures/vision-golden.json` are generated by executing that official
|
|
210
|
+
* implementation, so any change here must keep the Node counts byte-identical
|
|
211
|
+
* to the reference output.
|
|
212
|
+
*
|
|
213
|
+
* The V4.1 revision replaced the previous vision arithmetic wholesale: the
|
|
214
|
+
* position-dependent alignment padding (`COMPRESS_PAD_TO`, the odd-row pad and
|
|
215
|
+
* the final two-token alignment pad) is gone, `safe_resize` is a single pass
|
|
216
|
+
* instead of a budget loop, and the aspect-ratio clamp is inert because the
|
|
217
|
+
* official config pins `vision_max_wh_ratio` to `null`. The block length is now
|
|
218
|
+
* exactly `nLlmH * (nLlmW + 1) + 2` and does not depend on the absolute
|
|
219
|
+
* serialized position.
|
|
220
|
+
*
|
|
221
|
+
* These arithmetic results back intrinsic-grid estimates, never exact counts
|
|
222
|
+
* of the final request. The official expansion depends on the adapter's final
|
|
223
|
+
* request-image projection — including per-route pixel-budget or image-detail
|
|
224
|
+
* overrides and byte-cap reprojection — none of which is published through a
|
|
225
|
+
* public API, and a projected image can count FEWER tokens than its intrinsic
|
|
226
|
+
* grid suggests. The measurement layer therefore labels image-bearing nodes
|
|
227
|
+
* as estimates and keeps them outside exact rewrite proofs; the 640,000-pixel
|
|
228
|
+
* budget below documents the adapter default rather than establishing
|
|
229
|
+
* exactness.
|
|
230
|
+
*/
|
|
231
|
+
/** Official projection parameters pinned from the V4.1-Flash repository config. */
|
|
232
|
+
const DEEPSEEK_VISION_PROJECTION = Object.freeze({
|
|
233
|
+
sourceRepository: "deepseek-ai/DeepSeek-V4.1-Flash",
|
|
234
|
+
sourceRevision: "dba1be0a40aa45a94ad051997016db3960a90277",
|
|
235
|
+
/** `vision_config.patch_size` from the official config. */
|
|
236
|
+
visionPatchSize: 14,
|
|
237
|
+
/** `vision_config.downsample_ratio` from the official config. */
|
|
238
|
+
visionDownsampleRatio: 3,
|
|
239
|
+
/** `vision_config.max_image_tokens`: post-preprocessing cap per image, not a fixed value. */
|
|
240
|
+
visionMaxNTokens: 1024,
|
|
241
|
+
/** `vision_config.min_pixels`: tiny images are upscaled before patching. */
|
|
242
|
+
visionMinPixels: 295936,
|
|
243
|
+
/**
|
|
244
|
+
* `vision_config.max_wh_ratio` is `null` in the official V4.1 config, so the
|
|
245
|
+
* aspect-ratio clamp branch in `plan_image_grid`/`load_image` is never taken.
|
|
246
|
+
* It is modelled as `undefined` and guarded with an explicit nullish test:
|
|
247
|
+
* the guard must never degrade into a tautology such as `width >= 0`.
|
|
248
|
+
*/
|
|
249
|
+
visionMaxWhRatio: void 0,
|
|
250
|
+
/**
|
|
251
|
+
* Default per-image pixel budget used by the DeepSeek adapter's normal
|
|
252
|
+
* attachment projection (`DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET` in
|
|
253
|
+
* `@deepseek-ai/dsh-llm-deepseek`). Route overrides remain unobservable to
|
|
254
|
+
* this estimator. This is NOT the server-side ~1300x1300 resize budget.
|
|
255
|
+
*/
|
|
256
|
+
requestImagePixelBudget: 64e4
|
|
257
|
+
});
|
|
258
|
+
/** Stable identity for the deliberately approximate image-token counter. */
|
|
259
|
+
const DEEPSEEK_VISION_IMAGE_ESTIMATOR = Object.freeze({
|
|
260
|
+
id: `${DEEPSEEK_VISION_PROJECTION.sourceRepository}/image-token-estimate`,
|
|
261
|
+
revision: `${DEEPSEEK_VISION_PROJECTION.sourceRevision}:v1`
|
|
262
|
+
});
|
|
263
|
+
/**
|
|
264
|
+
* Estimate one image without claiming an exact serialized position or final
|
|
265
|
+
* adapter projection. Valid intrinsic dimensions use the official block length;
|
|
266
|
+
* invalid or unsafe metadata uses the documented fixed fallback. In both cases
|
|
267
|
+
* the official per-image budget is retained as a conservative upper bound.
|
|
268
|
+
*/
|
|
269
|
+
function estimateDeepSeekVisionImageTokens(width, height) {
|
|
270
|
+
if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0 || !Number.isSafeInteger(width * height)) return Object.freeze({
|
|
271
|
+
tokens: 256,
|
|
272
|
+
upperBoundTokens: DEEPSEEK_VISION_PROJECTION.visionMaxNTokens,
|
|
273
|
+
source: "default"
|
|
274
|
+
});
|
|
275
|
+
try {
|
|
276
|
+
const grid = deepSeekVisionImageGrid(width, height);
|
|
277
|
+
const tokens = deepSeekVisionImageBlockTokens(grid.nLlmH, grid.nLlmW, 0);
|
|
278
|
+
return Object.freeze({
|
|
279
|
+
tokens,
|
|
280
|
+
upperBoundTokens: DEEPSEEK_VISION_PROJECTION.visionMaxNTokens,
|
|
281
|
+
source: "intrinsic-grid",
|
|
282
|
+
paddingMinimumTokens: tokens,
|
|
283
|
+
paddingMaximumTokens: tokens
|
|
284
|
+
});
|
|
285
|
+
} catch {
|
|
286
|
+
return Object.freeze({
|
|
287
|
+
tokens: 256,
|
|
288
|
+
upperBoundTokens: DEEPSEEK_VISION_PROJECTION.visionMaxNTokens,
|
|
289
|
+
source: "default"
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Resolve the aligner grid for one image's intrinsic dimensions.
|
|
295
|
+
*
|
|
296
|
+
* Port of the arithmetic path of the official `plan_image_grid` plus the
|
|
297
|
+
* V4.1 `safe_resize`: optional aspect-ratio clamp (inert under V4.1),
|
|
298
|
+
* minimum-pixel upscale, patch-grid ceiling, and a single-pass budget solve.
|
|
299
|
+
*/
|
|
300
|
+
function deepSeekVisionImageGrid(width, height) {
|
|
301
|
+
const { visionPatchSize: patch, visionMaxWhRatio, visionMinPixels, visionDownsampleRatio: downsample, visionMaxNTokens: maxNTokens } = DEEPSEEK_VISION_PROJECTION;
|
|
302
|
+
let effectiveWidth = width;
|
|
303
|
+
let effectiveHeight = height;
|
|
304
|
+
if (visionMaxWhRatio !== void 0 && visionMaxWhRatio !== null && effectiveWidth > effectiveHeight * visionMaxWhRatio) effectiveWidth = effectiveHeight * visionMaxWhRatio;
|
|
305
|
+
if (effectiveWidth * effectiveHeight > 0 && effectiveWidth * effectiveHeight < visionMinPixels) {
|
|
306
|
+
const ratio = (visionMinPixels / (effectiveWidth * effectiveHeight)) ** .5;
|
|
307
|
+
effectiveWidth = Math.trunc(effectiveWidth * ratio);
|
|
308
|
+
effectiveHeight = Math.trunc(effectiveHeight * ratio);
|
|
309
|
+
}
|
|
310
|
+
const bestWidth = Math.ceil(effectiveWidth / patch) * patch;
|
|
311
|
+
const bestHeight = Math.ceil(effectiveHeight / patch) * patch;
|
|
312
|
+
const resolved = safeResize(effectiveHeight, effectiveWidth, bestHeight, bestWidth, patch, downsample, maxNTokens);
|
|
313
|
+
return Object.freeze({ ...resolved });
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Token count of one expanded image block, port of `num_image_tokens`.
|
|
317
|
+
*
|
|
318
|
+
* Under V4.1 this is the WHOLE block length: `nLlmH * (nLlmW + 1)` already
|
|
319
|
+
* covers one newline slot per grid row, and `+ 2` covers the start and end
|
|
320
|
+
* sentinels. There is no compress pad, no odd-row pad, and no alignment pad.
|
|
321
|
+
*
|
|
322
|
+
* `startTokenPos` is INERT: the V4.1 block length does not depend on the
|
|
323
|
+
* absolute serialized position. It is retained so the exported signature and
|
|
324
|
+
* the call graph stay stable; `tests/fixtures/vision-golden.json#startPositions`
|
|
325
|
+
* records the official implementation agreeing across every swept offset.
|
|
326
|
+
* @internal exported for direct golden-fixture comparison.
|
|
327
|
+
*/
|
|
328
|
+
function deepSeekVisionImageBlockTokens(nLlmH, nLlmW, startTokenPos) {
|
|
329
|
+
return nLlmH * (nLlmW + 1) + 2;
|
|
330
|
+
}
|
|
331
|
+
/** Port of the official `llm_grid`. */
|
|
332
|
+
function llmGrid(bestHeight, bestWidth, patch, downsample) {
|
|
333
|
+
return {
|
|
334
|
+
nLlmH: Math.ceil(Math.floor(bestHeight / patch) / downsample),
|
|
335
|
+
nLlmW: Math.ceil(Math.floor(bestWidth / patch) / downsample)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Port of the official `solve_resize_ratio` (V4.1: single solve, 2-tuple).
|
|
340
|
+
*
|
|
341
|
+
* The "very tall" branch is `(max_n_token - 2) // 2 * cell`, i.e. Python floor
|
|
342
|
+
* division. Under the pinned V4.1 parameters `max_n_token` is always 1024, so
|
|
343
|
+
* `max_n_token - 2` is always even and floor and truncation agree — the
|
|
344
|
+
* `Math.floor` below is defensive fidelity, not a reachable behavioural
|
|
345
|
+
* difference.
|
|
346
|
+
*/
|
|
347
|
+
function solveResizeRatio(height, width, patch, downsample, maxNTokens) {
|
|
348
|
+
const ratio = height / width;
|
|
349
|
+
const maxWFloat = Math.sqrt((maxNTokens - 2) / ratio + .25) - .5;
|
|
350
|
+
const maxHFloat = maxWFloat * ratio;
|
|
351
|
+
const cell = patch * downsample;
|
|
352
|
+
if (maxWFloat < 1) return {
|
|
353
|
+
bestHeight: Math.floor((maxNTokens - 2) / 2) * cell,
|
|
354
|
+
bestWidth: cell
|
|
355
|
+
};
|
|
356
|
+
if (maxHFloat < 1) return {
|
|
357
|
+
bestHeight: cell,
|
|
358
|
+
bestWidth: (maxNTokens - 3) * cell
|
|
359
|
+
};
|
|
360
|
+
const beta = Math.min(Math.floor(maxWFloat) * cell / width, Math.floor(maxHFloat) * cell / height);
|
|
361
|
+
return {
|
|
362
|
+
bestHeight: Math.floor(height * beta / patch) * patch,
|
|
363
|
+
bestWidth: Math.floor(width * beta / patch) * patch
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Port of the V4.1 `safe_resize`: one occupancy check and at most one solve.
|
|
368
|
+
* The previous budget-decrement loop no longer exists upstream.
|
|
369
|
+
*
|
|
370
|
+
* Deliberate, recorded divergence from spec R2.8: the official Python carries
|
|
371
|
+
* `assert num_image_tokens(n_llm_h, n_llm_w) <= max_n_token` after the solve,
|
|
372
|
+
* and this port does not reproduce that assert. The bound is unreachable under
|
|
373
|
+
* the pinned V4.1 parameters rather than merely unobserved —
|
|
374
|
+
* `solve_resize_ratio` derives its pixel budget directly from `max_n_token`, so
|
|
375
|
+
* the solved grid cannot exceed it. Independently fuzzed over 646,000 inputs (a
|
|
376
|
+
* dense 1..3000 sweep plus 400,000 random sizes up to 200,000 px): zero
|
|
377
|
+
* violations, worst observed block length exactly 1024, i.e. the cap itself.
|
|
378
|
+
* Reproducing the assert would add a branch no test can drive, so the tradeoff
|
|
379
|
+
* is recorded here instead. Re-check this if `visionMaxNTokens` is ever set to a
|
|
380
|
+
* value the solver was not designed for.
|
|
381
|
+
*/
|
|
382
|
+
function safeResize(height, width, initialBestHeight, initialBestWidth, patch, downsample, maxNTokens) {
|
|
383
|
+
let grid = llmGrid(initialBestHeight, initialBestWidth, patch, downsample);
|
|
384
|
+
let bestHeight = initialBestHeight;
|
|
385
|
+
let bestWidth = initialBestWidth;
|
|
386
|
+
if (deepSeekVisionImageBlockTokens(grid.nLlmH, grid.nLlmW, 0) > maxNTokens) {
|
|
387
|
+
const solved = solveResizeRatio(height, width, patch, downsample, maxNTokens);
|
|
388
|
+
bestHeight = solved.bestHeight;
|
|
389
|
+
bestWidth = solved.bestWidth;
|
|
390
|
+
grid = llmGrid(bestHeight, bestWidth, patch, downsample);
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
nLlmH: grid.nLlmH,
|
|
394
|
+
nLlmW: grid.nLlmW,
|
|
395
|
+
bestHeight,
|
|
396
|
+
bestWidth
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/token-count.ts
|
|
401
|
+
/** Build an explicit unavailable result without inventing an estimate. */
|
|
402
|
+
function unavailableTokenCount(reason) {
|
|
403
|
+
if (reason.length === 0) throw new TypeError("unavailable token count requires a reason");
|
|
404
|
+
return Object.freeze({
|
|
405
|
+
kind: "unavailable",
|
|
406
|
+
reason
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
/** Sum independent canonical fields only when every count has one identity. */
|
|
410
|
+
function countExactCanonicalTextFields(fields, counter, subject) {
|
|
411
|
+
if (subject.length === 0) throw new TypeError("canonical text field count requires a subject");
|
|
412
|
+
const values = fields.length === 0 ? [""] : fields;
|
|
413
|
+
let identity;
|
|
414
|
+
let tokens = 0;
|
|
415
|
+
for (const value of values) {
|
|
416
|
+
const count = counter(value);
|
|
417
|
+
if (count.kind !== "exact-tokenizer") return unavailableTokenCount(`${subject}: ${count.kind === "unavailable" ? count.reason : "canonical content requires an exact tokenizer count"}`);
|
|
418
|
+
if (identity !== void 0 && (identity.tokenizerId !== count.tokenizerId || identity.tokenizerRevision !== count.tokenizerRevision)) return unavailableTokenCount(`${subject}: tokenizer identity changed within one measurement`);
|
|
419
|
+
identity ??= count;
|
|
420
|
+
tokens += count.tokens;
|
|
421
|
+
if (!Number.isSafeInteger(tokens) || tokens < 0) return unavailableTokenCount(`${subject}: token sum is outside the safe integer range`);
|
|
422
|
+
}
|
|
423
|
+
if (identity === void 0) return unavailableTokenCount(`${subject}: no tokenizer identity`);
|
|
424
|
+
return Object.freeze({
|
|
425
|
+
...identity,
|
|
426
|
+
tokens
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/measurement.ts
|
|
431
|
+
/**
|
|
432
|
+
* Every model id served by the V4.1-Flash artifact carries the vision image
|
|
433
|
+
* counter. Membership is a set test, never a positional `modelIds[0]` read, so
|
|
434
|
+
* adding or reordering ids cannot silently narrow the gate.
|
|
435
|
+
*/
|
|
436
|
+
const VISION_MODEL_IDS = new Set(DEEPSEEK_V41_FLASH_TOKENIZER_ARTIFACT.modelIds);
|
|
437
|
+
/**
|
|
438
|
+
* Capture one route-bound view without calling patched Harness methods.
|
|
439
|
+
* Official `measure()` remains authoritative for request pressure; the bundled
|
|
440
|
+
* tokenizer supplies exact canonical content counts used by safe rewrites.
|
|
441
|
+
*/
|
|
442
|
+
function measureForCompaction(ctx, session) {
|
|
443
|
+
const header = session.requestHeader();
|
|
444
|
+
const measurement = ctx.tokenMeter.measure(session, header);
|
|
445
|
+
const target = header?.config;
|
|
446
|
+
const counter = bindCounter(target?.provider, target?.model);
|
|
447
|
+
const events = sessionEvents(session);
|
|
448
|
+
const measuredNodes = measurement.nodes.map((node) => {
|
|
449
|
+
const event = events[node.seq];
|
|
450
|
+
if (event === void 0) return {
|
|
451
|
+
seq: node.seq,
|
|
452
|
+
count: unavailableTokenCount(`surface node ${String(node.seq)} is missing`)
|
|
453
|
+
};
|
|
454
|
+
const message = deriveEventMessage(event);
|
|
455
|
+
if (message === null) return {
|
|
456
|
+
seq: node.seq,
|
|
457
|
+
count: unavailableTokenCount(`surface node ${String(node.seq)} is not model-visible`)
|
|
458
|
+
};
|
|
459
|
+
const count = countCanonicalContent(message.content, counter, `surface node ${String(node.seq)}`);
|
|
460
|
+
const intrinsicImageBlockEstimate = count.kind === "tokenizer-estimate" ? intrinsicImageDiagnostic(message.content, target) : void 0;
|
|
461
|
+
return {
|
|
462
|
+
seq: node.seq,
|
|
463
|
+
count,
|
|
464
|
+
...intrinsicImageBlockEstimate === void 0 ? {} : { intrinsicImageBlockEstimate }
|
|
465
|
+
};
|
|
466
|
+
});
|
|
467
|
+
const currentSurface = countSurfaceCounts(measuredNodes.map((node) => node.count), "current surface");
|
|
468
|
+
const intrinsicImageBlockEstimateTokens = measuredNodes.reduce((sum, node) => sum + (node.intrinsicImageBlockEstimate?.paddingMinimumTokens ?? 0), 0);
|
|
469
|
+
return Object.freeze({
|
|
470
|
+
...measurement,
|
|
471
|
+
...target === void 0 ? {} : {
|
|
472
|
+
providerRoute: target.provider,
|
|
473
|
+
modelId: target.model
|
|
474
|
+
},
|
|
475
|
+
measuredNodes: Object.freeze(measuredNodes),
|
|
476
|
+
currentSurface,
|
|
477
|
+
intrinsicImageBlockEstimateTokens,
|
|
478
|
+
countCanonicalText: counter.countText
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Count one canonical content walk in canonical field order.
|
|
483
|
+
*
|
|
484
|
+
* Text, reasoning, tool-call names/arguments, and nested text tool results are
|
|
485
|
+
* counted exactly with one tokenizer identity. Image blocks produce a bounded
|
|
486
|
+
* estimate because the adapter's final request-image projection is not
|
|
487
|
+
* publicly observable. A mixed text/image node is therefore an estimate and
|
|
488
|
+
* never qualifies for an exact rewrite proof.
|
|
489
|
+
*/
|
|
490
|
+
function countCanonicalContent(blocks, counter, subject) {
|
|
491
|
+
let identity;
|
|
492
|
+
let estimateIdentity;
|
|
493
|
+
let tokens = 0;
|
|
494
|
+
let upperBoundTokens = 0;
|
|
495
|
+
let firstRefusal;
|
|
496
|
+
const absorb = (count) => {
|
|
497
|
+
if (count.kind === "unavailable") {
|
|
498
|
+
firstRefusal ??= count;
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
if (count.kind === "exact-tokenizer") {
|
|
502
|
+
if (identity !== void 0 && (identity.tokenizerId !== count.tokenizerId || identity.tokenizerRevision !== count.tokenizerRevision)) {
|
|
503
|
+
firstRefusal ??= unavailableTokenCount(`${subject}: tokenizer identity changed within one measurement`);
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
identity ??= count;
|
|
507
|
+
tokens += count.tokens;
|
|
508
|
+
upperBoundTokens += count.tokens;
|
|
509
|
+
} else {
|
|
510
|
+
if (estimateIdentity !== void 0 && (estimateIdentity.estimatorId !== count.estimatorId || estimateIdentity.estimatorRevision !== count.estimatorRevision)) {
|
|
511
|
+
firstRefusal ??= unavailableTokenCount(`${subject}: image estimator identity changed within one measurement`);
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
estimateIdentity ??= {
|
|
515
|
+
estimatorId: count.estimatorId,
|
|
516
|
+
estimatorRevision: count.estimatorRevision
|
|
517
|
+
};
|
|
518
|
+
tokens += count.tokens;
|
|
519
|
+
upperBoundTokens += count.upperBoundTokens;
|
|
520
|
+
}
|
|
521
|
+
return Number.isSafeInteger(tokens) && tokens >= 0 && Number.isSafeInteger(upperBoundTokens) && upperBoundTokens >= tokens;
|
|
522
|
+
};
|
|
523
|
+
const walk = (content) => {
|
|
524
|
+
for (const block of content) switch (block.type) {
|
|
525
|
+
case "text":
|
|
526
|
+
case "reasoning":
|
|
527
|
+
if (!absorb(counter.countText(block.text))) return false;
|
|
528
|
+
break;
|
|
529
|
+
case "tool-call":
|
|
530
|
+
if (!absorb(counter.countText(block.name))) return false;
|
|
531
|
+
if (!absorb(counter.countText(block.arguments))) return false;
|
|
532
|
+
break;
|
|
533
|
+
case "image":
|
|
534
|
+
if (!absorb(counter.countImage(block.attachment))) return false;
|
|
535
|
+
break;
|
|
536
|
+
default:
|
|
537
|
+
firstRefusal ??= unavailableTokenCount(`${subject}: contains an unsupported content block`);
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
return true;
|
|
541
|
+
};
|
|
542
|
+
if (!walk(blocks)) {
|
|
543
|
+
if (firstRefusal?.kind === "unavailable") return unavailableTokenCount(`${subject}: ${firstRefusal.reason}`);
|
|
544
|
+
return unavailableTokenCount(`${subject}: contains content the canonical counter cannot count exactly`);
|
|
545
|
+
}
|
|
546
|
+
if (identity === void 0 && estimateIdentity === void 0) {
|
|
547
|
+
const empty = counter.countText("");
|
|
548
|
+
if (empty.kind !== "exact-tokenizer") return empty;
|
|
549
|
+
return empty;
|
|
550
|
+
}
|
|
551
|
+
if (!Number.isSafeInteger(tokens) || tokens < 0) return unavailableTokenCount(`${subject}: invalid token sum`);
|
|
552
|
+
if (estimateIdentity !== void 0) return Object.freeze({
|
|
553
|
+
kind: "tokenizer-estimate",
|
|
554
|
+
...estimateIdentity,
|
|
555
|
+
tokens,
|
|
556
|
+
upperBoundTokens
|
|
557
|
+
});
|
|
558
|
+
if (identity === void 0) return unavailableTokenCount(`${subject}: no tokenizer identity`);
|
|
559
|
+
return Object.freeze({
|
|
560
|
+
...identity,
|
|
561
|
+
tokens
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
function bindCounter(provider, model) {
|
|
565
|
+
if (provider === void 0 || model === void 0) {
|
|
566
|
+
const unavailable = () => unavailableTokenCount("canonical text: no durable provider/model request header");
|
|
567
|
+
return {
|
|
568
|
+
countText: unavailable,
|
|
569
|
+
countImage: () => unavailableTokenCount("canonical image: no durable provider/model request header")
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
if (!isDeepSeekBilledProvider(provider)) {
|
|
573
|
+
const reason = `canonical text: provider "${provider}" is not the supported DeepSeek route`;
|
|
574
|
+
return {
|
|
575
|
+
countText: () => unavailableTokenCount(reason),
|
|
576
|
+
countImage: () => unavailableTokenCount(`canonical image: provider "${provider}" is not the supported DeepSeek route`)
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
const tokenizer = deepSeekV4TokenizerForModel(model);
|
|
580
|
+
if (tokenizer === void 0) {
|
|
581
|
+
const reason = `canonical text: no verified bundled tokenizer for model "${model}"`;
|
|
582
|
+
return {
|
|
583
|
+
countText: () => unavailableTokenCount(reason),
|
|
584
|
+
countImage: (attachment) => countCanonicalImage(model, attachment)
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
countText: (text) => tokenizer.countText(text),
|
|
589
|
+
countImage: (attachment) => countCanonicalImage(model, attachment)
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Images never claim an exact count. Under V4.1 the official block length is
|
|
594
|
+
* position-independent, so the only remaining source of inexactness is the
|
|
595
|
+
* adapter's final request-image projection, which is not exposed through a
|
|
596
|
+
* public API; a route may even override the pixel budget or re-project under
|
|
597
|
+
* the byte cap. Valid dimensions therefore use the single official block
|
|
598
|
+
* length as a bounded estimate; malformed dimensions use a fixed default.
|
|
599
|
+
* Estimate-bearing nodes remain ineligible for exact rewrite proofs.
|
|
600
|
+
*/
|
|
601
|
+
function countCanonicalImage(model, attachment) {
|
|
602
|
+
if (!VISION_MODEL_IDS.has(model)) return unavailableTokenCount(`canonical image: model "${model}" has no vision image counter`);
|
|
603
|
+
const estimate = estimateDeepSeekVisionImageTokens(attachment.width, attachment.height);
|
|
604
|
+
return Object.freeze({
|
|
605
|
+
kind: "tokenizer-estimate",
|
|
606
|
+
tokens: estimate.tokens,
|
|
607
|
+
upperBoundTokens: estimate.upperBoundTokens,
|
|
608
|
+
estimatorId: DEEPSEEK_VISION_IMAGE_ESTIMATOR.id,
|
|
609
|
+
estimatorRevision: DEEPSEEK_VISION_IMAGE_ESTIMATOR.revision
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Intrinsic-grid diagnostic for one content walk: the official V4.1 block
|
|
614
|
+
* arithmetic on intrinsic dimensions, a single position-independent value
|
|
615
|
+
* reported as both the minimum and the maximum. Only images on the pinned
|
|
616
|
+
* DeepSeek vision route with usable metadata contribute.
|
|
617
|
+
*/
|
|
618
|
+
function intrinsicImageDiagnostic(blocks, target) {
|
|
619
|
+
if (target === void 0 || !isDeepSeekBilledProvider(target.provider) || !VISION_MODEL_IDS.has(target.model)) return void 0;
|
|
620
|
+
let paddingMinimumTokens = 0;
|
|
621
|
+
let paddingMaximumTokens = 0;
|
|
622
|
+
let seen = false;
|
|
623
|
+
const walk = (content) => {
|
|
624
|
+
for (const block of content) if (block.type === "image") {
|
|
625
|
+
const { width, height } = block.attachment;
|
|
626
|
+
const estimate = estimateDeepSeekVisionImageTokens(width, height);
|
|
627
|
+
if (estimate.source !== "intrinsic-grid" || estimate.paddingMinimumTokens === void 0 || estimate.paddingMaximumTokens === void 0) continue;
|
|
628
|
+
paddingMinimumTokens += estimate.paddingMinimumTokens;
|
|
629
|
+
paddingMaximumTokens += estimate.paddingMaximumTokens;
|
|
630
|
+
seen = true;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
walk(blocks);
|
|
634
|
+
return seen ? Object.freeze({
|
|
635
|
+
paddingMinimumTokens,
|
|
636
|
+
paddingMaximumTokens
|
|
637
|
+
}) : void 0;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Combine one surface's per-node counts under a single identity.
|
|
641
|
+
* @internal exported for direct mixed-identity guard tests.
|
|
642
|
+
*/
|
|
643
|
+
function countSurfaceCounts(counts, subject) {
|
|
644
|
+
if (counts.length === 0) return unavailableTokenCount(`${subject}: no surface nodes`);
|
|
645
|
+
let identity;
|
|
646
|
+
let estimateIdentity;
|
|
647
|
+
let tokens = 0;
|
|
648
|
+
let upperBoundTokens = 0;
|
|
649
|
+
for (const count of counts) {
|
|
650
|
+
if (count.kind === "unavailable") return unavailableTokenCount(`${subject}: ${count.reason}`);
|
|
651
|
+
if (count.kind === "exact-tokenizer") {
|
|
652
|
+
if (identity !== void 0 && (identity.tokenizerId !== count.tokenizerId || identity.tokenizerRevision !== count.tokenizerRevision)) return unavailableTokenCount(`${subject}: tokenizer identity changed within one measurement`);
|
|
653
|
+
identity ??= count;
|
|
654
|
+
tokens += count.tokens;
|
|
655
|
+
upperBoundTokens += count.tokens;
|
|
656
|
+
} else {
|
|
657
|
+
if (estimateIdentity !== void 0 && (estimateIdentity.estimatorId !== count.estimatorId || estimateIdentity.estimatorRevision !== count.estimatorRevision)) return unavailableTokenCount(`${subject}: image estimator identity changed within one measurement`);
|
|
658
|
+
estimateIdentity ??= {
|
|
659
|
+
estimatorId: count.estimatorId,
|
|
660
|
+
estimatorRevision: count.estimatorRevision
|
|
661
|
+
};
|
|
662
|
+
tokens += count.tokens;
|
|
663
|
+
upperBoundTokens += count.upperBoundTokens;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
if (!Number.isSafeInteger(tokens) || tokens < 0 || !Number.isSafeInteger(upperBoundTokens) || upperBoundTokens < tokens) return unavailableTokenCount(`${subject}: invalid token sum`);
|
|
667
|
+
if (estimateIdentity !== void 0) return Object.freeze({
|
|
668
|
+
kind: "tokenizer-estimate",
|
|
669
|
+
...estimateIdentity,
|
|
670
|
+
tokens,
|
|
671
|
+
upperBoundTokens
|
|
672
|
+
});
|
|
673
|
+
if (identity === void 0) return unavailableTokenCount(`${subject}: no tokenizer identity`);
|
|
674
|
+
return Object.freeze({
|
|
675
|
+
...identity,
|
|
676
|
+
tokens
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
z.object({
|
|
680
|
+
maxChars: z.number().step(1).min(1).default(5e4),
|
|
681
|
+
maxScanChars: z.number().step(1).min(1).default(25e4),
|
|
682
|
+
maxQueryChars: z.number().step(1).min(1).default(256)
|
|
683
|
+
});
|
|
684
|
+
const REF_PATTERN = /^session:\/\/([^/]+)\/event\/(\d+)$/;
|
|
685
|
+
const MAX_LINES = 1e3;
|
|
686
|
+
const DEFAULT_MAX_CHARS = 5e4;
|
|
687
|
+
const DEFAULT_MAX_SCAN_CHARS = 25e4;
|
|
688
|
+
const DEFAULT_MAX_QUERY_CHARS = 256;
|
|
689
|
+
const TRUNCATION_MARKER = "\n[context_compression_retrieve output truncated; reported lines describe the selected source range]\n";
|
|
690
|
+
const PROMPT = "When a compacted tool result contains a session://<session-id>/event/<seq> reference, or TailTrim contains a session://<session-id>/tailtrim/<seq> reference, use context_compression_retrieve with that exact ref and a narrow line range or query if the omitted evidence is necessary. The returned event content comes from the append-only session log, which is the source of truth.";
|
|
691
|
+
const OUTPUT = {
|
|
692
|
+
schema: { type: "string" },
|
|
693
|
+
render: (_args, value) => [{
|
|
694
|
+
type: "text",
|
|
695
|
+
text: value
|
|
696
|
+
}]
|
|
697
|
+
};
|
|
698
|
+
/**
|
|
699
|
+
* Register the current-session recovery tool and its stable guidance.
|
|
700
|
+
*
|
|
701
|
+
* @param ctx Plugin context providing the tool registry and system prompt.
|
|
702
|
+
* @param config Optional response, scan, and query bounds.
|
|
703
|
+
*/
|
|
704
|
+
function installContextCompressionRetrieve(ctx, config = {}) {
|
|
705
|
+
const maxChars = resolvePositiveInteger("maxChars", config.maxChars, DEFAULT_MAX_CHARS);
|
|
706
|
+
const maxScanChars = resolvePositiveInteger("maxScanChars", config.maxScanChars, DEFAULT_MAX_SCAN_CHARS);
|
|
707
|
+
const maxQueryChars = resolvePositiveInteger("maxQueryChars", config.maxQueryChars, DEFAULT_MAX_QUERY_CHARS);
|
|
708
|
+
ctx.systemPrompt.section({
|
|
709
|
+
name: "tool:context-compression-retrieve",
|
|
710
|
+
order: 114,
|
|
711
|
+
text: PROMPT
|
|
712
|
+
});
|
|
713
|
+
ctx.tools.register(defineTool({
|
|
714
|
+
name: "context_compression_retrieve",
|
|
715
|
+
description: "Recover exact content from one compacted tool result or TailTrim group using its current-session session:// reference.",
|
|
716
|
+
parameters: {
|
|
717
|
+
ref: {
|
|
718
|
+
type: "string",
|
|
719
|
+
required: true,
|
|
720
|
+
description: "Exact session://<current-session-id>/event/<seq> or /tailtrim/<seq> reference from a placeholder."
|
|
721
|
+
},
|
|
722
|
+
query: {
|
|
723
|
+
type: "string",
|
|
724
|
+
description: "Optional case-insensitive text to search for inside the original result."
|
|
725
|
+
},
|
|
726
|
+
start_line: {
|
|
727
|
+
type: "integer",
|
|
728
|
+
description: "Optional 1-based first line for a direct slice. Defaults to 1."
|
|
729
|
+
},
|
|
730
|
+
max_lines: {
|
|
731
|
+
type: "integer",
|
|
732
|
+
description: "Maximum lines to return. Defaults to 200; maximum 1000."
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
output: OUTPUT,
|
|
736
|
+
isConcurrencySafe: () => true,
|
|
737
|
+
execute(args, exec) {
|
|
738
|
+
if (exec.agent === void 0) throw new Error("context_compression_retrieve requires an agent session");
|
|
739
|
+
const match = REF_PATTERN.exec(args.ref);
|
|
740
|
+
const tailTrimRef = parseTailTrimRef(args.ref);
|
|
741
|
+
if (match === null && tailTrimRef === null) throw new Error("context_compression_retrieve: ref must be session://<session-id>/(event|tailtrim)/<seq>");
|
|
742
|
+
if ((match?.[1] ?? tailTrimRef?.sessionId) !== String(exec.agent.id)) throw new Error("context_compression_retrieve: a compression reference may only read the caller's current session");
|
|
743
|
+
if (tailTrimRef !== null) return Promise.resolve(recoverTailTrim(exec.agent.session, args.ref, tailTrimRef.manifestSeq, args.query, args.start_line, args.max_lines, {
|
|
744
|
+
maxChars,
|
|
745
|
+
maxScanChars,
|
|
746
|
+
maxQueryChars
|
|
747
|
+
}));
|
|
748
|
+
const seq = Number(match?.[2]);
|
|
749
|
+
const event = sessionEvents(exec.agent.session)[seq];
|
|
750
|
+
if (event?.type !== "tool/result") throw new Error(`context_compression_retrieve: event ${String(seq)} is not a tool/result in the current session`);
|
|
751
|
+
const maxLines = resolveMaxLines(args.max_lines);
|
|
752
|
+
const scan = scanBlocks(event.data.message.content, maxScanChars);
|
|
753
|
+
const scannedLines = splitScannedLines(scan);
|
|
754
|
+
const lines = scannedLines.lines;
|
|
755
|
+
const query = args.query;
|
|
756
|
+
if (query !== void 0 && exceedsCodePointLimit(query, maxQueryChars)) throw new Error(`context_compression_retrieve: query must be at most ${String(maxQueryChars)} Unicode code points`);
|
|
757
|
+
const selected = query === void 0 || query === "" ? directSlice(lines, args.start_line ?? 1, maxLines, scan.complete, scannedLines.partialTail) : querySlice(lines, query, maxLines, scan.complete, scannedLines.partialTail);
|
|
758
|
+
const total = scan.complete ? String(lines.length) : `at least ${String(lines.length)}`;
|
|
759
|
+
const output = `${[
|
|
760
|
+
`source: ${args.ref}`,
|
|
761
|
+
`tool_call_id: ${event.data.message.toolCallId}`,
|
|
762
|
+
`status: ${event.data.message.isError === true ? "error" : "completed"}`,
|
|
763
|
+
`lines: ${String(selected.start)}-${String(selected.end)} of ${total}`,
|
|
764
|
+
scan.complete ? "" : "note: source scan limit reached; later lines were not inspected",
|
|
765
|
+
selected.partialLine === void 0 ? "" : `note: line ${String(selected.partialLine)} is a partial prefix ending at the source scan limit`,
|
|
766
|
+
selected.omitted ? query === void 0 || query === "" ? "note: additional source lines were omitted" : "note: additional matching or neighboring lines were omitted" : "",
|
|
767
|
+
"--- original tool result ---"
|
|
768
|
+
].filter(Boolean).join("\n")}\n${selected.text}`;
|
|
769
|
+
return Promise.resolve(boundCodePoints(output, maxChars));
|
|
770
|
+
}
|
|
771
|
+
}));
|
|
772
|
+
}
|
|
773
|
+
function recoverTailTrim(session, ref, manifestSeq, query, startLine, requestedMaxLines, bounds) {
|
|
774
|
+
const published = validatePublishedTailTrim(session, manifestSeq);
|
|
775
|
+
if (published === null || published.ref !== ref) throw new Error("context_compression_retrieve: ref is not a valid published TailTrim group");
|
|
776
|
+
if (query !== void 0 && exceedsCodePointLimit(query, bounds.maxQueryChars)) throw new Error(`context_compression_retrieve: query must be at most ${String(bounds.maxQueryChars)} Unicode code points`);
|
|
777
|
+
const fixedHeader = [
|
|
778
|
+
`source: ${ref}`,
|
|
779
|
+
"kind: tailtrim-group",
|
|
780
|
+
`records: ${String(published.roots.length)}`,
|
|
781
|
+
"--- original tool group (jsonl) ---"
|
|
782
|
+
].join("\n");
|
|
783
|
+
const scanBudget = Math.max(0, bounds.maxScanChars - codePointLength$1(`${fixedHeader}\n`));
|
|
784
|
+
const scan = consumeChunks(renderGroupRecordChunks(published.roots), scanBudget);
|
|
785
|
+
const scannedLines = splitScannedLines(scan);
|
|
786
|
+
const maxLines = resolveMaxLines(requestedMaxLines);
|
|
787
|
+
const selected = query === void 0 || query === "" ? directSlice(scannedLines.lines, startLine ?? 1, maxLines, scan.complete, scannedLines.partialTail) : querySlice(scannedLines.lines, query, maxLines, scan.complete, scannedLines.partialTail);
|
|
788
|
+
return boundCodePoints(`${[
|
|
789
|
+
fixedHeader.split("\n").slice(0, 3).join("\n"),
|
|
790
|
+
scan.complete ? "" : "note: source scan limit reached; later records were not inspected",
|
|
791
|
+
selected.omitted ? "note: additional group records were omitted" : "",
|
|
792
|
+
"--- original tool group (jsonl) ---"
|
|
793
|
+
].filter(Boolean).join("\n")}\n${selected.text}`, bounds.maxChars);
|
|
794
|
+
}
|
|
795
|
+
function resolvePositiveInteger(name, value, fallback) {
|
|
796
|
+
const resolved = value ?? fallback;
|
|
797
|
+
if (!Number.isSafeInteger(resolved) || resolved <= 0) throw new TypeError(`tool-context-retrieve: ${name} must be a positive safe integer`);
|
|
798
|
+
return resolved;
|
|
799
|
+
}
|
|
800
|
+
function resolveMaxLines(value) {
|
|
801
|
+
const resolved = value ?? 200;
|
|
802
|
+
if (!Number.isSafeInteger(resolved) || resolved < 1 || resolved > MAX_LINES) throw new Error(`context_compression_retrieve: max_lines must be an integer from 1 to ${String(MAX_LINES)}`);
|
|
803
|
+
return resolved;
|
|
804
|
+
}
|
|
805
|
+
function scanBlocks(blocks, maxChars) {
|
|
806
|
+
return consumeChunks(renderBlockChunks(blocks), maxChars);
|
|
807
|
+
}
|
|
808
|
+
function* renderBlockChunks(blocks) {
|
|
809
|
+
let first = true;
|
|
810
|
+
for (const block of blocks) {
|
|
811
|
+
if (!first) yield "\n";
|
|
812
|
+
first = false;
|
|
813
|
+
if (block.type === "text") yield block.text;
|
|
814
|
+
else yield* jsonTokens(block);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
function* renderGroupRecordChunks(roots) {
|
|
818
|
+
let first = true;
|
|
819
|
+
for (const root of roots) {
|
|
820
|
+
if (!first) yield "\n";
|
|
821
|
+
first = false;
|
|
822
|
+
const message = root.data.message;
|
|
823
|
+
yield* jsonTokens({
|
|
824
|
+
seq: root.seq,
|
|
825
|
+
type: root.type,
|
|
826
|
+
message: {
|
|
827
|
+
id: message.id,
|
|
828
|
+
role: message.role,
|
|
829
|
+
content: message.content,
|
|
830
|
+
source: message.source
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
function* jsonTokens(value) {
|
|
836
|
+
if (value === null) {
|
|
837
|
+
yield "null";
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
switch (typeof value) {
|
|
841
|
+
case "string":
|
|
842
|
+
yield "\"";
|
|
843
|
+
for (const point of value) yield jsonScalar(point).slice(1, -1);
|
|
844
|
+
yield "\"";
|
|
845
|
+
return;
|
|
846
|
+
case "number":
|
|
847
|
+
case "boolean":
|
|
848
|
+
yield jsonScalar(value);
|
|
849
|
+
return;
|
|
850
|
+
case "object":
|
|
851
|
+
if (Array.isArray(value)) {
|
|
852
|
+
yield "[";
|
|
853
|
+
for (let index = 0; index < value.length; index++) {
|
|
854
|
+
if (index > 0) yield ",";
|
|
855
|
+
yield* jsonTokens(value[index]);
|
|
856
|
+
}
|
|
857
|
+
yield "]";
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
yield "{";
|
|
861
|
+
let first = true;
|
|
862
|
+
for (const key in value) {
|
|
863
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
864
|
+
if (!first) yield ",";
|
|
865
|
+
first = false;
|
|
866
|
+
yield* jsonTokens(key);
|
|
867
|
+
yield ":";
|
|
868
|
+
yield* jsonTokens(value[key]);
|
|
869
|
+
}
|
|
870
|
+
yield "}";
|
|
871
|
+
return;
|
|
872
|
+
default: throw new TypeError("context_compression_retrieve: source content is not JSON-serializable");
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function jsonScalar(value) {
|
|
876
|
+
return JSON.stringify(value);
|
|
877
|
+
}
|
|
878
|
+
function consumeChunks(chunks, maxChars) {
|
|
879
|
+
const output = [];
|
|
880
|
+
let remaining = maxChars;
|
|
881
|
+
for (const chunk of chunks) {
|
|
882
|
+
const prefix = codePointPrefix(chunk, remaining);
|
|
883
|
+
output.push(prefix.text);
|
|
884
|
+
remaining -= prefix.count;
|
|
885
|
+
if (!prefix.complete) return {
|
|
886
|
+
text: output.join(""),
|
|
887
|
+
complete: false
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
return {
|
|
891
|
+
text: output.join(""),
|
|
892
|
+
complete: true
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
function splitScannedLines(scan) {
|
|
896
|
+
const lines = scan.text.split("\n");
|
|
897
|
+
const partialTail = !scan.complete && !scan.text.endsWith("\n");
|
|
898
|
+
if (scan.text.endsWith("\n")) lines.pop();
|
|
899
|
+
return {
|
|
900
|
+
lines,
|
|
901
|
+
partialTail
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
function directSlice(lines, startLine, maxLines, scanComplete, partialTail) {
|
|
905
|
+
if (!Number.isSafeInteger(startLine) || startLine < 1) throw new Error("context_compression_retrieve: start_line must be a positive safe integer");
|
|
906
|
+
if (startLine > lines.length) throw new Error(`context_compression_retrieve: start_line ${String(startLine)} is ${scanComplete ? "outside the source line range" : "beyond the source scan limit"}`);
|
|
907
|
+
const startIndex = startLine - 1;
|
|
908
|
+
const selected = lines.slice(startIndex, startIndex + maxLines);
|
|
909
|
+
return {
|
|
910
|
+
text: selected.join("\n"),
|
|
911
|
+
start: startIndex + 1,
|
|
912
|
+
end: startIndex + selected.length,
|
|
913
|
+
omitted: startIndex > 0 || startIndex + selected.length < lines.length || !scanComplete,
|
|
914
|
+
...partialTail && startIndex + selected.length === lines.length ? { partialLine: lines.length } : {}
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
function querySlice(lines, query, maxLines, scanComplete, partialTail) {
|
|
918
|
+
const needle = query.toLowerCase();
|
|
919
|
+
const chosen = /* @__PURE__ */ new Set();
|
|
920
|
+
let matched = false;
|
|
921
|
+
let omitted = !scanComplete;
|
|
922
|
+
for (let index = 0; index < lines.length; index++) {
|
|
923
|
+
const line = lines[index];
|
|
924
|
+
if (line === void 0 || !line.toLowerCase().includes(needle)) continue;
|
|
925
|
+
matched = true;
|
|
926
|
+
for (let row = Math.max(0, index - 2); row <= Math.min(lines.length - 1, index + 2); row++) {
|
|
927
|
+
if (chosen.has(row)) continue;
|
|
928
|
+
if (chosen.size >= maxLines) {
|
|
929
|
+
omitted = true;
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
chosen.add(row);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (!matched) return {
|
|
936
|
+
text: scanComplete ? "[no matches]" : "[no matches within source scan limit]",
|
|
937
|
+
start: 0,
|
|
938
|
+
end: 0,
|
|
939
|
+
omitted
|
|
940
|
+
};
|
|
941
|
+
const ordered = [...chosen].sort((a, b) => a - b);
|
|
942
|
+
const rendered = [];
|
|
943
|
+
let previous = -2;
|
|
944
|
+
let start = 0;
|
|
945
|
+
let end = 0;
|
|
946
|
+
for (const index of ordered) {
|
|
947
|
+
const line = lines[index];
|
|
948
|
+
if (line === void 0) continue;
|
|
949
|
+
if (index > previous + 1) rendered.push("...");
|
|
950
|
+
rendered.push(`${String(index + 1)}: ${line}`);
|
|
951
|
+
if (start === 0) start = index + 1;
|
|
952
|
+
end = index + 1;
|
|
953
|
+
previous = index;
|
|
954
|
+
}
|
|
955
|
+
return {
|
|
956
|
+
text: rendered.join("\n"),
|
|
957
|
+
start,
|
|
958
|
+
end,
|
|
959
|
+
omitted,
|
|
960
|
+
...partialTail && ordered.includes(lines.length - 1) ? { partialLine: lines.length } : {}
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
function boundCodePoints(text, maxChars) {
|
|
964
|
+
if (codePointPrefix(text, maxChars).complete) return text;
|
|
965
|
+
const marker = codePointPrefix(TRUNCATION_MARKER, maxChars);
|
|
966
|
+
if (!marker.complete) return marker.text;
|
|
967
|
+
return codePointPrefix(text, maxChars - marker.count).text + marker.text;
|
|
968
|
+
}
|
|
969
|
+
function codePointPrefix(text, maxChars) {
|
|
970
|
+
const output = [];
|
|
971
|
+
let count = 0;
|
|
972
|
+
for (const point of text) {
|
|
973
|
+
if (count >= maxChars) return {
|
|
974
|
+
text: output.join(""),
|
|
975
|
+
count,
|
|
976
|
+
complete: false
|
|
977
|
+
};
|
|
978
|
+
output.push(point);
|
|
979
|
+
count++;
|
|
980
|
+
}
|
|
981
|
+
return {
|
|
982
|
+
text: output.join(""),
|
|
983
|
+
count,
|
|
984
|
+
complete: true
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
function exceedsCodePointLimit(text, limit) {
|
|
988
|
+
let count = 0;
|
|
989
|
+
for (const _point of text) {
|
|
990
|
+
count++;
|
|
991
|
+
if (count > limit) return true;
|
|
992
|
+
}
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
function codePointLength$1(text) {
|
|
996
|
+
let count = 0;
|
|
997
|
+
for (const _point of text) count++;
|
|
998
|
+
return count;
|
|
999
|
+
}
|
|
1000
|
+
//#endregion
|
|
1001
|
+
//#region src/types.ts
|
|
1002
|
+
/** User-facing mixed strategy profile. */
|
|
1003
|
+
const COMPRESSION_PROFILES = [
|
|
1004
|
+
"off",
|
|
1005
|
+
"native",
|
|
1006
|
+
"balanced",
|
|
1007
|
+
"cache-strict",
|
|
1008
|
+
"savings",
|
|
1009
|
+
"adaptive",
|
|
1010
|
+
"custom"
|
|
1011
|
+
];
|
|
1012
|
+
//#endregion
|
|
1013
|
+
//#region src/value.ts
|
|
1014
|
+
/** Version-neutral immutable-value and closed-union helpers for the plugin runtime. */
|
|
1015
|
+
/**
|
|
1016
|
+
* Freeze an object graph in place without relying on a Harness utility export.
|
|
1017
|
+
* Live AbortSignals remain mutable so request cancellation continues to work.
|
|
1018
|
+
* @param value - Value to freeze recursively.
|
|
1019
|
+
* @returns The same deeply frozen value.
|
|
1020
|
+
*/
|
|
1021
|
+
function deepFreeze(value) {
|
|
1022
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1023
|
+
const pending = [{
|
|
1024
|
+
kind: "visit",
|
|
1025
|
+
node: value
|
|
1026
|
+
}];
|
|
1027
|
+
while (pending.length > 0) {
|
|
1028
|
+
const task = pending.pop();
|
|
1029
|
+
/* v8 ignore next -- the loop condition guarantees one pending task. */
|
|
1030
|
+
if (task === void 0) continue;
|
|
1031
|
+
if (task.kind === "property") {
|
|
1032
|
+
pending.push({
|
|
1033
|
+
kind: "visit",
|
|
1034
|
+
node: task.source[task.key]
|
|
1035
|
+
});
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
const node = task.node;
|
|
1039
|
+
if (node === null || typeof node !== "object" || node instanceof AbortSignal || seen.has(node)) continue;
|
|
1040
|
+
seen.add(node);
|
|
1041
|
+
Object.freeze(node);
|
|
1042
|
+
const keys = Object.keys(node);
|
|
1043
|
+
for (let index = keys.length - 1; index >= 0; index--) {
|
|
1044
|
+
const key = keys[index];
|
|
1045
|
+
/* v8 ignore next -- the loop is bounded by the captured key count. */
|
|
1046
|
+
if (key === void 0) continue;
|
|
1047
|
+
pending.push({
|
|
1048
|
+
kind: "property",
|
|
1049
|
+
source: node,
|
|
1050
|
+
key
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return value;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Throw for an impossible member of a closed discriminated union.
|
|
1058
|
+
* @param value - Value that escaped its closed union type.
|
|
1059
|
+
* @param context - Optional switch-site label.
|
|
1060
|
+
* @returns Never returns.
|
|
1061
|
+
*/
|
|
1062
|
+
function assertNever(value, context) {
|
|
1063
|
+
const rendered = JSON.stringify(value) ?? String(value);
|
|
1064
|
+
throw new Error(`unreachable variant${context === void 0 ? "" : ` in ${context}`}: ${rendered}`);
|
|
1065
|
+
}
|
|
1066
|
+
//#endregion
|
|
1067
|
+
//#region src/custom-policy.ts
|
|
1068
|
+
/** Strict versioned Custom policy parsing and effective-token resolution. */
|
|
1069
|
+
const budgetSchema = z.object({
|
|
1070
|
+
enabled: z.boolean().required(),
|
|
1071
|
+
trigger: z.number().required(),
|
|
1072
|
+
target: z.number().required()
|
|
1073
|
+
}).required();
|
|
1074
|
+
const legacyHistorySchema = z.object({
|
|
1075
|
+
enabled: z.boolean().required(),
|
|
1076
|
+
trigger: z.number().required(),
|
|
1077
|
+
keepRecentTurns: z.number().step(1).min(0).required(),
|
|
1078
|
+
keepRecent: z.number().required(),
|
|
1079
|
+
minReclaim: z.number().required()
|
|
1080
|
+
}).required();
|
|
1081
|
+
const historySchema = z.object({
|
|
1082
|
+
enabled: z.boolean().required(),
|
|
1083
|
+
trigger: z.number().required(),
|
|
1084
|
+
keepRecentToolCalls: z.number().step(1).min(0).required(),
|
|
1085
|
+
keepRecentTokens: z.number().required(),
|
|
1086
|
+
minReclaim: z.number().required()
|
|
1087
|
+
}).required();
|
|
1088
|
+
const tailTrimSchema = z.object({
|
|
1089
|
+
enabled: z.boolean().required(),
|
|
1090
|
+
trigger: z.number().required()
|
|
1091
|
+
}).required();
|
|
1092
|
+
const customCompressionPolicyV1InputSchema = z.object({
|
|
1093
|
+
version: z.const(1).required(),
|
|
1094
|
+
unit: z.union(["tokens", "context-percent"]).required(),
|
|
1095
|
+
fresh: budgetSchema,
|
|
1096
|
+
aggregate: budgetSchema,
|
|
1097
|
+
history: legacyHistorySchema,
|
|
1098
|
+
prefixPolicy: z.union(["preserve", "pressure-break"]).required()
|
|
1099
|
+
}).required();
|
|
1100
|
+
const customCompressionPolicyV2InputSchema = z.object({
|
|
1101
|
+
version: z.const(2).required(),
|
|
1102
|
+
unit: z.union(["tokens", "context-percent"]).required(),
|
|
1103
|
+
fresh: budgetSchema,
|
|
1104
|
+
aggregate: budgetSchema,
|
|
1105
|
+
history: legacyHistorySchema,
|
|
1106
|
+
prefixPolicy: z.union(["preserve", "pressure-break"]).required(),
|
|
1107
|
+
tailTrim: tailTrimSchema
|
|
1108
|
+
}).required();
|
|
1109
|
+
const customCompressionPolicyV3InputSchema = z.object({
|
|
1110
|
+
version: z.const(3).required(),
|
|
1111
|
+
unit: z.union(["tokens", "context-percent"]).required(),
|
|
1112
|
+
fresh: budgetSchema,
|
|
1113
|
+
aggregate: budgetSchema,
|
|
1114
|
+
history: historySchema,
|
|
1115
|
+
prefixPolicy: z.union(["preserve", "pressure-break"]).required(),
|
|
1116
|
+
tailTrim: tailTrimSchema
|
|
1117
|
+
}).required();
|
|
1118
|
+
/**
|
|
1119
|
+
* JSON-survivable union of every Custom document a Host form may carry.
|
|
1120
|
+
*
|
|
1121
|
+
* A Host Config field that a browser edits must survive the round trip the
|
|
1122
|
+
* settings surface performs: the Host serves the field's form schema as
|
|
1123
|
+
* `schema.toJSON()` and the client rehydrates it to validate the value it was
|
|
1124
|
+
* served. {@link CustomCompressionPolicySchema} cannot survive that trip — a
|
|
1125
|
+
* schemastery `transform` serializes its `transform` type and inner schema but
|
|
1126
|
+
* never its callback, so the rehydrated node throws `callback is not a function`
|
|
1127
|
+
* on the very document it is meant to accept, the client's form never leaves
|
|
1128
|
+
* `loading`, and every control rendering from it stays disabled.
|
|
1129
|
+
*
|
|
1130
|
+
* This union validates the same three input shapes with no callback, so it is
|
|
1131
|
+
* the schema a live-editable field may expose. The transform stays the gate for
|
|
1132
|
+
* values the runtime resolves itself.
|
|
1133
|
+
*/
|
|
1134
|
+
const CustomCompressionPolicyInputSchema = z.union([
|
|
1135
|
+
customCompressionPolicyV1InputSchema,
|
|
1136
|
+
customCompressionPolicyV2InputSchema,
|
|
1137
|
+
customCompressionPolicyV3InputSchema
|
|
1138
|
+
]);
|
|
1139
|
+
/** Canonical Custom document accepted by Host settings and the runtime resolver. */
|
|
1140
|
+
const CustomCompressionPolicySchema = z.transform(z.any().required(), (value) => {
|
|
1141
|
+
assertExactPolicyShape(value);
|
|
1142
|
+
const canonical = canonicalizeCustomPolicy(value.version === 1 ? customCompressionPolicyV1InputSchema(value) : value.version === 2 ? customCompressionPolicyV2InputSchema(value) : customCompressionPolicyV3InputSchema(value));
|
|
1143
|
+
assertCanonicalRelations(canonical);
|
|
1144
|
+
return deepFreeze(structuredClone(canonical));
|
|
1145
|
+
});
|
|
1146
|
+
/** Balanced-equivalent Custom policy stored as one token-canonical document. */
|
|
1147
|
+
const DEFAULT_CUSTOM_COMPRESSION_POLICY = deepFreeze({
|
|
1148
|
+
version: 3,
|
|
1149
|
+
unit: "tokens",
|
|
1150
|
+
fresh: {
|
|
1151
|
+
enabled: true,
|
|
1152
|
+
trigger: 8192,
|
|
1153
|
+
target: 3072
|
|
1154
|
+
},
|
|
1155
|
+
aggregate: {
|
|
1156
|
+
enabled: true,
|
|
1157
|
+
trigger: 32768,
|
|
1158
|
+
target: 12288
|
|
1159
|
+
},
|
|
1160
|
+
history: {
|
|
1161
|
+
enabled: true,
|
|
1162
|
+
trigger: 5e5,
|
|
1163
|
+
keepRecentToolCalls: 10,
|
|
1164
|
+
keepRecentTokens: 64e3,
|
|
1165
|
+
minReclaim: 96e3
|
|
1166
|
+
},
|
|
1167
|
+
prefixPolicy: "pressure-break",
|
|
1168
|
+
tailTrim: {
|
|
1169
|
+
enabled: false,
|
|
1170
|
+
trigger: 7e5
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
/**
|
|
1174
|
+
* Resolve one validated Custom document to the same token policy used by public presets.
|
|
1175
|
+
* @param value - untrusted or typed Custom settings value.
|
|
1176
|
+
* @param options - routed model capacity for context-percent documents.
|
|
1177
|
+
* @returns a detached deeply immutable effective token policy.
|
|
1178
|
+
*/
|
|
1179
|
+
function resolveCustomPolicy(value, options = {}) {
|
|
1180
|
+
const policy = canonicalizeCustomPolicy(CustomCompressionPolicySchema(value));
|
|
1181
|
+
const effective = (name, amount) => {
|
|
1182
|
+
if (policy.unit === "tokens") return amount;
|
|
1183
|
+
const contextWindow = options.contextWindowTokens;
|
|
1184
|
+
if (!Number.isSafeInteger(contextWindow) || contextWindow === void 0 || contextWindow <= 0) throw new Error("Custom context-percent policy requires a resolved positive model context window");
|
|
1185
|
+
const tokens = Math.floor(contextWindow * amount / 100);
|
|
1186
|
+
if (!Number.isSafeInteger(tokens) || amount > 0 && tokens <= 0) throw new Error(`Custom ${name} has no valid effective token value for this model`);
|
|
1187
|
+
return tokens;
|
|
1188
|
+
};
|
|
1189
|
+
const resolved = {
|
|
1190
|
+
profile: "custom",
|
|
1191
|
+
nativeToolResultEnabled: false,
|
|
1192
|
+
freshEnabled: policy.fresh.enabled,
|
|
1193
|
+
aggregateEnabled: policy.aggregate.enabled,
|
|
1194
|
+
historyMode: !policy.history.enabled ? "disabled" : policy.prefixPolicy === "preserve" ? "capacity-pressure" : "routine",
|
|
1195
|
+
nativeTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1196
|
+
nativeTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1197
|
+
freshTriggerTokens: effective("Fresh trigger", policy.fresh.trigger),
|
|
1198
|
+
freshTargetTokens: effective("Fresh target", policy.fresh.target),
|
|
1199
|
+
aggregateTriggerTokens: effective("Aggregate trigger", policy.aggregate.trigger),
|
|
1200
|
+
aggregateTargetTokens: effective("Aggregate target", policy.aggregate.target),
|
|
1201
|
+
historyTriggerTokens: effective("History trigger", policy.history.trigger),
|
|
1202
|
+
historyKeepRecentToolCalls: policy.history.keepRecentToolCalls,
|
|
1203
|
+
historyKeepRecentTokens: effective("History recent token tail", policy.history.keepRecentTokens),
|
|
1204
|
+
historyMinReclaimTokens: effective("History min-reclaim", policy.history.minReclaim),
|
|
1205
|
+
tailTrim: {
|
|
1206
|
+
enabled: policy.tailTrim.enabled,
|
|
1207
|
+
triggerTokens: effective("TailTrim trigger", policy.tailTrim.trigger)
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
assertEffectiveRelations(resolved);
|
|
1211
|
+
return deepFreeze(resolved);
|
|
1212
|
+
}
|
|
1213
|
+
function canonicalizeCustomPolicy(policy) {
|
|
1214
|
+
if (policy.version === 3) return policy;
|
|
1215
|
+
return {
|
|
1216
|
+
version: 3,
|
|
1217
|
+
unit: policy.unit,
|
|
1218
|
+
fresh: policy.fresh,
|
|
1219
|
+
aggregate: policy.aggregate,
|
|
1220
|
+
history: {
|
|
1221
|
+
enabled: policy.history.enabled,
|
|
1222
|
+
trigger: policy.history.trigger,
|
|
1223
|
+
keepRecentToolCalls: 10,
|
|
1224
|
+
keepRecentTokens: policy.history.keepRecent,
|
|
1225
|
+
minReclaim: policy.history.minReclaim
|
|
1226
|
+
},
|
|
1227
|
+
prefixPolicy: policy.prefixPolicy,
|
|
1228
|
+
tailTrim: policy.version === 1 ? {
|
|
1229
|
+
enabled: false,
|
|
1230
|
+
trigger: 7e5
|
|
1231
|
+
} : policy.tailTrim
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function measuredValues(policy) {
|
|
1235
|
+
return [
|
|
1236
|
+
policy.fresh.trigger,
|
|
1237
|
+
policy.fresh.target,
|
|
1238
|
+
policy.aggregate.trigger,
|
|
1239
|
+
policy.aggregate.target,
|
|
1240
|
+
policy.history.trigger,
|
|
1241
|
+
policy.history.keepRecentTokens,
|
|
1242
|
+
policy.history.minReclaim,
|
|
1243
|
+
policy.tailTrim.trigger
|
|
1244
|
+
];
|
|
1245
|
+
}
|
|
1246
|
+
function validMeasuredValues(policy) {
|
|
1247
|
+
const values = measuredValues(policy);
|
|
1248
|
+
if (![
|
|
1249
|
+
policy.fresh.trigger,
|
|
1250
|
+
policy.fresh.target,
|
|
1251
|
+
policy.aggregate.trigger,
|
|
1252
|
+
policy.aggregate.target,
|
|
1253
|
+
policy.history.trigger,
|
|
1254
|
+
policy.history.minReclaim,
|
|
1255
|
+
policy.tailTrim.trigger
|
|
1256
|
+
].every((value) => value > 0) || policy.history.keepRecentTokens < 0 || !Number.isSafeInteger(policy.history.keepRecentToolCalls) || policy.history.keepRecentToolCalls < 0) return false;
|
|
1257
|
+
return policy.unit === "tokens" ? values.every(Number.isSafeInteger) : values.every((value) => Number.isFinite(value) && value <= 100);
|
|
1258
|
+
}
|
|
1259
|
+
function assertCanonicalRelations(policy) {
|
|
1260
|
+
if (!validMeasuredValues(policy)) throw new TypeError("Custom measured values must use the selected canonical unit");
|
|
1261
|
+
if (policy.fresh.target >= policy.fresh.trigger) throw new TypeError("Custom Fresh target must be below trigger");
|
|
1262
|
+
if (policy.aggregate.target >= policy.aggregate.trigger) throw new TypeError("Custom Aggregate target must be below trigger");
|
|
1263
|
+
if (policy.history.minReclaim > policy.history.trigger) throw new TypeError("Custom History min-reclaim must not exceed its trigger");
|
|
1264
|
+
}
|
|
1265
|
+
function assertExactPolicyShape(value) {
|
|
1266
|
+
if (!isPlainRecord$1(value)) throw new TypeError("Custom must be a plain object");
|
|
1267
|
+
if (value.version !== 1 && value.version !== 2 && value.version !== 3) throw new TypeError("Custom version must be 1, 2, or 3");
|
|
1268
|
+
assertExactKeys(value, value.version === 1 ? [
|
|
1269
|
+
"version",
|
|
1270
|
+
"unit",
|
|
1271
|
+
"fresh",
|
|
1272
|
+
"aggregate",
|
|
1273
|
+
"history",
|
|
1274
|
+
"prefixPolicy"
|
|
1275
|
+
] : [
|
|
1276
|
+
"version",
|
|
1277
|
+
"unit",
|
|
1278
|
+
"fresh",
|
|
1279
|
+
"aggregate",
|
|
1280
|
+
"history",
|
|
1281
|
+
"prefixPolicy",
|
|
1282
|
+
"tailTrim"
|
|
1283
|
+
], "Custom");
|
|
1284
|
+
assertExactKeys(value.fresh, [
|
|
1285
|
+
"enabled",
|
|
1286
|
+
"trigger",
|
|
1287
|
+
"target"
|
|
1288
|
+
], "Custom Fresh");
|
|
1289
|
+
assertExactKeys(value.aggregate, [
|
|
1290
|
+
"enabled",
|
|
1291
|
+
"trigger",
|
|
1292
|
+
"target"
|
|
1293
|
+
], "Custom Aggregate");
|
|
1294
|
+
assertExactKeys(value.history, value.version === 3 ? [
|
|
1295
|
+
"enabled",
|
|
1296
|
+
"trigger",
|
|
1297
|
+
"keepRecentToolCalls",
|
|
1298
|
+
"keepRecentTokens",
|
|
1299
|
+
"minReclaim"
|
|
1300
|
+
] : [
|
|
1301
|
+
"enabled",
|
|
1302
|
+
"trigger",
|
|
1303
|
+
"keepRecentTurns",
|
|
1304
|
+
"keepRecent",
|
|
1305
|
+
"minReclaim"
|
|
1306
|
+
], "Custom History");
|
|
1307
|
+
if (value.version !== 1) assertExactKeys(value.tailTrim, ["enabled", "trigger"], "Custom TailTrim");
|
|
1308
|
+
}
|
|
1309
|
+
function assertExactKeys(value, allowed, label) {
|
|
1310
|
+
if (!isPlainRecord$1(value)) throw new TypeError(`${label} must be a plain object`);
|
|
1311
|
+
const allowedKeys = new Set(allowed);
|
|
1312
|
+
const unknown = Object.keys(value).find((key) => !allowedKeys.has(key));
|
|
1313
|
+
if (unknown !== void 0) throw new TypeError(`${label}: unknown key "${unknown}"`);
|
|
1314
|
+
}
|
|
1315
|
+
function isPlainRecord$1(value) {
|
|
1316
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1317
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1318
|
+
return prototype === Object.prototype || prototype === null;
|
|
1319
|
+
}
|
|
1320
|
+
function assertEffectiveRelations(policy) {
|
|
1321
|
+
if (policy.freshTargetTokens >= policy.freshTriggerTokens) throw new Error("Custom effective Fresh target must be below trigger");
|
|
1322
|
+
if (policy.aggregateTargetTokens >= policy.aggregateTriggerTokens) throw new Error("Custom effective Aggregate target must be below trigger");
|
|
1323
|
+
if (policy.historyMinReclaimTokens > policy.historyTriggerTokens) throw new Error("Custom effective History min-reclaim must not exceed its trigger");
|
|
1324
|
+
}
|
|
1325
|
+
//#endregion
|
|
1326
|
+
//#region src/config.ts
|
|
1327
|
+
/** Configuration resolution for the mixed deterministic context-compression selector. */
|
|
1328
|
+
/** Settings namespace shared by the Host service and browser selector. */
|
|
1329
|
+
const CONTEXT_COMPRESSION_SETTINGS_NAMESPACE = "context-compression";
|
|
1330
|
+
/** Fixed native fallback marker. */
|
|
1331
|
+
const PRUNE_MARKER = "\n\n[... tool result middle pruned ...]\n\n";
|
|
1332
|
+
/**
|
|
1333
|
+
* The one Auto Compact threshold contract shared by the settings UI, the
|
|
1334
|
+
* persisted settings schema, and the runtime resolver. Every integer in the
|
|
1335
|
+
* range is valid and entered directly in the UI.
|
|
1336
|
+
*/
|
|
1337
|
+
const AUTO_COMPACT_THRESHOLD_LIMITS = deepFreeze({
|
|
1338
|
+
min: 50,
|
|
1339
|
+
max: 90,
|
|
1340
|
+
step: 1,
|
|
1341
|
+
default: 80
|
|
1342
|
+
});
|
|
1343
|
+
/** Narrow one untrusted value to a valid Auto Compact threshold percent. */
|
|
1344
|
+
function isValidAutoCompactThresholdPercent(value) {
|
|
1345
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= AUTO_COMPACT_THRESHOLD_LIMITS.min && value <= AUTO_COMPACT_THRESHOLD_LIMITS.max;
|
|
1346
|
+
}
|
|
1347
|
+
const AUTO_COMPACT_DEFAULT = deepFreeze({ thresholdPercent: AUTO_COMPACT_THRESHOLD_LIMITS.default });
|
|
1348
|
+
/** Accept JSON-object records while rejecting class instances and exotic prototypes. */
|
|
1349
|
+
function isPlainRecord(value) {
|
|
1350
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1351
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1352
|
+
return prototype === Object.prototype || prototype === null;
|
|
1353
|
+
}
|
|
1354
|
+
/** Reject exotic prototypes anywhere in the JSON-like settings tree. */
|
|
1355
|
+
function assertPlainDataTree(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
1356
|
+
if (value === null || typeof value !== "object") return;
|
|
1357
|
+
if (seen.has(value)) return;
|
|
1358
|
+
seen.add(value);
|
|
1359
|
+
if (Array.isArray(value)) {
|
|
1360
|
+
for (const entry of value) assertPlainDataTree(entry, seen);
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
if (!isPlainRecord(value)) throw new TypeError("Context-compression settings must contain only plain objects");
|
|
1364
|
+
for (const entry of Object.values(value)) assertPlainDataTree(entry, seen);
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Strictly parse the persisted autoCompact section. Schemastery object
|
|
1368
|
+
* defaults silently absorb null, empty, and extra-key sections, so this stays
|
|
1369
|
+
* hand-validated beside the top-level unknown-key check.
|
|
1370
|
+
*/
|
|
1371
|
+
function parseAutoCompactSettings(value) {
|
|
1372
|
+
if (value === void 0) return AUTO_COMPACT_DEFAULT;
|
|
1373
|
+
if (!isPlainRecord(value)) throw new TypeError("Context-compression autoCompact must be a plain object");
|
|
1374
|
+
const keys = Object.keys(value);
|
|
1375
|
+
if (keys.length !== 1 || keys[0] !== "thresholdPercent") throw new TypeError(`Context-compression autoCompact: expected exactly "thresholdPercent", got "${keys.join("\", \"")}"`);
|
|
1376
|
+
const thresholdPercent = value.thresholdPercent;
|
|
1377
|
+
if (!isValidAutoCompactThresholdPercent(thresholdPercent)) throw new TypeError(`Context-compression autoCompact.thresholdPercent (${String(thresholdPercent)}) must be an integer between ${String(AUTO_COMPACT_THRESHOLD_LIMITS.min)} and ${String(AUTO_COMPACT_THRESHOLD_LIMITS.max)}`);
|
|
1378
|
+
return { thresholdPercent };
|
|
1379
|
+
}
|
|
1380
|
+
/** Settings schema used by the user-facing profile selector. */
|
|
1381
|
+
const contextCompressionSettingsInputSchema = z.object({
|
|
1382
|
+
profile: z.union([...COMPRESSION_PROFILES]).default("balanced"),
|
|
1383
|
+
custom: CustomCompressionPolicySchema.default(DEFAULT_CUSTOM_COMPRESSION_POLICY)
|
|
1384
|
+
});
|
|
1385
|
+
/**
|
|
1386
|
+
* Reject a section that is PRESENT but not a usable value. Schemastery
|
|
1387
|
+
* `.default(...)` silently substitutes null and undefined, which would turn a
|
|
1388
|
+
* hand-corrupted store into the (lossy) default policy; only genuinely absent
|
|
1389
|
+
* keys may inherit defaults, and that distinction must be made before any
|
|
1390
|
+
* default can fire.
|
|
1391
|
+
*/
|
|
1392
|
+
function assertPresentSection(candidate, key, valid) {
|
|
1393
|
+
if (!Object.hasOwn(candidate, key)) return;
|
|
1394
|
+
if (!valid(candidate[key])) throw new TypeError(`Context-compression settings: "${key}" is present but invalid (${String(candidate[key])})`);
|
|
1395
|
+
}
|
|
1396
|
+
const isSupportedProfile = (value) => typeof value === "string" && COMPRESSION_PROFILES.includes(value);
|
|
1397
|
+
const isUsableCustomDocument = (value) => isPlainRecord(value);
|
|
1398
|
+
const DEFAULT_CONTEXT_COMPRESSION_SETTINGS = {
|
|
1399
|
+
profile: "balanced",
|
|
1400
|
+
custom: structuredClone(DEFAULT_CUSTOM_COMPRESSION_POLICY),
|
|
1401
|
+
autoCompact: { thresholdPercent: AUTO_COMPACT_THRESHOLD_LIMITS.default }
|
|
1402
|
+
};
|
|
1403
|
+
/**
|
|
1404
|
+
* Parse one settings document with the persisted-section semantics: `undefined`
|
|
1405
|
+
* inherits the defaults (an absent section), while `null` is an explicitly
|
|
1406
|
+
* invalid document and must never silently become the default policy.
|
|
1407
|
+
*/
|
|
1408
|
+
function parseContextCompressionSettings(value) {
|
|
1409
|
+
if (!isPlainRecord(value)) throw new TypeError("Context-compression settings must be a plain object");
|
|
1410
|
+
const keys = Object.keys(value);
|
|
1411
|
+
if (keys.length === 0 || !keys.includes("profile") || !keys.includes("custom")) throw new TypeError("Context-compression settings document is missing its complete shape");
|
|
1412
|
+
return ContextCompressionSettingsSchema(value);
|
|
1413
|
+
}
|
|
1414
|
+
/** Settings schema used by the user-facing profile selector. */
|
|
1415
|
+
const ContextCompressionSettingsSchema = z.transform(z.any().required(), (value) => {
|
|
1416
|
+
if (!isPlainRecord(value)) throw new TypeError("Context-compression settings must be a plain object");
|
|
1417
|
+
assertPlainDataTree(value);
|
|
1418
|
+
const candidate = structuredClone(value);
|
|
1419
|
+
const unknown = Object.keys(candidate).find((key) => key !== "profile" && key !== "custom" && key !== "autoCompact");
|
|
1420
|
+
if (unknown !== void 0) throw new TypeError(`Context-compression settings: unknown key "${unknown}"`);
|
|
1421
|
+
assertPresentSection(candidate, "profile", isSupportedProfile);
|
|
1422
|
+
assertPresentSection(candidate, "custom", isUsableCustomDocument);
|
|
1423
|
+
const autoCompact = parseAutoCompactSettings(candidate.autoCompact);
|
|
1424
|
+
return {
|
|
1425
|
+
...contextCompressionSettingsInputSchema(candidate),
|
|
1426
|
+
autoCompact
|
|
1427
|
+
};
|
|
1428
|
+
}).default(DEFAULT_CONTEXT_COMPRESSION_SETTINGS);
|
|
1429
|
+
/** Low-friction defaults; token budgets live in resolved profile policy. */
|
|
1430
|
+
const DEFAULTS = deepFreeze({
|
|
1431
|
+
profile: "balanced",
|
|
1432
|
+
custom: DEFAULT_CUSTOM_COMPRESSION_POLICY,
|
|
1433
|
+
headChars: 4096,
|
|
1434
|
+
tailChars: 1024
|
|
1435
|
+
});
|
|
1436
|
+
const CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
1437
|
+
"profile",
|
|
1438
|
+
"custom",
|
|
1439
|
+
"headChars",
|
|
1440
|
+
"tailChars",
|
|
1441
|
+
"nativeTriggerTokens",
|
|
1442
|
+
"nativeTargetTokens",
|
|
1443
|
+
"freshTriggerTokens",
|
|
1444
|
+
"freshTargetTokens",
|
|
1445
|
+
"aggregateTriggerTokens",
|
|
1446
|
+
"aggregateTargetTokens",
|
|
1447
|
+
"historyTriggerTokens",
|
|
1448
|
+
"historyKeepRecentToolCalls",
|
|
1449
|
+
"historyKeepRecentTokens",
|
|
1450
|
+
"historyMinReclaimTokens",
|
|
1451
|
+
"autoCompactThresholdPercent"
|
|
1452
|
+
]);
|
|
1453
|
+
const LEGACY_GATE_REPLACEMENTS = Object.freeze({
|
|
1454
|
+
thresholdChars: "nativeTriggerTokens",
|
|
1455
|
+
freshThresholdChars: "freshTriggerTokens",
|
|
1456
|
+
freshTargetChars: "freshTargetTokens",
|
|
1457
|
+
freshBatchTriggerChars: "aggregateTriggerTokens",
|
|
1458
|
+
freshBatchTargetChars: "aggregateTargetTokens",
|
|
1459
|
+
historyTriggerChars: "historyTriggerTokens",
|
|
1460
|
+
historyKeepRecentChars: "historyKeepRecentTokens",
|
|
1461
|
+
historyMinReclaimChars: "historyMinReclaimTokens",
|
|
1462
|
+
historyKeepRecentTurns: "historyKeepRecentToolCalls"
|
|
1463
|
+
});
|
|
1464
|
+
/**
|
|
1465
|
+
* Count Unicode code points without splitting surrogate pairs.
|
|
1466
|
+
* @param text - text whose code points are counted.
|
|
1467
|
+
* @returns the number of Unicode code points.
|
|
1468
|
+
*/
|
|
1469
|
+
function codePointLength(text) {
|
|
1470
|
+
let length = 0;
|
|
1471
|
+
for (const _point of text) length++;
|
|
1472
|
+
return length;
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Test whether a settings value names a supported compression profile.
|
|
1476
|
+
* @param value - untrusted settings value.
|
|
1477
|
+
* @returns whether the value is a supported compression profile.
|
|
1478
|
+
*/
|
|
1479
|
+
function isCompressionProfile(value) {
|
|
1480
|
+
return typeof value === "string" && COMPRESSION_PROFILES.includes(value);
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Resolve and validate plugin configuration.
|
|
1484
|
+
* @param config - optional composition overrides.
|
|
1485
|
+
* @returns a detached, deeply immutable configuration snapshot.
|
|
1486
|
+
*/
|
|
1487
|
+
function resolveConfig(config = {}) {
|
|
1488
|
+
for (const key of Object.keys(config)) {
|
|
1489
|
+
const replacement = LEGACY_GATE_REPLACEMENTS[key];
|
|
1490
|
+
if (replacement !== void 0) throw new Error(`ToolResultPruneConfig: legacy gate "${key}" is no longer accepted; choose "${replacement}" manually in tokens (no character conversion is applied)`);
|
|
1491
|
+
if (!CONFIG_KEYS.has(key)) throw new Error(`ToolResultPruneConfig: unknown key "${key}"`);
|
|
1492
|
+
}
|
|
1493
|
+
const resolved = {
|
|
1494
|
+
profile: config.profile ?? DEFAULTS.profile,
|
|
1495
|
+
custom: config.custom === void 0 ? DEFAULT_CUSTOM_COMPRESSION_POLICY : CustomCompressionPolicySchema(config.custom),
|
|
1496
|
+
headChars: config.headChars ?? DEFAULTS.headChars,
|
|
1497
|
+
tailChars: config.tailChars ?? DEFAULTS.tailChars,
|
|
1498
|
+
...config.nativeTriggerTokens === void 0 ? {} : { nativeTriggerTokens: config.nativeTriggerTokens },
|
|
1499
|
+
...config.nativeTargetTokens === void 0 ? {} : { nativeTargetTokens: config.nativeTargetTokens },
|
|
1500
|
+
...config.freshTriggerTokens === void 0 ? {} : { freshTriggerTokens: config.freshTriggerTokens },
|
|
1501
|
+
...config.freshTargetTokens === void 0 ? {} : { freshTargetTokens: config.freshTargetTokens },
|
|
1502
|
+
...config.aggregateTriggerTokens === void 0 ? {} : { aggregateTriggerTokens: config.aggregateTriggerTokens },
|
|
1503
|
+
...config.aggregateTargetTokens === void 0 ? {} : { aggregateTargetTokens: config.aggregateTargetTokens },
|
|
1504
|
+
...config.historyTriggerTokens === void 0 ? {} : { historyTriggerTokens: config.historyTriggerTokens },
|
|
1505
|
+
...config.historyKeepRecentToolCalls === void 0 ? {} : { historyKeepRecentToolCalls: config.historyKeepRecentToolCalls },
|
|
1506
|
+
...config.historyKeepRecentTokens === void 0 ? {} : { historyKeepRecentTokens: config.historyKeepRecentTokens },
|
|
1507
|
+
...config.historyMinReclaimTokens === void 0 ? {} : { historyMinReclaimTokens: config.historyMinReclaimTokens },
|
|
1508
|
+
...config.autoCompactThresholdPercent === void 0 ? {} : { autoCompactThresholdPercent: config.autoCompactThresholdPercent }
|
|
1509
|
+
};
|
|
1510
|
+
if (!isCompressionProfile(resolved.profile)) throw new Error(`ToolResultPruneConfig: unsupported profile "${String(resolved.profile)}"`);
|
|
1511
|
+
assertNonNegativeInteger("headChars", resolved.headChars);
|
|
1512
|
+
assertNonNegativeInteger("tailChars", resolved.tailChars);
|
|
1513
|
+
for (const key of [
|
|
1514
|
+
"nativeTriggerTokens",
|
|
1515
|
+
"nativeTargetTokens",
|
|
1516
|
+
"freshTriggerTokens",
|
|
1517
|
+
"freshTargetTokens",
|
|
1518
|
+
"aggregateTriggerTokens",
|
|
1519
|
+
"aggregateTargetTokens",
|
|
1520
|
+
"historyTriggerTokens",
|
|
1521
|
+
"historyMinReclaimTokens"
|
|
1522
|
+
]) {
|
|
1523
|
+
const value = resolved[key];
|
|
1524
|
+
if (value !== void 0) assertPositiveInteger(key, value);
|
|
1525
|
+
}
|
|
1526
|
+
if (resolved.historyKeepRecentToolCalls !== void 0) assertNonNegativeInteger("historyKeepRecentToolCalls", resolved.historyKeepRecentToolCalls);
|
|
1527
|
+
if (resolved.historyKeepRecentTokens !== void 0) assertNonNegativeInteger("historyKeepRecentTokens", resolved.historyKeepRecentTokens);
|
|
1528
|
+
if (resolved.autoCompactThresholdPercent !== void 0 && !isValidAutoCompactThresholdPercent(resolved.autoCompactThresholdPercent)) throw new Error(`ToolResultPruneConfig: autoCompactThresholdPercent (${String(resolved.autoCompactThresholdPercent)}) must be an integer between ${String(AUTO_COMPACT_THRESHOLD_LIMITS.min)} and ${String(AUTO_COMPACT_THRESHOLD_LIMITS.max)}`);
|
|
1529
|
+
assertTargetBelowTrigger("native", resolved.nativeTargetTokens, resolved.nativeTriggerTokens);
|
|
1530
|
+
assertTargetBelowTrigger("fresh", resolved.freshTargetTokens, resolved.freshTriggerTokens);
|
|
1531
|
+
assertTargetBelowTrigger("aggregate", resolved.aggregateTargetTokens, resolved.aggregateTriggerTokens);
|
|
1532
|
+
return deepFreeze(structuredClone(resolved));
|
|
1533
|
+
}
|
|
1534
|
+
/** Per-profile History linkage ratios applied to the Auto Compact watermark. */
|
|
1535
|
+
const AUTO_COMPACT_HISTORY_RATIOS = Object.freeze({
|
|
1536
|
+
balanced: Object.freeze({
|
|
1537
|
+
trigger: .625,
|
|
1538
|
+
minReclaim: .12,
|
|
1539
|
+
keepRecentTokens: .08
|
|
1540
|
+
}),
|
|
1541
|
+
savings: Object.freeze({
|
|
1542
|
+
trigger: .5,
|
|
1543
|
+
minReclaim: .16,
|
|
1544
|
+
keepRecentTokens: .08
|
|
1545
|
+
}),
|
|
1546
|
+
"cache-strict": Object.freeze({
|
|
1547
|
+
trigger: .75,
|
|
1548
|
+
minReclaim: .16,
|
|
1549
|
+
keepRecentTokens: .08
|
|
1550
|
+
}),
|
|
1551
|
+
adaptive: Object.freeze({
|
|
1552
|
+
trigger: .625,
|
|
1553
|
+
minReclaim: .12,
|
|
1554
|
+
keepRecentTokens: .08
|
|
1555
|
+
})
|
|
1556
|
+
});
|
|
1557
|
+
/** Micro-compact last-chance ratio: `D = floor(A × 0.875)`. */
|
|
1558
|
+
const MICRO_DEADLINE_RATIO = .875;
|
|
1559
|
+
/**
|
|
1560
|
+
* Resolve the Auto-Compact-linked History watermarks for one standard profile.
|
|
1561
|
+
*
|
|
1562
|
+
* `A = floor(C × a)` is the Auto Compact token watermark for the routed
|
|
1563
|
+
* context window `C` and the user threshold `a = p / 100`; the History
|
|
1564
|
+
* trigger, minimum reclaim, and recent-token tail scale with `A`, and the
|
|
1565
|
+
* micro-compact last-chance deadline is `D = floor(A × 0.875)`. At the shipped
|
|
1566
|
+
* defaults (`C = 1,000,000`, `p = 80`) the ratios reproduce the previous fixed
|
|
1567
|
+
* preset numbers exactly. Custom stays manual and Off/Native run no History,
|
|
1568
|
+
* so none of them link.
|
|
1569
|
+
*/
|
|
1570
|
+
function resolveAutoCompactLinkage(profile, options) {
|
|
1571
|
+
const ratios = profile === "custom" ? void 0 : AUTO_COMPACT_HISTORY_RATIOS[profile];
|
|
1572
|
+
const contextWindow = options.contextWindowTokens;
|
|
1573
|
+
const threshold = options.autoCompactThresholdPercent;
|
|
1574
|
+
if (ratios === void 0) return void 0;
|
|
1575
|
+
if (!isValidAutoCompactThresholdPercent(threshold)) return void 0;
|
|
1576
|
+
if (!Number.isSafeInteger(contextWindow) || contextWindow === void 0 || contextWindow <= 0) return void 0;
|
|
1577
|
+
const autoCompactTokens = Math.floor(contextWindow * (threshold / 100));
|
|
1578
|
+
if (!Number.isSafeInteger(autoCompactTokens) || autoCompactTokens <= 0) return void 0;
|
|
1579
|
+
const linked = {
|
|
1580
|
+
autoCompactTokens,
|
|
1581
|
+
microDeadlineTokens: Math.floor(autoCompactTokens * MICRO_DEADLINE_RATIO),
|
|
1582
|
+
historyTriggerTokens: Math.floor(ratios.trigger * autoCompactTokens),
|
|
1583
|
+
historyMinReclaimTokens: Math.floor(ratios.minReclaim * autoCompactTokens),
|
|
1584
|
+
historyKeepRecentTokens: Math.floor(ratios.keepRecentTokens * autoCompactTokens)
|
|
1585
|
+
};
|
|
1586
|
+
for (const value of Object.values(linked)) if (!Number.isSafeInteger(value) || value <= 0) return void 0;
|
|
1587
|
+
return linked;
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Resolve one public profile into a complete mixed-strategy policy.
|
|
1591
|
+
* @param config - validated composition configuration.
|
|
1592
|
+
* @param profile - profile frozen for the target Session.
|
|
1593
|
+
* @param custom - versioned Custom document used only by the `custom` profile.
|
|
1594
|
+
* @param options - routed capacity and the frozen Auto Compact threshold used
|
|
1595
|
+
* to resolve context-percent Custom values and standard-profile linkage.
|
|
1596
|
+
* @returns the effective deterministic compression policy.
|
|
1597
|
+
*/
|
|
1598
|
+
function resolvePolicy(config, profile, custom = DEFAULT_CUSTOM_COMPRESSION_POLICY, options = {}) {
|
|
1599
|
+
if (profile === "custom") return resolveCustomPolicy(custom, options);
|
|
1600
|
+
const preset = {
|
|
1601
|
+
off: {
|
|
1602
|
+
nativeToolResultEnabled: false,
|
|
1603
|
+
freshEnabled: false,
|
|
1604
|
+
aggregateEnabled: false,
|
|
1605
|
+
historyMode: "disabled",
|
|
1606
|
+
nativeTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1607
|
+
nativeTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1608
|
+
freshTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1609
|
+
freshTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1610
|
+
aggregateTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1611
|
+
aggregateTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1612
|
+
historyTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1613
|
+
historyKeepRecentToolCalls: 10,
|
|
1614
|
+
historyKeepRecentTokens: 64e3,
|
|
1615
|
+
historyMinReclaimTokens: Number.MAX_SAFE_INTEGER
|
|
1616
|
+
},
|
|
1617
|
+
native: {
|
|
1618
|
+
nativeToolResultEnabled: true,
|
|
1619
|
+
freshEnabled: false,
|
|
1620
|
+
aggregateEnabled: false,
|
|
1621
|
+
historyMode: "disabled",
|
|
1622
|
+
nativeTriggerTokens: 4096,
|
|
1623
|
+
nativeTargetTokens: 2048,
|
|
1624
|
+
freshTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1625
|
+
freshTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1626
|
+
aggregateTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1627
|
+
aggregateTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1628
|
+
historyTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1629
|
+
historyKeepRecentToolCalls: 10,
|
|
1630
|
+
historyKeepRecentTokens: 64e3,
|
|
1631
|
+
historyMinReclaimTokens: Number.MAX_SAFE_INTEGER
|
|
1632
|
+
},
|
|
1633
|
+
balanced: {
|
|
1634
|
+
nativeToolResultEnabled: false,
|
|
1635
|
+
freshEnabled: true,
|
|
1636
|
+
aggregateEnabled: true,
|
|
1637
|
+
historyMode: "routine",
|
|
1638
|
+
nativeTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1639
|
+
nativeTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1640
|
+
freshTriggerTokens: 8192,
|
|
1641
|
+
freshTargetTokens: 3072,
|
|
1642
|
+
aggregateTriggerTokens: 32768,
|
|
1643
|
+
aggregateTargetTokens: 12288,
|
|
1644
|
+
historyTriggerTokens: 5e5,
|
|
1645
|
+
historyKeepRecentToolCalls: 10,
|
|
1646
|
+
historyKeepRecentTokens: 64e3,
|
|
1647
|
+
historyMinReclaimTokens: 96e3
|
|
1648
|
+
},
|
|
1649
|
+
"cache-strict": {
|
|
1650
|
+
nativeToolResultEnabled: false,
|
|
1651
|
+
freshEnabled: true,
|
|
1652
|
+
aggregateEnabled: true,
|
|
1653
|
+
historyMode: "capacity-pressure",
|
|
1654
|
+
nativeTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1655
|
+
nativeTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1656
|
+
freshTriggerTokens: 8192,
|
|
1657
|
+
freshTargetTokens: 3072,
|
|
1658
|
+
aggregateTriggerTokens: 32768,
|
|
1659
|
+
aggregateTargetTokens: 12288,
|
|
1660
|
+
historyTriggerTokens: 6e5,
|
|
1661
|
+
historyKeepRecentToolCalls: 10,
|
|
1662
|
+
historyKeepRecentTokens: 64e3,
|
|
1663
|
+
historyMinReclaimTokens: 128e3
|
|
1664
|
+
},
|
|
1665
|
+
savings: {
|
|
1666
|
+
nativeToolResultEnabled: false,
|
|
1667
|
+
freshEnabled: true,
|
|
1668
|
+
aggregateEnabled: true,
|
|
1669
|
+
historyMode: "routine",
|
|
1670
|
+
nativeTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1671
|
+
nativeTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1672
|
+
freshTriggerTokens: 4096,
|
|
1673
|
+
freshTargetTokens: 1536,
|
|
1674
|
+
aggregateTriggerTokens: 16384,
|
|
1675
|
+
aggregateTargetTokens: 4096,
|
|
1676
|
+
historyTriggerTokens: 4e5,
|
|
1677
|
+
historyKeepRecentToolCalls: 10,
|
|
1678
|
+
historyKeepRecentTokens: 64e3,
|
|
1679
|
+
historyMinReclaimTokens: 128e3
|
|
1680
|
+
},
|
|
1681
|
+
adaptive: {
|
|
1682
|
+
nativeToolResultEnabled: false,
|
|
1683
|
+
freshEnabled: true,
|
|
1684
|
+
aggregateEnabled: true,
|
|
1685
|
+
historyMode: "adaptive",
|
|
1686
|
+
nativeTriggerTokens: Number.MAX_SAFE_INTEGER,
|
|
1687
|
+
nativeTargetTokens: Number.MAX_SAFE_INTEGER,
|
|
1688
|
+
freshTriggerTokens: 8192,
|
|
1689
|
+
freshTargetTokens: 3072,
|
|
1690
|
+
aggregateTriggerTokens: 32768,
|
|
1691
|
+
aggregateTargetTokens: 12288,
|
|
1692
|
+
historyTriggerTokens: 5e5,
|
|
1693
|
+
historyKeepRecentToolCalls: 10,
|
|
1694
|
+
historyKeepRecentTokens: 64e3,
|
|
1695
|
+
historyMinReclaimTokens: 96e3
|
|
1696
|
+
}
|
|
1697
|
+
}[profile];
|
|
1698
|
+
const linkage = resolveAutoCompactLinkage(profile, options);
|
|
1699
|
+
const policy = {
|
|
1700
|
+
profile,
|
|
1701
|
+
...preset,
|
|
1702
|
+
nativeTriggerTokens: config.nativeTriggerTokens ?? preset.nativeTriggerTokens,
|
|
1703
|
+
nativeTargetTokens: config.nativeTargetTokens ?? preset.nativeTargetTokens,
|
|
1704
|
+
freshTriggerTokens: config.freshTriggerTokens ?? preset.freshTriggerTokens,
|
|
1705
|
+
freshTargetTokens: config.freshTargetTokens ?? preset.freshTargetTokens,
|
|
1706
|
+
aggregateTriggerTokens: config.aggregateTriggerTokens ?? preset.aggregateTriggerTokens,
|
|
1707
|
+
aggregateTargetTokens: config.aggregateTargetTokens ?? preset.aggregateTargetTokens,
|
|
1708
|
+
historyTriggerTokens: config.historyTriggerTokens ?? linkage?.historyTriggerTokens ?? preset.historyTriggerTokens,
|
|
1709
|
+
historyKeepRecentToolCalls: config.historyKeepRecentToolCalls ?? preset.historyKeepRecentToolCalls,
|
|
1710
|
+
historyKeepRecentTokens: config.historyKeepRecentTokens ?? linkage?.historyKeepRecentTokens ?? preset.historyKeepRecentTokens,
|
|
1711
|
+
historyMinReclaimTokens: config.historyMinReclaimTokens ?? linkage?.historyMinReclaimTokens ?? preset.historyMinReclaimTokens,
|
|
1712
|
+
...linkage === void 0 ? {} : {
|
|
1713
|
+
autoCompactTokens: linkage.autoCompactTokens,
|
|
1714
|
+
microDeadlineTokens: linkage.microDeadlineTokens
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
if (policy.nativeTargetTokens >= policy.nativeTriggerTokens && profile === "native") throw new Error("context compression policy: native target must be below trigger");
|
|
1718
|
+
if (policy.freshTargetTokens >= policy.freshTriggerTokens && policy.freshEnabled) throw new Error("context compression policy: fresh target must be below trigger");
|
|
1719
|
+
if (policy.aggregateTargetTokens >= policy.aggregateTriggerTokens && policy.freshEnabled) throw new Error("context compression policy: aggregate target must be below trigger");
|
|
1720
|
+
return deepFreeze(policy);
|
|
1721
|
+
}
|
|
1722
|
+
function assertTargetBelowTrigger(label, target, trigger) {
|
|
1723
|
+
if (target === void 0 !== (trigger === void 0)) throw new Error(`ToolResultPruneConfig: ${label} target and trigger tokens must be provided together`);
|
|
1724
|
+
if (target !== void 0 && trigger !== void 0 && target >= trigger) throw new Error(`ToolResultPruneConfig: ${label} target tokens must be below trigger tokens`);
|
|
1725
|
+
}
|
|
1726
|
+
function assertPositiveInteger(name, value) {
|
|
1727
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`ToolResultPruneConfig: ${name} (${String(value)}) must be a positive safe integer`);
|
|
1728
|
+
}
|
|
1729
|
+
function assertNonNegativeInteger(name, value) {
|
|
1730
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new Error(`ToolResultPruneConfig: ${name} (${String(value)}) must be a non-negative safe integer`);
|
|
1731
|
+
}
|
|
1732
|
+
//#endregion
|
|
1733
|
+
//#region src/reducers.ts
|
|
1734
|
+
/** Deterministic, evidence-backed reducers for fresh tool results. */
|
|
1735
|
+
const ANSI_PATTERN = /\u001B(?:\[[0-?]*[ -/]*[@-~]|\][^\u0007]*(?:\u0007|\u001B\\))/gu;
|
|
1736
|
+
const IMPORTANT_PATTERN = new RegExp([
|
|
1737
|
+
String.raw`\b(?:error|failed|failure|fatal|panic|exception|warning|warn|conflict|denied|forbidden|`,
|
|
1738
|
+
String.raw`timeout|timed out|not found|cannot|unable|invalid|exit(?:ed)?\s+(?:code|status)|traceback|`,
|
|
1739
|
+
String.raw`assert(?:ion)?|segmentation fault|oom|out of memory)\b`
|
|
1740
|
+
].join(""), "i");
|
|
1741
|
+
const STATUS_PATTERN = new RegExp([String.raw`\b(?:success|succeeded|passed|installed|added|removed|updated|built|compiled|`, String.raw`tests?\s+(?:passed|failed)|exit(?:ed)?\s+(?:code|status))\b`].join(""), "i");
|
|
1742
|
+
const PATH_LINE_PATTERN = /^(.*?):(\d+)(?::\d+)?(?::|\s+-\s+)(.*)$/;
|
|
1743
|
+
const GIT_STATUS_PATTERN = new RegExp([String.raw`^(?:On branch|Your branch|HEAD detached|Changes |Untracked |Unmerged |\s*(?:modified|deleted|`, String.raw`new file|renamed|both modified):)`].join(""), "i");
|
|
1744
|
+
/**
|
|
1745
|
+
* Select a reducer from verified tool, command, and content evidence.
|
|
1746
|
+
* @param input - original result text, recovery source, and output budget.
|
|
1747
|
+
* @returns a verified candidate, or `null` when every reducer fails open.
|
|
1748
|
+
*/
|
|
1749
|
+
function reduceFreshToolResult(input) {
|
|
1750
|
+
const normalized = normalizeTerminalText(input.text);
|
|
1751
|
+
const prepared = {
|
|
1752
|
+
...input,
|
|
1753
|
+
text: normalized
|
|
1754
|
+
};
|
|
1755
|
+
const command = extractCommand(input.argumentsText);
|
|
1756
|
+
const name = input.toolName.toLowerCase();
|
|
1757
|
+
const candidates = [];
|
|
1758
|
+
if (looksLikeJson(normalized)) candidates.push(() => reduceJson(prepared));
|
|
1759
|
+
if (isSearchTool(name, command)) candidates.push(() => reduceSearch(prepared));
|
|
1760
|
+
if (isReadTool(name)) candidates.push(() => reduceHead(prepared, "pi-head"));
|
|
1761
|
+
if (isGitCommand(name, command)) candidates.push(() => reduceGit(prepared, command));
|
|
1762
|
+
if (isPackageCommand(command)) candidates.push(() => reducePatternLog(prepared, "hypa-package", packagePattern()));
|
|
1763
|
+
if (isBuildOrTestCommand(command)) candidates.push(() => reducePatternLog(prepared, "hypa-build-test", buildPattern()));
|
|
1764
|
+
if (isShellTool(name) || command !== "") candidates.push(() => reduceShell(prepared));
|
|
1765
|
+
candidates.push(() => reduceSalient(prepared, "generic-salience"));
|
|
1766
|
+
for (const make of candidates) {
|
|
1767
|
+
const candidate = make();
|
|
1768
|
+
if (candidate !== null && verifyReduction(input, candidate)) return candidate;
|
|
1769
|
+
}
|
|
1770
|
+
return null;
|
|
1771
|
+
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Build a recoverable placeholder for an old tool result.
|
|
1774
|
+
* @param input - tool identity, source reference, size, status, and retained evidence.
|
|
1775
|
+
* @returns a lossy placeholder that cites the immutable source event.
|
|
1776
|
+
*/
|
|
1777
|
+
function historicalPlaceholder(input) {
|
|
1778
|
+
const anchor = input.compact ? "" : importantAnchor(input.text, 360);
|
|
1779
|
+
const lines = [
|
|
1780
|
+
"[Old tool result content cleared from active context]",
|
|
1781
|
+
`tool: ${input.toolName || "unknown"}`,
|
|
1782
|
+
`status: ${input.isError ? "error" : "completed"}`,
|
|
1783
|
+
`original_chars: ${String(input.charsBefore)}`,
|
|
1784
|
+
`source: ${input.sourceRef}`,
|
|
1785
|
+
"retrieve: context_compression_retrieve({\"ref\":\"" + input.sourceRef + "\"})"
|
|
1786
|
+
];
|
|
1787
|
+
if (anchor !== "") lines.push(`retained_anchor: ${anchor}`);
|
|
1788
|
+
return {
|
|
1789
|
+
text: lines.join("\n"),
|
|
1790
|
+
reducer: input.compact ? "pair-preserving-tail-aging" : "historical-tool-result-aging",
|
|
1791
|
+
lossy: true
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Validate shrinkage, budget, recovery, and error retention.
|
|
1796
|
+
* @param input - original reducer input and its safety requirements.
|
|
1797
|
+
* @param output - candidate reduced text and reducer metadata.
|
|
1798
|
+
* @returns whether the candidate is safe to land.
|
|
1799
|
+
*/
|
|
1800
|
+
function verifyReduction(input, output) {
|
|
1801
|
+
const before = codePointLength(input.text);
|
|
1802
|
+
const after = codePointLength(output.text);
|
|
1803
|
+
if (after <= 0 || after >= before || after > input.budgetChars) return false;
|
|
1804
|
+
if (output.lossy && !output.text.includes(input.sourceRef)) return false;
|
|
1805
|
+
if ((input.isError || IMPORTANT_PATTERN.test(input.text)) && !IMPORTANT_PATTERN.test(output.text) && !output.text.includes("status: error")) return false;
|
|
1806
|
+
return true;
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Strip ANSI, collapse carriage-return progress redraws, and fold exact repeats.
|
|
1810
|
+
* @param text - raw terminal output.
|
|
1811
|
+
* @returns normalized terminal text.
|
|
1812
|
+
*/
|
|
1813
|
+
function normalizeTerminalText(text) {
|
|
1814
|
+
const logical = text.replace(ANSI_PATTERN, "").split("\n").map((line) => {
|
|
1815
|
+
return line.split("\r").filter((part) => part !== "").at(-1) ?? "";
|
|
1816
|
+
});
|
|
1817
|
+
const folded = [];
|
|
1818
|
+
let previous;
|
|
1819
|
+
let count = 0;
|
|
1820
|
+
const flush = () => {
|
|
1821
|
+
if (previous === void 0) return;
|
|
1822
|
+
folded.push(previous);
|
|
1823
|
+
if (count > 1) folded.push(`[previous line repeated ${String(count - 1)} more times]`);
|
|
1824
|
+
};
|
|
1825
|
+
for (const line of logical) {
|
|
1826
|
+
if (line === previous) {
|
|
1827
|
+
count++;
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
flush();
|
|
1831
|
+
previous = line;
|
|
1832
|
+
count = 1;
|
|
1833
|
+
}
|
|
1834
|
+
flush();
|
|
1835
|
+
return folded.join("\n");
|
|
1836
|
+
}
|
|
1837
|
+
function reduceHead(input, reducer) {
|
|
1838
|
+
const marker = omissionMarker(input, reducer);
|
|
1839
|
+
const available = input.budgetChars - codePointLength(marker) - 1;
|
|
1840
|
+
if (available <= 0) return null;
|
|
1841
|
+
const head = takeWholeLinesFromHead(input.text, available);
|
|
1842
|
+
if (head === input.text || head === "") return null;
|
|
1843
|
+
return {
|
|
1844
|
+
text: `${head}\n${marker}`,
|
|
1845
|
+
reducer,
|
|
1846
|
+
lossy: true
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
function reduceTail(input, reducer) {
|
|
1850
|
+
const marker = omissionMarker(input, reducer);
|
|
1851
|
+
const available = input.budgetChars - codePointLength(marker) - 1;
|
|
1852
|
+
if (available <= 0) return null;
|
|
1853
|
+
const tail = takeWholeLinesFromTail(input.text, available);
|
|
1854
|
+
if (tail === input.text || tail === "") return null;
|
|
1855
|
+
return {
|
|
1856
|
+
text: `${marker}\n${tail}`,
|
|
1857
|
+
reducer,
|
|
1858
|
+
lossy: true
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
function reduceJson(input) {
|
|
1862
|
+
let value;
|
|
1863
|
+
try {
|
|
1864
|
+
value = JSON.parse(input.text);
|
|
1865
|
+
} catch {
|
|
1866
|
+
return null;
|
|
1867
|
+
}
|
|
1868
|
+
const minified = JSON.stringify(value);
|
|
1869
|
+
if (codePointLength(minified) < codePointLength(input.text) && codePointLength(minified) <= input.budgetChars) return {
|
|
1870
|
+
text: minified,
|
|
1871
|
+
reducer: "json-minify",
|
|
1872
|
+
lossy: false
|
|
1873
|
+
};
|
|
1874
|
+
const envelope = {
|
|
1875
|
+
$dsh_compression: {
|
|
1876
|
+
kind: "json-preview",
|
|
1877
|
+
source: input.sourceRef,
|
|
1878
|
+
original_chars: codePointLength(input.text)
|
|
1879
|
+
},
|
|
1880
|
+
value: shrinkJson(value, 0)
|
|
1881
|
+
};
|
|
1882
|
+
const text = JSON.stringify(envelope, null, 2);
|
|
1883
|
+
if (codePointLength(text) <= input.budgetChars) return {
|
|
1884
|
+
text,
|
|
1885
|
+
reducer: "json-structure-preview",
|
|
1886
|
+
lossy: true
|
|
1887
|
+
};
|
|
1888
|
+
return null;
|
|
1889
|
+
}
|
|
1890
|
+
function shrinkJson(value, depth) {
|
|
1891
|
+
if (depth >= 5) {
|
|
1892
|
+
if (Array.isArray(value)) return `[array length=${String(value.length)} omitted]`;
|
|
1893
|
+
if (typeof value === "object" && value !== null) return "[object omitted]";
|
|
1894
|
+
return value;
|
|
1895
|
+
}
|
|
1896
|
+
if (Array.isArray(value)) {
|
|
1897
|
+
if (value.length <= 8) return value.map((entry) => shrinkJson(entry, depth + 1));
|
|
1898
|
+
return [
|
|
1899
|
+
...value.slice(0, 3).map((entry) => shrinkJson(entry, depth + 1)),
|
|
1900
|
+
{ $dsh_omitted_items: value.length - 5 },
|
|
1901
|
+
...value.slice(-2).map((entry) => shrinkJson(entry, depth + 1))
|
|
1902
|
+
];
|
|
1903
|
+
}
|
|
1904
|
+
if (typeof value !== "object" || value === null) {
|
|
1905
|
+
if (typeof value === "string" && codePointLength(value) > 800) return `${Array.from(value).slice(0, 500).join("")}…[${String(codePointLength(value) - 700)} chars omitted]…${Array.from(value).slice(-200).join("")}`;
|
|
1906
|
+
return value;
|
|
1907
|
+
}
|
|
1908
|
+
const entries = Object.entries(value);
|
|
1909
|
+
const important = entries.filter(([key]) => /error|warn|status|code|message|path|file|line|summary/i.test(key));
|
|
1910
|
+
const selected = entries.length <= 18 ? entries : [
|
|
1911
|
+
...entries.slice(0, 10),
|
|
1912
|
+
...important.filter((entry) => !entries.slice(0, 10).includes(entry)).slice(0, 6),
|
|
1913
|
+
...entries.slice(-2)
|
|
1914
|
+
];
|
|
1915
|
+
const result = {};
|
|
1916
|
+
for (const [key, entry] of selected) result[key] = shrinkJson(entry, depth + 1);
|
|
1917
|
+
if (selected.length < entries.length) result.$dsh_omitted_keys = entries.length - selected.length;
|
|
1918
|
+
return result;
|
|
1919
|
+
}
|
|
1920
|
+
function reduceSearch(input) {
|
|
1921
|
+
const lines = splitLines(input.text);
|
|
1922
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1923
|
+
const ungrouped = [];
|
|
1924
|
+
for (const line of lines) {
|
|
1925
|
+
const match = PATH_LINE_PATTERN.exec(line);
|
|
1926
|
+
const row = {
|
|
1927
|
+
line,
|
|
1928
|
+
important: IMPORTANT_PATTERN.test(line)
|
|
1929
|
+
};
|
|
1930
|
+
if (match === null) {
|
|
1931
|
+
ungrouped.push(row);
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
const path = match[1] ?? "<unknown>";
|
|
1935
|
+
const bucket = groups.get(path) ?? [];
|
|
1936
|
+
bucket.push(row);
|
|
1937
|
+
groups.set(path, bucket);
|
|
1938
|
+
}
|
|
1939
|
+
if (groups.size === 0) return reduceSalient(input, "search-salience");
|
|
1940
|
+
const selected = [];
|
|
1941
|
+
let omitted = 0;
|
|
1942
|
+
for (const [path, rows] of groups) {
|
|
1943
|
+
const keep = /* @__PURE__ */ new Set([0, rows.length - 1]);
|
|
1944
|
+
rows.forEach((row, index) => {
|
|
1945
|
+
if (row.important) keep.add(index);
|
|
1946
|
+
});
|
|
1947
|
+
for (let index = 0; index < rows.length && keep.size < 5; index++) keep.add(index);
|
|
1948
|
+
const indexes = [...keep].filter((index) => index >= 0).sort((a, b) => a - b);
|
|
1949
|
+
selected.push(`## ${path} (${String(rows.length)} matches)`);
|
|
1950
|
+
for (const index of indexes) {
|
|
1951
|
+
const row = rows[index];
|
|
1952
|
+
if (row !== void 0) selected.push(row.line);
|
|
1953
|
+
}
|
|
1954
|
+
omitted += rows.length - indexes.length;
|
|
1955
|
+
}
|
|
1956
|
+
for (const row of ungrouped.filter((row) => row.important).slice(0, 12)) selected.push(row.line);
|
|
1957
|
+
const text = fitLines([`[search results compressed; ${String(omitted)} matches omitted; source: ${input.sourceRef}]`, ...selected], input.budgetChars, input.sourceRef);
|
|
1958
|
+
return text === null ? null : {
|
|
1959
|
+
text,
|
|
1960
|
+
reducer: "search-by-file",
|
|
1961
|
+
lossy: true
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
function reduceGit(input, command) {
|
|
1965
|
+
const lines = splitLines(input.text);
|
|
1966
|
+
const lower = command.toLowerCase();
|
|
1967
|
+
let keep;
|
|
1968
|
+
let reducer;
|
|
1969
|
+
if (/\bgit\s+(?:diff|show)\b/.test(lower)) {
|
|
1970
|
+
reducer = "hypa-git-diff";
|
|
1971
|
+
keep = lines.filter((line) => /^(?:diff --git|index |--- |\+\+\+ |@@ |[+-](?![+-]))/.test(line) || IMPORTANT_PATTERN.test(line));
|
|
1972
|
+
} else if (/\bgit\s+(?:status|switch|checkout|merge|rebase|cherry-pick)\b/.test(lower)) {
|
|
1973
|
+
reducer = "hypa-git-status";
|
|
1974
|
+
keep = lines.filter((line) => GIT_STATUS_PATTERN.test(line) || IMPORTANT_PATTERN.test(line));
|
|
1975
|
+
} else {
|
|
1976
|
+
reducer = "hypa-git-log";
|
|
1977
|
+
keep = lines.filter((line) => /^(?:commit\s+[0-9a-f]+|Author:|Date:|[0-9a-f]{7,}\s)/i.test(line) || IMPORTANT_PATTERN.test(line));
|
|
1978
|
+
}
|
|
1979
|
+
if (keep.length === 0) return reduceSalient(input, reducer);
|
|
1980
|
+
const text = fitLines([
|
|
1981
|
+
`[git output compressed; source: ${input.sourceRef}]`,
|
|
1982
|
+
...keep,
|
|
1983
|
+
...lines.slice(-8)
|
|
1984
|
+
], input.budgetChars, input.sourceRef);
|
|
1985
|
+
return text === null ? null : {
|
|
1986
|
+
text,
|
|
1987
|
+
reducer,
|
|
1988
|
+
lossy: true
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
function reducePatternLog(input, reducer, pattern) {
|
|
1992
|
+
const lines = splitLines(input.text);
|
|
1993
|
+
const important = lines.filter((line) => pattern.test(line) || IMPORTANT_PATTERN.test(line) || STATUS_PATTERN.test(line));
|
|
1994
|
+
const text = fitLines([
|
|
1995
|
+
`[command output compressed by ${reducer}; source: ${input.sourceRef}]`,
|
|
1996
|
+
...important,
|
|
1997
|
+
...lines.slice(-20)
|
|
1998
|
+
], input.budgetChars, input.sourceRef);
|
|
1999
|
+
return text === null ? null : {
|
|
2000
|
+
text,
|
|
2001
|
+
reducer,
|
|
2002
|
+
lossy: true
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
function reduceShell(input) {
|
|
2006
|
+
const lines = splitLines(input.text);
|
|
2007
|
+
const important = lines.filter((line) => IMPORTANT_PATTERN.test(line));
|
|
2008
|
+
if (important.length === 0) return reduceTail(input, "pi-tail");
|
|
2009
|
+
const text = fitLines([
|
|
2010
|
+
`[shell/log output compressed; source: ${input.sourceRef}]`,
|
|
2011
|
+
...important,
|
|
2012
|
+
"--- final output ---",
|
|
2013
|
+
...lines.slice(-40)
|
|
2014
|
+
], input.budgetChars, input.sourceRef);
|
|
2015
|
+
return text === null ? null : {
|
|
2016
|
+
text,
|
|
2017
|
+
reducer: "shell-salience-tail",
|
|
2018
|
+
lossy: true
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
function reduceSalient(input, reducer) {
|
|
2022
|
+
const lines = splitLines(input.text);
|
|
2023
|
+
if (lines.length < 3) return reduceHead(input, reducer);
|
|
2024
|
+
const marker = omissionMarker(input, reducer);
|
|
2025
|
+
const headBudget = Math.max(1, Math.floor((input.budgetChars - codePointLength(marker)) * .34));
|
|
2026
|
+
const tailBudget = headBudget;
|
|
2027
|
+
const head = takeWholeLinesFromHead(input.text, headBudget);
|
|
2028
|
+
const tail = takeWholeLinesFromTail(input.text, tailBudget);
|
|
2029
|
+
const text = fitLines([
|
|
2030
|
+
head,
|
|
2031
|
+
...lines.filter((line) => IMPORTANT_PATTERN.test(line) || STATUS_PATTERN.test(line)).slice(0, 24),
|
|
2032
|
+
marker,
|
|
2033
|
+
tail
|
|
2034
|
+
], input.budgetChars, input.sourceRef);
|
|
2035
|
+
return text === null ? null : {
|
|
2036
|
+
text,
|
|
2037
|
+
reducer,
|
|
2038
|
+
lossy: true
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
function omissionMarker(input, reducer) {
|
|
2042
|
+
return `[... ${reducer} omitted content; original_chars=${String(codePointLength(input.text))}; source=${input.sourceRef}; retrieve with context_compression_retrieve ...]`;
|
|
2043
|
+
}
|
|
2044
|
+
function importantAnchor(text, maxChars) {
|
|
2045
|
+
const lines = splitLines(normalizeTerminalText(text));
|
|
2046
|
+
const chosen = lines.find((line) => IMPORTANT_PATTERN.test(line)) ?? lines.at(-1) ?? "";
|
|
2047
|
+
return Array.from(chosen.trim()).slice(0, maxChars).join("");
|
|
2048
|
+
}
|
|
2049
|
+
function fitLines(lines, budgetChars, requiredRef) {
|
|
2050
|
+
const unique = [];
|
|
2051
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2052
|
+
for (const line of lines) {
|
|
2053
|
+
if (line === "" || seen.has(line)) continue;
|
|
2054
|
+
seen.add(line);
|
|
2055
|
+
unique.push(line);
|
|
2056
|
+
}
|
|
2057
|
+
const output = [];
|
|
2058
|
+
let used = 0;
|
|
2059
|
+
for (const line of unique) {
|
|
2060
|
+
const cost = codePointLength(line) + (output.length === 0 ? 0 : 1);
|
|
2061
|
+
if (used + cost > budgetChars) continue;
|
|
2062
|
+
output.push(line);
|
|
2063
|
+
used += cost;
|
|
2064
|
+
}
|
|
2065
|
+
const text = output.join("\n");
|
|
2066
|
+
return text.includes(requiredRef) ? text : null;
|
|
2067
|
+
}
|
|
2068
|
+
function takeWholeLinesFromHead(text, budgetChars) {
|
|
2069
|
+
const output = [];
|
|
2070
|
+
let used = 0;
|
|
2071
|
+
for (const line of splitLines(text)) {
|
|
2072
|
+
const cost = codePointLength(line) + (output.length === 0 ? 0 : 1);
|
|
2073
|
+
if (used + cost > budgetChars) break;
|
|
2074
|
+
output.push(line);
|
|
2075
|
+
used += cost;
|
|
2076
|
+
}
|
|
2077
|
+
if (output.length === 0) return Array.from(text).slice(0, budgetChars).join("");
|
|
2078
|
+
return output.join("\n");
|
|
2079
|
+
}
|
|
2080
|
+
function takeWholeLinesFromTail(text, budgetChars) {
|
|
2081
|
+
const lines = splitLines(text);
|
|
2082
|
+
const output = [];
|
|
2083
|
+
let used = 0;
|
|
2084
|
+
for (let index = lines.length - 1; index >= 0; index--) {
|
|
2085
|
+
const line = lines[index];
|
|
2086
|
+
if (line === void 0) continue;
|
|
2087
|
+
const cost = codePointLength(line) + (output.length === 0 ? 0 : 1);
|
|
2088
|
+
if (used + cost > budgetChars) break;
|
|
2089
|
+
output.unshift(line);
|
|
2090
|
+
used += cost;
|
|
2091
|
+
}
|
|
2092
|
+
if (output.length === 0) return Array.from(text).slice(-budgetChars).join("");
|
|
2093
|
+
return output.join("\n");
|
|
2094
|
+
}
|
|
2095
|
+
function splitLines(text) {
|
|
2096
|
+
const lines = text.split("\n");
|
|
2097
|
+
if (text.endsWith("\n")) lines.pop();
|
|
2098
|
+
return lines;
|
|
2099
|
+
}
|
|
2100
|
+
function extractCommand(argumentsText) {
|
|
2101
|
+
try {
|
|
2102
|
+
const parsed = JSON.parse(argumentsText);
|
|
2103
|
+
if (typeof parsed !== "object" || parsed === null) return "";
|
|
2104
|
+
const record = parsed;
|
|
2105
|
+
for (const key of [
|
|
2106
|
+
"command",
|
|
2107
|
+
"cmd",
|
|
2108
|
+
"script",
|
|
2109
|
+
"input"
|
|
2110
|
+
]) {
|
|
2111
|
+
const value = record[key];
|
|
2112
|
+
if (typeof value === "string") return value;
|
|
2113
|
+
}
|
|
2114
|
+
} catch {
|
|
2115
|
+
return "";
|
|
2116
|
+
}
|
|
2117
|
+
return "";
|
|
2118
|
+
}
|
|
2119
|
+
function looksLikeJson(text) {
|
|
2120
|
+
const trimmed = text.trim();
|
|
2121
|
+
return trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
2122
|
+
}
|
|
2123
|
+
function isReadTool(name) {
|
|
2124
|
+
return /(?:^|[-_/])(?:read|cat|view|open_file)(?:$|[-_/])/.test(name);
|
|
2125
|
+
}
|
|
2126
|
+
function isSearchTool(name, command) {
|
|
2127
|
+
return /(?:grep|search|glob|find|ripgrep|rg)/.test(name) || /(?:^|\s)(?:rg|grep|find|fd)\s/.test(command);
|
|
2128
|
+
}
|
|
2129
|
+
function isShellTool(name) {
|
|
2130
|
+
return /(?:bash|shell|terminal|powershell|pwsh|exec|command)/.test(name);
|
|
2131
|
+
}
|
|
2132
|
+
function isGitCommand(name, command) {
|
|
2133
|
+
return name.includes("git") || /(?:^|\s)git\s/.test(command);
|
|
2134
|
+
}
|
|
2135
|
+
function isPackageCommand(command) {
|
|
2136
|
+
return /(?:^|\s)(?:npm|pnpm|yarn|bun|pip|pip3|uv|poetry)\s/.test(command);
|
|
2137
|
+
}
|
|
2138
|
+
function isBuildOrTestCommand(command) {
|
|
2139
|
+
return new RegExp([String.raw`(?:^|\s)(?:tsc|dotnet\s+(?:build|test)|pytest|cargo\s+(?:build|test|check)|go\s+test|mvn\s+test|`, String.raw`gradle|npm\s+(?:test|run\s+build)|pnpm\s+(?:test|build|lint)|yarn\s+(?:test|build|lint))\b`].join("")).test(command);
|
|
2140
|
+
}
|
|
2141
|
+
function packagePattern() {
|
|
2142
|
+
return new RegExp([String.raw`(?:ERR!|WARN|warning|error|failed|conflict|peer dep|added\s+\d+|removed\s+\d+|installed|success|`, String.raw`up to date|packages?\s+(?:added|removed|changed)|resolution|No matching distribution|Could not find a version)`].join(""), "i");
|
|
2143
|
+
}
|
|
2144
|
+
function buildPattern() {
|
|
2145
|
+
return new RegExp([
|
|
2146
|
+
String.raw`(?:error\s+TS\d+|warning\s+TS\d+|FAILED|FAIL\b|AssertionError|expected|actual|`,
|
|
2147
|
+
String.raw`tests?\s+(?:run|passed|failed|skipped)|Build\s+(?:succeeded|FAILED)|\d+\s+Error\(s\)|`,
|
|
2148
|
+
String.raw`\d+\s+Warning\(s\)|Finished\s+test|compilation failed)`
|
|
2149
|
+
].join(""), "i");
|
|
2150
|
+
}
|
|
2151
|
+
//#endregion
|
|
2152
|
+
//#region src/deepseek-official-pricing.ts
|
|
2153
|
+
/** Checked-in DeepSeek official prices and fixed-point provider-usage accounting. */
|
|
2154
|
+
const DEEPSEEK_OFFICIAL_PRICE_CATALOG_VERSION = "deepseek-official-2026-09-23";
|
|
2155
|
+
/**
|
|
2156
|
+
* Wall-clock time at which the checked-in official price pages were verified.
|
|
2157
|
+
* This timestamp covers EVERY bundled row: a partial re-verification must not
|
|
2158
|
+
* leave some rows silently reported as fresher than they are.
|
|
2159
|
+
*/
|
|
2160
|
+
const DEEPSEEK_OFFICIAL_PRICE_CHECKED_AT = "2026-09-23T01:48:36+08:00";
|
|
2161
|
+
/**
|
|
2162
|
+
* The single V4.1-Flash price tuple.
|
|
2163
|
+
*
|
|
2164
|
+
* `deepseek-flash`, `deepseek-v4-flash`, and `deepseek-v4-flash-vision-exp` are
|
|
2165
|
+
* all served by DeepSeek-V4.1-Flash, so all three share this one object rather
|
|
2166
|
+
* than repeating the strings: they cannot drift apart by construction.
|
|
2167
|
+
*/
|
|
2168
|
+
const V41_FLASH_PRICES = modelPrices("DeepSeek-V4.1-Flash", [
|
|
2169
|
+
"0.003",
|
|
2170
|
+
"0.15",
|
|
2171
|
+
"0.6"
|
|
2172
|
+
], [
|
|
2173
|
+
"0.006",
|
|
2174
|
+
"0.3",
|
|
2175
|
+
"1.2"
|
|
2176
|
+
], [
|
|
2177
|
+
"0.02",
|
|
2178
|
+
"1",
|
|
2179
|
+
"4"
|
|
2180
|
+
], [
|
|
2181
|
+
"0.04",
|
|
2182
|
+
"2",
|
|
2183
|
+
"8"
|
|
2184
|
+
]);
|
|
2185
|
+
const PRICES = Object.freeze({
|
|
2186
|
+
"deepseek-flash": V41_FLASH_PRICES,
|
|
2187
|
+
"deepseek-v4-flash": V41_FLASH_PRICES,
|
|
2188
|
+
"deepseek-v4-pro": modelPrices("DeepSeek-V4-Pro-0813", [
|
|
2189
|
+
"0.022",
|
|
2190
|
+
"0.66",
|
|
2191
|
+
"1.98"
|
|
2192
|
+
], [
|
|
2193
|
+
"0.044",
|
|
2194
|
+
"1.32",
|
|
2195
|
+
"3.96"
|
|
2196
|
+
], [
|
|
2197
|
+
"0.15",
|
|
2198
|
+
"4.5",
|
|
2199
|
+
"13.5"
|
|
2200
|
+
], [
|
|
2201
|
+
"0.30",
|
|
2202
|
+
"9.0",
|
|
2203
|
+
"27.0"
|
|
2204
|
+
]),
|
|
2205
|
+
"deepseek-v4-flash-vision-exp": V41_FLASH_PRICES
|
|
2206
|
+
});
|
|
2207
|
+
const PEAK_RULE = "Asia/Shanghai Mon-Fri 09:00-12:00,14:00-18:00";
|
|
2208
|
+
const USD_SOURCE = "https://api-docs.deepseek.com/quick_start/pricing/";
|
|
2209
|
+
const CNY_SOURCE = "https://api-docs.deepseek.com/zh-cn/quick_start/pricing/";
|
|
2210
|
+
/**
|
|
2211
|
+
* Resolve one immutable official price record; aliases and compatible gateways fail closed.
|
|
2212
|
+
*
|
|
2213
|
+
* Every DeepSeek-billed provider route (`deepseek`, `deepseek-official`, and the
|
|
2214
|
+
* signed-in `deepseek-account` route) is priced on the same published page, so
|
|
2215
|
+
* the record reports the provider and base-url class it actually resolved
|
|
2216
|
+
* instead of overwriting them with the API-key names.
|
|
2217
|
+
* @param input - exact provider, endpoint, route, model, currency, and timestamp applicability.
|
|
2218
|
+
* @returns An immutable price record or an explicit unpriced reason.
|
|
2219
|
+
*/
|
|
2220
|
+
function resolveOfficialDeepSeekPrice(input) {
|
|
2221
|
+
if (!isDeepSeekBilledProvider(input.provider)) return unpriced("unknown provider route");
|
|
2222
|
+
if (!isDeepSeekOfficialBaseUrlClass(input.baseUrlClass)) return unpriced("unknown base-url applicability");
|
|
2223
|
+
if (input.apiRoute !== "chat-completions" && input.apiRoute !== "responses") return unpriced("unknown API route");
|
|
2224
|
+
if (!isOfficialModel(input.modelId)) return unpriced("unknown model id");
|
|
2225
|
+
if (input.currency !== "USD" && input.currency !== "CNY") return unpriced("unknown currency");
|
|
2226
|
+
const band = priceBandAt(input.at);
|
|
2227
|
+
if (band === void 0) return unpriced("invalid price timestamp");
|
|
2228
|
+
const model = PRICES[input.modelId];
|
|
2229
|
+
const [inputCacheHit, inputCacheMiss, output] = model[input.currency][band];
|
|
2230
|
+
return {
|
|
2231
|
+
kind: "priced",
|
|
2232
|
+
record: Object.freeze({
|
|
2233
|
+
catalogVersion: DEEPSEEK_OFFICIAL_PRICE_CATALOG_VERSION,
|
|
2234
|
+
checkedAt: DEEPSEEK_OFFICIAL_PRICE_CHECKED_AT,
|
|
2235
|
+
provider: input.provider,
|
|
2236
|
+
baseUrlClass: input.baseUrlClass,
|
|
2237
|
+
apiRoute: input.apiRoute,
|
|
2238
|
+
modelId: input.modelId,
|
|
2239
|
+
modelVersion: model.version,
|
|
2240
|
+
currency: input.currency,
|
|
2241
|
+
unitTokens: 1e6,
|
|
2242
|
+
band,
|
|
2243
|
+
inputCacheHit,
|
|
2244
|
+
inputCacheMiss,
|
|
2245
|
+
output,
|
|
2246
|
+
sourceUrl: input.currency === "USD" ? USD_SOURCE : CNY_SOURCE,
|
|
2247
|
+
sourceLocale: input.currency === "USD" ? "en" : "zh-CN",
|
|
2248
|
+
peakRule: PEAK_RULE
|
|
2249
|
+
})
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* Classify a timestamp under the published Beijing peak schedule.
|
|
2254
|
+
* @param at - absolute request time to interpret in Asia/Shanghai.
|
|
2255
|
+
* @returns Peak/off-peak, or undefined for an invalid timestamp.
|
|
2256
|
+
*/
|
|
2257
|
+
function priceBandAt(at) {
|
|
2258
|
+
if (!Number.isFinite(at.getTime())) return void 0;
|
|
2259
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
2260
|
+
timeZone: "Asia/Shanghai",
|
|
2261
|
+
weekday: "short",
|
|
2262
|
+
hour: "2-digit",
|
|
2263
|
+
minute: "2-digit",
|
|
2264
|
+
second: "2-digit",
|
|
2265
|
+
hourCycle: "h23"
|
|
2266
|
+
}).formatToParts(at);
|
|
2267
|
+
const values = Object.fromEntries(parts.map((part) => [part.type, part.value]));
|
|
2268
|
+
const weekday = values.weekday;
|
|
2269
|
+
const hour = Number(values.hour);
|
|
2270
|
+
const minute = Number(values.minute);
|
|
2271
|
+
const second = Number(values.second);
|
|
2272
|
+
if (weekday === void 0 || !Number.isInteger(hour) || !Number.isInteger(minute) || !Number.isInteger(second)) return void 0;
|
|
2273
|
+
const workday = weekday !== "Sat" && weekday !== "Sun";
|
|
2274
|
+
const seconds = hour * 3600 + minute * 60 + second;
|
|
2275
|
+
return workday && (seconds >= 32400 && seconds < 43200 || seconds >= 50400 && seconds < 64800) ? "peak" : "off-peak";
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* Price one completed request, returning a range when it spans a published band boundary.
|
|
2279
|
+
* @param input - exact applicability, request interval, and complete disjoint usage buckets.
|
|
2280
|
+
* @returns Fixed-point exact/range cost or an explicit unpriced reason.
|
|
2281
|
+
*/
|
|
2282
|
+
function priceOfficialDeepSeekUsage(input) {
|
|
2283
|
+
for (const [name, value] of Object.entries(input.usage)) if (!Number.isSafeInteger(value) || value < 0) return unpriced(`invalid ${name}`);
|
|
2284
|
+
if (input.completedAt.getTime() < input.startedAt.getTime()) return unpriced("completion timestamp precedes request start");
|
|
2285
|
+
const start = resolveOfficialDeepSeekPrice({
|
|
2286
|
+
...input,
|
|
2287
|
+
at: input.startedAt
|
|
2288
|
+
});
|
|
2289
|
+
if (start.kind === "unpriced") return start;
|
|
2290
|
+
const end = resolveOfficialDeepSeekPrice({
|
|
2291
|
+
...input,
|
|
2292
|
+
at: input.completedAt
|
|
2293
|
+
});
|
|
2294
|
+
if (end.kind === "unpriced") return end;
|
|
2295
|
+
const startAmount = amountFor(start.record, input.usage);
|
|
2296
|
+
if (startAmount === void 0) return unpriced("invalid decimal price record");
|
|
2297
|
+
const crossesBoundary = spansPublishedPriceBoundary(input.startedAt, input.completedAt);
|
|
2298
|
+
if (start.record.band === end.record.band && !crossesBoundary) return {
|
|
2299
|
+
kind: "exact",
|
|
2300
|
+
currency: start.record.currency,
|
|
2301
|
+
band: start.record.band,
|
|
2302
|
+
...startAmount
|
|
2303
|
+
};
|
|
2304
|
+
const comparisonRecord = start.record.band === end.record.band ? priceRecordInBand(start.record, start.record.band === "peak" ? "off-peak" : "peak") : end.record;
|
|
2305
|
+
const endAmount = amountFor(comparisonRecord, input.usage);
|
|
2306
|
+
if (endAmount === void 0) return unpriced("invalid decimal price record");
|
|
2307
|
+
const startFemto = BigInt(startAmount.femtoUnits);
|
|
2308
|
+
const endFemto = BigInt(endAmount.femtoUnits);
|
|
2309
|
+
return {
|
|
2310
|
+
kind: "range",
|
|
2311
|
+
currency: start.record.currency,
|
|
2312
|
+
bands: [start.record.band, comparisonRecord.band],
|
|
2313
|
+
minimum: startFemto <= endFemto ? startAmount : endAmount,
|
|
2314
|
+
maximum: startFemto <= endFemto ? endAmount : startAmount
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2317
|
+
/** Detect any published UTC band boundary, even when both endpoints share a band. */
|
|
2318
|
+
function spansPublishedPriceBoundary(startedAt, completedAt) {
|
|
2319
|
+
const start = startedAt.getTime();
|
|
2320
|
+
const end = completedAt.getTime();
|
|
2321
|
+
if (end <= start) return false;
|
|
2322
|
+
const dayMs = 864e5;
|
|
2323
|
+
if (end - start >= 7 * dayMs) return true;
|
|
2324
|
+
const firstDay = Math.floor(start / dayMs) * dayMs;
|
|
2325
|
+
for (let day = firstDay; day <= end; day += dayMs) {
|
|
2326
|
+
const weekday = new Date(day).getUTCDay();
|
|
2327
|
+
if (weekday === 0 || weekday === 6) continue;
|
|
2328
|
+
for (const hour of [
|
|
2329
|
+
1,
|
|
2330
|
+
4,
|
|
2331
|
+
6,
|
|
2332
|
+
10
|
|
2333
|
+
]) {
|
|
2334
|
+
const boundary = day + hour * 60 * 60 * 1e3;
|
|
2335
|
+
if (boundary > start && boundary <= end) return true;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
return false;
|
|
2339
|
+
}
|
|
2340
|
+
function priceRecordInBand(record, band) {
|
|
2341
|
+
const [inputCacheHit, inputCacheMiss, output] = PRICES[record.modelId][record.currency][band];
|
|
2342
|
+
return Object.freeze({
|
|
2343
|
+
...record,
|
|
2344
|
+
band,
|
|
2345
|
+
inputCacheHit,
|
|
2346
|
+
inputCacheMiss,
|
|
2347
|
+
output
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
/**
|
|
2351
|
+
* Parse a non-negative decimal rate into nano-currency units, without Number arithmetic.
|
|
2352
|
+
* @param value - canonical non-negative decimal with at most nine fractional digits.
|
|
2353
|
+
* @returns Integer nano-units, or undefined when the decimal is invalid.
|
|
2354
|
+
*/
|
|
2355
|
+
function decimalRateNanoUnits(value) {
|
|
2356
|
+
const match = /^(0|[1-9]\d*)(?:\.(\d{1,9}))?$/u.exec(value);
|
|
2357
|
+
if (match === null) return void 0;
|
|
2358
|
+
const whole = match[1] ?? "0";
|
|
2359
|
+
const fraction = (match[2] ?? "").padEnd(9, "0");
|
|
2360
|
+
return BigInt(whole) * 1000000000n + BigInt(fraction || "0");
|
|
2361
|
+
}
|
|
2362
|
+
function amountFor(record, usage) {
|
|
2363
|
+
const hit = decimalRateNanoUnits(record.inputCacheHit);
|
|
2364
|
+
const miss = decimalRateNanoUnits(record.inputCacheMiss);
|
|
2365
|
+
const output = decimalRateNanoUnits(record.output);
|
|
2366
|
+
if (hit === void 0 || miss === void 0 || output === void 0) return void 0;
|
|
2367
|
+
const femtoUnits = BigInt(usage.cacheReadTokens) * hit + BigInt(usage.cacheMissTokens) * miss + BigInt(usage.outputTokens) * output;
|
|
2368
|
+
return {
|
|
2369
|
+
femtoUnits: femtoUnits.toString(),
|
|
2370
|
+
decimal: formatFemto(femtoUnits)
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
function formatFemto(value) {
|
|
2374
|
+
const digits = value.toString().padStart(16, "0");
|
|
2375
|
+
const whole = digits.slice(0, -15);
|
|
2376
|
+
const fraction = digits.slice(-15).replace(/0+$/u, "");
|
|
2377
|
+
return fraction.length === 0 ? whole : `${whole}.${fraction}`;
|
|
2378
|
+
}
|
|
2379
|
+
function modelPrices(version, usdOffPeak, usdPeak, cnyOffPeak, cnyPeak) {
|
|
2380
|
+
return Object.freeze({
|
|
2381
|
+
version,
|
|
2382
|
+
USD: Object.freeze({
|
|
2383
|
+
"off-peak": usdOffPeak,
|
|
2384
|
+
peak: usdPeak
|
|
2385
|
+
}),
|
|
2386
|
+
CNY: Object.freeze({
|
|
2387
|
+
"off-peak": cnyOffPeak,
|
|
2388
|
+
peak: cnyPeak
|
|
2389
|
+
})
|
|
2390
|
+
});
|
|
2391
|
+
}
|
|
2392
|
+
function isOfficialModel(value) {
|
|
2393
|
+
return Object.prototype.hasOwnProperty.call(PRICES, value);
|
|
2394
|
+
}
|
|
2395
|
+
function unpriced(reason) {
|
|
2396
|
+
return {
|
|
2397
|
+
kind: "unpriced",
|
|
2398
|
+
reason
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
//#endregion
|
|
2402
|
+
//#region src/adaptive-cost.ts
|
|
2403
|
+
/**
|
|
2404
|
+
* Bound Adaptive's benefit and cache-loss exposure without attributing the
|
|
2405
|
+
* request-level cache split to individual messages. Removed tokens are not
|
|
2406
|
+
* charged again as part of the retained suffix.
|
|
2407
|
+
* @param input - exact planned reclaim, adjacent request measurement, and same-revision nodes.
|
|
2408
|
+
* @returns Conservative removal/cache-risk bounds or an explicit unknown reason.
|
|
2409
|
+
*/
|
|
2410
|
+
function deriveAdaptiveTokenBounds(input) {
|
|
2411
|
+
if (!isCount(input.exactReclaimedTokens) || input.exactReclaimedTokens === 0) return unknown("invalid-reclaimed-token-count");
|
|
2412
|
+
if (!isCount(input.earliestChangedSeq)) return unknown("invalid-earliest-changed-seq");
|
|
2413
|
+
if (!isCount(input.previousPromptTokens)) return unknown("invalid-previous-prompt-tokens");
|
|
2414
|
+
if (input.expectedTokenizerRevision.length === 0) return unknown("expected-tokenizer-revision-unavailable");
|
|
2415
|
+
const request = input.previousRequestMeasurement;
|
|
2416
|
+
let margin = 0;
|
|
2417
|
+
let measurementKind;
|
|
2418
|
+
if (request.kind === "unavailable") return unknown("request-measurement-unavailable");
|
|
2419
|
+
if (request.kind === "exact-tokenizer") {
|
|
2420
|
+
if (!isCount(request.tokens) || request.tokens !== input.previousPromptTokens) return unknown("exact-request-usage-mismatch");
|
|
2421
|
+
if (request.tokenizerRevision !== input.expectedTokenizerRevision) return unknown("request-tokenizer-revision-mismatch");
|
|
2422
|
+
measurementKind = request.kind;
|
|
2423
|
+
} else {
|
|
2424
|
+
const calibration = request.calibration;
|
|
2425
|
+
if (calibration === void 0) return unknown("estimate-calibration-unavailable");
|
|
2426
|
+
if (!isCount(request.tokens) || !isCount(request.upperBoundTokens) || request.upperBoundTokens < request.tokens || input.previousPromptTokens > request.upperBoundTokens || !isCount(calibration.sampleCount) || !isCount(calibration.conservativeMarginTokens)) return unknown("invalid-estimate-calibration");
|
|
2427
|
+
margin = calibration.conservativeMarginTokens;
|
|
2428
|
+
measurementKind = request.kind;
|
|
2429
|
+
}
|
|
2430
|
+
const reclaimedLowerBoundTokens = Math.max(0, input.exactReclaimedTokens - margin);
|
|
2431
|
+
if (reclaimedLowerBoundTokens === 0) return unknown("reclaim-not-positive-after-margin");
|
|
2432
|
+
let identity;
|
|
2433
|
+
let exactPrefixLowerBoundTokens = 0;
|
|
2434
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2435
|
+
for (const node of input.measuredNodes) {
|
|
2436
|
+
if (!isCount(node.seq) || seen.has(node.seq)) return unknown("invalid-measured-node-sequence");
|
|
2437
|
+
seen.add(node.seq);
|
|
2438
|
+
if (node.seq >= input.earliestChangedSeq || node.count.kind !== "exact-tokenizer") continue;
|
|
2439
|
+
if (!isCount(node.count.tokens)) return unknown("invalid-exact-prefix-count");
|
|
2440
|
+
if (node.count.tokenizerRevision !== input.expectedTokenizerRevision) return unknown("exact-prefix-tokenizer-revision-mismatch");
|
|
2441
|
+
if (identity !== void 0 && (identity.tokenizerId !== node.count.tokenizerId || identity.tokenizerRevision !== node.count.tokenizerRevision)) return unknown("exact-prefix-tokenizer-identity-mismatch");
|
|
2442
|
+
identity ??= node.count;
|
|
2443
|
+
exactPrefixLowerBoundTokens += node.count.tokens;
|
|
2444
|
+
if (!isCount(exactPrefixLowerBoundTokens)) return unknown("exact-prefix-overflow");
|
|
2445
|
+
}
|
|
2446
|
+
const accounted = exactPrefixLowerBoundTokens + reclaimedLowerBoundTokens;
|
|
2447
|
+
if (!isCount(accounted) || accounted > input.previousPromptTokens) return unknown("adaptive-bounds-exceed-previous-prompt");
|
|
2448
|
+
return {
|
|
2449
|
+
kind: "available",
|
|
2450
|
+
measurementKind,
|
|
2451
|
+
reclaimedLowerBoundTokens,
|
|
2452
|
+
affectedRetainedSuffixUpperBoundTokens: input.previousPromptTokens - accounted,
|
|
2453
|
+
exactPrefixLowerBoundTokens
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Allow routine History only when D*P_hit is strictly greater than A*(P_miss-P_hit).
|
|
2458
|
+
* @param input - capacity state, token bounds, request hit cap, and official input rates.
|
|
2459
|
+
* @returns Capacity override or a strict fixed-point conservative-cost decision.
|
|
2460
|
+
*/
|
|
2461
|
+
function decideConservativeAdaptive(input) {
|
|
2462
|
+
if (input.capacityPressure) return {
|
|
2463
|
+
allowHistory: true,
|
|
2464
|
+
reason: "capacity-override"
|
|
2465
|
+
};
|
|
2466
|
+
if (input.bounds.kind === "unknown") return {
|
|
2467
|
+
allowHistory: false,
|
|
2468
|
+
reason: input.bounds.reason
|
|
2469
|
+
};
|
|
2470
|
+
if (input.inputCacheHitRate === void 0 || input.inputCacheMissRate === void 0) return {
|
|
2471
|
+
allowHistory: false,
|
|
2472
|
+
reason: "adaptive-unknown-price"
|
|
2473
|
+
};
|
|
2474
|
+
const hit = decimalRateNanoUnits(input.inputCacheHitRate);
|
|
2475
|
+
const miss = decimalRateNanoUnits(input.inputCacheMissRate);
|
|
2476
|
+
if (hit === void 0 || miss === void 0 || miss < hit) return {
|
|
2477
|
+
allowHistory: false,
|
|
2478
|
+
reason: "adaptive-unknown-price"
|
|
2479
|
+
};
|
|
2480
|
+
if (input.observedCacheReadTokens !== void 0 && !isCount(input.observedCacheReadTokens)) return {
|
|
2481
|
+
allowHistory: false,
|
|
2482
|
+
reason: "adaptive-invalid-cache-telemetry"
|
|
2483
|
+
};
|
|
2484
|
+
const affectedHitUpperBound = input.observedCacheReadTokens === void 0 ? input.bounds.affectedRetainedSuffixUpperBoundTokens : Math.min(input.bounds.affectedRetainedSuffixUpperBoundTokens, input.observedCacheReadTokens);
|
|
2485
|
+
const minimumRemovalValue = BigInt(input.bounds.reclaimedLowerBoundTokens) * hit;
|
|
2486
|
+
const maximumCacheLossPenalty = BigInt(affectedHitUpperBound) * (miss - hit);
|
|
2487
|
+
return {
|
|
2488
|
+
allowHistory: minimumRemovalValue > maximumCacheLossPenalty,
|
|
2489
|
+
reason: minimumRemovalValue > maximumCacheLossPenalty ? "cost-interval-clearly-favourable" : "cache-risk-not-clearly-paid-back",
|
|
2490
|
+
minimumRemovalValue: minimumRemovalValue.toString(),
|
|
2491
|
+
maximumCacheLossPenalty: maximumCacheLossPenalty.toString()
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
function unknown(reason) {
|
|
2495
|
+
return {
|
|
2496
|
+
kind: "unknown",
|
|
2497
|
+
reason
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
function isCount(value) {
|
|
2501
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
2502
|
+
}
|
|
2503
|
+
//#endregion
|
|
2504
|
+
//#region src/audit.ts
|
|
2505
|
+
/** Stable prefix used to locate one JSON audit record in Harness runtime logs. */
|
|
2506
|
+
const COMPRESSION_AUDIT_PREFIX = "context-compression audit ";
|
|
2507
|
+
/**
|
|
2508
|
+
* Encode one stable single-line audit message.
|
|
2509
|
+
* @param record - content-free structured audit record.
|
|
2510
|
+
* @returns the fixed prefix followed by one JSON object.
|
|
2511
|
+
*/
|
|
2512
|
+
function formatCompressionAudit(record) {
|
|
2513
|
+
return `${COMPRESSION_AUDIT_PREFIX}${JSON.stringify(record)}`;
|
|
2514
|
+
}
|
|
2515
|
+
/**
|
|
2516
|
+
* Publish one audit message through the Harness logger.
|
|
2517
|
+
* @param logger - current plugin logger.
|
|
2518
|
+
* @param record - structured record committed by the caller.
|
|
2519
|
+
*/
|
|
2520
|
+
function emitCompressionAudit(logger, record) {
|
|
2521
|
+
try {
|
|
2522
|
+
logger.info(formatCompressionAudit(record));
|
|
2523
|
+
} catch {}
|
|
2524
|
+
}
|
|
2525
|
+
//#endregion
|
|
2526
|
+
//#region src/index.ts
|
|
2527
|
+
/**
|
|
2528
|
+
* Replay-safe, model-free context-compression selector for tool results.
|
|
2529
|
+
*
|
|
2530
|
+
* Standard profiles never rewrite ordinary Assistant prose. The only durable
|
|
2531
|
+
* replacements emitted here are content-only `tool/result` rewrites whose
|
|
2532
|
+
* full source remains in the append-only Session log.
|
|
2533
|
+
*
|
|
2534
|
+
* @module dsh-context-compression-selector-runtime
|
|
2535
|
+
*/
|
|
2536
|
+
const RICH_BLOCK_PRESSURE_COST = 256;
|
|
2537
|
+
/** Routed-context utilization required before capacity-pressure History may age sent history. */
|
|
2538
|
+
const CAPACITY_PRESSURE_RATIO = .7;
|
|
2539
|
+
/**
|
|
2540
|
+
* Read this plugin's settings document from whichever settings surface the
|
|
2541
|
+
* running Harness exposes.
|
|
2542
|
+
*
|
|
2543
|
+
* 0.1.x hosts answered `ctx.settings.get(namespace)`; 0.2.0 replaced that with
|
|
2544
|
+
* the `SettingsForms` service, whose only read API is `describe()`. Its
|
|
2545
|
+
* descriptors key on the profile row's entry id -- which is why
|
|
2546
|
+
* `CONTEXT_COMPRESSION_SETTINGS_NAMESPACE` and the `cordis.patch.yml` row id
|
|
2547
|
+
* must stay identical. `describe()` is synchronous, so this stays a plain read
|
|
2548
|
+
* on the compaction hot path.
|
|
2549
|
+
*/
|
|
2550
|
+
function readSettingsDocument(ctx) {
|
|
2551
|
+
const service = ctx.get("settings");
|
|
2552
|
+
if (service === void 0) return { kind: "absent" };
|
|
2553
|
+
if (typeof service.get === "function") {
|
|
2554
|
+
const value = service.get(CONTEXT_COMPRESSION_SETTINGS_NAMESPACE);
|
|
2555
|
+
return value === void 0 ? { kind: "absent" } : {
|
|
2556
|
+
kind: "present",
|
|
2557
|
+
value
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
if (typeof service.describe === "function") {
|
|
2561
|
+
for (const descriptor of service.describe()) if (descriptor.ns === "context-compression" && descriptor.value !== void 0) return {
|
|
2562
|
+
kind: "present",
|
|
2563
|
+
value: descriptor.value
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
return { kind: "absent" };
|
|
2567
|
+
}
|
|
2568
|
+
/** Mixed deterministic selector behind the existing `ctx.toolResultPruner` seam. */
|
|
2569
|
+
var ToolResultPruner = class extends Service {
|
|
2570
|
+
static inject = ["tokenMeter"];
|
|
2571
|
+
static Config = z.object({
|
|
2572
|
+
profile: z.union([...COMPRESSION_PROFILES]).default(DEFAULTS.profile),
|
|
2573
|
+
custom: CustomCompressionPolicySchema.default(DEFAULT_CUSTOM_COMPRESSION_POLICY),
|
|
2574
|
+
headChars: z.number().step(1).min(0).default(DEFAULTS.headChars),
|
|
2575
|
+
tailChars: z.number().step(1).min(0).default(DEFAULTS.tailChars),
|
|
2576
|
+
nativeTriggerTokens: z.number().step(1).min(1).required(false),
|
|
2577
|
+
nativeTargetTokens: z.number().step(1).min(1).required(false),
|
|
2578
|
+
freshTriggerTokens: z.number().step(1).min(1).required(false),
|
|
2579
|
+
freshTargetTokens: z.number().step(1).min(1).required(false),
|
|
2580
|
+
aggregateTriggerTokens: z.number().step(1).min(1).required(false),
|
|
2581
|
+
aggregateTargetTokens: z.number().step(1).min(1).required(false),
|
|
2582
|
+
historyTriggerTokens: z.number().step(1).min(1).required(false),
|
|
2583
|
+
historyKeepRecentToolCalls: z.number().step(1).min(0).required(false),
|
|
2584
|
+
historyKeepRecentTokens: z.number().step(1).min(0).required(false),
|
|
2585
|
+
historyMinReclaimTokens: z.number().step(1).min(1).required(false),
|
|
2586
|
+
autoCompactThresholdPercent: z.number().step(1).min(50).max(90).required(false)
|
|
2587
|
+
});
|
|
2588
|
+
/** Resolved immutable deployment configuration. */
|
|
2589
|
+
config;
|
|
2590
|
+
/** Complete canonical setting document frozen when each Session first reaches this root service. */
|
|
2591
|
+
sessionSettings = /* @__PURE__ */ new WeakMap();
|
|
2592
|
+
/** Original result seqs whose first-exposure KEEP/REDUCE decision has committed. */
|
|
2593
|
+
firstExposure = /* @__PURE__ */ new WeakMap();
|
|
2594
|
+
/** Runtime prerequisite warnings deduplicated per Session and failure key. */
|
|
2595
|
+
warnedFailures = /* @__PURE__ */ new WeakMap();
|
|
2596
|
+
/** Last Adaptive postflight attempt emitted per Session; keeps diagnostics bounded and independent. */
|
|
2597
|
+
postflightDiagnostics = /* @__PURE__ */ new WeakMap();
|
|
2598
|
+
/** Current pre-step chain identity, shared by this producer and downstream compaction-basic. */
|
|
2599
|
+
activeRequestBoundaries = /* @__PURE__ */ new WeakMap();
|
|
2600
|
+
/** Boundary identity that already attempted one fully preflighted TailTrim publication. */
|
|
2601
|
+
tailTrimBoundaryAttempts = /* @__PURE__ */ new WeakMap();
|
|
2602
|
+
/** Last effective policy audit key emitted for each Session. */
|
|
2603
|
+
policyResolutionAudits = /* @__PURE__ */ new WeakMap();
|
|
2604
|
+
constructor(ctx, config = {}) {
|
|
2605
|
+
super(ctx, "toolResultPruner");
|
|
2606
|
+
ctx.inject(["tools", "systemPrompt"], (recoveryCtx) => {
|
|
2607
|
+
installContextCompressionRetrieve(recoveryCtx);
|
|
2608
|
+
});
|
|
2609
|
+
this.config = resolveConfig(config);
|
|
2610
|
+
ctx.on("session/event", (session, event) => {
|
|
2611
|
+
if (event.type !== "compaction/summary") return;
|
|
2612
|
+
emitCompressionAudit(ctx.logger, {
|
|
2613
|
+
schemaVersion: 1,
|
|
2614
|
+
kind: "native-auto-compact",
|
|
2615
|
+
sessionId: String(session.id),
|
|
2616
|
+
manifestEventType: "compaction/summary",
|
|
2617
|
+
manifestSeq: event.seq,
|
|
2618
|
+
reducer: "llm-summary",
|
|
2619
|
+
provider: event.data.provider,
|
|
2620
|
+
model: event.data.model,
|
|
2621
|
+
tokensBefore: event.data.shadowedTokenCount,
|
|
2622
|
+
tokensAfter: null
|
|
2623
|
+
});
|
|
2624
|
+
});
|
|
2625
|
+
ctx.on("agent/pre-step", async ({ agent, signal, turn, step }, next) => {
|
|
2626
|
+
const boundary = {};
|
|
2627
|
+
this.activeRequestBoundaries.set(agent.session, boundary);
|
|
2628
|
+
try {
|
|
2629
|
+
if (!signal.aborted) try {
|
|
2630
|
+
this.runRequestBoundary(agent.session, turn, step - 1, signal);
|
|
2631
|
+
} catch (error) {
|
|
2632
|
+
this.auditFailure(agent.session, "fresh", "request-boundary", error);
|
|
2633
|
+
ctx.logger.warn("context-compression fresh pass failed open: %o", error);
|
|
2634
|
+
}
|
|
2635
|
+
return await next();
|
|
2636
|
+
} finally {
|
|
2637
|
+
if (this.activeRequestBoundaries.get(agent.session) === boundary) this.activeRequestBoundaries.delete(agent.session);
|
|
2638
|
+
}
|
|
2639
|
+
}, { prepend: true });
|
|
2640
|
+
ctx.on("agent/turn-stopping", ({ agent, turn, signal }) => {
|
|
2641
|
+
if (signal.aborted) return;
|
|
2642
|
+
try {
|
|
2643
|
+
const step = this.latestCompletedToolStep(agent.session, turn);
|
|
2644
|
+
if (step !== void 0) this.runRequestBoundary(agent.session, turn, step, signal);
|
|
2645
|
+
} catch (error) {
|
|
2646
|
+
this.auditFailure(agent.session, "fresh", "terminal-pass", error);
|
|
2647
|
+
ctx.logger.warn("context-compression terminal pass failed open: %o", error);
|
|
2648
|
+
}
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* Measure text content in Unicode code points; non-text blocks cost zero.
|
|
2653
|
+
* @param blocks - tool-result content to measure.
|
|
2654
|
+
* @returns total Unicode code points across text blocks.
|
|
2655
|
+
*/
|
|
2656
|
+
measureContent(blocks) {
|
|
2657
|
+
let chars = 0;
|
|
2658
|
+
for (const block of blocks) if (block.type === "text") chars += codePointLength(block.text);
|
|
2659
|
+
return chars;
|
|
2660
|
+
}
|
|
2661
|
+
pressureCost(blocks) {
|
|
2662
|
+
let cost = 0;
|
|
2663
|
+
for (const block of blocks) switch (block.type) {
|
|
2664
|
+
case "text":
|
|
2665
|
+
case "reasoning":
|
|
2666
|
+
cost += codePointLength(block.text);
|
|
2667
|
+
break;
|
|
2668
|
+
case "tool-call":
|
|
2669
|
+
cost += RICH_BLOCK_PRESSURE_COST + codePointLength(block.name) + codePointLength(block.arguments);
|
|
2670
|
+
break;
|
|
2671
|
+
default: {
|
|
2672
|
+
const serialized = JSON.stringify(block);
|
|
2673
|
+
cost += Math.max(RICH_BLOCK_PRESSURE_COST, codePointLength(serialized));
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
return cost;
|
|
2677
|
+
}
|
|
2678
|
+
/**
|
|
2679
|
+
* Apply the configured native head/middle/tail transform.
|
|
2680
|
+
* @param blocks - original tool-result content.
|
|
2681
|
+
* @returns reduced content, or `null` when no reduction is required.
|
|
2682
|
+
*/
|
|
2683
|
+
pruneContent(blocks) {
|
|
2684
|
+
return this.nativePruneContent(blocks, this.config.headChars + codePointLength(PRUNE_MARKER) + this.config.tailChars, this.config.headChars, this.config.tailChars);
|
|
2685
|
+
}
|
|
2686
|
+
/**
|
|
2687
|
+
* Run one stable-surface pass. `fresh` is invoked before every request and
|
|
2688
|
+
* only reduces original oversized results. `pressure` is called by
|
|
2689
|
+
* compaction-basic and may additionally age old results at one high-water.
|
|
2690
|
+
* @param session - session whose current tool-result surface may be rewritten.
|
|
2691
|
+
* @param options - pass stage and optional completed-step coordinates.
|
|
2692
|
+
* @returns landed replacements and aggregate Unicode-code-point savings.
|
|
2693
|
+
*/
|
|
2694
|
+
pruneSession(session, options = {}) {
|
|
2695
|
+
const stage = options.stage ?? "pressure";
|
|
2696
|
+
const contextWindowTokens = options.contextWindowTokens ?? this.contextWindowForRequest(session);
|
|
2697
|
+
const policy = this.activePolicy(session, contextWindowTokens, stage);
|
|
2698
|
+
if (policy === void 0) return emptyResult();
|
|
2699
|
+
const profile = policy.profile;
|
|
2700
|
+
const view = measureForCompaction(this.ctx, session);
|
|
2701
|
+
if (stage === "fresh") return this.decideFreshStep(session, options, policy, view);
|
|
2702
|
+
if (profile === "off") return emptyResult();
|
|
2703
|
+
const landed = [];
|
|
2704
|
+
if (policy.nativeToolResultEnabled) {
|
|
2705
|
+
const eligible = this.snapshot(session, view).filter((candidate) => candidate.call.name !== "context_compression_retrieve");
|
|
2706
|
+
const exactUnavailable = eligible.some((candidate) => candidate.count.kind !== "exact-tokenizer");
|
|
2707
|
+
if (exactUnavailable) this.warnExactUnavailable(session, view, "native");
|
|
2708
|
+
const planned = eligible.map((candidate) => this.planNative(candidate, session, stage, policy, view)).filter((entry) => entry !== null);
|
|
2709
|
+
landed.push(...this.landAll(session, planned));
|
|
2710
|
+
if (landed.length === 0) {
|
|
2711
|
+
const exact = eligible.flatMap((candidate) => candidate.count.kind === "exact-tokenizer" ? [candidate.count.tokens] : []);
|
|
2712
|
+
this.auditComponent(session, policy, "native-tool-result", "pressure", "skipped", exactUnavailable ? "exact-tokenizer-unavailable" : exact.length === 0 ? "no-tool-result-candidates" : Math.max(...exact) <= policy.nativeTriggerTokens ? "at-or-below-trigger" : planned.length === 0 ? "no-valid-reduction" : "recovery-tool-unavailable", {
|
|
2713
|
+
measurementKind: exactUnavailable ? "unavailable" : "exact-tokenizer",
|
|
2714
|
+
...exact.length === 0 ? {} : { currentTokens: Math.max(...exact) },
|
|
2715
|
+
triggerTokens: policy.nativeTriggerTokens,
|
|
2716
|
+
targetTokens: policy.nativeTargetTokens
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
return summarize(landed);
|
|
2720
|
+
}
|
|
2721
|
+
let historyOutcome = {
|
|
2722
|
+
kind: "planned",
|
|
2723
|
+
plans: []
|
|
2724
|
+
};
|
|
2725
|
+
let historyAllowed = false;
|
|
2726
|
+
if (policy.historyMode === "adaptive") {
|
|
2727
|
+
historyOutcome = this.planHistoricalAging(session, policy, view);
|
|
2728
|
+
if (historyOutcome.kind === "planned") {
|
|
2729
|
+
const capacityPressure = this.capacityPressureActive(session, view, policy);
|
|
2730
|
+
historyAllowed = this.adaptiveHistoryAllowed(session, view, historyOutcome.plans, capacityPressure);
|
|
2731
|
+
if (historyAllowed) landed.push(...this.landAll(session, historyOutcome.plans));
|
|
2732
|
+
}
|
|
2733
|
+
} else {
|
|
2734
|
+
historyAllowed = this.historyAllowed(session, policy, view);
|
|
2735
|
+
if (historyAllowed) {
|
|
2736
|
+
historyOutcome = this.planHistoricalAging(session, policy, view);
|
|
2737
|
+
if (historyOutcome.kind === "planned") landed.push(...this.landAll(session, historyOutcome.plans));
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
if (!landed.some((entry) => entry.stage === "pressure")) this.auditHistoryEvaluation(session, policy, view, historyAllowed, historyOutcome);
|
|
2741
|
+
if (policy.tailTrim?.enabled === true) {
|
|
2742
|
+
const tailView = measureForCompaction(this.ctx, session);
|
|
2743
|
+
this.landOldestTailTrimGroup(session, policy, tailView);
|
|
2744
|
+
} else this.auditComponent(session, policy, "tail-trim", "pressure", "disabled", "profile-policy");
|
|
2745
|
+
return summarize(landed);
|
|
2746
|
+
}
|
|
2747
|
+
activeSettings(session) {
|
|
2748
|
+
const frozen = this.sessionSettings.get(session);
|
|
2749
|
+
if (frozen !== void 0) return frozen;
|
|
2750
|
+
const raw = readSettingsDocument(this.ctx);
|
|
2751
|
+
let resolved;
|
|
2752
|
+
let settingsSource;
|
|
2753
|
+
let autoCompactThresholdSource;
|
|
2754
|
+
let settingsInvalidFallback;
|
|
2755
|
+
if (raw.kind === "present") {
|
|
2756
|
+
try {
|
|
2757
|
+
resolved = parseContextCompressionSettings(raw.value);
|
|
2758
|
+
} catch (error) {
|
|
2759
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2760
|
+
this.auditFailure(session, "pressure", "policy-resolution", error);
|
|
2761
|
+
this.warnOnce(session, `settings-invalid:${reason}`, "context-compression froze this session effectively off because the stored settings document is invalid: %s", reason);
|
|
2762
|
+
resolved = ContextCompressionSettingsSchema({ profile: "off" });
|
|
2763
|
+
settingsInvalidFallback = "lossless-off";
|
|
2764
|
+
}
|
|
2765
|
+
settingsSource = settingsInvalidFallback === void 0 ? "host-settings" : "plugin-config-fallback";
|
|
2766
|
+
autoCompactThresholdSource = settingsInvalidFallback === void 0 ? "host-settings" : "schema-default";
|
|
2767
|
+
} else {
|
|
2768
|
+
resolved = {
|
|
2769
|
+
profile: this.config.profile,
|
|
2770
|
+
custom: this.config.custom,
|
|
2771
|
+
autoCompact: { thresholdPercent: this.config.autoCompactThresholdPercent ?? AUTO_COMPACT_THRESHOLD_LIMITS.default }
|
|
2772
|
+
};
|
|
2773
|
+
settingsSource = "plugin-config";
|
|
2774
|
+
autoCompactThresholdSource = this.config.autoCompactThresholdPercent === void 0 ? "schema-default" : "generation-config";
|
|
2775
|
+
}
|
|
2776
|
+
if (this.config.autoCompactThresholdPercent !== void 0) {
|
|
2777
|
+
resolved = {
|
|
2778
|
+
...resolved,
|
|
2779
|
+
autoCompact: { thresholdPercent: this.config.autoCompactThresholdPercent }
|
|
2780
|
+
};
|
|
2781
|
+
autoCompactThresholdSource = "generation-config";
|
|
2782
|
+
}
|
|
2783
|
+
const snapshot = deepFreeze(structuredClone(resolved));
|
|
2784
|
+
this.sessionSettings.set(session, snapshot);
|
|
2785
|
+
emitCompressionAudit(this.ctx.logger, {
|
|
2786
|
+
schemaVersion: 1,
|
|
2787
|
+
kind: "policy-frozen",
|
|
2788
|
+
sessionId: String(session.id),
|
|
2789
|
+
settingsSource,
|
|
2790
|
+
autoCompactThresholdSource,
|
|
2791
|
+
...settingsInvalidFallback === void 0 ? {} : { settingsInvalidFallback },
|
|
2792
|
+
settings: snapshot,
|
|
2793
|
+
deploymentConfig: this.config
|
|
2794
|
+
});
|
|
2795
|
+
return snapshot;
|
|
2796
|
+
}
|
|
2797
|
+
activePolicy(session, contextWindowTokens, stage = "pressure") {
|
|
2798
|
+
const settings = this.activeSettings(session);
|
|
2799
|
+
try {
|
|
2800
|
+
const policy = resolvePolicy(this.config, settings.profile, settings.custom, {
|
|
2801
|
+
...contextWindowTokens === void 0 ? {} : { contextWindowTokens },
|
|
2802
|
+
autoCompactThresholdPercent: settings.autoCompact.thresholdPercent
|
|
2803
|
+
});
|
|
2804
|
+
const route = this.routeAuditFact(session);
|
|
2805
|
+
const auditKey = JSON.stringify({
|
|
2806
|
+
policy,
|
|
2807
|
+
contextWindowTokens: contextWindowTokens ?? null,
|
|
2808
|
+
route: route ?? null
|
|
2809
|
+
});
|
|
2810
|
+
if (this.policyResolutionAudits.get(session) !== auditKey) {
|
|
2811
|
+
this.policyResolutionAudits.set(session, auditKey);
|
|
2812
|
+
const overriddenLinkedFields = [
|
|
2813
|
+
"historyTriggerTokens",
|
|
2814
|
+
"historyKeepRecentTokens",
|
|
2815
|
+
"historyMinReclaimTokens"
|
|
2816
|
+
].filter((key) => this.config[key] !== void 0).length;
|
|
2817
|
+
emitCompressionAudit(this.ctx.logger, {
|
|
2818
|
+
schemaVersion: 1,
|
|
2819
|
+
kind: "policy-resolved",
|
|
2820
|
+
sessionId: String(session.id),
|
|
2821
|
+
policy,
|
|
2822
|
+
...contextWindowTokens === void 0 ? {} : { contextWindowTokens },
|
|
2823
|
+
coordination: {
|
|
2824
|
+
thresholdPercent: settings.autoCompact.thresholdPercent,
|
|
2825
|
+
...policy.autoCompactTokens === void 0 ? {} : { autoCompactTokens: policy.autoCompactTokens },
|
|
2826
|
+
...policy.microDeadlineTokens === void 0 ? {} : { microDeadlineTokens: policy.microDeadlineTokens },
|
|
2827
|
+
paramSource: settings.profile === "custom" ? "custom-manual" : overriddenLinkedFields === 3 ? "deployment-override" : overriddenLinkedFields > 0 ? "mixed" : policy.microDeadlineTokens === void 0 ? "fixed-preset" : "auto-compact-linked"
|
|
2828
|
+
},
|
|
2829
|
+
...route === void 0 ? {} : { route },
|
|
2830
|
+
...route === void 0 ? {} : this.tokenizerAuditFact(route)
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
return policy;
|
|
2834
|
+
} catch (error) {
|
|
2835
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2836
|
+
this.auditFailure(session, stage, "policy-resolution", error);
|
|
2837
|
+
this.warnOnce(session, `custom-policy:${settings.profile}:${reason}`, "context-compression kept original tool results because the Custom policy is not effective: %s", reason);
|
|
2838
|
+
return;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
/** Routed provider/model when the durable request header names one route. */
|
|
2842
|
+
routeAuditFact(session) {
|
|
2843
|
+
const header = session.requestHeader()?.config;
|
|
2844
|
+
if (header === void 0 || header.provider.length === 0 || header.model.length === 0) return void 0;
|
|
2845
|
+
return {
|
|
2846
|
+
provider: header.provider,
|
|
2847
|
+
model: header.model
|
|
2848
|
+
};
|
|
2849
|
+
}
|
|
2850
|
+
/** Bundled tokenizer identity for one route, when the route is eligible. */
|
|
2851
|
+
tokenizerAuditFact(route) {
|
|
2852
|
+
const identity = isDeepSeekBilledProvider(route.provider) ? deepSeekV4TokenizerForModel(route.model)?.countText("") : void 0;
|
|
2853
|
+
if (identity?.kind === "exact-tokenizer") return { tokenizer: {
|
|
2854
|
+
repository: identity.tokenizerId,
|
|
2855
|
+
revision: identity.tokenizerRevision
|
|
2856
|
+
} };
|
|
2857
|
+
return { tokenizer: {
|
|
2858
|
+
repository: "unavailable",
|
|
2859
|
+
revision: "unavailable"
|
|
2860
|
+
} };
|
|
2861
|
+
}
|
|
2862
|
+
contextWindowForRequest(session) {
|
|
2863
|
+
const settings = this.activeSettings(session);
|
|
2864
|
+
if (settings.profile === "off" || settings.profile === "native") return void 0;
|
|
2865
|
+
if (settings.profile === "custom" && settings.custom.unit !== "context-percent") return void 0;
|
|
2866
|
+
const config = session.requestHeader()?.config;
|
|
2867
|
+
const routed = session.requestContext();
|
|
2868
|
+
if (config === void 0 || config.provider.length === 0 || config.model.length === 0 || routed === void 0) return;
|
|
2869
|
+
if (routed.provider !== config.provider || routed.model !== config.model) {
|
|
2870
|
+
this.warnOnce(session, `custom-context-window-route:${config.provider}\0${config.model}`, "context-compression kept the context-linked policy inactive because durable route capacity belongs to %s/%s, not %s/%s", routed.provider, routed.model, config.provider, config.model);
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
if (!Number.isSafeInteger(routed.contextWindow) || routed.contextWindow === void 0 || routed.contextWindow <= 0) {
|
|
2874
|
+
this.warnOnce(session, `custom-context-window-capacity:${config.provider}\0${config.model}`, "context-compression kept the context-linked policy inactive because %s/%s has no positive durable context capacity", config.provider, config.model);
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
return routed.contextWindow;
|
|
2878
|
+
}
|
|
2879
|
+
runRequestBoundary(session, turn, step, signal) {
|
|
2880
|
+
const contextWindowTokens = this.contextWindowForRequest(session);
|
|
2881
|
+
if (signal.aborted) return;
|
|
2882
|
+
const policy = this.activePolicy(session, contextWindowTokens, "fresh");
|
|
2883
|
+
if (policy === void 0) return;
|
|
2884
|
+
const capacity = contextWindowTokens === void 0 ? {} : { contextWindowTokens };
|
|
2885
|
+
this.pruneSession(session, {
|
|
2886
|
+
stage: "fresh",
|
|
2887
|
+
freshTurn: turn,
|
|
2888
|
+
freshStep: step,
|
|
2889
|
+
...capacity
|
|
2890
|
+
});
|
|
2891
|
+
if (policy.historyMode !== "disabled" || policy.tailTrim?.enabled === true) this.pruneSession(session, {
|
|
2892
|
+
stage: "pressure",
|
|
2893
|
+
...capacity
|
|
2894
|
+
});
|
|
2895
|
+
}
|
|
2896
|
+
/** Resolve historical-aging authority without accepting caller-supplied elevation. */
|
|
2897
|
+
historyAllowed(session, policy, view) {
|
|
2898
|
+
switch (policy.historyMode) {
|
|
2899
|
+
case "disabled": return false;
|
|
2900
|
+
case "routine": return true;
|
|
2901
|
+
case "capacity-pressure": return this.capacityPressureActive(session, view, policy);
|
|
2902
|
+
case "adaptive": return false;
|
|
2903
|
+
/* v8 ignore next -- closed-union exhaustiveness guard */
|
|
2904
|
+
default: return assertNever(policy.historyMode, "history mode");
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
/**
|
|
2908
|
+
* Match the compaction-basic pressure gate using public durable data. The
|
|
2909
|
+
* frozen Auto Compact deadline `D = floor(A x 0.875)` replaces the legacy
|
|
2910
|
+
* fixed 0.7 ratio once the standard-profile linkage resolved; without
|
|
2911
|
+
* linkage the 0.7 ratio is the documented fallback and reproduces the
|
|
2912
|
+
* previous behavior.
|
|
2913
|
+
*/
|
|
2914
|
+
capacityPressureActive(session, view, policy) {
|
|
2915
|
+
const deadline = policy.microDeadlineTokens;
|
|
2916
|
+
if (deadline !== void 0) return view.totalTokens >= deadline;
|
|
2917
|
+
const header = session.requestHeader()?.config;
|
|
2918
|
+
const routed = session.requestContext();
|
|
2919
|
+
const contextWindow = routed?.contextWindow;
|
|
2920
|
+
if (header === void 0 || routed === void 0 || routed.provider !== header.provider || routed.model !== header.model || contextWindow === void 0 || !Number.isSafeInteger(contextWindow) || contextWindow <= 0) return false;
|
|
2921
|
+
return view.totalTokens >= Math.floor(contextWindow * CAPACITY_PRESSURE_RATIO);
|
|
2922
|
+
}
|
|
2923
|
+
/** Emit one bounded, independently correlatable postflight cost diagnostic per completed attempt. */
|
|
2924
|
+
logAdaptivePostflight(session, usage) {
|
|
2925
|
+
const attemptId = String(usage.attemptId);
|
|
2926
|
+
if (this.postflightDiagnostics.get(session) === attemptId) return;
|
|
2927
|
+
this.postflightDiagnostics.set(session, attemptId);
|
|
2928
|
+
const key = usage.key;
|
|
2929
|
+
let priceRecord;
|
|
2930
|
+
let cost;
|
|
2931
|
+
if (key === void 0) cost = {
|
|
2932
|
+
kind: "unpriced",
|
|
2933
|
+
reason: "measurement key unavailable"
|
|
2934
|
+
};
|
|
2935
|
+
else if (usage.responseModelId !== key.modelId) cost = {
|
|
2936
|
+
kind: "unpriced",
|
|
2937
|
+
reason: "response model mismatch or unavailable"
|
|
2938
|
+
};
|
|
2939
|
+
else if (usage.observedOutputTokens === void 0) cost = {
|
|
2940
|
+
kind: "unpriced",
|
|
2941
|
+
reason: "output token count unavailable"
|
|
2942
|
+
};
|
|
2943
|
+
else if (usage.cacheStatus !== "complete" || usage.cacheReadTokens === void 0 || usage.cacheMissTokens === void 0) cost = {
|
|
2944
|
+
kind: "unpriced",
|
|
2945
|
+
reason: "complete cache split unavailable"
|
|
2946
|
+
};
|
|
2947
|
+
else {
|
|
2948
|
+
const startedAt = new Date(usage.startedAtMs);
|
|
2949
|
+
const completedAt = new Date(usage.completedAtMs);
|
|
2950
|
+
const resolution = resolveOfficialDeepSeekPrice({
|
|
2951
|
+
provider: key.provider,
|
|
2952
|
+
baseUrlClass: key.baseUrlClass,
|
|
2953
|
+
apiRoute: key.apiRoute,
|
|
2954
|
+
modelId: key.modelId,
|
|
2955
|
+
currency: "USD",
|
|
2956
|
+
at: startedAt
|
|
2957
|
+
});
|
|
2958
|
+
if (resolution.kind === "priced") priceRecord = {
|
|
2959
|
+
catalogVersion: resolution.record.catalogVersion,
|
|
2960
|
+
checkedAt: resolution.record.checkedAt,
|
|
2961
|
+
sourceUrl: resolution.record.sourceUrl,
|
|
2962
|
+
currency: resolution.record.currency,
|
|
2963
|
+
modelId: resolution.record.modelId,
|
|
2964
|
+
apiRoute: resolution.record.apiRoute,
|
|
2965
|
+
startBand: resolution.record.band
|
|
2966
|
+
};
|
|
2967
|
+
cost = priceOfficialDeepSeekUsage({
|
|
2968
|
+
provider: key.provider,
|
|
2969
|
+
baseUrlClass: key.baseUrlClass,
|
|
2970
|
+
apiRoute: key.apiRoute,
|
|
2971
|
+
modelId: key.modelId,
|
|
2972
|
+
currency: "USD",
|
|
2973
|
+
startedAt,
|
|
2974
|
+
completedAt,
|
|
2975
|
+
usage: {
|
|
2976
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
2977
|
+
cacheMissTokens: usage.cacheMissTokens,
|
|
2978
|
+
outputTokens: usage.observedOutputTokens
|
|
2979
|
+
}
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
this.ctx.logger.debug(`context-compression adaptive postflight ${JSON.stringify({
|
|
2983
|
+
sessionId: String(session.id),
|
|
2984
|
+
providerRequestOrdinal: Number(usage.providerRequestOrdinal),
|
|
2985
|
+
attemptId,
|
|
2986
|
+
startedAtMs: usage.startedAtMs,
|
|
2987
|
+
completedAtMs: usage.completedAtMs,
|
|
2988
|
+
measurementKind: usage.measurement.kind,
|
|
2989
|
+
catalogVersion: DEEPSEEK_OFFICIAL_PRICE_CATALOG_VERSION,
|
|
2990
|
+
...priceRecord === void 0 ? {} : { priceRecord },
|
|
2991
|
+
usage: {
|
|
2992
|
+
promptTokens: usage.observedPromptTokens,
|
|
2993
|
+
...usage.observedOutputTokens === void 0 ? {} : { outputTokens: usage.observedOutputTokens },
|
|
2994
|
+
cacheStatus: usage.cacheStatus ?? "unknown",
|
|
2995
|
+
...usage.cacheReadTokens === void 0 ? {} : { cacheReadTokens: usage.cacheReadTokens },
|
|
2996
|
+
...usage.cacheMissTokens === void 0 ? {} : { cacheMissTokens: usage.cacheMissTokens }
|
|
2997
|
+
},
|
|
2998
|
+
cost
|
|
2999
|
+
})}`);
|
|
3000
|
+
}
|
|
3001
|
+
/** Decide one already-planned History batch from adjacent request-level facts only. */
|
|
3002
|
+
adaptiveHistoryAllowed(session, view, plans, capacityPressure) {
|
|
3003
|
+
const log = (allowHistory, reason, detail = {}) => {
|
|
3004
|
+
this.ctx.logger.debug(`context-compression adaptive ${JSON.stringify({
|
|
3005
|
+
sessionId: String(session.id),
|
|
3006
|
+
allowHistory,
|
|
3007
|
+
reason,
|
|
3008
|
+
catalogVersion: DEEPSEEK_OFFICIAL_PRICE_CATALOG_VERSION,
|
|
3009
|
+
...detail
|
|
3010
|
+
})}`);
|
|
3011
|
+
return allowHistory;
|
|
3012
|
+
};
|
|
3013
|
+
const usage = view.lastCompletedUsage;
|
|
3014
|
+
if (usage !== void 0) this.logAdaptivePostflight(session, usage);
|
|
3015
|
+
if (plans.length === 0) return false;
|
|
3016
|
+
if (capacityPressure) return log(true, "capacity-override");
|
|
3017
|
+
const currentKey = view.latestEnvelopeKey;
|
|
3018
|
+
if (usage === void 0) return log(false, "usage-unavailable");
|
|
3019
|
+
if (usage.key === void 0 || currentKey === void 0) return log(false, "measurement-key-unavailable");
|
|
3020
|
+
if (!sameProviderMeasurementKey(usage.key, currentKey)) return log(false, "measurement-key-mismatch");
|
|
3021
|
+
if (usage.responseModelId !== usage.key.modelId) return log(false, "response-model-mismatch-or-unavailable");
|
|
3022
|
+
if (usage.cacheStatus !== "complete" || usage.cacheReadTokens === void 0 || usage.cacheMissTokens === void 0) return log(false, "cache-split-incomplete");
|
|
3023
|
+
const price = resolveOfficialDeepSeekPrice({
|
|
3024
|
+
provider: usage.key.provider,
|
|
3025
|
+
baseUrlClass: usage.key.baseUrlClass,
|
|
3026
|
+
apiRoute: usage.key.apiRoute,
|
|
3027
|
+
modelId: usage.key.modelId,
|
|
3028
|
+
currency: "USD",
|
|
3029
|
+
at: /* @__PURE__ */ new Date()
|
|
3030
|
+
});
|
|
3031
|
+
if (price.kind === "unpriced") return log(false, `adaptive-unknown-price:${price.reason}`);
|
|
3032
|
+
const bounds = deriveAdaptiveTokenBounds({
|
|
3033
|
+
exactReclaimedTokens: plans.reduce((sum, plan) => sum + plan.tokensBefore - plan.tokensAfter, 0),
|
|
3034
|
+
earliestChangedSeq: Math.min(...plans.map((plan) => plan.candidate.seq)),
|
|
3035
|
+
previousPromptTokens: usage.observedPromptTokens,
|
|
3036
|
+
expectedTokenizerRevision: usage.key.tokenizerRevision,
|
|
3037
|
+
previousRequestMeasurement: usage.measurement,
|
|
3038
|
+
measuredNodes: view.measuredNodes
|
|
3039
|
+
});
|
|
3040
|
+
const decision = decideConservativeAdaptive({
|
|
3041
|
+
capacityPressure: false,
|
|
3042
|
+
bounds,
|
|
3043
|
+
inputCacheHitRate: price.record.inputCacheHit,
|
|
3044
|
+
inputCacheMissRate: price.record.inputCacheMiss,
|
|
3045
|
+
observedCacheReadTokens: usage.cacheReadTokens
|
|
3046
|
+
});
|
|
3047
|
+
return log(decision.allowHistory, decision.reason, {
|
|
3048
|
+
priceBand: price.record.band,
|
|
3049
|
+
observedPromptTokens: usage.observedPromptTokens,
|
|
3050
|
+
observedCacheReadTokens: usage.cacheReadTokens,
|
|
3051
|
+
bounds,
|
|
3052
|
+
..."minimumRemovalValue" in decision ? { minimumRemovalValue: decision.minimumRemovalValue } : {},
|
|
3053
|
+
..."maximumCacheLossPenalty" in decision ? { maximumCacheLossPenalty: decision.maximumCacheLossPenalty } : {}
|
|
3054
|
+
});
|
|
3055
|
+
}
|
|
3056
|
+
latestCompletedToolStep(session, turn) {
|
|
3057
|
+
let latest;
|
|
3058
|
+
for (const event of sessionEvents(session)) if (event.type === "step/end" && event.data.turn === turn) latest = event.data.step;
|
|
3059
|
+
return latest;
|
|
3060
|
+
}
|
|
3061
|
+
decisions(session) {
|
|
3062
|
+
let decisions = this.firstExposure.get(session);
|
|
3063
|
+
if (decisions === void 0) {
|
|
3064
|
+
decisions = /* @__PURE__ */ new Set();
|
|
3065
|
+
this.firstExposure.set(session, decisions);
|
|
3066
|
+
}
|
|
3067
|
+
return decisions;
|
|
3068
|
+
}
|
|
3069
|
+
decideFreshStep(session, options, policy, view) {
|
|
3070
|
+
if (options.freshTurn === void 0 || options.freshStep === void 0) {
|
|
3071
|
+
this.auditComponent(session, policy, "fresh", "fresh", policy.freshEnabled ? "skipped" : "disabled", policy.freshEnabled ? "missing-completed-step-coordinates" : "profile-policy");
|
|
3072
|
+
this.auditComponent(session, policy, "aggregate", "fresh", policy.aggregateEnabled ? "skipped" : "disabled", policy.aggregateEnabled ? "missing-completed-step-coordinates" : "profile-policy");
|
|
3073
|
+
return emptyResult();
|
|
3074
|
+
}
|
|
3075
|
+
const decisions = this.decisions(session);
|
|
3076
|
+
const candidates = this.snapshot(session, view).filter((candidate) => typeof candidate.event.surfaceOp !== "object" && candidate.event.data.turn === options.freshTurn && candidate.event.data.step === options.freshStep && !decisions.has(candidate.seq));
|
|
3077
|
+
if (candidates.length === 0) {
|
|
3078
|
+
this.auditComponent(session, policy, "fresh", "fresh", policy.freshEnabled ? "skipped" : "disabled", policy.freshEnabled ? "no-new-tool-result-candidates" : "profile-policy");
|
|
3079
|
+
this.auditComponent(session, policy, "aggregate", "fresh", policy.aggregateEnabled ? "skipped" : "disabled", policy.aggregateEnabled ? "no-new-tool-result-candidates" : "profile-policy");
|
|
3080
|
+
return emptyResult();
|
|
3081
|
+
}
|
|
3082
|
+
const plans = /* @__PURE__ */ new Map();
|
|
3083
|
+
let freshPlanned = 0;
|
|
3084
|
+
const exactCandidateTokens = candidates.map((candidate) => exactTokens(candidate.count));
|
|
3085
|
+
const exactAvailable = exactCandidateTokens.every((tokens) => tokens !== void 0);
|
|
3086
|
+
const maxCandidateTokens = exactAvailable ? Math.max(...exactCandidateTokens) : void 0;
|
|
3087
|
+
if (policy.freshEnabled) {
|
|
3088
|
+
if (candidates.some((candidate) => candidate.call.name !== "context_compression_retrieve" && candidate.count.kind !== "exact-tokenizer")) this.warnExactUnavailable(session, view, "fresh");
|
|
3089
|
+
for (const candidate of candidates) {
|
|
3090
|
+
if (candidate.call.name === "context_compression_retrieve") continue;
|
|
3091
|
+
const plan = this.planFresh(candidate, session, policy, view);
|
|
3092
|
+
if (plan !== null) {
|
|
3093
|
+
plans.set(candidate.seq, plan);
|
|
3094
|
+
freshPlanned += 1;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
let aggregateInputTokens;
|
|
3099
|
+
let aggregatePlanned = 0;
|
|
3100
|
+
if (policy.aggregateEnabled) {
|
|
3101
|
+
const aggregateAvailable = exactAvailable;
|
|
3102
|
+
if (!aggregateAvailable) this.warnExactUnavailable(session, view, "aggregate");
|
|
3103
|
+
let total = aggregateAvailable ? candidates.reduce((sum, candidate) => sum + (plans.get(candidate.seq)?.tokensAfter ?? exactTokens(candidate.count) ?? 0), 0) : 0;
|
|
3104
|
+
if (aggregateAvailable) aggregateInputTokens = total;
|
|
3105
|
+
if (aggregateAvailable && total > policy.aggregateTriggerTokens) {
|
|
3106
|
+
const remaining = candidates.filter((candidate) => candidate.call.name !== "context_compression_retrieve").sort((a, b) => Number(this.isError(a)) - Number(this.isError(b)) || (plans.get(b.seq)?.tokensAfter ?? exactTokens(b.count) ?? 0) - (plans.get(a.seq)?.tokensAfter ?? exactTokens(a.count) ?? 0));
|
|
3107
|
+
for (const candidate of remaining) {
|
|
3108
|
+
const previous = plans.get(candidate.seq);
|
|
3109
|
+
const plan = this.planAggregate(candidate, session, view);
|
|
3110
|
+
const previousTokens = previous?.tokensAfter ?? exactTokens(candidate.count) ?? 0;
|
|
3111
|
+
if (plan === null || plan.tokensAfter >= previousTokens) continue;
|
|
3112
|
+
plans.set(candidate.seq, plan);
|
|
3113
|
+
aggregatePlanned += 1;
|
|
3114
|
+
total -= previousTokens - plan.tokensAfter;
|
|
3115
|
+
if (total <= policy.aggregateTargetTokens) break;
|
|
3116
|
+
}
|
|
3117
|
+
if (total > policy.aggregateTargetTokens) this.ctx.logger.warn("context-compression fresh aggregate residual: %d tokens exceed target %d", total, policy.aggregateTargetTokens);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
const landed = this.landAll(session, candidates.map((candidate) => plans.get(candidate.seq)).filter((plan) => plan !== void 0));
|
|
3121
|
+
const freshLanded = landed.some((entry) => entry.stage === "fresh" && plans.get(entry.originalSeq)?.component === "fresh");
|
|
3122
|
+
const aggregateLanded = landed.some((entry) => entry.stage === "fresh" && plans.get(entry.originalSeq)?.component === "aggregate");
|
|
3123
|
+
if (!freshLanded) this.auditComponent(session, policy, "fresh", "fresh", policy.freshEnabled ? "skipped" : "disabled", !policy.freshEnabled ? "profile-policy" : !exactAvailable ? "exact-tokenizer-unavailable" : (maxCandidateTokens ?? 0) <= policy.freshTriggerTokens ? "at-or-below-trigger" : freshPlanned > 0 && aggregatePlanned > 0 ? "superseded-by-aggregate" : freshPlanned === 0 ? "no-valid-reduction" : "recovery-tool-unavailable", {
|
|
3124
|
+
measurementKind: exactAvailable ? "exact-tokenizer" : "unavailable",
|
|
3125
|
+
...maxCandidateTokens === void 0 ? {} : { currentTokens: maxCandidateTokens },
|
|
3126
|
+
triggerTokens: policy.freshTriggerTokens,
|
|
3127
|
+
targetTokens: policy.freshTargetTokens
|
|
3128
|
+
});
|
|
3129
|
+
if (!aggregateLanded) this.auditComponent(session, policy, "aggregate", "fresh", policy.aggregateEnabled ? "skipped" : "disabled", !policy.aggregateEnabled ? "profile-policy" : !exactAvailable ? "exact-tokenizer-unavailable" : (aggregateInputTokens ?? 0) <= policy.aggregateTriggerTokens ? "at-or-below-trigger" : aggregatePlanned === 0 ? "no-valid-reduction" : "recovery-tool-unavailable", {
|
|
3130
|
+
measurementKind: exactAvailable ? "exact-tokenizer" : "unavailable",
|
|
3131
|
+
...aggregateInputTokens === void 0 ? {} : { currentTokens: aggregateInputTokens },
|
|
3132
|
+
triggerTokens: policy.aggregateTriggerTokens,
|
|
3133
|
+
targetTokens: policy.aggregateTargetTokens
|
|
3134
|
+
});
|
|
3135
|
+
for (const candidate of candidates) decisions.add(candidate.seq);
|
|
3136
|
+
return summarize(landed);
|
|
3137
|
+
}
|
|
3138
|
+
snapshot(session, view) {
|
|
3139
|
+
const events = sessionEvents(session);
|
|
3140
|
+
const calls = /* @__PURE__ */ new Map();
|
|
3141
|
+
for (const event of events) if (event.type === "tool/call") calls.set(event.data.callId, {
|
|
3142
|
+
name: event.data.name,
|
|
3143
|
+
arguments: event.data.arguments
|
|
3144
|
+
});
|
|
3145
|
+
const candidates = [];
|
|
3146
|
+
const measured = new Map(view.measuredNodes.map((node) => [node.seq, node.count]));
|
|
3147
|
+
const projectionPrices = new Map(view.nodes.map((node) => [node.seq, node.tokens]));
|
|
3148
|
+
for (const seq of [...session.surface.nodes]) {
|
|
3149
|
+
const event = events[seq];
|
|
3150
|
+
if (event?.type !== "tool/result") continue;
|
|
3151
|
+
const shadowedHeuristicTokenCount = projectionPrices.get(seq);
|
|
3152
|
+
if (shadowedHeuristicTokenCount === void 0) throw new Error(`surface node ${String(seq)} is absent from the atomic legacy projection`);
|
|
3153
|
+
const content = event.data.message.content;
|
|
3154
|
+
candidates.push({
|
|
3155
|
+
seq,
|
|
3156
|
+
event,
|
|
3157
|
+
call: calls.get(event.data.message.toolCallId) ?? {
|
|
3158
|
+
name: "unknown",
|
|
3159
|
+
arguments: "{}"
|
|
3160
|
+
},
|
|
3161
|
+
count: onlyTextBlocks(content) === null ? unavailableCount(`surface node ${String(seq)} contains unsupported rich tool-result content`) : measured.get(seq) ?? unavailableCount(`surface node ${String(seq)} is absent from the atomic token view`),
|
|
3162
|
+
shadowedHeuristicTokenCount,
|
|
3163
|
+
characterPressure: this.pressureCost(content)
|
|
3164
|
+
});
|
|
3165
|
+
}
|
|
3166
|
+
return candidates;
|
|
3167
|
+
}
|
|
3168
|
+
planNative(candidate, session, stage, policy, view) {
|
|
3169
|
+
if (candidate.call.name === "context_compression_retrieve") return null;
|
|
3170
|
+
const tokensBefore = exactTokens(candidate.count);
|
|
3171
|
+
if (tokensBefore === void 0 || tokensBefore <= policy.nativeTriggerTokens) return null;
|
|
3172
|
+
const content = candidate.event.data.message.content;
|
|
3173
|
+
if (onlyTextBlocks(content) === null) return null;
|
|
3174
|
+
const sourceSeq = this.rootToolResultSeq(session, candidate.seq);
|
|
3175
|
+
const marker = recoveryMarker(this.sourceRef(session, sourceSeq), "tool result middle pruned");
|
|
3176
|
+
let head = this.config.headChars;
|
|
3177
|
+
let tail = this.config.tailChars;
|
|
3178
|
+
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
3179
|
+
const threshold = head + codePointLength(marker) + tail;
|
|
3180
|
+
const pruned = this.nativePruneContent(content, threshold, head, tail, marker);
|
|
3181
|
+
if (pruned !== null) {
|
|
3182
|
+
const plan = this.plan(candidate, pruned, sourceSeq, "native-head-tail", stage, "native-tool-result", void 0, view);
|
|
3183
|
+
if (plan !== null && plan.tokensAfter <= policy.nativeTargetTokens) return plan;
|
|
3184
|
+
}
|
|
3185
|
+
if (head === 0 && tail === 0) break;
|
|
3186
|
+
head = Math.floor(head / 2);
|
|
3187
|
+
tail = Math.floor(tail / 2);
|
|
3188
|
+
}
|
|
3189
|
+
return this.planAggregate(candidate, session, view, "native-whole-result", stage, policy.nativeTargetTokens, "native-tool-result");
|
|
3190
|
+
}
|
|
3191
|
+
planFresh(candidate, session, policy, view) {
|
|
3192
|
+
if (typeof candidate.event.surfaceOp === "object") return null;
|
|
3193
|
+
const content = candidate.event.data.message.content;
|
|
3194
|
+
const tokensBefore = exactTokens(candidate.count);
|
|
3195
|
+
if (tokensBefore === void 0 || tokensBefore <= policy.freshTriggerTokens) return null;
|
|
3196
|
+
const sourceSeq = candidate.seq;
|
|
3197
|
+
const sourceRef = this.sourceRef(session, sourceSeq);
|
|
3198
|
+
const textBlock = onlyTextBlock(content);
|
|
3199
|
+
if (textBlock !== null) {
|
|
3200
|
+
let budgetChars = Math.max(1, Math.floor(codePointLength(textBlock.text) * .75));
|
|
3201
|
+
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
3202
|
+
const output = reduceFreshToolResult({
|
|
3203
|
+
toolName: candidate.call.name,
|
|
3204
|
+
argumentsText: candidate.call.arguments,
|
|
3205
|
+
text: textBlock.text,
|
|
3206
|
+
budgetChars,
|
|
3207
|
+
sourceRef,
|
|
3208
|
+
isError: candidate.event.data.message.isError === true || candidate.event.data.error !== void 0
|
|
3209
|
+
});
|
|
3210
|
+
if (output !== null) {
|
|
3211
|
+
const plan = this.plan(candidate, [{
|
|
3212
|
+
...textBlock,
|
|
3213
|
+
text: output.text
|
|
3214
|
+
}], sourceSeq, output.reducer, "fresh", "fresh", void 0, view);
|
|
3215
|
+
if (plan !== null && plan.tokensAfter <= policy.freshTargetTokens) return plan;
|
|
3216
|
+
}
|
|
3217
|
+
if (budgetChars === 1) break;
|
|
3218
|
+
budgetChars = Math.max(1, Math.floor(budgetChars / 2));
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
return this.planAggregate(candidate, session, view, "fresh-whole-result", "fresh", policy.freshTargetTokens, "fresh");
|
|
3222
|
+
}
|
|
3223
|
+
planAggregate(candidate, session, view, reducer = "fresh-step-aggregate", stage = "fresh", targetTokens, component = "aggregate", historyMode) {
|
|
3224
|
+
if (this.isError(candidate)) return this.planErrorEvidence(candidate, session, view, stage, targetTokens, component, historyMode);
|
|
3225
|
+
const sourceSeq = this.rootToolResultSeq(session, candidate.seq);
|
|
3226
|
+
const sourceRef = this.sourceRef(session, sourceSeq);
|
|
3227
|
+
const text = [
|
|
3228
|
+
"[Tool result reduced to satisfy the completed-step aggregate budget]",
|
|
3229
|
+
`tool: ${candidate.call.name}`,
|
|
3230
|
+
`source: ${sourceRef}`,
|
|
3231
|
+
"Use context_compression_retrieve with this source if the omitted evidence is necessary."
|
|
3232
|
+
].join("\n");
|
|
3233
|
+
const plan = this.plan(candidate, [{
|
|
3234
|
+
type: "text",
|
|
3235
|
+
text
|
|
3236
|
+
}], sourceSeq, reducer, stage, component, historyMode, view);
|
|
3237
|
+
return plan !== null && (targetTokens === void 0 || plan.tokensAfter <= targetTokens) ? plan : null;
|
|
3238
|
+
}
|
|
3239
|
+
/** Preserve bounded diagnostic evidence whenever an all-text error is reduced. */
|
|
3240
|
+
planErrorEvidence(candidate, session, view, stage, targetTokens, component = "aggregate", historyMode) {
|
|
3241
|
+
if (!this.isError(candidate)) return null;
|
|
3242
|
+
const blocks = onlyTextBlocks(candidate.event.data.message.content);
|
|
3243
|
+
if (blocks === null) return null;
|
|
3244
|
+
const text = blocks.map((block) => block.text).join("\n");
|
|
3245
|
+
const sourceSeq = this.rootToolResultSeq(session, candidate.seq);
|
|
3246
|
+
const sourceRef = this.sourceRef(session, sourceSeq);
|
|
3247
|
+
const output = historicalPlaceholder({
|
|
3248
|
+
toolName: candidate.call.name,
|
|
3249
|
+
sourceRef,
|
|
3250
|
+
charsBefore: codePointLength(text),
|
|
3251
|
+
isError: true,
|
|
3252
|
+
text,
|
|
3253
|
+
compact: false
|
|
3254
|
+
});
|
|
3255
|
+
if (!verifyReduction({
|
|
3256
|
+
toolName: candidate.call.name,
|
|
3257
|
+
argumentsText: candidate.call.arguments,
|
|
3258
|
+
text,
|
|
3259
|
+
budgetChars: 1200,
|
|
3260
|
+
sourceRef,
|
|
3261
|
+
isError: true
|
|
3262
|
+
}, output)) return null;
|
|
3263
|
+
const plan = this.plan(candidate, [{
|
|
3264
|
+
type: "text",
|
|
3265
|
+
text: output.text
|
|
3266
|
+
}], sourceSeq, "error-evidence-placeholder", stage, component, historyMode, view);
|
|
3267
|
+
return plan !== null && (targetTokens === void 0 || plan.tokensAfter <= targetTokens) ? plan : null;
|
|
3268
|
+
}
|
|
3269
|
+
isError(candidate) {
|
|
3270
|
+
return candidate.event.data.message.isError === true || candidate.event.data.error !== void 0;
|
|
3271
|
+
}
|
|
3272
|
+
historyOutcome(plans) {
|
|
3273
|
+
return {
|
|
3274
|
+
kind: "planned",
|
|
3275
|
+
plans: [...plans]
|
|
3276
|
+
};
|
|
3277
|
+
}
|
|
3278
|
+
planHistoricalAging(session, policy, view) {
|
|
3279
|
+
const candidates = this.snapshot(session, view);
|
|
3280
|
+
const exact = [];
|
|
3281
|
+
for (const candidate of candidates) {
|
|
3282
|
+
const tokens = exactTokens(candidate.count);
|
|
3283
|
+
if (tokens === void 0) {
|
|
3284
|
+
this.warnExactUnavailable(session, view, "history");
|
|
3285
|
+
return { kind: "exact-tokenizer-unavailable" };
|
|
3286
|
+
}
|
|
3287
|
+
exact.push(tokens);
|
|
3288
|
+
}
|
|
3289
|
+
const total = exact.reduce((sum, tokens) => sum + tokens, 0);
|
|
3290
|
+
const trigger = policy.historyTriggerTokens;
|
|
3291
|
+
const deadline = policy.microDeadlineTokens;
|
|
3292
|
+
const lastChance = deadline !== void 0 && view.totalTokens >= deadline;
|
|
3293
|
+
if (total <= trigger && !lastChance) return { kind: "below-profile-trigger" };
|
|
3294
|
+
const protectedSeqs = this.protectedHistoryCandidateSeqs(candidates, policy);
|
|
3295
|
+
const isUnsafe = (candidate) => {
|
|
3296
|
+
if (candidate.call.name === "context_compression_retrieve") return true;
|
|
3297
|
+
return onlyTextBlock(candidate.event.data.message.content)?.text.includes("[Old tool result content cleared from active context]") === true;
|
|
3298
|
+
};
|
|
3299
|
+
const safe = candidates.filter((candidate) => !isUnsafe(candidate));
|
|
3300
|
+
const eligible = safe.filter((candidate) => !protectedSeqs.has(candidate.seq));
|
|
3301
|
+
if (eligible.length === 0) return safe.length === 0 ? { kind: "no-safe-candidates" } : { kind: "protected-working-set" };
|
|
3302
|
+
const planned = [];
|
|
3303
|
+
let reclaim = 0;
|
|
3304
|
+
const microTarget = deadline === void 0 ? void 0 : Math.max(0, deadline - policy.historyMinReclaimTokens);
|
|
3305
|
+
const required = Math.max(policy.historyMinReclaimTokens, total - trigger, ...microTarget === void 0 ? [] : [view.totalTokens - microTarget]);
|
|
3306
|
+
const batchTarget = microTarget === void 0 ? policy.historyMinReclaimTokens : required;
|
|
3307
|
+
for (const candidate of eligible) {
|
|
3308
|
+
const block = onlyTextBlock(candidate.event.data.message.content);
|
|
3309
|
+
const sourceSeq = this.rootToolResultSeq(session, candidate.seq);
|
|
3310
|
+
if (block === null) {
|
|
3311
|
+
const plan = this.planAggregate(candidate, session, view, "historical-rich-whole-result", "pressure", void 0, "history", policy.historyMode);
|
|
3312
|
+
if (plan === null) continue;
|
|
3313
|
+
planned.push(plan);
|
|
3314
|
+
reclaim += plan.tokensBefore - plan.tokensAfter;
|
|
3315
|
+
if (reclaim >= required) break;
|
|
3316
|
+
continue;
|
|
3317
|
+
}
|
|
3318
|
+
const output = historicalPlaceholder({
|
|
3319
|
+
toolName: candidate.call.name,
|
|
3320
|
+
sourceRef: this.sourceRef(session, sourceSeq),
|
|
3321
|
+
charsBefore: codePointLength(block.text),
|
|
3322
|
+
isError: candidate.event.data.message.isError === true || candidate.event.data.error !== void 0,
|
|
3323
|
+
text: block.text,
|
|
3324
|
+
compact: false
|
|
3325
|
+
});
|
|
3326
|
+
if (!verifyReduction({
|
|
3327
|
+
toolName: candidate.call.name,
|
|
3328
|
+
argumentsText: candidate.call.arguments,
|
|
3329
|
+
text: block.text,
|
|
3330
|
+
budgetChars: 1200,
|
|
3331
|
+
sourceRef: this.sourceRef(session, sourceSeq),
|
|
3332
|
+
isError: candidate.event.data.message.isError === true || candidate.event.data.error !== void 0
|
|
3333
|
+
}, output)) continue;
|
|
3334
|
+
const plan = this.plan(candidate, [{
|
|
3335
|
+
...block,
|
|
3336
|
+
text: output.text
|
|
3337
|
+
}], sourceSeq, output.reducer, "pressure", "history", policy.historyMode, view);
|
|
3338
|
+
if (plan === null) continue;
|
|
3339
|
+
planned.push(plan);
|
|
3340
|
+
reclaim += plan.tokensBefore - plan.tokensAfter;
|
|
3341
|
+
if (reclaim >= required) break;
|
|
3342
|
+
}
|
|
3343
|
+
if (reclaim >= batchTarget && planned.length > 0) return this.historyOutcome(planned);
|
|
3344
|
+
return lastChance ? {
|
|
3345
|
+
kind: "cannot-reach-deadline-target",
|
|
3346
|
+
reclaim,
|
|
3347
|
+
required
|
|
3348
|
+
} : {
|
|
3349
|
+
kind: "insufficient-reclaim",
|
|
3350
|
+
reclaim,
|
|
3351
|
+
required
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3354
|
+
protectedHistoryResultSeqs(session, policy, view) {
|
|
3355
|
+
const candidates = this.snapshot(session, view);
|
|
3356
|
+
if (candidates.some((candidate) => exactTokens(candidate.count) === void 0)) return null;
|
|
3357
|
+
return this.protectedHistoryCandidateSeqs(candidates, policy);
|
|
3358
|
+
}
|
|
3359
|
+
/** Select the newest completed tool calls and token tail for History-derived stages. */
|
|
3360
|
+
protectedHistoryCandidateSeqs(candidates, policy) {
|
|
3361
|
+
const protectedSeqs = /* @__PURE__ */ new Set();
|
|
3362
|
+
for (let index = candidates.length - 1; index >= 0 && candidates.length - index <= policy.historyKeepRecentToolCalls; index--) {
|
|
3363
|
+
const candidate = candidates[index];
|
|
3364
|
+
if (candidate !== void 0) protectedSeqs.add(candidate.seq);
|
|
3365
|
+
}
|
|
3366
|
+
let recentTokens = 0;
|
|
3367
|
+
for (let index = candidates.length - 1; index >= 0 && recentTokens < policy.historyKeepRecentTokens; index--) {
|
|
3368
|
+
const candidate = candidates[index];
|
|
3369
|
+
if (candidate === void 0) continue;
|
|
3370
|
+
protectedSeqs.add(candidate.seq);
|
|
3371
|
+
recentTokens += exactTokens(candidate.count) ?? 0;
|
|
3372
|
+
}
|
|
3373
|
+
return protectedSeqs;
|
|
3374
|
+
}
|
|
3375
|
+
/** Atomically replace at most one oldest safe completed tool-call group. */
|
|
3376
|
+
landOldestTailTrimGroup(session, policy, view) {
|
|
3377
|
+
const tailTrim = policy.tailTrim;
|
|
3378
|
+
if (tailTrim?.enabled !== true) return;
|
|
3379
|
+
const events = sessionEvents(session);
|
|
3380
|
+
if (view.currentSurface.kind !== "exact-tokenizer" || view.currentSurface.tokens <= tailTrim.triggerTokens) {
|
|
3381
|
+
if (view.currentSurface.kind !== "exact-tokenizer") this.warnExactUnavailable(session, view, "tailtrim");
|
|
3382
|
+
this.auditComponent(session, policy, "tail-trim", "pressure", "skipped", view.currentSurface.kind !== "exact-tokenizer" ? "exact-tokenizer-unavailable" : "at-or-below-trigger", {
|
|
3383
|
+
measurementKind: view.currentSurface.kind,
|
|
3384
|
+
...view.currentSurface.kind === "exact-tokenizer" ? { currentTokens: view.currentSurface.tokens } : {},
|
|
3385
|
+
triggerTokens: tailTrim.triggerTokens
|
|
3386
|
+
});
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
const surfaceCount = view.currentSurface;
|
|
3390
|
+
if (!this.hasRecoveryTool(session)) {
|
|
3391
|
+
this.auditComponent(session, policy, "tail-trim", "pressure", "skipped", "recovery-tool-unavailable", {
|
|
3392
|
+
measurementKind: "exact-tokenizer",
|
|
3393
|
+
currentTokens: surfaceCount.tokens,
|
|
3394
|
+
triggerTokens: tailTrim.triggerTokens
|
|
3395
|
+
});
|
|
3396
|
+
return;
|
|
3397
|
+
}
|
|
3398
|
+
if (!this.hasOpenTurn(session)) {
|
|
3399
|
+
this.auditComponent(session, policy, "tail-trim", "pressure", "skipped", "no-open-turn", {
|
|
3400
|
+
measurementKind: "exact-tokenizer",
|
|
3401
|
+
currentTokens: surfaceCount.tokens,
|
|
3402
|
+
triggerTokens: tailTrim.triggerTokens
|
|
3403
|
+
});
|
|
3404
|
+
return;
|
|
3405
|
+
}
|
|
3406
|
+
const protectedResults = this.protectedHistoryResultSeqs(session, policy, view);
|
|
3407
|
+
if (protectedResults === null) {
|
|
3408
|
+
this.auditComponent(session, policy, "tail-trim", "pressure", "skipped", "exact-tokenizer-unavailable-in-protected-set", {
|
|
3409
|
+
measurementKind: "unavailable",
|
|
3410
|
+
currentTokens: surfaceCount.tokens,
|
|
3411
|
+
triggerTokens: tailTrim.triggerTokens
|
|
3412
|
+
});
|
|
3413
|
+
return;
|
|
3414
|
+
}
|
|
3415
|
+
const measured = new Map(view.measuredNodes.map((node) => [node.seq, node.count]));
|
|
3416
|
+
const heuristic = new Map(view.nodes.map((node) => [node.seq, node.tokens]));
|
|
3417
|
+
const completedTurns = /* @__PURE__ */ new Set();
|
|
3418
|
+
const completedSteps = /* @__PURE__ */ new Set();
|
|
3419
|
+
for (const event of events) if (event.type === "turn/end") completedTurns.add(event.data.turn);
|
|
3420
|
+
else if (event.type === "step/end") completedSteps.add(`${String(event.data.turn)}:${String(event.data.step)}`);
|
|
3421
|
+
const firstCompletedSurfaceTurn = session.surface.nodes.map((seq) => events[seq]).filter((event) => (event?.type === "assistant/message" || event?.type === "tool/result") && completedTurns.has(event.data.turn)).reduce((first, event) => first === void 0 ? event.data.turn : Math.min(first, event.data.turn), void 0);
|
|
3422
|
+
const nodes = [...session.surface.nodes];
|
|
3423
|
+
for (let index = 0; index < nodes.length; index++) {
|
|
3424
|
+
const assistantSeq = nodes[index];
|
|
3425
|
+
if (assistantSeq === void 0) continue;
|
|
3426
|
+
const assistant = events[assistantSeq];
|
|
3427
|
+
if (assistant?.type !== "assistant/message" || assistant.data.interrupted === true || assistant.data.message.content.length === 0 || assistant.data.message.content.some((block) => block.type !== "tool-call") || assistant.data.turn === firstCompletedSurfaceTurn || !completedTurns.has(assistant.data.turn) || !completedSteps.has(`${String(assistant.data.turn)}:${String(assistant.data.step)}`)) continue;
|
|
3428
|
+
const calls = assistant.data.message.content;
|
|
3429
|
+
if (calls.some((call) => call.name === "context_compression_retrieve")) continue;
|
|
3430
|
+
const callIds = calls.map((call) => String(call.id));
|
|
3431
|
+
if (new Set(callIds).size !== callIds.length) continue;
|
|
3432
|
+
const resultSeqs = nodes.slice(index + 1, index + 1 + calls.length);
|
|
3433
|
+
if (resultSeqs.length !== calls.length || resultSeqs.some((seq) => protectedResults.has(seq))) continue;
|
|
3434
|
+
const results = resultSeqs.map((seq) => events[seq]);
|
|
3435
|
+
if (results.some((event) => {
|
|
3436
|
+
if (event?.type !== "tool/result" || event.data.turn !== assistant.data.turn || event.data.step !== assistant.data.step || event.data.error !== void 0) return true;
|
|
3437
|
+
const message = event.data.message;
|
|
3438
|
+
if (message.isError === true) return true;
|
|
3439
|
+
return message.content.some((contentBlock) => contentBlock.type !== "text");
|
|
3440
|
+
})) continue;
|
|
3441
|
+
const next = events[nodes[index + 1 + calls.length] ?? -1];
|
|
3442
|
+
if (next?.type === "tool/result" && next.data.turn === assistant.data.turn && next.data.step === assistant.data.step) continue;
|
|
3443
|
+
const resultIds = results.map((event) => event?.type === "tool/result" ? String(event.data.message.toolCallId) : "");
|
|
3444
|
+
if (new Set(resultIds).size !== resultIds.length || resultIds.some((id, resultIndex) => id !== callIds[resultIndex])) continue;
|
|
3445
|
+
const shadowedSeqs = [assistantSeq, ...resultSeqs];
|
|
3446
|
+
const roots = shadowedSeqs.map((seq) => this.uniqueAppendRoot(session, seq));
|
|
3447
|
+
if (roots.some((root) => root === null)) continue;
|
|
3448
|
+
const sourceEventSeqs = roots;
|
|
3449
|
+
if (new Set(sourceEventSeqs).size !== sourceEventSeqs.length) continue;
|
|
3450
|
+
const counts = shadowedSeqs.map((seq) => measured.get(seq));
|
|
3451
|
+
if (counts.some((count) => count?.kind !== "exact-tokenizer")) continue;
|
|
3452
|
+
const exactCounts = counts;
|
|
3453
|
+
if (exactCounts.some((count) => count.tokenizerId !== surfaceCount.tokenizerId || count.tokenizerRevision !== surfaceCount.tokenizerRevision)) continue;
|
|
3454
|
+
const tokensBefore = exactCounts.reduce((sum, count) => sum + count.tokens, 0);
|
|
3455
|
+
const manifestSeq = events.length;
|
|
3456
|
+
const ref = tailTrimRef(String(session.id), manifestSeq);
|
|
3457
|
+
const stub = tailTrimStub(ref, calls.map((call) => call.name), sourceEventSeqs);
|
|
3458
|
+
if (stub === null) continue;
|
|
3459
|
+
const stubCount = countExactCanonicalTextFields([stub], (candidate) => view.countCanonicalText(candidate), "TailTrim group stub");
|
|
3460
|
+
if (stubCount.kind !== "exact-tokenizer" || stubCount.tokenizerId !== surfaceCount.tokenizerId || stubCount.tokenizerRevision !== surfaceCount.tokenizerRevision || stubCount.tokens <= 0 || tokensBefore - stubCount.tokens < policy.historyMinReclaimTokens) continue;
|
|
3461
|
+
const heuristicTokens = shadowedSeqs.reduce((sum, seq) => sum + (heuristic.get(seq) ?? 0), 0);
|
|
3462
|
+
const range = {
|
|
3463
|
+
start: assistantSeq,
|
|
3464
|
+
end: resultSeqs.at(-1) ?? assistantSeq
|
|
3465
|
+
};
|
|
3466
|
+
if (!this.reserveTailTrimBoundaryAttempt(session)) {
|
|
3467
|
+
this.auditComponent(session, policy, "tail-trim", "pressure", "skipped", "already-attempted-at-request-boundary", {
|
|
3468
|
+
measurementKind: "exact-tokenizer",
|
|
3469
|
+
currentTokens: surfaceCount.tokens,
|
|
3470
|
+
triggerTokens: tailTrim.triggerTokens
|
|
3471
|
+
});
|
|
3472
|
+
return;
|
|
3473
|
+
}
|
|
3474
|
+
const manifest = session.append("compaction/prune", {
|
|
3475
|
+
shadowedRange: range,
|
|
3476
|
+
shadowedSeqs,
|
|
3477
|
+
shadowedTokenCount: heuristicTokens
|
|
3478
|
+
});
|
|
3479
|
+
let replacement;
|
|
3480
|
+
try {
|
|
3481
|
+
replacement = session.append("user/message", tailTrimMessage(stub), {
|
|
3482
|
+
surfaceOp: {
|
|
3483
|
+
op: "replace",
|
|
3484
|
+
startSeq: range.start,
|
|
3485
|
+
endSeq: range.end
|
|
3486
|
+
},
|
|
3487
|
+
sourceEventSeqs: [manifest.seq, ...shadowedSeqs]
|
|
3488
|
+
});
|
|
3489
|
+
} catch (error) {
|
|
3490
|
+
this.auditPublicationFailure(session, "pressure", "tail-trim", manifest.seq, error);
|
|
3491
|
+
return;
|
|
3492
|
+
}
|
|
3493
|
+
emitCompressionAudit(this.ctx.logger, {
|
|
3494
|
+
schemaVersion: 1,
|
|
3495
|
+
kind: "rewrite",
|
|
3496
|
+
sessionId: String(session.id),
|
|
3497
|
+
profile: policy.profile,
|
|
3498
|
+
component: "tail-trim",
|
|
3499
|
+
stage: "pressure",
|
|
3500
|
+
reducer: "pair-preserving-tail-trim",
|
|
3501
|
+
manifestEventType: "compaction/prune",
|
|
3502
|
+
manifestSeq: manifest.seq,
|
|
3503
|
+
replacementSeq: replacement.seq,
|
|
3504
|
+
sourceSeqs: sourceEventSeqs,
|
|
3505
|
+
tokensBefore,
|
|
3506
|
+
tokensAfter: stubCount.tokens,
|
|
3507
|
+
tokensRemoved: tokensBefore - stubCount.tokens,
|
|
3508
|
+
tokenizerId: stubCount.tokenizerId,
|
|
3509
|
+
tokenizerRevision: stubCount.tokenizerRevision
|
|
3510
|
+
});
|
|
3511
|
+
return;
|
|
3512
|
+
}
|
|
3513
|
+
this.auditComponent(session, policy, "tail-trim", "pressure", "skipped", "no-safe-eligible-tool-group", {
|
|
3514
|
+
measurementKind: "exact-tokenizer",
|
|
3515
|
+
currentTokens: surfaceCount.tokens,
|
|
3516
|
+
triggerTokens: tailTrim.triggerTokens
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
reserveTailTrimBoundaryAttempt(session) {
|
|
3520
|
+
const boundary = this.activeRequestBoundaries.get(session);
|
|
3521
|
+
if (boundary === void 0) return true;
|
|
3522
|
+
if (this.tailTrimBoundaryAttempts.get(session) === boundary) return false;
|
|
3523
|
+
this.tailTrimBoundaryAttempts.set(session, boundary);
|
|
3524
|
+
return true;
|
|
3525
|
+
}
|
|
3526
|
+
uniqueAppendRoot(session, seq) {
|
|
3527
|
+
const events = sessionEvents(session);
|
|
3528
|
+
const pending = [{
|
|
3529
|
+
seq,
|
|
3530
|
+
depth: 0
|
|
3531
|
+
}];
|
|
3532
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3533
|
+
const roots = /* @__PURE__ */ new Set();
|
|
3534
|
+
while (pending.length > 0) {
|
|
3535
|
+
const next = pending.pop();
|
|
3536
|
+
if (next === void 0 || next.depth > 64 || visited.has(next.seq)) continue;
|
|
3537
|
+
visited.add(next.seq);
|
|
3538
|
+
if (visited.size > 64) return null;
|
|
3539
|
+
const event = events[next.seq];
|
|
3540
|
+
if (event === void 0 || event.type !== "assistant/message" && event.type !== "tool/result") return null;
|
|
3541
|
+
if (event.surfaceOp === "append") roots.add(event.seq);
|
|
3542
|
+
else if (typeof event.surfaceOp === "object") {
|
|
3543
|
+
const sources = event.sourceEventSeqs;
|
|
3544
|
+
if (sources === void 0 || sources.length === 0) return null;
|
|
3545
|
+
for (const source of sources) pending.push({
|
|
3546
|
+
seq: source,
|
|
3547
|
+
depth: next.depth + 1
|
|
3548
|
+
});
|
|
3549
|
+
} else return null;
|
|
3550
|
+
if (roots.size > 1) return null;
|
|
3551
|
+
}
|
|
3552
|
+
return roots.size === 1 ? [...roots][0] ?? null : null;
|
|
3553
|
+
}
|
|
3554
|
+
plan(candidate, content, sourceSeq, reducer, stage, component, historyMode, view) {
|
|
3555
|
+
const countBefore = candidate.count;
|
|
3556
|
+
if (countBefore.kind !== "exact-tokenizer") return null;
|
|
3557
|
+
const countAfter = countToolContent(content, view);
|
|
3558
|
+
if (countAfter.kind !== "exact-tokenizer" || countAfter.tokenizerId !== countBefore.tokenizerId || countAfter.tokenizerRevision !== countBefore.tokenizerRevision) return null;
|
|
3559
|
+
const tokensBefore = countBefore.tokens;
|
|
3560
|
+
const tokensAfter = countAfter.tokens;
|
|
3561
|
+
if (tokensAfter <= 0 || tokensAfter >= tokensBefore) return null;
|
|
3562
|
+
const charsBefore = candidate.characterPressure;
|
|
3563
|
+
const charsAfter = this.pressureCost(content);
|
|
3564
|
+
return {
|
|
3565
|
+
candidate,
|
|
3566
|
+
content,
|
|
3567
|
+
sourceSeq,
|
|
3568
|
+
reducer,
|
|
3569
|
+
stage,
|
|
3570
|
+
component,
|
|
3571
|
+
...historyMode === void 0 ? {} : { historyMode },
|
|
3572
|
+
charsBefore,
|
|
3573
|
+
charsAfter,
|
|
3574
|
+
tokensBefore,
|
|
3575
|
+
tokensAfter,
|
|
3576
|
+
tokenizerId: countBefore.tokenizerId,
|
|
3577
|
+
tokenizerRevision: countBefore.tokenizerRevision
|
|
3578
|
+
};
|
|
3579
|
+
}
|
|
3580
|
+
land(session, plan) {
|
|
3581
|
+
const { candidate } = plan;
|
|
3582
|
+
const message = freezeMessage({
|
|
3583
|
+
...candidate.event.data.message,
|
|
3584
|
+
content: plan.content
|
|
3585
|
+
});
|
|
3586
|
+
const manifest = session.append("compaction/prune", {
|
|
3587
|
+
shadowedRange: {
|
|
3588
|
+
start: SessionSeq(candidate.seq),
|
|
3589
|
+
end: SessionSeq(candidate.seq)
|
|
3590
|
+
},
|
|
3591
|
+
shadowedSeqs: [SessionSeq(candidate.seq)],
|
|
3592
|
+
shadowedTokenCount: candidate.shadowedHeuristicTokenCount
|
|
3593
|
+
});
|
|
3594
|
+
let replacement;
|
|
3595
|
+
try {
|
|
3596
|
+
replacement = session.append("tool/result", {
|
|
3597
|
+
...candidate.event.data,
|
|
3598
|
+
message
|
|
3599
|
+
}, {
|
|
3600
|
+
surfaceOp: {
|
|
3601
|
+
op: "replace",
|
|
3602
|
+
startSeq: SessionSeq(candidate.seq),
|
|
3603
|
+
endSeq: SessionSeq(candidate.seq)
|
|
3604
|
+
},
|
|
3605
|
+
sourceEventSeqs: [SessionSeq(candidate.seq)]
|
|
3606
|
+
});
|
|
3607
|
+
} catch (error) {
|
|
3608
|
+
this.auditPublicationFailure(session, plan.stage, plan.component, manifest.seq, error);
|
|
3609
|
+
return null;
|
|
3610
|
+
}
|
|
3611
|
+
emitCompressionAudit(this.ctx.logger, {
|
|
3612
|
+
schemaVersion: 1,
|
|
3613
|
+
kind: "rewrite",
|
|
3614
|
+
sessionId: String(session.id),
|
|
3615
|
+
profile: this.activeSettings(session).profile,
|
|
3616
|
+
component: plan.component,
|
|
3617
|
+
stage: plan.stage,
|
|
3618
|
+
reducer: plan.reducer,
|
|
3619
|
+
...plan.historyMode === void 0 ? {} : { historyMode: plan.historyMode },
|
|
3620
|
+
manifestEventType: "compaction/prune",
|
|
3621
|
+
manifestSeq: manifest.seq,
|
|
3622
|
+
replacementSeq: replacement.seq,
|
|
3623
|
+
sourceSeqs: [plan.sourceSeq],
|
|
3624
|
+
tokensBefore: plan.tokensBefore,
|
|
3625
|
+
tokensAfter: plan.tokensAfter,
|
|
3626
|
+
tokensRemoved: plan.tokensBefore - plan.tokensAfter,
|
|
3627
|
+
tokenizerId: plan.tokenizerId,
|
|
3628
|
+
tokenizerRevision: plan.tokenizerRevision
|
|
3629
|
+
});
|
|
3630
|
+
return {
|
|
3631
|
+
originalSeq: candidate.seq,
|
|
3632
|
+
sourceSeq: plan.sourceSeq,
|
|
3633
|
+
replacementSeq: replacement.seq,
|
|
3634
|
+
callId: candidate.event.data.message.toolCallId,
|
|
3635
|
+
reducer: plan.reducer,
|
|
3636
|
+
stage: plan.stage,
|
|
3637
|
+
charsBefore: plan.charsBefore,
|
|
3638
|
+
charsAfter: plan.charsAfter,
|
|
3639
|
+
tokensBefore: plan.tokensBefore,
|
|
3640
|
+
tokensAfter: plan.tokensAfter
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
landAll(session, plans) {
|
|
3644
|
+
if (plans.length === 0) return [];
|
|
3645
|
+
if (!this.hasRecoveryTool(session)) {
|
|
3646
|
+
this.warnOnce(session, "missing-context-retrieve", "context-compression kept original tool results because context_compression_retrieve is unavailable");
|
|
3647
|
+
return [];
|
|
3648
|
+
}
|
|
3649
|
+
if (!this.hasOpenTurn(session)) throw new Error("tool-result pruning cannot append a surface replacement outside any open turn");
|
|
3650
|
+
const landed = [];
|
|
3651
|
+
for (const plan of plans) {
|
|
3652
|
+
const entry = this.land(session, plan);
|
|
3653
|
+
if (entry === null) break;
|
|
3654
|
+
landed.push(entry);
|
|
3655
|
+
}
|
|
3656
|
+
return landed;
|
|
3657
|
+
}
|
|
3658
|
+
hasRecoveryTool(session) {
|
|
3659
|
+
const tools = this.ctx.get("tools");
|
|
3660
|
+
if (tools === void 0) return false;
|
|
3661
|
+
const agent = this.ctx.get("agents")?.get(session.id);
|
|
3662
|
+
return tools.get("context_compression_retrieve", agent) !== void 0;
|
|
3663
|
+
}
|
|
3664
|
+
auditHistoryEvaluation(session, policy, view, allowed, outcome) {
|
|
3665
|
+
if (policy.historyMode === "disabled") {
|
|
3666
|
+
this.auditComponent(session, policy, "history", "pressure", "disabled", "profile-policy", { historyMode: policy.historyMode });
|
|
3667
|
+
return;
|
|
3668
|
+
}
|
|
3669
|
+
if (!allowed && outcome.kind === "planned") {
|
|
3670
|
+
const deadlineTrigger = policy.microDeadlineTokens;
|
|
3671
|
+
const capacity = deadlineTrigger === void 0 ? session.requestContext()?.contextWindow : void 0;
|
|
3672
|
+
const capacityTrigger = deadlineTrigger !== void 0 ? deadlineTrigger : Number.isSafeInteger(capacity) && capacity !== void 0 && capacity > 0 ? Math.floor(capacity * CAPACITY_PRESSURE_RATIO) : void 0;
|
|
3673
|
+
this.auditComponent(session, policy, "history", "pressure", "skipped", policy.historyMode === "capacity-pressure" ? "below-micro-deadline" : "adaptive-cost-rejected", {
|
|
3674
|
+
historyMode: policy.historyMode,
|
|
3675
|
+
measurementKind: view.currentSurface.kind,
|
|
3676
|
+
currentTokens: view.totalTokens,
|
|
3677
|
+
...capacityTrigger === void 0 ? {} : { triggerTokens: capacityTrigger }
|
|
3678
|
+
});
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
const deadline = policy.microDeadlineTokens;
|
|
3682
|
+
const lastChance = deadline !== void 0 && view.totalTokens >= deadline;
|
|
3683
|
+
const detail = (extra = {}) => ({
|
|
3684
|
+
historyMode: policy.historyMode,
|
|
3685
|
+
measurementKind: outcome.kind === "exact-tokenizer-unavailable" ? "unavailable" : "exact-tokenizer",
|
|
3686
|
+
currentTokens: view.totalTokens,
|
|
3687
|
+
...outcome.kind === "insufficient-reclaim" || outcome.kind === "cannot-reach-deadline-target" ? {
|
|
3688
|
+
reclaimTokens: outcome.reclaim,
|
|
3689
|
+
requiredTokens: outcome.required
|
|
3690
|
+
} : {},
|
|
3691
|
+
...extra
|
|
3692
|
+
});
|
|
3693
|
+
switch (outcome.kind) {
|
|
3694
|
+
case "exact-tokenizer-unavailable":
|
|
3695
|
+
this.auditComponent(session, policy, "history", "pressure", "skipped", "exact-tokenizer-unavailable", detail({ triggerTokens: policy.historyTriggerTokens }));
|
|
3696
|
+
return;
|
|
3697
|
+
case "below-profile-trigger":
|
|
3698
|
+
this.auditComponent(session, policy, "history", "pressure", "skipped", "below-profile-trigger", detail({ triggerTokens: policy.historyTriggerTokens }));
|
|
3699
|
+
return;
|
|
3700
|
+
case "no-safe-candidates":
|
|
3701
|
+
this.auditComponent(session, policy, "history", "pressure", "skipped", "no-safe-candidates", detail({ triggerTokens: policy.historyTriggerTokens }));
|
|
3702
|
+
return;
|
|
3703
|
+
case "protected-working-set":
|
|
3704
|
+
this.auditComponent(session, policy, "history", "pressure", "skipped", "protected-working-set", detail({ triggerTokens: policy.historyTriggerTokens }));
|
|
3705
|
+
return;
|
|
3706
|
+
case "insufficient-reclaim":
|
|
3707
|
+
this.auditComponent(session, policy, "history", "pressure", "skipped", "insufficient-reclaim", detail({ triggerTokens: policy.historyTriggerTokens }));
|
|
3708
|
+
return;
|
|
3709
|
+
case "cannot-reach-deadline-target":
|
|
3710
|
+
this.auditComponent(session, policy, "history", "pressure", "skipped", "cannot-reach-deadline-target", detail(lastChance ? { triggerTokens: deadline } : {}));
|
|
3711
|
+
return;
|
|
3712
|
+
case "planned":
|
|
3713
|
+
this.auditComponent(session, policy, "history", "pressure", "skipped", outcome.plans.length > 0 ? "recovery-tool-unavailable" : "insufficient-reclaim", detail({ triggerTokens: lastChance ? deadline : policy.historyTriggerTokens }));
|
|
3714
|
+
return;
|
|
3715
|
+
/* v8 ignore next -- closed-union exhaustiveness guard */
|
|
3716
|
+
default: return assertNever(outcome, "history plan outcome");
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
auditComponent(session, policy, component, stage, status, reason, detail = {}) {
|
|
3720
|
+
emitCompressionAudit(this.ctx.logger, {
|
|
3721
|
+
schemaVersion: 1,
|
|
3722
|
+
kind: "component-evaluation",
|
|
3723
|
+
sessionId: String(session.id),
|
|
3724
|
+
profile: policy.profile,
|
|
3725
|
+
component,
|
|
3726
|
+
stage,
|
|
3727
|
+
status,
|
|
3728
|
+
reason,
|
|
3729
|
+
...detail
|
|
3730
|
+
});
|
|
3731
|
+
}
|
|
3732
|
+
auditFailure(session, stage, operation, error) {
|
|
3733
|
+
emitCompressionAudit(this.ctx.logger, {
|
|
3734
|
+
schemaVersion: 1,
|
|
3735
|
+
kind: "failure",
|
|
3736
|
+
sessionId: String(session.id),
|
|
3737
|
+
stage,
|
|
3738
|
+
operation,
|
|
3739
|
+
errorName: error instanceof Error ? error.name : "UnknownError",
|
|
3740
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
3741
|
+
});
|
|
3742
|
+
}
|
|
3743
|
+
auditPublicationFailure(session, stage, component, manifestSeq, error) {
|
|
3744
|
+
emitCompressionAudit(this.ctx.logger, {
|
|
3745
|
+
schemaVersion: 1,
|
|
3746
|
+
kind: "failure",
|
|
3747
|
+
sessionId: String(session.id),
|
|
3748
|
+
stage,
|
|
3749
|
+
operation: "publication",
|
|
3750
|
+
component,
|
|
3751
|
+
manifestSeq,
|
|
3752
|
+
errorName: error instanceof Error ? error.name : "UnknownError",
|
|
3753
|
+
errorMessage: "surface replacement append failed after compaction/prune committed"
|
|
3754
|
+
});
|
|
3755
|
+
}
|
|
3756
|
+
warnExactUnavailable(session, view, gate) {
|
|
3757
|
+
const provider = view.providerRoute ?? "unbound-provider";
|
|
3758
|
+
const model = view.modelId ?? "unbound-model";
|
|
3759
|
+
this.warnOnce(session, `exact-tokenizer:${gate}:${provider}\0${model}`, "context-compression %s kept original tool results because exact tokenizer counts are unavailable for %s/%s", gate, provider, model);
|
|
3760
|
+
}
|
|
3761
|
+
warnOnce(session, key, message, ...args) {
|
|
3762
|
+
let warned = this.warnedFailures.get(session);
|
|
3763
|
+
if (warned === void 0) {
|
|
3764
|
+
warned = /* @__PURE__ */ new Set();
|
|
3765
|
+
this.warnedFailures.set(session, warned);
|
|
3766
|
+
}
|
|
3767
|
+
if (warned.has(key)) return;
|
|
3768
|
+
warned.add(key);
|
|
3769
|
+
this.ctx.logger.warn(message, ...args);
|
|
3770
|
+
}
|
|
3771
|
+
/** Surface replacements are durable turn work; reject before writing the audit half. */
|
|
3772
|
+
hasOpenTurn(session) {
|
|
3773
|
+
let open = false;
|
|
3774
|
+
for (const event of sessionEvents(session)) if (event.type === "turn/start") open = true;
|
|
3775
|
+
else if (event.type === "turn/end") open = false;
|
|
3776
|
+
return open;
|
|
3777
|
+
}
|
|
3778
|
+
rootToolResultSeq(session, seq) {
|
|
3779
|
+
const events = sessionEvents(session);
|
|
3780
|
+
let current = seq;
|
|
3781
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3782
|
+
while (!seen.has(current)) {
|
|
3783
|
+
seen.add(current);
|
|
3784
|
+
const event = events[current];
|
|
3785
|
+
if (event?.type !== "tool/result" || typeof event.surfaceOp !== "object") return current;
|
|
3786
|
+
const previous = event.sourceEventSeqs?.[0];
|
|
3787
|
+
if (previous === void 0) return current;
|
|
3788
|
+
current = previous;
|
|
3789
|
+
}
|
|
3790
|
+
return seq;
|
|
3791
|
+
}
|
|
3792
|
+
sourceRef(session, seq) {
|
|
3793
|
+
return `session://${session.id}/event/${String(seq)}`;
|
|
3794
|
+
}
|
|
3795
|
+
nativePruneContent(blocks, thresholdChars, headChars, tailChars, marker = PRUNE_MARKER) {
|
|
3796
|
+
const totalChars = this.measureContent(blocks);
|
|
3797
|
+
if (totalChars <= thresholdChars) return null;
|
|
3798
|
+
const markerChars = codePointLength(marker);
|
|
3799
|
+
const safeHead = Math.max(0, Math.min(headChars, thresholdChars - markerChars));
|
|
3800
|
+
const safeTail = Math.max(0, Math.min(tailChars, thresholdChars - markerChars - safeHead));
|
|
3801
|
+
const removedStart = safeHead;
|
|
3802
|
+
const removedEnd = totalChars - safeTail;
|
|
3803
|
+
const pruned = [];
|
|
3804
|
+
let consumed = 0;
|
|
3805
|
+
let markerInserted = false;
|
|
3806
|
+
for (const block of blocks) {
|
|
3807
|
+
if (block.type !== "text") {
|
|
3808
|
+
pruned.push(block);
|
|
3809
|
+
continue;
|
|
3810
|
+
}
|
|
3811
|
+
const points = Array.from(block.text);
|
|
3812
|
+
const blockStart = consumed;
|
|
3813
|
+
const blockEnd = blockStart + points.length;
|
|
3814
|
+
const headEnd = Math.min(points.length, Math.max(0, removedStart - blockStart));
|
|
3815
|
+
const tailStart = Math.min(points.length, Math.max(0, removedEnd - blockStart));
|
|
3816
|
+
const insertion = blockStart < removedEnd && blockEnd > removedStart && !markerInserted ? marker : "";
|
|
3817
|
+
if (insertion !== "") markerInserted = true;
|
|
3818
|
+
const text = points.slice(0, headEnd).join("") + insertion + points.slice(tailStart).join("");
|
|
3819
|
+
if (text !== "") pruned.push({
|
|
3820
|
+
...block,
|
|
3821
|
+
text
|
|
3822
|
+
});
|
|
3823
|
+
consumed = blockEnd;
|
|
3824
|
+
}
|
|
3825
|
+
if (!markerInserted) return null;
|
|
3826
|
+
const charsAfter = this.measureContent(pruned);
|
|
3827
|
+
return charsAfter <= thresholdChars && charsAfter < totalChars ? pruned : null;
|
|
3828
|
+
}
|
|
3829
|
+
};
|
|
3830
|
+
function onlyTextBlock(blocks) {
|
|
3831
|
+
return blocks.length === 1 && blocks[0]?.type === "text" ? blocks[0] : null;
|
|
3832
|
+
}
|
|
3833
|
+
function onlyTextBlocks(blocks) {
|
|
3834
|
+
return blocks.every((block) => block.type === "text") ? blocks : null;
|
|
3835
|
+
}
|
|
3836
|
+
function countToolContent(blocks, view) {
|
|
3837
|
+
const text = onlyTextBlocks(blocks);
|
|
3838
|
+
if (text === null) return unavailableCount("tool result contains unsupported rich content");
|
|
3839
|
+
return countExactCanonicalTextFields(text.map((block) => block.text), (candidate) => view.countCanonicalText(candidate), "tool result replacement");
|
|
3840
|
+
}
|
|
3841
|
+
function exactTokens(count) {
|
|
3842
|
+
return count.kind === "exact-tokenizer" ? count.tokens : void 0;
|
|
3843
|
+
}
|
|
3844
|
+
function sameProviderMeasurementKey(left, right) {
|
|
3845
|
+
return left.provider === right.provider && left.baseUrlClass === right.baseUrlClass && left.apiRoute === right.apiRoute && left.modelId === right.modelId && left.requestTemplateRevision === right.requestTemplateRevision && left.tokenizerRevision === right.tokenizerRevision && left.modality === right.modality;
|
|
3846
|
+
}
|
|
3847
|
+
function unavailableCount(reason) {
|
|
3848
|
+
return Object.freeze({
|
|
3849
|
+
kind: "unavailable",
|
|
3850
|
+
reason
|
|
3851
|
+
});
|
|
3852
|
+
}
|
|
3853
|
+
function recoveryMarker(sourceRef, label) {
|
|
3854
|
+
return `\n\n[... ${label}; source=${sourceRef}; use context_compression_retrieve if needed ...]\n\n`;
|
|
3855
|
+
}
|
|
3856
|
+
function summarize(entries) {
|
|
3857
|
+
return {
|
|
3858
|
+
pruned: entries,
|
|
3859
|
+
charsRemoved: entries.reduce((sum, entry) => sum + entry.charsBefore - entry.charsAfter, 0),
|
|
3860
|
+
tokensRemoved: entries.reduce((sum, entry) => sum + entry.tokensBefore - entry.tokensAfter, 0)
|
|
3861
|
+
};
|
|
3862
|
+
}
|
|
3863
|
+
function emptyResult() {
|
|
3864
|
+
return {
|
|
3865
|
+
pruned: [],
|
|
3866
|
+
charsRemoved: 0,
|
|
3867
|
+
tokensRemoved: 0
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
//#endregion
|
|
3871
|
+
export { AUTO_COMPACT_THRESHOLD_LIMITS, COMPRESSION_PROFILES, CONTEXT_COMPRESSION_SETTINGS_NAMESPACE, ContextCompressionSettingsSchema, CustomCompressionPolicyInputSchema, CustomCompressionPolicySchema, DEFAULTS, DEFAULT_CUSTOM_COMPRESSION_POLICY, PRUNE_MARKER, ToolResultPruner, ToolResultPruner as default, codePointLength, historicalPlaceholder, isCompressionProfile, isValidAutoCompactThresholdPercent, measureForCompaction, normalizeTerminalText, parseContextCompressionSettings, reduceFreshToolResult, resolveConfig, resolveCustomPolicy, resolvePolicy, verifyReduction };
|