@monotykamary/pi-vcc 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/npm-shrinkwrap.json +2 -2
  2. package/package.json +1 -1
  3. package/src/core/brief.ts +245 -1
  4. package/src/core/causal-keys.ts +56 -0
  5. package/src/core/summarize.ts +53 -3
  6. package/__tests__/helpers.ts +0 -135
  7. package/__tests__/root-causes.test.ts +0 -858
  8. package/demo.gif +0 -0
  9. package/knip.json +0 -4
  10. package/scripts/audit-sessions.ts +0 -88
  11. package/scripts/benchmark-real-sessions.ts +0 -25
  12. package/scripts/compare-before-after.ts +0 -36
  13. package/scripts/dump-branch-output.ts +0 -20
  14. package/stress-debug.ts +0 -202
  15. package/stress-degradation-curve.ts +0 -251
  16. package/tests/australian-government.test.ts +0 -479
  17. package/tests/before-compact-hook.test.ts +0 -185
  18. package/tests/before-compact.test.ts +0 -227
  19. package/tests/breadcrumbs.test.ts +0 -312
  20. package/tests/brief.test.ts +0 -215
  21. package/tests/build-sections.test.ts +0 -59
  22. package/tests/compile.test.ts +0 -89
  23. package/tests/content.test.ts +0 -66
  24. package/tests/deep-extraction.test.ts +0 -322
  25. package/tests/extract-goals.test.ts +0 -86
  26. package/tests/extract-preferences.test.ts +0 -30
  27. package/tests/filter-noise.test.ts +0 -61
  28. package/tests/fixtures.ts +0 -61
  29. package/tests/format-recall.test.ts +0 -87
  30. package/tests/format.test.ts +0 -73
  31. package/tests/government-domain.test.ts +0 -542
  32. package/tests/lineage.test.ts +0 -33
  33. package/tests/load-messages.test.ts +0 -49
  34. package/tests/model-threshold-hook.test.ts +0 -311
  35. package/tests/model-threshold.test.ts +0 -223
  36. package/tests/normalize.test.ts +0 -104
  37. package/tests/proactive-threshold.test.ts +0 -490
  38. package/tests/real-sessions.test.ts +0 -38
  39. package/tests/recall-expand.test.ts +0 -15
  40. package/tests/recall-scope.test.ts +0 -32
  41. package/tests/recall-tool-scope.test.ts +0 -67
  42. package/tests/render-entries.test.ts +0 -92
  43. package/tests/report.test.ts +0 -58
  44. package/tests/sanitize.test.ts +0 -24
  45. package/tests/search-entries.test.ts +0 -233
  46. package/tests/stress-100-compactions.test.ts +0 -373
  47. package/tests/support/load-session.ts +0 -23
  48. package/tests/support/real-sessions.ts +0 -51
  49. package/tests/threshold-integration.test.ts +0 -335
@@ -1,49 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import { mkdtempSync, writeFileSync, rmSync } from "fs";
3
- import { tmpdir } from "os";
4
- import { join } from "path";
5
- import { loadAllMessages } from "../src/core/load-messages";
6
-
7
- describe("loadAllMessages", () => {
8
- it("loads all message entries when no lineage filter is provided", () => {
9
- const dir = mkdtempSync(join(tmpdir(), "pi-vcc-load-all-"));
10
- const file = join(dir, "session.jsonl");
11
- try {
12
- const lines = [
13
- JSON.stringify({ type: "session", id: "s1" }),
14
- JSON.stringify({ type: "message", id: "m1", message: { role: "user", content: "u1" } }),
15
- JSON.stringify({ type: "custom", id: "c1", customType: "x", data: {} }),
16
- JSON.stringify({ type: "message", id: "m2", message: { role: "assistant", content: [{ type: "text", text: "a1" }] } }),
17
- JSON.stringify({ type: "message", id: "m3", message: { role: "toolResult", toolName: "read", content: [{ type: "text", text: "ok" }] } }),
18
- ];
19
- writeFileSync(file, lines.join("\n") + "\n", "utf8");
20
-
21
- const loaded = loadAllMessages(file, false);
22
- expect(loaded.rendered).toHaveLength(3);
23
- expect(loaded.rawMessages).toHaveLength(3);
24
- expect(loaded.rendered.map((e) => e.index)).toEqual([0, 1, 2]);
25
- } finally {
26
- rmSync(dir, { recursive: true, force: true });
27
- }
28
- });
29
-
30
- it("filters messages by allowed lineage entry IDs and preserves original message index", () => {
31
- const dir = mkdtempSync(join(tmpdir(), "pi-vcc-load-filter-"));
32
- const file = join(dir, "session.jsonl");
33
- try {
34
- const lines = [
35
- JSON.stringify({ type: "message", id: "m1", message: { role: "user", content: "u1" } }),
36
- JSON.stringify({ type: "message", id: "m2", message: { role: "assistant", content: [{ type: "text", text: "a1" }] } }),
37
- JSON.stringify({ type: "message", id: "m3", message: { role: "user", content: "u2" } }),
38
- ];
39
- writeFileSync(file, lines.join("\n") + "\n", "utf8");
40
-
41
- const loaded = loadAllMessages(file, false, new Set(["m2"]));
42
- expect(loaded.rendered).toHaveLength(1);
43
- expect(loaded.rawMessages).toHaveLength(1);
44
- expect(loaded.rendered[0].index).toBe(1);
45
- } finally {
46
- rmSync(dir, { recursive: true, force: true });
47
- }
48
- });
49
- });
@@ -1,311 +0,0 @@
1
- import { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from "bun:test";
2
- import { existsSync, unlinkSync, writeFileSync, readFileSync, mkdtempSync, rmSync } from "fs";
3
- import { tmpdir } from "os";
4
- import { join } from "path";
5
- import { registerBeforeCompactHook, PI_VCC_COMPACT_INSTRUCTION } from "../src/hooks/before-compact";
6
-
7
- let tmpDir: string;
8
- let CONFIG_PATH: string;
9
-
10
- beforeAll(() => {
11
- tmpDir = mkdtempSync(join(tmpdir(), "pi-vcc-test-"));
12
- CONFIG_PATH = join(tmpDir, "pi-vcc-config.json");
13
- process.env.PI_VCC_CONFIG_PATH = CONFIG_PATH;
14
- });
15
-
16
- afterAll(() => {
17
- delete process.env.PI_VCC_CONFIG_PATH;
18
- rmSync(tmpDir, { recursive: true, force: true });
19
- });
20
-
21
- function createMockPi(model?: { id: string; provider?: string; contextWindow?: number }) {
22
- const handlers: Record<string, ((e: any, c: any) => any)[]> = {};
23
- const notifyCalls: Array<{ msg: string; level: string }> = [];
24
- const ctx = {
25
- hasUI: true,
26
- model: model ?? undefined,
27
- ui: {
28
- notify: (msg: string, level: string) => {
29
- notifyCalls.push({ msg, level });
30
- },
31
- },
32
- } as any;
33
- const pi = {
34
- on: (eventName: string, h: (e: any, c: any) => any) => {
35
- if (!handlers[eventName]) handlers[eventName] = [];
36
- handlers[eventName].push(h);
37
- },
38
- } as any;
39
- const emit = (eventName: string, event: any = {}) => {
40
- const hs = handlers[eventName] ?? [];
41
- let result: any;
42
- for (const h of hs) result = h(event, ctx);
43
- return result;
44
- };
45
- return { pi, ctx, emit, notifyCalls };
46
- }
47
-
48
- function setConfig(cfg: Record<string, unknown>) {
49
- writeFileSync(CONFIG_PATH, JSON.stringify(cfg));
50
- }
51
-
52
- function makeEvent(branchEntries: any[], customInstructions?: string, tokensBefore = 100000) {
53
- return {
54
- type: "session_before_compact",
55
- customInstructions,
56
- branchEntries,
57
- preparation: {
58
- previousSummary: undefined,
59
- fileOps: { read: [], written: [], edited: [] },
60
- tokensBefore,
61
- },
62
- signal: new AbortController().signal,
63
- };
64
- }
65
-
66
- const msg = (id: string, role: "user" | "assistant" | "toolResult", content = "x") => ({
67
- id,
68
- type: "message",
69
- message: { role, content },
70
- });
71
-
72
- describe("session_before_compact: per-model threshold", () => {
73
- afterEach(() => {
74
- if (existsSync(CONFIG_PATH)) unlinkSync(CONFIG_PATH);
75
- });
76
-
77
- test("compaction proceeds when context is below per-model threshold", () => {
78
- // The threshold guard was removed because session_before_compact carries no
79
- // reason field — manual /compact and auto-compaction are indistinguishable.
80
- // The proactive trigger handles compacting earlier than pi-core's global
81
- // threshold; this hook no longer blocks compaction that arrives before the
82
- // per-model threshold.
83
- setConfig({
84
- debug: false,
85
- overrideDefaultCompaction: true,
86
- modelThresholds: {
87
- "neuralwatt/GLM-5.1": { reserveTokens: 32768 },
88
- },
89
- });
90
- const { pi, ctx, emit } = createMockPi({
91
- id: "GLM-5.1",
92
- provider: "neuralwatt",
93
- contextWindow: 200000,
94
- });
95
- registerBeforeCompactHook(pi);
96
-
97
- // 100k tokens < 200k - 32768 = 167232 → below threshold, but proceeds
98
- const entries = [
99
- msg("m1", "user", "hello"),
100
- msg("m2", "assistant", "hi"),
101
- msg("m3", "user", "do work"),
102
- msg("m4", "assistant", "done"),
103
- ];
104
- const result = emit("session_before_compact", makeEvent(entries, undefined, 100000));
105
- // No longer cancelled — threshold guard removed
106
- expect(result.cancel).toBeUndefined();
107
- expect(result.compaction).toBeDefined();
108
- });
109
-
110
- test("compaction proceeds when context exceeds per-model threshold", () => {
111
- setConfig({
112
- debug: false,
113
- overrideDefaultCompaction: true,
114
- modelThresholds: {
115
- "neuralwatt/GLM-5.1": { reserveTokens: 32768 },
116
- },
117
- });
118
- const { pi, ctx, emit } = createMockPi({
119
- id: "GLM-5.1",
120
- provider: "neuralwatt",
121
- contextWindow: 200000,
122
- });
123
- registerBeforeCompactHook(pi);
124
-
125
- // 180k tokens > 200k - 32768 = 167232 → above threshold
126
- const entries = [
127
- msg("m1", "user", "hello"),
128
- msg("m2", "assistant", "hi"),
129
- msg("m3", "user", "do work"),
130
- msg("m4", "assistant", "done"),
131
- ];
132
- const result = emit("session_before_compact", makeEvent(entries, undefined, 180000));
133
- expect(result.cancel).toBeUndefined();
134
- expect(result.compaction).toBeDefined();
135
- });
136
-
137
- test("compaction proceeds with globalThreshold compactPercent", () => {
138
- // compactPercent: 65 → reserve = 128000 * 0.35 = 44800 → threshold = 83200
139
- setConfig({
140
- debug: false,
141
- overrideDefaultCompaction: true,
142
- globalThreshold: { compactPercent: 65 },
143
- });
144
- const { pi, ctx, emit } = createMockPi({
145
- id: "some-model",
146
- provider: "some-provider",
147
- contextWindow: 128000,
148
- });
149
- registerBeforeCompactHook(pi);
150
-
151
- // 50k tokens < 83200 → below threshold, but proceeds
152
- const entries = [
153
- msg("m1", "user", "hello"),
154
- msg("m2", "assistant", "hi"),
155
- msg("m3", "user", "do work"),
156
- msg("m4", "assistant", "done"),
157
- ];
158
- const result = emit("session_before_compact", makeEvent(entries, undefined, 50000));
159
- expect(result.cancel).toBeUndefined();
160
- expect(result.compaction).toBeDefined();
161
- });
162
-
163
- test("compaction proceeds with modelThreshold compactPercent", () => {
164
- // compactPercent: 80 → reserve = 200000 * 0.20 = 40000 → threshold = 160000
165
- setConfig({
166
- debug: false,
167
- overrideDefaultCompaction: true,
168
- modelThresholds: {
169
- "neuralwatt/GLM-5.1": { compactPercent: 80 },
170
- },
171
- });
172
- const { pi, ctx, emit } = createMockPi({
173
- id: "GLM-5.1",
174
- provider: "neuralwatt",
175
- contextWindow: 200000,
176
- });
177
- registerBeforeCompactHook(pi);
178
-
179
- // 100k tokens < 160000 → below threshold, but proceeds
180
- const entries = [
181
- msg("m1", "user", "hello"),
182
- msg("m2", "assistant", "hi"),
183
- msg("m3", "user", "do work"),
184
- msg("m4", "assistant", "done"),
185
- ];
186
- const result = emit("session_before_compact", makeEvent(entries, undefined, 100000));
187
- expect(result.cancel).toBeUndefined();
188
- expect(result.compaction).toBeDefined();
189
- });
190
-
191
- test("compaction proceeds with defaultThreshold", () => {
192
- setConfig({
193
- debug: false,
194
- overrideDefaultCompaction: true,
195
- defaultThreshold: { reserveTokens: 16384 },
196
- });
197
- const { pi, ctx, emit } = createMockPi({
198
- id: "some-model",
199
- provider: "some-provider",
200
- contextWindow: 128000,
201
- });
202
- registerBeforeCompactHook(pi);
203
-
204
- // 50k tokens < 128k - 16384 = 111616 → below threshold, but proceeds
205
- const entries = [
206
- msg("m1", "user", "hello"),
207
- msg("m2", "assistant", "hi"),
208
- msg("m3", "user", "do work"),
209
- msg("m4", "assistant", "done"),
210
- ];
211
- const result = emit("session_before_compact", makeEvent(entries, undefined, 50000));
212
- expect(result.cancel).toBeUndefined();
213
- expect(result.compaction).toBeDefined();
214
- });
215
-
216
- test("does NOT cancel on /pi-vcc even when below threshold", () => {
217
- setConfig({
218
- debug: false,
219
- overrideDefaultCompaction: true,
220
- modelThresholds: {
221
- "neuralwatt/GLM-5.1": { reserveTokens: 32768 },
222
- },
223
- });
224
- const { pi, ctx, emit } = createMockPi({
225
- id: "GLM-5.1",
226
- provider: "neuralwatt",
227
- contextWindow: 200000,
228
- });
229
- registerBeforeCompactHook(pi);
230
-
231
- // Below threshold, but explicit /pi-vcc command
232
- const entries = [
233
- msg("m1", "user", "hello"),
234
- msg("m2", "assistant", "hi"),
235
- msg("m3", "user", "do work"),
236
- msg("m4", "assistant", "done"),
237
- ];
238
- const result = emit("session_before_compact", makeEvent(entries, PI_VCC_COMPACT_INSTRUCTION, 100000));
239
- expect(result.cancel).toBeUndefined();
240
- expect(result.compaction).toBeDefined();
241
- });
242
-
243
- test("compaction proceeds when no modelThresholds configured", () => {
244
- setConfig({
245
- debug: false,
246
- overrideDefaultCompaction: true,
247
- });
248
- const { pi, ctx, emit } = createMockPi({
249
- id: "GLM-5.1",
250
- provider: "neuralwatt",
251
- contextWindow: 200000,
252
- });
253
- registerBeforeCompactHook(pi);
254
-
255
- const entries = [
256
- msg("m1", "user", "hello"),
257
- msg("m2", "assistant", "hi"),
258
- msg("m3", "user", "do work"),
259
- msg("m4", "assistant", "done"),
260
- ];
261
- const result = emit("session_before_compact", makeEvent(entries, undefined, 100000));
262
- expect(result.cancel === true).toBe(false);
263
- });
264
-
265
- test("compaction proceeds when model is undefined", () => {
266
- setConfig({
267
- debug: false,
268
- overrideDefaultCompaction: true,
269
- modelThresholds: {
270
- "GLM-5.1": { reserveTokens: 32768 },
271
- },
272
- });
273
- const { pi, ctx, emit } = createMockPi(undefined);
274
- registerBeforeCompactHook(pi);
275
-
276
- const entries = [
277
- msg("m1", "user", "hello"),
278
- msg("m2", "assistant", "hi"),
279
- msg("m3", "user", "do work"),
280
- msg("m4", "assistant", "done"),
281
- ];
282
- const result = emit("session_before_compact", makeEvent(entries, undefined, 100000));
283
- expect(result.cancel === true).toBe(false);
284
- });
285
-
286
- test("compaction proceeds with overrideDefaultCompaction false", () => {
287
- setConfig({
288
- debug: false,
289
- overrideDefaultCompaction: false,
290
- modelThresholds: {
291
- "neuralwatt/GLM-5.1": { reserveTokens: 32768 },
292
- },
293
- });
294
- const { pi, ctx, emit } = createMockPi({
295
- id: "GLM-5.1",
296
- provider: "neuralwatt",
297
- contextWindow: 200000,
298
- });
299
- registerBeforeCompactHook(pi);
300
-
301
- // Below threshold, overrideDefaultCompaction: false → pi-vcc doesn't
302
- // handle the summary, but also doesn't cancel (threshold guard removed)
303
- const entries = [
304
- msg("m1", "user", "hello"),
305
- msg("m2", "assistant", "hi"),
306
- ];
307
- const result = emit("session_before_compact", makeEvent(entries, undefined, 100000));
308
- // Not cancelled — returns undefined (pi-vcc doesn't handle it)
309
- expect(result).toBeUndefined();
310
- });
311
- });
@@ -1,223 +0,0 @@
1
- import { describe, test, expect } from "bun:test";
2
- import { getModelThreshold, resolveReserveTokens, type PiVccSettings, type ModelThreshold } from "../src/core/settings";
3
-
4
- const t = (reserveTokens: number, keepRecentTokens?: number): ModelThreshold => ({
5
- reserveTokens,
6
- ...(keepRecentTokens !== undefined ? { keepRecentTokens } : {}),
7
- });
8
-
9
- describe("getModelThreshold", () => {
10
- test("returns undefined when no modelThresholds and no defaultThreshold", () => {
11
- const settings: PiVccSettings = {
12
- overrideDefaultCompaction: true,
13
- debug: false,
14
- };
15
- expect(getModelThreshold(settings, { id: "GLM-5.1", provider: "neuralwatt" })).toBeUndefined();
16
- });
17
-
18
- test("returns undefined when model is undefined and no defaultThreshold", () => {
19
- const settings: PiVccSettings = {
20
- overrideDefaultCompaction: true,
21
- debug: false,
22
- modelThresholds: { "neuralwatt/GLM-5.1": t(32768) },
23
- };
24
- expect(getModelThreshold(settings, undefined)).toBeUndefined();
25
- });
26
-
27
- test("returns defaultThreshold when model doesn't match any key", () => {
28
- const settings: PiVccSettings = {
29
- overrideDefaultCompaction: true,
30
- debug: false,
31
- modelThresholds: { "neuralwatt/GLM-5.1": t(32768) },
32
- defaultThreshold: t(8192),
33
- };
34
- expect(getModelThreshold(settings, { id: "other-model", provider: "other" })).toEqual(t(8192));
35
- });
36
-
37
- test("returns defaultThreshold when model is undefined", () => {
38
- const settings: PiVccSettings = {
39
- overrideDefaultCompaction: true,
40
- debug: false,
41
- defaultThreshold: t(8192),
42
- };
43
- expect(getModelThreshold(settings, undefined)).toEqual(t(8192));
44
- });
45
-
46
- test("matches on provider/modelId key", () => {
47
- const threshold = t(32768, 40000);
48
- const settings: PiVccSettings = {
49
- overrideDefaultCompaction: true,
50
- debug: false,
51
- modelThresholds: {
52
- "neuralwatt/zai-org/GLM-5.1-FP8": threshold,
53
- },
54
- defaultThreshold: t(8192),
55
- };
56
- expect(getModelThreshold(settings, { id: "zai-org/GLM-5.1-FP8", provider: "neuralwatt" })).toEqual(threshold);
57
- });
58
-
59
- test("matches on modelId-only key when provider/modelId not found", () => {
60
- const threshold = t(16384);
61
- const settings: PiVccSettings = {
62
- overrideDefaultCompaction: true,
63
- debug: false,
64
- modelThresholds: {
65
- "zai-org/GLM-5.1-FP8": threshold,
66
- },
67
- defaultThreshold: t(8192),
68
- };
69
- // provider/modelId doesn't match, but modelId does
70
- expect(getModelThreshold(settings, { id: "zai-org/GLM-5.1-FP8", provider: "other-provider" })).toEqual(threshold);
71
- });
72
-
73
- test("provider/modelId takes precedence over modelId-only key", () => {
74
- const providerThreshold = t(32768);
75
- const modelIdThreshold = t(16384);
76
- const settings: PiVccSettings = {
77
- overrideDefaultCompaction: true,
78
- debug: false,
79
- modelThresholds: {
80
- "neuralwatt/GLM-5.1": providerThreshold,
81
- "GLM-5.1": modelIdThreshold,
82
- },
83
- };
84
- expect(getModelThreshold(settings, { id: "GLM-5.1", provider: "neuralwatt" })).toEqual(providerThreshold);
85
- });
86
-
87
- test("falls through to modelId-only when provider is absent", () => {
88
- const threshold = t(16384);
89
- const settings: PiVccSettings = {
90
- overrideDefaultCompaction: true,
91
- debug: false,
92
- modelThresholds: {
93
- "GLM-5.1": threshold,
94
- },
95
- };
96
- expect(getModelThreshold(settings, { id: "GLM-5.1" })).toEqual(threshold);
97
- });
98
-
99
- test("falls through to defaultThreshold when neither key matches", () => {
100
- const settings: PiVccSettings = {
101
- overrideDefaultCompaction: true,
102
- debug: false,
103
- modelThresholds: {
104
- "neuralwatt/GLM-5.1": t(32768),
105
- },
106
- defaultThreshold: t(8192),
107
- };
108
- expect(getModelThreshold(settings, { id: "Kimi-K2.6", provider: "neuralwatt" })).toEqual(t(8192));
109
- });
110
-
111
- test("returns globalThreshold when model doesn't match any key", () => {
112
- const threshold: ModelThreshold = { compactPercent: 65 };
113
- const settings: PiVccSettings = {
114
- overrideDefaultCompaction: true,
115
- debug: false,
116
- modelThresholds: { "neuralwatt/GLM-5.1": t(32768) },
117
- globalThreshold: threshold,
118
- };
119
- expect(getModelThreshold(settings, { id: "other-model", provider: "other" })).toEqual(threshold);
120
- });
121
-
122
- test("globalThreshold takes precedence over defaultThreshold", () => {
123
- const settings: PiVccSettings = {
124
- overrideDefaultCompaction: true,
125
- debug: false,
126
- globalThreshold: t(65536),
127
- defaultThreshold: t(8192),
128
- };
129
- expect(getModelThreshold(settings, { id: "unknown", provider: "other" })).toEqual(t(65536));
130
- });
131
-
132
- test("returns globalThreshold when model is undefined", () => {
133
- const threshold: ModelThreshold = { compactPercent: 70 };
134
- const settings: PiVccSettings = {
135
- overrideDefaultCompaction: true,
136
- debug: false,
137
- globalThreshold: threshold,
138
- };
139
- expect(getModelThreshold(settings, undefined)).toEqual(threshold);
140
- });
141
-
142
- test("falls back to defaultThreshold when globalThreshold is not set", () => {
143
- const settings: PiVccSettings = {
144
- overrideDefaultCompaction: true,
145
- debug: false,
146
- defaultThreshold: t(8192),
147
- };
148
- expect(getModelThreshold(settings, { id: "unknown", provider: "other" })).toEqual(t(8192));
149
- });
150
-
151
- test("works with multiple modelThresholds entries", () => {
152
- const settings: PiVccSettings = {
153
- overrideDefaultCompaction: true,
154
- debug: false,
155
- modelThresholds: {
156
- "neuralwatt/zai-org/GLM-5.1-FP8": t(32768),
157
- "neuralwatt/moonshotai/Kimi-K2.6": t(65536),
158
- "makora/deepseek-ai/DeepSeek-V4-Pro": t(32768),
159
- },
160
- defaultThreshold: t(16384),
161
- };
162
-
163
- expect(getModelThreshold(settings, { id: "zai-org/GLM-5.1-FP8", provider: "neuralwatt" })).toEqual(t(32768));
164
- expect(getModelThreshold(settings, { id: "moonshotai/Kimi-K2.6", provider: "neuralwatt" })).toEqual(t(65536));
165
- expect(getModelThreshold(settings, { id: "deepseek-ai/DeepSeek-V4-Pro", provider: "makora" })).toEqual(t(32768));
166
- expect(getModelThreshold(settings, { id: "unknown-model", provider: "other" })).toEqual(t(16384));
167
- });
168
- });
169
-
170
- describe("resolveReserveTokens", () => {
171
- test("returns reserveTokens when set", () => {
172
- expect(resolveReserveTokens({ reserveTokens: 32768 }, 128000)).toBe(32768);
173
- });
174
-
175
- test("returns undefined when neither reserveTokens nor compactPercent is set", () => {
176
- expect(resolveReserveTokens({}, 128000)).toBeUndefined();
177
- });
178
-
179
- test("returns undefined when only keepRecentTokens is set", () => {
180
- expect(resolveReserveTokens({ keepRecentTokens: 20000 }, 128000)).toBeUndefined();
181
- });
182
-
183
- test("computes reserveTokens from compactPercent", () => {
184
- // compactPercent: 65 on 128k window → reserve = 128000 * (1 - 65/100) = 44800
185
- expect(resolveReserveTokens({ compactPercent: 65 }, 128000)).toBe(44800);
186
- });
187
-
188
- test("compactPercent: 50 → reserve is exactly half", () => {
189
- expect(resolveReserveTokens({ compactPercent: 50 }, 200000)).toBe(100000);
190
- });
191
-
192
- test("compactPercent: 80 → reserve is 20%", () => {
193
- expect(resolveReserveTokens({ compactPercent: 80 }, 200000)).toBe(40000);
194
- });
195
-
196
- test("reserveTokens takes precedence over compactPercent", () => {
197
- expect(resolveReserveTokens({ reserveTokens: 32768, compactPercent: 65 }, 128000)).toBe(32768);
198
- });
199
-
200
- test("returns undefined for compactPercent < 1", () => {
201
- expect(resolveReserveTokens({ compactPercent: 0 }, 128000)).toBeUndefined();
202
- });
203
-
204
- test("returns undefined for compactPercent > 99", () => {
205
- expect(resolveReserveTokens({ compactPercent: 100 }, 128000)).toBeUndefined();
206
- });
207
-
208
- test("returns undefined when contextWindow is 0", () => {
209
- expect(resolveReserveTokens({ compactPercent: 65 }, 0)).toBeUndefined();
210
- });
211
-
212
- test("reserveTokens still works when contextWindow is 0", () => {
213
- expect(resolveReserveTokens({ reserveTokens: 32768 }, 0)).toBe(32768);
214
- });
215
-
216
- test("compactPercent = 1 → reserve is 99% of contextWindow", () => {
217
- expect(resolveReserveTokens({ compactPercent: 1 }, 128000)).toBe(126720);
218
- });
219
-
220
- test("compactPercent = 99 → reserve is 1% of contextWindow", () => {
221
- expect(resolveReserveTokens({ compactPercent: 99 }, 128000)).toBe(1280);
222
- });
223
- });
@@ -1,104 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import { normalize } from "../src/core/normalize";
3
- import {
4
- userMsg,
5
- assistantText,
6
- assistantWithThinking,
7
- assistantWithToolCall,
8
- toolResult,
9
- } from "./fixtures";
10
-
11
- describe("normalize", () => {
12
- it("returns empty for empty input", () => {
13
- expect(normalize([])).toEqual([]);
14
- });
15
-
16
- it("normalizes user message (string content)", () => {
17
- const blocks = normalize([userMsg("fix the bug")]);
18
- expect(blocks).toEqual([{ kind: "user", text: "fix the bug", sourceIndex: 0 }]);
19
- });
20
-
21
- it("normalizes assistant text message", () => {
22
- const blocks = normalize([assistantText("done")]);
23
- expect(blocks).toEqual([{ kind: "assistant", text: "done", sourceIndex: 0 }]);
24
- });
25
-
26
- it("normalizes assistant string content", () => {
27
- const msg = { ...assistantText("done"), content: "plain text" } as any;
28
- expect(normalize([msg])).toEqual([{ kind: "assistant", text: "plain text", sourceIndex: 0 }]);
29
- });
30
-
31
- it("splits assistant thinking + text", () => {
32
- const blocks = normalize([assistantWithThinking("result", "hmm")]);
33
- expect(blocks).toHaveLength(2);
34
- expect(blocks[0]).toEqual({
35
- kind: "thinking", text: "hmm", redacted: false, sourceIndex: 0,
36
- });
37
- expect(blocks[1]).toEqual({ kind: "assistant", text: "result", sourceIndex: 0 });
38
- });
39
-
40
- it("normalizes tool call", () => {
41
- const blocks = normalize([assistantWithToolCall("Read", { path: "a.ts" })]);
42
- expect(blocks).toEqual([{
43
- kind: "tool_call", name: "Read", args: { path: "a.ts" }, sourceIndex: 0,
44
- }]);
45
- });
46
-
47
- it("normalizes tool result", () => {
48
- const blocks = normalize([toolResult("Read", "file contents")]);
49
- expect(blocks).toEqual([{
50
- kind: "tool_result", name: "Read",
51
- text: "file contents", isError: false, sourceIndex: 0,
52
- }]);
53
- });
54
-
55
- it("normalizes error tool result", () => {
56
- const blocks = normalize([toolResult("Edit", "not found", true)]);
57
- expect(blocks[0]).toMatchObject({
58
- kind: "tool_result", isError: true,
59
- });
60
- });
61
-
62
- it("handles mixed message sequence", () => {
63
- const blocks = normalize([
64
- userMsg("fix it"),
65
- assistantWithToolCall("Read", { path: "x.ts" }),
66
- toolResult("Read", "code"),
67
- assistantText("done"),
68
- ]);
69
- expect(blocks).toHaveLength(4);
70
- expect(blocks.map((b) => b.kind)).toEqual([
71
- "user", "tool_call", "tool_result", "assistant",
72
- ]);
73
- });
74
-
75
- it("produces image placeholder for user image content", () => {
76
- const msg = {
77
- role: "user" as const,
78
- content: [
79
- { type: "text" as const, text: "look at this" },
80
- { type: "image" as const, data: "abc", mimeType: "image/png" },
81
- ],
82
- timestamp: Date.now(),
83
- };
84
- const blocks = normalize([msg]);
85
- expect(blocks).toHaveLength(2);
86
- expect(blocks[0]).toEqual({ kind: "user", text: "look at this", sourceIndex: 0 });
87
- expect(blocks[1]).toEqual({ kind: "user", text: "[image: image/png]", sourceIndex: 0 });
88
- });
89
-
90
- it("normalizes bashExecution messages", () => {
91
- const msg = { role: "bashExecution", command: "ls -la", output: "files", exitCode: 0 } as any;
92
- const blocks = normalize([msg]);
93
- expect(blocks).toEqual([
94
- { kind: "bash", command: "ls -la", output: "files", exitCode: 0, sourceIndex: 0 },
95
- ]);
96
- });
97
-
98
- it("skips truly unknown message roles gracefully", () => {
99
- const custom = { role: "custom", content: "hello" } as any;
100
- expect(normalize([custom])).toEqual([]);
101
- });
102
- });
103
-
104
-