@animalabs/context-manager 0.3.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 (161) hide show
  1. package/dist/src/adaptive/chunker.d.ts +70 -0
  2. package/dist/src/adaptive/chunker.d.ts.map +1 -0
  3. package/dist/src/adaptive/chunker.js +233 -0
  4. package/dist/src/adaptive/chunker.js.map +1 -0
  5. package/dist/src/adaptive/folding-strategy.d.ts +118 -0
  6. package/dist/src/adaptive/folding-strategy.d.ts.map +1 -0
  7. package/dist/src/adaptive/folding-strategy.js +12 -0
  8. package/dist/src/adaptive/folding-strategy.js.map +1 -0
  9. package/dist/src/adaptive/index.d.ts +17 -0
  10. package/dist/src/adaptive/index.d.ts.map +1 -0
  11. package/dist/src/adaptive/index.js +23 -0
  12. package/dist/src/adaptive/index.js.map +1 -0
  13. package/dist/src/adaptive/picker.d.ts +103 -0
  14. package/dist/src/adaptive/picker.d.ts.map +1 -0
  15. package/dist/src/adaptive/picker.js +357 -0
  16. package/dist/src/adaptive/picker.js.map +1 -0
  17. package/dist/src/adaptive/render.d.ts +39 -0
  18. package/dist/src/adaptive/render.d.ts.map +1 -0
  19. package/dist/src/adaptive/render.js +104 -0
  20. package/dist/src/adaptive/render.js.map +1 -0
  21. package/dist/src/adaptive/strategies/flat-profile.d.ts +19 -0
  22. package/dist/src/adaptive/strategies/flat-profile.d.ts.map +1 -0
  23. package/dist/src/adaptive/strategies/flat-profile.js +86 -0
  24. package/dist/src/adaptive/strategies/flat-profile.js.map +1 -0
  25. package/dist/src/adaptive/strategies/oldest-first.d.ts +19 -0
  26. package/dist/src/adaptive/strategies/oldest-first.d.ts.map +1 -0
  27. package/dist/src/adaptive/strategies/oldest-first.js +39 -0
  28. package/dist/src/adaptive/strategies/oldest-first.js.map +1 -0
  29. package/dist/src/context-manager.d.ts +74 -2
  30. package/dist/src/context-manager.d.ts.map +1 -1
  31. package/dist/src/context-manager.js +150 -10
  32. package/dist/src/context-manager.js.map +1 -1
  33. package/dist/src/index.d.ts +1 -1
  34. package/dist/src/index.d.ts.map +1 -1
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/message-store.d.ts +36 -1
  37. package/dist/src/message-store.d.ts.map +1 -1
  38. package/dist/src/message-store.js +134 -8
  39. package/dist/src/message-store.js.map +1 -1
  40. package/dist/src/strategies/autobiographical.d.ts +422 -5
  41. package/dist/src/strategies/autobiographical.d.ts.map +1 -1
  42. package/dist/src/strategies/autobiographical.js +2248 -98
  43. package/dist/src/strategies/autobiographical.js.map +1 -1
  44. package/dist/src/strategies/index.d.ts +1 -1
  45. package/dist/src/strategies/index.d.ts.map +1 -1
  46. package/dist/src/strategies/index.js.map +1 -1
  47. package/dist/src/types/index.d.ts +2 -2
  48. package/dist/src/types/index.d.ts.map +1 -1
  49. package/dist/src/types/index.js +1 -1
  50. package/dist/src/types/index.js.map +1 -1
  51. package/dist/src/types/message.d.ts +38 -0
  52. package/dist/src/types/message.d.ts.map +1 -1
  53. package/dist/src/types/strategy.d.ts +302 -10
  54. package/dist/src/types/strategy.d.ts.map +1 -1
  55. package/dist/src/types/strategy.js +28 -0
  56. package/dist/src/types/strategy.js.map +1 -1
  57. package/dist/test/adaptive/branching.test.d.ts +20 -0
  58. package/dist/test/adaptive/branching.test.d.ts.map +1 -0
  59. package/dist/test/adaptive/branching.test.js +176 -0
  60. package/dist/test/adaptive/branching.test.js.map +1 -0
  61. package/dist/test/adaptive/chunker.test.d.ts +2 -0
  62. package/dist/test/adaptive/chunker.test.d.ts.map +1 -0
  63. package/dist/test/adaptive/chunker.test.js +120 -0
  64. package/dist/test/adaptive/chunker.test.js.map +1 -0
  65. package/dist/test/adaptive/deep-levels.test.d.ts +10 -0
  66. package/dist/test/adaptive/deep-levels.test.d.ts.map +1 -0
  67. package/dist/test/adaptive/deep-levels.test.js +167 -0
  68. package/dist/test/adaptive/deep-levels.test.js.map +1 -0
  69. package/dist/test/adaptive/doc-plus-chat.test.d.ts +10 -0
  70. package/dist/test/adaptive/doc-plus-chat.test.d.ts.map +1 -0
  71. package/dist/test/adaptive/doc-plus-chat.test.js +194 -0
  72. package/dist/test/adaptive/doc-plus-chat.test.js.map +1 -0
  73. package/dist/test/adaptive/hard-fail.test.d.ts +9 -0
  74. package/dist/test/adaptive/hard-fail.test.d.ts.map +1 -0
  75. package/dist/test/adaptive/hard-fail.test.js +154 -0
  76. package/dist/test/adaptive/hard-fail.test.js.map +1 -0
  77. package/dist/test/adaptive/harness.d.ts +109 -0
  78. package/dist/test/adaptive/harness.d.ts.map +1 -0
  79. package/dist/test/adaptive/harness.js +263 -0
  80. package/dist/test/adaptive/harness.js.map +1 -0
  81. package/dist/test/adaptive/ingestion.test.d.ts +13 -0
  82. package/dist/test/adaptive/ingestion.test.d.ts.map +1 -0
  83. package/dist/test/adaptive/ingestion.test.js +306 -0
  84. package/dist/test/adaptive/ingestion.test.js.map +1 -0
  85. package/dist/test/adaptive/integration.test.d.ts +12 -0
  86. package/dist/test/adaptive/integration.test.d.ts.map +1 -0
  87. package/dist/test/adaptive/integration.test.js +247 -0
  88. package/dist/test/adaptive/integration.test.js.map +1 -0
  89. package/dist/test/adaptive/long-chronicle.test.d.ts +13 -0
  90. package/dist/test/adaptive/long-chronicle.test.d.ts.map +1 -0
  91. package/dist/test/adaptive/long-chronicle.test.js +186 -0
  92. package/dist/test/adaptive/long-chronicle.test.js.map +1 -0
  93. package/dist/test/adaptive/persistence.test.d.ts +11 -0
  94. package/dist/test/adaptive/persistence.test.d.ts.map +1 -0
  95. package/dist/test/adaptive/persistence.test.js +200 -0
  96. package/dist/test/adaptive/persistence.test.js.map +1 -0
  97. package/dist/test/adaptive/picker.test.d.ts +2 -0
  98. package/dist/test/adaptive/picker.test.d.ts.map +1 -0
  99. package/dist/test/adaptive/picker.test.js +333 -0
  100. package/dist/test/adaptive/picker.test.js.map +1 -0
  101. package/dist/test/adaptive/render.test.d.ts +2 -0
  102. package/dist/test/adaptive/render.test.d.ts.map +1 -0
  103. package/dist/test/adaptive/render.test.js +109 -0
  104. package/dist/test/adaptive/render.test.js.map +1 -0
  105. package/dist/test/adaptive/shard-immutability.test.d.ts +14 -0
  106. package/dist/test/adaptive/shard-immutability.test.d.ts.map +1 -0
  107. package/dist/test/adaptive/shard-immutability.test.js +184 -0
  108. package/dist/test/adaptive/shard-immutability.test.js.map +1 -0
  109. package/dist/test/adaptive/strategy-integration.test.d.ts +9 -0
  110. package/dist/test/adaptive/strategy-integration.test.d.ts.map +1 -0
  111. package/dist/test/adaptive/strategy-integration.test.js +293 -0
  112. package/dist/test/adaptive/strategy-integration.test.js.map +1 -0
  113. package/dist/test/message-store-sequence.test.d.ts +25 -0
  114. package/dist/test/message-store-sequence.test.d.ts.map +1 -0
  115. package/dist/test/message-store-sequence.test.js +157 -0
  116. package/dist/test/message-store-sequence.test.js.map +1 -0
  117. package/dist/test/non-blocking-compile.test.d.ts +22 -0
  118. package/dist/test/non-blocking-compile.test.d.ts.map +1 -0
  119. package/dist/test/non-blocking-compile.test.js +114 -0
  120. package/dist/test/non-blocking-compile.test.js.map +1 -0
  121. package/dist/test/pins.test.d.ts +10 -0
  122. package/dist/test/pins.test.d.ts.map +1 -0
  123. package/dist/test/pins.test.js +164 -0
  124. package/dist/test/pins.test.js.map +1 -0
  125. package/dist/test/recall-positioning.test.d.ts +16 -0
  126. package/dist/test/recall-positioning.test.d.ts.map +1 -0
  127. package/dist/test/recall-positioning.test.js +181 -0
  128. package/dist/test/recall-positioning.test.js.map +1 -0
  129. package/dist/test/search.test.d.ts +10 -0
  130. package/dist/test/search.test.d.ts.map +1 -0
  131. package/dist/test/search.test.js +141 -0
  132. package/dist/test/search.test.js.map +1 -0
  133. package/dist/test/speculation-cap.test.d.ts +17 -0
  134. package/dist/test/speculation-cap.test.d.ts.map +1 -0
  135. package/dist/test/speculation-cap.test.js +157 -0
  136. package/dist/test/speculation-cap.test.js.map +1 -0
  137. package/dist/test/strategy-persistence.test.d.ts +15 -0
  138. package/dist/test/strategy-persistence.test.d.ts.map +1 -0
  139. package/dist/test/strategy-persistence.test.js +244 -0
  140. package/dist/test/strategy-persistence.test.js.map +1 -0
  141. package/dist/test/tool-pruning.test.d.ts +10 -0
  142. package/dist/test/tool-pruning.test.d.ts.map +1 -0
  143. package/dist/test/tool-pruning.test.js +140 -0
  144. package/dist/test/tool-pruning.test.js.map +1 -0
  145. package/dist/tsconfig.tsbuildinfo +1 -1
  146. package/package.json +1 -1
  147. package/src/adaptive/chunker.ts +323 -0
  148. package/src/adaptive/folding-strategy.ts +129 -0
  149. package/src/adaptive/index.ts +51 -0
  150. package/src/adaptive/picker.ts +442 -0
  151. package/src/adaptive/render.ts +115 -0
  152. package/src/adaptive/strategies/flat-profile.ts +96 -0
  153. package/src/adaptive/strategies/oldest-first.ts +48 -0
  154. package/src/context-manager.ts +174 -10
  155. package/src/index.ts +1 -1
  156. package/src/message-store.ts +147 -8
  157. package/src/strategies/autobiographical.ts +2468 -166
  158. package/src/strategies/index.ts +1 -1
  159. package/src/types/index.ts +14 -1
  160. package/src/types/message.ts +43 -0
  161. package/src/types/strategy.ts +332 -10
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Tests that selectAdaptive throws OverBudgetError when the picker has
3
+ * folded everything it can but the result still exceeds the hard budget.
4
+ *
5
+ * The strategy's philosophy: surface the overage rather than silently
6
+ * dropping entries. The host decides how to recover.
7
+ */
8
+ import { describe, it, before, after, beforeEach } from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import { rmSync, existsSync } from 'node:fs';
11
+ import { ContextManager, AutobiographicalStrategy } from '../../src/index.js';
12
+ import { OverBudgetError } from '../../src/adaptive/index.js';
13
+ const TEST_STORE_PATH = './test-adaptive-hard-fail-store';
14
+ function cleanup() {
15
+ if (existsSync(TEST_STORE_PATH)) {
16
+ rmSync(TEST_STORE_PATH, { recursive: true, force: true });
17
+ }
18
+ }
19
+ function makeMockMembrane() {
20
+ let callCount = 0;
21
+ return {
22
+ complete: async () => {
23
+ callCount++;
24
+ return {
25
+ content: [{ type: 'text', text: `[mock summary #${callCount}]` }],
26
+ };
27
+ },
28
+ get callCount() {
29
+ return callCount;
30
+ },
31
+ };
32
+ }
33
+ describe('AutobiographicalStrategy — adaptive OverBudgetError', () => {
34
+ before(() => cleanup());
35
+ after(() => cleanup());
36
+ beforeEach(() => cleanup());
37
+ it('throws OverBudgetError when tail alone exceeds budget', async () => {
38
+ const mock = makeMockMembrane();
39
+ const strategy = new AutobiographicalStrategy({
40
+ adaptiveResolution: true,
41
+ targetChunkTokens: 200,
42
+ // Large tail window: everything we add will be tail-resident, NOT
43
+ // foldable. The picker has nothing to do with it.
44
+ recentWindowTokens: 100_000,
45
+ });
46
+ const manager = await ContextManager.open({
47
+ path: TEST_STORE_PATH,
48
+ strategy,
49
+ membrane: mock,
50
+ });
51
+ // Add enough content that the tail alone is huge.
52
+ for (let i = 0; i < 30; i++) {
53
+ manager.addMessage('User', [{ type: 'text', text: `Msg ${i}. ` + 'word '.repeat(80) }]);
54
+ }
55
+ while (!manager.isReady()) {
56
+ await manager.tick();
57
+ }
58
+ // Budget so tight that even the tail can't fit. Picker has nothing it
59
+ // can fold (tail is treated as pinned).
60
+ await assert.rejects(() => manager.compile({ maxTokens: 500, reserveForResponse: 100 }), (err) => {
61
+ assert.ok(err instanceof OverBudgetError, `expected OverBudgetError, got ${err?.constructor?.name}`);
62
+ const e = err;
63
+ assert.ok(e.actual > e.budget, `actual ${e.actual} should exceed budget ${e.budget}`);
64
+ assert.ok(e.diagnostics.tailTokens > 0, 'diagnostics should include tail tokens');
65
+ return true;
66
+ });
67
+ manager.close();
68
+ });
69
+ it('does NOT throw when picker can fold its way under budget', async () => {
70
+ const mock = makeMockMembrane();
71
+ const strategy = new AutobiographicalStrategy({
72
+ adaptiveResolution: true,
73
+ targetChunkTokens: 100,
74
+ recentWindowTokens: 200,
75
+ });
76
+ const manager = await ContextManager.open({
77
+ path: TEST_STORE_PATH,
78
+ strategy,
79
+ membrane: mock,
80
+ });
81
+ for (let i = 0; i < 30; i++) {
82
+ manager.addMessage('User', [{ type: 'text', text: `Msg ${i}. ` + 'word '.repeat(40) }]);
83
+ }
84
+ while (!manager.isReady()) {
85
+ await manager.tick();
86
+ }
87
+ // Tight but reachable budget — picker should fold middle content to fit.
88
+ const compiled = await manager.compile({ maxTokens: 3000, reserveForResponse: 200 });
89
+ assert.ok(compiled.messages.length > 0);
90
+ manager.close();
91
+ });
92
+ it('does NOT throw under generous budget', async () => {
93
+ const mock = makeMockMembrane();
94
+ const strategy = new AutobiographicalStrategy({
95
+ adaptiveResolution: true,
96
+ targetChunkTokens: 100,
97
+ recentWindowTokens: 200,
98
+ });
99
+ const manager = await ContextManager.open({
100
+ path: TEST_STORE_PATH,
101
+ strategy,
102
+ membrane: mock,
103
+ });
104
+ for (let i = 0; i < 10; i++) {
105
+ manager.addMessage('User', [{ type: 'text', text: `Msg ${i}` }]);
106
+ }
107
+ const compiled = await manager.compile({ maxTokens: 100_000, reserveForResponse: 200 });
108
+ assert.ok(compiled.messages.length > 0);
109
+ manager.close();
110
+ });
111
+ it('error diagnostics include useful state info', async () => {
112
+ const mock = makeMockMembrane();
113
+ const strategy = new AutobiographicalStrategy({
114
+ adaptiveResolution: true,
115
+ targetChunkTokens: 200,
116
+ recentWindowTokens: 100_000,
117
+ });
118
+ const manager = await ContextManager.open({
119
+ path: TEST_STORE_PATH,
120
+ strategy,
121
+ membrane: mock,
122
+ });
123
+ for (let i = 0; i < 20; i++) {
124
+ manager.addMessage('User', [{ type: 'text', text: `Msg ${i}. ` + 'word '.repeat(80) }]);
125
+ }
126
+ while (!manager.isReady()) {
127
+ await manager.tick();
128
+ }
129
+ let caught = null;
130
+ try {
131
+ await manager.compile({ maxTokens: 400, reserveForResponse: 100 });
132
+ }
133
+ catch (err) {
134
+ if (err instanceof OverBudgetError)
135
+ caught = err;
136
+ }
137
+ assert.ok(caught, 'should have caught OverBudgetError');
138
+ if (!caught) {
139
+ manager.close();
140
+ return;
141
+ }
142
+ assert.equal(typeof caught.budget, 'number');
143
+ assert.equal(typeof caught.actual, 'number');
144
+ assert.equal(typeof caught.diagnostics.headTokens, 'number');
145
+ assert.equal(typeof caught.diagnostics.tailTokens, 'number');
146
+ assert.equal(typeof caught.diagnostics.middleTokens, 'number');
147
+ assert.equal(typeof caught.diagnostics.deepestLevel, 'number');
148
+ // The error message should be informative
149
+ assert.ok(caught.message.includes('exhausted'));
150
+ assert.ok(caught.message.includes('hard budget'));
151
+ manager.close();
152
+ });
153
+ });
154
+ //# sourceMappingURL=hard-fail.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hard-fail.test.js","sourceRoot":"","sources":["../../../test/adaptive/hard-fail.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Synthetic test harness for the adaptive-resolution picker.
3
+ *
4
+ * Builds a minimal in-memory chronicle simulator with:
5
+ * - Chunks (PickerChunk shape) with deterministic token counts.
6
+ * - A mocked LLM that produces deterministic "summaries" by hashing input.
7
+ * - A summary tree that grows as the LLM is called.
8
+ * - A function to run the picker against a built state.
9
+ *
10
+ * The mock LLM doesn't actually call any API — it constructs fake summary
11
+ * content so we can validate picker logic, fold semantics, and cache stickiness
12
+ * without external dependencies.
13
+ */
14
+ import type { SummaryEntry } from '../../src/types/strategy.js';
15
+ import type { PickerChunk } from '../../src/adaptive/picker.js';
16
+ import type { ChunkId, SummaryId } from '../../src/adaptive/folding-strategy.js';
17
+ /**
18
+ * Default tokens per recall pair, regardless of source level.
19
+ * The design specifies that L_k summaries target a constant size (~2K tokens
20
+ * by convention) so each level's recall pair has predictable footprint.
21
+ */
22
+ export declare const DEFAULT_RECALL_PAIR_TOKENS = 200;
23
+ /** Default group size: how many L_k items combine into one L_{k+1}. */
24
+ export declare const DEFAULT_MERGE_THRESHOLD = 6;
25
+ export interface MockChronicleOptions {
26
+ /** Tokens for each recall pair. */
27
+ recallPairTokens?: number;
28
+ /** L_k items per L_{k+1} parent. */
29
+ mergeThreshold?: number;
30
+ }
31
+ /**
32
+ * Mutable in-memory chronicle. Mirrors what the real strategy would manage
33
+ * but trimmed to just what the picker needs.
34
+ */
35
+ export declare class MockChronicle {
36
+ readonly chunks: PickerChunk[];
37
+ readonly summaries: Map<string, SummaryEntry>;
38
+ readonly recallPairTokens: Map<string, number>;
39
+ private summaryCounter;
40
+ readonly mergeThreshold: number;
41
+ readonly defaultRecallPairTokens: number;
42
+ /** Mock LLM call count, for telemetry assertions. */
43
+ llmCalls: number;
44
+ constructor(opts?: MockChronicleOptions);
45
+ /**
46
+ * Add a chunk to the chronicle (e.g., a chat message or a doc shard).
47
+ *
48
+ * @returns the created PickerChunk
49
+ */
50
+ addChunk(opts: {
51
+ id: ChunkId;
52
+ rawTokens: number;
53
+ bodyGroupId?: string;
54
+ pinned?: boolean;
55
+ lockedByAgent?: boolean;
56
+ }): PickerChunk;
57
+ /**
58
+ * Produce an L1 summary for a range of chunks. Mocks the LLM call.
59
+ * Idempotent: the same chunk range produces the same summary id.
60
+ *
61
+ * @returns the produced SummaryEntry (or the existing one if already produced)
62
+ */
63
+ produceL1(chunkIds: ChunkId[]): SummaryEntry;
64
+ /**
65
+ * Produce an L_{k+1} summary from N L_k summaries.
66
+ */
67
+ produceUpper(level: number, sourceSummaryIds: SummaryId[]): SummaryEntry;
68
+ /**
69
+ * Speculative pre-producer: when N siblings exist sharing the same potential
70
+ * parent at level k+1, eagerly produce L_{k+1}. Bottom-up, idempotent.
71
+ * Mirrors the design's background pre-producer (§7 #2 settled decisions).
72
+ */
73
+ preProduceUpperLevels(): void;
74
+ /**
75
+ * Build a head/tail bookkeeping from chunk indices.
76
+ */
77
+ computeHeadTail(opts: {
78
+ headTokens: number;
79
+ tailTokens: number;
80
+ }): {
81
+ headTokens: number;
82
+ tailTokens: number;
83
+ headChunkIds: Set<ChunkId>;
84
+ tailChunkIds: Set<ChunkId>;
85
+ };
86
+ /**
87
+ * Apply a snapshot of resolutions back onto the chunks (used after running
88
+ * the picker to commit the new state for subsequent turns).
89
+ */
90
+ applyResolutions(resolutions: ReadonlyMap<ChunkId, number>): void;
91
+ private linkChunksToL1;
92
+ private allocSummaryId;
93
+ private firstChunkSeq;
94
+ private sumTokens;
95
+ }
96
+ /**
97
+ * Convenience: build N chunks with uniform token size, then auto-produce
98
+ * the full summary chain (L1, L2, …) bottom-up via the mergeThreshold rule.
99
+ *
100
+ * Returns the populated MockChronicle.
101
+ */
102
+ export declare function buildChronicleWithChain(opts: {
103
+ chunkCount: number;
104
+ tokensPerChunk: number;
105
+ mergeThreshold?: number;
106
+ recallPairTokens?: number;
107
+ produceChain?: boolean;
108
+ }): MockChronicle;
109
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../test/adaptive/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AAEjF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,uEAAuE;AACvE,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,MAAM,WAAW,oBAAoB;IACnC,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oCAAoC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,CAAM;IACpC,QAAQ,CAAC,SAAS,4BAAsC;IACxD,QAAQ,CAAC,gBAAgB,sBAAgC;IACzD,OAAO,CAAC,cAAc,CAA6B;IAEnD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IAEzC,qDAAqD;IACrD,QAAQ,SAAK;gBAED,IAAI,GAAE,oBAAyB;IAK3C;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE;QACb,EAAE,EAAE,OAAO,CAAC;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,GAAG,WAAW;IAcf;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,YAAY;IA2B5C;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,YAAY;IA2CxE;;;;OAIG;IACH,qBAAqB,IAAI,IAAI;IA8B7B;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG;QACF,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;KAC5B;IA4BD;;;OAGG;IACH,gBAAgB,CAAC,WAAW,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,IAAI;IASjE,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,SAAS;CAKlB;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,aAAa,CAmBhB"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Synthetic test harness for the adaptive-resolution picker.
3
+ *
4
+ * Builds a minimal in-memory chronicle simulator with:
5
+ * - Chunks (PickerChunk shape) with deterministic token counts.
6
+ * - A mocked LLM that produces deterministic "summaries" by hashing input.
7
+ * - A summary tree that grows as the LLM is called.
8
+ * - A function to run the picker against a built state.
9
+ *
10
+ * The mock LLM doesn't actually call any API — it constructs fake summary
11
+ * content so we can validate picker logic, fold semantics, and cache stickiness
12
+ * without external dependencies.
13
+ */
14
+ /**
15
+ * Default tokens per recall pair, regardless of source level.
16
+ * The design specifies that L_k summaries target a constant size (~2K tokens
17
+ * by convention) so each level's recall pair has predictable footprint.
18
+ */
19
+ export const DEFAULT_RECALL_PAIR_TOKENS = 200;
20
+ /** Default group size: how many L_k items combine into one L_{k+1}. */
21
+ export const DEFAULT_MERGE_THRESHOLD = 6;
22
+ /**
23
+ * Mutable in-memory chronicle. Mirrors what the real strategy would manage
24
+ * but trimmed to just what the picker needs.
25
+ */
26
+ export class MockChronicle {
27
+ chunks = [];
28
+ summaries = new Map();
29
+ recallPairTokens = new Map();
30
+ summaryCounter = new Map(); // level -> next id
31
+ mergeThreshold;
32
+ defaultRecallPairTokens;
33
+ /** Mock LLM call count, for telemetry assertions. */
34
+ llmCalls = 0;
35
+ constructor(opts = {}) {
36
+ this.mergeThreshold = opts.mergeThreshold ?? DEFAULT_MERGE_THRESHOLD;
37
+ this.defaultRecallPairTokens = opts.recallPairTokens ?? DEFAULT_RECALL_PAIR_TOKENS;
38
+ }
39
+ /**
40
+ * Add a chunk to the chronicle (e.g., a chat message or a doc shard).
41
+ *
42
+ * @returns the created PickerChunk
43
+ */
44
+ addChunk(opts) {
45
+ const chunk = {
46
+ id: opts.id,
47
+ sequence: this.chunks.length,
48
+ rawTokens: opts.rawTokens,
49
+ currentResolution: 0,
50
+ lockedByAgent: opts.lockedByAgent ?? false,
51
+ bodyGroupId: opts.bodyGroupId,
52
+ pinned: opts.pinned ?? false,
53
+ };
54
+ this.chunks.push(chunk);
55
+ return chunk;
56
+ }
57
+ /**
58
+ * Produce an L1 summary for a range of chunks. Mocks the LLM call.
59
+ * Idempotent: the same chunk range produces the same summary id.
60
+ *
61
+ * @returns the produced SummaryEntry (or the existing one if already produced)
62
+ */
63
+ produceL1(chunkIds) {
64
+ const sortedIds = [...chunkIds].sort();
65
+ const sourceKey = `L1:${sortedIds.join(',')}`;
66
+ // Idempotency check
67
+ for (const existing of this.summaries.values()) {
68
+ if (existing.level === 1 && summaryKey(existing) === sourceKey) {
69
+ return existing;
70
+ }
71
+ }
72
+ const id = this.allocSummaryId(1);
73
+ const entry = {
74
+ id,
75
+ level: 1,
76
+ content: `[mock L1 summary covering ${chunkIds.length} chunks: ${chunkIds.join(', ')}]`,
77
+ tokens: this.defaultRecallPairTokens,
78
+ sourceLevel: 0,
79
+ sourceIds: [...chunkIds],
80
+ sourceRange: { first: chunkIds[0], last: chunkIds[chunkIds.length - 1] },
81
+ created: Date.now(),
82
+ };
83
+ this.summaries.set(id, entry);
84
+ this.recallPairTokens.set(id, this.defaultRecallPairTokens);
85
+ this.linkChunksToL1(chunkIds, id);
86
+ this.llmCalls++;
87
+ return entry;
88
+ }
89
+ /**
90
+ * Produce an L_{k+1} summary from N L_k summaries.
91
+ */
92
+ produceUpper(level, sourceSummaryIds) {
93
+ if (level < 2) {
94
+ throw new Error('produceUpper requires level >= 2; use produceL1 for level 1');
95
+ }
96
+ const sortedIds = [...sourceSummaryIds].sort();
97
+ const sourceKey = `L${level}:${sortedIds.join(',')}`;
98
+ for (const existing of this.summaries.values()) {
99
+ if (existing.level === level && summaryKey(existing) === sourceKey) {
100
+ return existing;
101
+ }
102
+ }
103
+ const sources = sourceSummaryIds.map((sid) => {
104
+ const s = this.summaries.get(sid);
105
+ if (!s)
106
+ throw new Error(`produceUpper: unknown source summary ${sid}`);
107
+ if (s.level !== level - 1) {
108
+ throw new Error(`produceUpper: source ${sid} is level ${s.level}, expected ${level - 1}`);
109
+ }
110
+ return s;
111
+ });
112
+ const id = this.allocSummaryId(level);
113
+ const entry = {
114
+ id,
115
+ level,
116
+ content: `[mock L${level} summary merging ${sourceSummaryIds.length} L${level - 1}s]`,
117
+ tokens: this.defaultRecallPairTokens,
118
+ sourceLevel: level - 1,
119
+ sourceIds: [...sourceSummaryIds],
120
+ sourceRange: {
121
+ first: sources[0].sourceRange.first,
122
+ last: sources[sources.length - 1].sourceRange.last,
123
+ },
124
+ created: Date.now(),
125
+ };
126
+ this.summaries.set(id, entry);
127
+ this.recallPairTokens.set(id, this.defaultRecallPairTokens);
128
+ for (const sid of sourceSummaryIds) {
129
+ const s = this.summaries.get(sid);
130
+ s.parentId = id;
131
+ }
132
+ this.llmCalls++;
133
+ return entry;
134
+ }
135
+ /**
136
+ * Speculative pre-producer: when N siblings exist sharing the same potential
137
+ * parent at level k+1, eagerly produce L_{k+1}. Bottom-up, idempotent.
138
+ * Mirrors the design's background pre-producer (§7 #2 settled decisions).
139
+ */
140
+ preProduceUpperLevels() {
141
+ let changed = true;
142
+ while (changed) {
143
+ changed = false;
144
+ // Find the deepest existing level
145
+ let maxLevel = 0;
146
+ for (const s of this.summaries.values())
147
+ maxLevel = Math.max(maxLevel, s.level);
148
+ for (let level = 1; level <= maxLevel; level++) {
149
+ // Group orphaned (parentId === undefined) summaries at this level
150
+ // by their position in source order. Take consecutive groups of
151
+ // mergeThreshold and produce the L_{k+1} parent.
152
+ const orphans = [];
153
+ for (const s of this.summaries.values()) {
154
+ if (s.level === level && !s.parentId)
155
+ orphans.push(s);
156
+ }
157
+ // Sort by sequence of the first chunk in their source range.
158
+ orphans.sort((a, b) => this.firstChunkSeq(a) - this.firstChunkSeq(b));
159
+ while (orphans.length >= this.mergeThreshold) {
160
+ const group = orphans.splice(0, this.mergeThreshold);
161
+ this.produceUpper(level + 1, group.map((s) => s.id));
162
+ changed = true;
163
+ }
164
+ }
165
+ }
166
+ }
167
+ /**
168
+ * Build a head/tail bookkeeping from chunk indices.
169
+ */
170
+ computeHeadTail(opts) {
171
+ const headIds = new Set();
172
+ const tailIds = new Set();
173
+ let used = 0;
174
+ for (let i = 0; i < this.chunks.length; i++) {
175
+ const c = this.chunks[i];
176
+ if (used + c.rawTokens > opts.headTokens)
177
+ break;
178
+ headIds.add(c.id);
179
+ used += c.rawTokens;
180
+ }
181
+ used = 0;
182
+ for (let i = this.chunks.length - 1; i >= 0; i--) {
183
+ const c = this.chunks[i];
184
+ if (used + c.rawTokens > opts.tailTokens && tailIds.size > 0)
185
+ break;
186
+ tailIds.add(c.id);
187
+ used += c.rawTokens;
188
+ if (used >= opts.tailTokens)
189
+ break;
190
+ }
191
+ return {
192
+ headTokens: this.sumTokens(headIds),
193
+ tailTokens: this.sumTokens(tailIds),
194
+ headChunkIds: headIds,
195
+ tailChunkIds: tailIds,
196
+ };
197
+ }
198
+ /**
199
+ * Apply a snapshot of resolutions back onto the chunks (used after running
200
+ * the picker to commit the new state for subsequent turns).
201
+ */
202
+ applyResolutions(resolutions) {
203
+ for (const c of this.chunks) {
204
+ const r = resolutions.get(c.id);
205
+ if (r !== undefined && r !== c.currentResolution) {
206
+ c.currentResolution = r;
207
+ }
208
+ }
209
+ }
210
+ linkChunksToL1(chunkIds, l1Id) {
211
+ const idSet = new Set(chunkIds);
212
+ for (const c of this.chunks) {
213
+ if (idSet.has(c.id))
214
+ c.l1Id = l1Id;
215
+ }
216
+ }
217
+ allocSummaryId(level) {
218
+ const n = (this.summaryCounter.get(level) ?? 0);
219
+ this.summaryCounter.set(level, n + 1);
220
+ return `L${level}-${n}`;
221
+ }
222
+ firstChunkSeq(s) {
223
+ const c = this.chunks.find((ch) => ch.id === s.sourceRange.first);
224
+ return c?.sequence ?? 0;
225
+ }
226
+ sumTokens(ids) {
227
+ let t = 0;
228
+ for (const c of this.chunks)
229
+ if (ids.has(c.id))
230
+ t += c.rawTokens;
231
+ return t;
232
+ }
233
+ }
234
+ function summaryKey(s) {
235
+ return `L${s.level}:${[...s.sourceIds].sort().join(',')}`;
236
+ }
237
+ /**
238
+ * Convenience: build N chunks with uniform token size, then auto-produce
239
+ * the full summary chain (L1, L2, …) bottom-up via the mergeThreshold rule.
240
+ *
241
+ * Returns the populated MockChronicle.
242
+ */
243
+ export function buildChronicleWithChain(opts) {
244
+ const chronicle = new MockChronicle({
245
+ mergeThreshold: opts.mergeThreshold,
246
+ recallPairTokens: opts.recallPairTokens,
247
+ });
248
+ for (let i = 0; i < opts.chunkCount; i++) {
249
+ chronicle.addChunk({ id: `c-${i.toString().padStart(4, '0')}`, rawTokens: opts.tokensPerChunk });
250
+ }
251
+ if (opts.produceChain ?? true) {
252
+ // Produce L1s in batches of mergeThreshold (one L1 per batch).
253
+ const threshold = chronicle.mergeThreshold;
254
+ for (let i = 0; i < chronicle.chunks.length; i += threshold) {
255
+ const group = chronicle.chunks.slice(i, i + threshold).map((c) => c.id);
256
+ chronicle.produceL1(group);
257
+ }
258
+ // Bottom-up pre-produce L2, L3, …
259
+ chronicle.preProduceUpperLevels();
260
+ }
261
+ return chronicle;
262
+ }
263
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../../../test/adaptive/harness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * End-to-end test of ingestion-time chunking + adaptive picker.
3
+ *
4
+ * Verifies that:
5
+ * - A large message is automatically chunked into shards at ingestion
6
+ * - Shards are stored with shared bodyGroupId
7
+ * - The picker folds shards under budget pressure
8
+ * - The render path produces ONE combined entry for the whole doc, with
9
+ * inline summary text replacing folded shards
10
+ * - Byte-faithful reassembly when no folding has happened
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=ingestion.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingestion.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/ingestion.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}