@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.
- package/dist/src/adaptive/chunker.d.ts +70 -0
- package/dist/src/adaptive/chunker.d.ts.map +1 -0
- package/dist/src/adaptive/chunker.js +233 -0
- package/dist/src/adaptive/chunker.js.map +1 -0
- package/dist/src/adaptive/folding-strategy.d.ts +118 -0
- package/dist/src/adaptive/folding-strategy.d.ts.map +1 -0
- package/dist/src/adaptive/folding-strategy.js +12 -0
- package/dist/src/adaptive/folding-strategy.js.map +1 -0
- package/dist/src/adaptive/index.d.ts +17 -0
- package/dist/src/adaptive/index.d.ts.map +1 -0
- package/dist/src/adaptive/index.js +23 -0
- package/dist/src/adaptive/index.js.map +1 -0
- package/dist/src/adaptive/picker.d.ts +103 -0
- package/dist/src/adaptive/picker.d.ts.map +1 -0
- package/dist/src/adaptive/picker.js +357 -0
- package/dist/src/adaptive/picker.js.map +1 -0
- package/dist/src/adaptive/render.d.ts +39 -0
- package/dist/src/adaptive/render.d.ts.map +1 -0
- package/dist/src/adaptive/render.js +104 -0
- package/dist/src/adaptive/render.js.map +1 -0
- package/dist/src/adaptive/strategies/flat-profile.d.ts +19 -0
- package/dist/src/adaptive/strategies/flat-profile.d.ts.map +1 -0
- package/dist/src/adaptive/strategies/flat-profile.js +86 -0
- package/dist/src/adaptive/strategies/flat-profile.js.map +1 -0
- package/dist/src/adaptive/strategies/oldest-first.d.ts +19 -0
- package/dist/src/adaptive/strategies/oldest-first.d.ts.map +1 -0
- package/dist/src/adaptive/strategies/oldest-first.js +39 -0
- package/dist/src/adaptive/strategies/oldest-first.js.map +1 -0
- package/dist/src/context-manager.d.ts +74 -2
- package/dist/src/context-manager.d.ts.map +1 -1
- package/dist/src/context-manager.js +150 -10
- package/dist/src/context-manager.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/message-store.d.ts +36 -1
- package/dist/src/message-store.d.ts.map +1 -1
- package/dist/src/message-store.js +134 -8
- package/dist/src/message-store.js.map +1 -1
- package/dist/src/strategies/autobiographical.d.ts +422 -5
- package/dist/src/strategies/autobiographical.d.ts.map +1 -1
- package/dist/src/strategies/autobiographical.js +2248 -98
- package/dist/src/strategies/autobiographical.js.map +1 -1
- package/dist/src/strategies/index.d.ts +1 -1
- package/dist/src/strategies/index.d.ts.map +1 -1
- package/dist/src/strategies/index.js.map +1 -1
- package/dist/src/types/index.d.ts +2 -2
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/message.d.ts +38 -0
- package/dist/src/types/message.d.ts.map +1 -1
- package/dist/src/types/strategy.d.ts +302 -10
- package/dist/src/types/strategy.d.ts.map +1 -1
- package/dist/src/types/strategy.js +28 -0
- package/dist/src/types/strategy.js.map +1 -1
- package/dist/test/adaptive/branching.test.d.ts +20 -0
- package/dist/test/adaptive/branching.test.d.ts.map +1 -0
- package/dist/test/adaptive/branching.test.js +176 -0
- package/dist/test/adaptive/branching.test.js.map +1 -0
- package/dist/test/adaptive/chunker.test.d.ts +2 -0
- package/dist/test/adaptive/chunker.test.d.ts.map +1 -0
- package/dist/test/adaptive/chunker.test.js +120 -0
- package/dist/test/adaptive/chunker.test.js.map +1 -0
- package/dist/test/adaptive/deep-levels.test.d.ts +10 -0
- package/dist/test/adaptive/deep-levels.test.d.ts.map +1 -0
- package/dist/test/adaptive/deep-levels.test.js +167 -0
- package/dist/test/adaptive/deep-levels.test.js.map +1 -0
- package/dist/test/adaptive/doc-plus-chat.test.d.ts +10 -0
- package/dist/test/adaptive/doc-plus-chat.test.d.ts.map +1 -0
- package/dist/test/adaptive/doc-plus-chat.test.js +194 -0
- package/dist/test/adaptive/doc-plus-chat.test.js.map +1 -0
- package/dist/test/adaptive/hard-fail.test.d.ts +9 -0
- package/dist/test/adaptive/hard-fail.test.d.ts.map +1 -0
- package/dist/test/adaptive/hard-fail.test.js +154 -0
- package/dist/test/adaptive/hard-fail.test.js.map +1 -0
- package/dist/test/adaptive/harness.d.ts +109 -0
- package/dist/test/adaptive/harness.d.ts.map +1 -0
- package/dist/test/adaptive/harness.js +263 -0
- package/dist/test/adaptive/harness.js.map +1 -0
- package/dist/test/adaptive/ingestion.test.d.ts +13 -0
- package/dist/test/adaptive/ingestion.test.d.ts.map +1 -0
- package/dist/test/adaptive/ingestion.test.js +306 -0
- package/dist/test/adaptive/ingestion.test.js.map +1 -0
- package/dist/test/adaptive/integration.test.d.ts +12 -0
- package/dist/test/adaptive/integration.test.d.ts.map +1 -0
- package/dist/test/adaptive/integration.test.js +247 -0
- package/dist/test/adaptive/integration.test.js.map +1 -0
- package/dist/test/adaptive/long-chronicle.test.d.ts +13 -0
- package/dist/test/adaptive/long-chronicle.test.d.ts.map +1 -0
- package/dist/test/adaptive/long-chronicle.test.js +186 -0
- package/dist/test/adaptive/long-chronicle.test.js.map +1 -0
- package/dist/test/adaptive/persistence.test.d.ts +11 -0
- package/dist/test/adaptive/persistence.test.d.ts.map +1 -0
- package/dist/test/adaptive/persistence.test.js +200 -0
- package/dist/test/adaptive/persistence.test.js.map +1 -0
- package/dist/test/adaptive/picker.test.d.ts +2 -0
- package/dist/test/adaptive/picker.test.d.ts.map +1 -0
- package/dist/test/adaptive/picker.test.js +333 -0
- package/dist/test/adaptive/picker.test.js.map +1 -0
- package/dist/test/adaptive/render.test.d.ts +2 -0
- package/dist/test/adaptive/render.test.d.ts.map +1 -0
- package/dist/test/adaptive/render.test.js +109 -0
- package/dist/test/adaptive/render.test.js.map +1 -0
- package/dist/test/adaptive/shard-immutability.test.d.ts +14 -0
- package/dist/test/adaptive/shard-immutability.test.d.ts.map +1 -0
- package/dist/test/adaptive/shard-immutability.test.js +184 -0
- package/dist/test/adaptive/shard-immutability.test.js.map +1 -0
- package/dist/test/adaptive/strategy-integration.test.d.ts +9 -0
- package/dist/test/adaptive/strategy-integration.test.d.ts.map +1 -0
- package/dist/test/adaptive/strategy-integration.test.js +293 -0
- package/dist/test/adaptive/strategy-integration.test.js.map +1 -0
- package/dist/test/message-store-sequence.test.d.ts +25 -0
- package/dist/test/message-store-sequence.test.d.ts.map +1 -0
- package/dist/test/message-store-sequence.test.js +157 -0
- package/dist/test/message-store-sequence.test.js.map +1 -0
- package/dist/test/non-blocking-compile.test.d.ts +22 -0
- package/dist/test/non-blocking-compile.test.d.ts.map +1 -0
- package/dist/test/non-blocking-compile.test.js +114 -0
- package/dist/test/non-blocking-compile.test.js.map +1 -0
- package/dist/test/pins.test.d.ts +10 -0
- package/dist/test/pins.test.d.ts.map +1 -0
- package/dist/test/pins.test.js +164 -0
- package/dist/test/pins.test.js.map +1 -0
- package/dist/test/recall-positioning.test.d.ts +16 -0
- package/dist/test/recall-positioning.test.d.ts.map +1 -0
- package/dist/test/recall-positioning.test.js +181 -0
- package/dist/test/recall-positioning.test.js.map +1 -0
- package/dist/test/search.test.d.ts +10 -0
- package/dist/test/search.test.d.ts.map +1 -0
- package/dist/test/search.test.js +141 -0
- package/dist/test/search.test.js.map +1 -0
- package/dist/test/speculation-cap.test.d.ts +17 -0
- package/dist/test/speculation-cap.test.d.ts.map +1 -0
- package/dist/test/speculation-cap.test.js +157 -0
- package/dist/test/speculation-cap.test.js.map +1 -0
- package/dist/test/strategy-persistence.test.d.ts +15 -0
- package/dist/test/strategy-persistence.test.d.ts.map +1 -0
- package/dist/test/strategy-persistence.test.js +244 -0
- package/dist/test/strategy-persistence.test.js.map +1 -0
- package/dist/test/tool-pruning.test.d.ts +10 -0
- package/dist/test/tool-pruning.test.d.ts.map +1 -0
- package/dist/test/tool-pruning.test.js +140 -0
- package/dist/test/tool-pruning.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/adaptive/chunker.ts +323 -0
- package/src/adaptive/folding-strategy.ts +129 -0
- package/src/adaptive/index.ts +51 -0
- package/src/adaptive/picker.ts +442 -0
- package/src/adaptive/render.ts +115 -0
- package/src/adaptive/strategies/flat-profile.ts +96 -0
- package/src/adaptive/strategies/oldest-first.ts +48 -0
- package/src/context-manager.ts +174 -10
- package/src/index.ts +1 -1
- package/src/message-store.ts +147 -8
- package/src/strategies/autobiographical.ts +2468 -166
- package/src/strategies/index.ts +1 -1
- package/src/types/index.ts +14 -1
- package/src/types/message.ts +43 -0
- package/src/types/strategy.ts +332 -10
package/src/strategies/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { PassthroughStrategy } from './passthrough.js';
|
|
2
|
-
export { AutobiographicalStrategy } from './autobiographical.js';
|
|
2
|
+
export { AutobiographicalStrategy, type AutobiographicalProgressSnapshot } from './autobiographical.js';
|
package/src/types/index.ts
CHANGED
|
@@ -37,6 +37,19 @@ export type {
|
|
|
37
37
|
PhaseType,
|
|
38
38
|
KnowledgeConfig,
|
|
39
39
|
ResettableStrategy,
|
|
40
|
+
ProtectedRange,
|
|
41
|
+
SearchQuery,
|
|
42
|
+
SearchResult,
|
|
43
|
+
SearchableStrategy,
|
|
44
|
+
PinnableStrategy,
|
|
45
|
+
RenderStats,
|
|
46
|
+
RenderStatsCapableStrategy,
|
|
40
47
|
} from './strategy.js';
|
|
41
48
|
|
|
42
|
-
export {
|
|
49
|
+
export {
|
|
50
|
+
DEFAULT_AUTOBIOGRAPHICAL_CONFIG,
|
|
51
|
+
isResettableStrategy,
|
|
52
|
+
isPinnableStrategy,
|
|
53
|
+
isSearchableStrategy,
|
|
54
|
+
isRenderStatsCapable,
|
|
55
|
+
} from './strategy.js';
|
package/src/types/message.ts
CHANGED
|
@@ -47,6 +47,40 @@ export interface StoredMessage {
|
|
|
47
47
|
timestamp: Date;
|
|
48
48
|
/** IDs of messages that caused this one (from Chronicle causation) */
|
|
49
49
|
causedBy?: MessageId[];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* If this message is a shard of a larger logical message (chunked at
|
|
53
|
+
* ingestion because it exceeded `chunkThreshold`), this is the stable
|
|
54
|
+
* group id shared with sibling shards. Shards with the same id are
|
|
55
|
+
* concatenated into one API message at render time. Null/undefined
|
|
56
|
+
* for messages that fit in a single chunk. See
|
|
57
|
+
* `docs/adaptive-resolution-design.md` §3.6.
|
|
58
|
+
*/
|
|
59
|
+
bodyGroupId?: string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The shard's order within its bodyGroup, starting at 0. Only meaningful
|
|
63
|
+
* when `bodyGroupId` is set. Used to reassemble shards into byte-faithful
|
|
64
|
+
* order at render time.
|
|
65
|
+
*/
|
|
66
|
+
shardIndex?: number;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Current display resolution for this chunk:
|
|
70
|
+
* - 0 = render raw content
|
|
71
|
+
* - k>0 = render the L_k summary that covers this chunk
|
|
72
|
+
*
|
|
73
|
+
* Set by the picker (or the agent in V2). Default 0. See
|
|
74
|
+
* `docs/adaptive-resolution-design.md` §3.3.
|
|
75
|
+
*/
|
|
76
|
+
currentResolution?: number;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* If true, the picker must not change `currentResolution` for this chunk.
|
|
80
|
+
* Set by `lockChunk()` (programmatic API) or, in V2, by the agent's
|
|
81
|
+
* `unfold` tool. Default false.
|
|
82
|
+
*/
|
|
83
|
+
lockedByAgent?: boolean;
|
|
50
84
|
}
|
|
51
85
|
|
|
52
86
|
/**
|
|
@@ -85,6 +119,15 @@ export interface StoredMessageInternal {
|
|
|
85
119
|
metadata?: MessageMetadata;
|
|
86
120
|
timestamp: number; // Unix timestamp for storage
|
|
87
121
|
causedBy?: MessageId[];
|
|
122
|
+
|
|
123
|
+
/** See StoredMessage.bodyGroupId */
|
|
124
|
+
bodyGroupId?: string;
|
|
125
|
+
/** See StoredMessage.shardIndex */
|
|
126
|
+
shardIndex?: number;
|
|
127
|
+
/** See StoredMessage.currentResolution */
|
|
128
|
+
currentResolution?: number;
|
|
129
|
+
/** See StoredMessage.lockedByAgent */
|
|
130
|
+
lockedByAgent?: boolean;
|
|
88
131
|
}
|
|
89
132
|
|
|
90
133
|
/**
|
package/src/types/strategy.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { JsStore } from '@animalabs/chronicle';
|
|
2
|
+
import type { Membrane, ContentBlock } from '@animalabs/membrane';
|
|
2
3
|
import type { StoredMessage, MessageId, Sequence } from './message.js';
|
|
3
4
|
import type { ContextEntry, TokenBudget, PendingWork } from './context.js';
|
|
4
5
|
|
|
@@ -48,6 +49,22 @@ export interface StrategyContext {
|
|
|
48
49
|
membrane?: Membrane;
|
|
49
50
|
/** Current sequence number */
|
|
50
51
|
currentSequence: Sequence;
|
|
52
|
+
/**
|
|
53
|
+
* Underlying Chronicle store. Strategies may register their own state slots
|
|
54
|
+
* (via `store.registerState`) for durable strategy state that needs to
|
|
55
|
+
* survive process restart and follow Chronicle branches.
|
|
56
|
+
*
|
|
57
|
+
* State IDs should be scoped under `namespace` to avoid collisions with
|
|
58
|
+
* other strategies or with the message/context-log states.
|
|
59
|
+
*/
|
|
60
|
+
store: JsStore;
|
|
61
|
+
/**
|
|
62
|
+
* Namespace under which this strategy should scope its state IDs.
|
|
63
|
+
* Use as a prefix: e.g. `${namespace}/autobio:summaries`. Always defined;
|
|
64
|
+
* defaults to a stable per-manager value when no caller-supplied namespace
|
|
65
|
+
* exists, so strategies never need to handle the unscoped case.
|
|
66
|
+
*/
|
|
67
|
+
namespace: string;
|
|
51
68
|
}
|
|
52
69
|
|
|
53
70
|
/**
|
|
@@ -108,6 +125,41 @@ export interface ContextStrategy {
|
|
|
108
125
|
log: ContextLogView,
|
|
109
126
|
budget: TokenBudget
|
|
110
127
|
): ContextEntry[];
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Optional: at ingestion time, decide whether to split this incoming
|
|
131
|
+
* message into multiple shards (because it's too large to be a single
|
|
132
|
+
* fold unit). If returned, the framework stores each shard as a separate
|
|
133
|
+
* StoredMessage with the shared `bodyGroupId` and per-shard `shardIndex`,
|
|
134
|
+
* and the render path will reassemble them into one API message at
|
|
135
|
+
* compile time.
|
|
136
|
+
*
|
|
137
|
+
* Return null or undefined to skip chunking (message stored as one record).
|
|
138
|
+
*
|
|
139
|
+
* See `docs/adaptive-resolution-design.md` §3.6.
|
|
140
|
+
*/
|
|
141
|
+
chunkIngressMessage?(
|
|
142
|
+
participant: string,
|
|
143
|
+
content: ContentBlock[]
|
|
144
|
+
): IngressChunkResult | null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Result of a strategy's ingestion-time chunking decision.
|
|
149
|
+
*/
|
|
150
|
+
export interface IngressChunkResult {
|
|
151
|
+
/** Stable id shared by all shards of this message. */
|
|
152
|
+
bodyGroupId: string;
|
|
153
|
+
/**
|
|
154
|
+
* The shards in source order. Each becomes a separate StoredMessage.
|
|
155
|
+
* Concatenating the shards' text content must reproduce the original
|
|
156
|
+
* message body byte-for-byte.
|
|
157
|
+
*/
|
|
158
|
+
shards: Array<{
|
|
159
|
+
content: ContentBlock[];
|
|
160
|
+
/** Order within the bodyGroup, starting at 0. */
|
|
161
|
+
shardIndex: number;
|
|
162
|
+
}>;
|
|
111
163
|
}
|
|
112
164
|
|
|
113
165
|
/**
|
|
@@ -128,6 +180,109 @@ export function isResettableStrategy(s: ContextStrategy): s is ResettableStrateg
|
|
|
128
180
|
return 'resetHeadWindow' in s && typeof (s as ResettableStrategy).resetHeadWindow === 'function';
|
|
129
181
|
}
|
|
130
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Strategy that supports protected ranges (pins + documents).
|
|
185
|
+
* Pinned ranges are excluded from compression and render raw at their
|
|
186
|
+
* original chronological position. Implemented by AutobiographicalStrategy.
|
|
187
|
+
*/
|
|
188
|
+
export interface PinnableStrategy extends ContextStrategy {
|
|
189
|
+
pinRange(firstMessageId: string, lastMessageId: string, opts?: { name?: string }): string;
|
|
190
|
+
markDocument(messageId: string, opts?: { name?: string }): string;
|
|
191
|
+
unpin(pinId: string): boolean;
|
|
192
|
+
listPins(): ReadonlyArray<ProtectedRange>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Type guard for strategies that support pins / documents. */
|
|
196
|
+
export function isPinnableStrategy(s: ContextStrategy): s is PinnableStrategy {
|
|
197
|
+
return (
|
|
198
|
+
'pinRange' in s &&
|
|
199
|
+
typeof (s as PinnableStrategy).pinRange === 'function' &&
|
|
200
|
+
'unpin' in s &&
|
|
201
|
+
typeof (s as PinnableStrategy).unpin === 'function'
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Query for `searchSummaries`. At least one of `text` or `regex` should be
|
|
207
|
+
* provided to constrain results; otherwise all summaries pass.
|
|
208
|
+
*/
|
|
209
|
+
export interface SearchQuery {
|
|
210
|
+
/** Case-insensitive substring match against summary content. */
|
|
211
|
+
text?: string;
|
|
212
|
+
/** Regex match against summary content (overrides `text` if both set). */
|
|
213
|
+
regex?: RegExp;
|
|
214
|
+
/** Filter by summary level(s). Default: all levels. */
|
|
215
|
+
levels?: SummaryLevel[];
|
|
216
|
+
/** Maximum number of results to return. Default: 50. */
|
|
217
|
+
limit?: number;
|
|
218
|
+
/**
|
|
219
|
+
* Include summaries that have been merged into a higher-level summary.
|
|
220
|
+
* Default: false (only "live" unmerged summaries are returned).
|
|
221
|
+
*/
|
|
222
|
+
includeMerged?: boolean;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Result of a single search match. */
|
|
226
|
+
export interface SearchResult {
|
|
227
|
+
summary: SummaryEntry;
|
|
228
|
+
/** Number of times the query pattern matched in the summary content. */
|
|
229
|
+
matches: number;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Strategy that supports searching its summary archive. Implemented by
|
|
234
|
+
* AutobiographicalStrategy.
|
|
235
|
+
*/
|
|
236
|
+
export interface SearchableStrategy extends ContextStrategy {
|
|
237
|
+
searchSummaries(query: SearchQuery): SearchResult[];
|
|
238
|
+
getSummary(id: string): SummaryEntry | null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Type guard for strategies that support search. */
|
|
242
|
+
export function isSearchableStrategy(s: ContextStrategy): s is SearchableStrategy {
|
|
243
|
+
return (
|
|
244
|
+
'searchSummaries' in s &&
|
|
245
|
+
typeof (s as SearchableStrategy).searchSummaries === 'function' &&
|
|
246
|
+
'getSummary' in s &&
|
|
247
|
+
typeof (s as SearchableStrategy).getSummary === 'function'
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Per-render observability stats from a strategy. Counts and token sums for
|
|
253
|
+
* head / tail / summaries / pending work, suitable for TUIs and dashboards
|
|
254
|
+
* that want to display "how much of the context is folded vs raw" at a
|
|
255
|
+
* glance. Token sums use the strategy's own estimates so they line up with
|
|
256
|
+
* the numbers `select()` uses for budget math.
|
|
257
|
+
*/
|
|
258
|
+
export interface RenderStats {
|
|
259
|
+
head: { messages: number; tokens: number };
|
|
260
|
+
tail: { messages: number; tokens: number };
|
|
261
|
+
summaries: {
|
|
262
|
+
l1: { count: number; tokens: number };
|
|
263
|
+
l2: { count: number; tokens: number };
|
|
264
|
+
l3: { count: number; tokens: number };
|
|
265
|
+
};
|
|
266
|
+
pending: { chunks: number; merges: number };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Strategy that can produce render-time observability stats. Implemented by
|
|
271
|
+
* AutobiographicalStrategy. Optional capability — strategies that don't
|
|
272
|
+
* implement it simply have `ContextManager.getRenderStats()` return `null`.
|
|
273
|
+
*/
|
|
274
|
+
export interface RenderStatsCapableStrategy extends ContextStrategy {
|
|
275
|
+
getRenderStats(store: MessageStoreView): RenderStats;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Type guard for strategies that produce render stats. */
|
|
279
|
+
export function isRenderStatsCapable(s: ContextStrategy): s is RenderStatsCapableStrategy {
|
|
280
|
+
return (
|
|
281
|
+
'getRenderStats' in s &&
|
|
282
|
+
typeof (s as RenderStatsCapableStrategy).getRenderStats === 'function'
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
131
286
|
/**
|
|
132
287
|
* Configuration for the Autobiographical strategy.
|
|
133
288
|
*/
|
|
@@ -181,35 +336,169 @@ export interface AutobiographicalConfig {
|
|
|
181
336
|
l2BudgetTokens?: number;
|
|
182
337
|
/** Token budget for L1 summaries in select() (default: 30000) */
|
|
183
338
|
l1BudgetTokens?: number;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* When true (default), each selected summary emits as its own positioned
|
|
342
|
+
* Q/A recall pair, sorted chronologically by source range. When false,
|
|
343
|
+
* all selected summaries are concatenated into one Q/A pair between head
|
|
344
|
+
* and tail (legacy behavior pre-2026-05-10).
|
|
345
|
+
*
|
|
346
|
+
* Per-region positioning is the spec-faithful behavior: it lets the agent
|
|
347
|
+
* see each memory in its temporal place rather than as a wall of unrelated
|
|
348
|
+
* recollections from another speaker. Without it, hierarchical compression
|
|
349
|
+
* is structurally similar to the dual-recall corruption pattern that
|
|
350
|
+
* caused Lena's context degradation on Hermes.
|
|
351
|
+
*/
|
|
352
|
+
positionedRecallPairs?: boolean;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Template for the per-pair recall question header. Substitutions:
|
|
356
|
+
* {id} — summary id (e.g. "L1-3")
|
|
357
|
+
* {level} — summary level (1, 2, or 3)
|
|
358
|
+
* {first} — first source message id
|
|
359
|
+
* {last} — last source message id
|
|
360
|
+
* Default: '[Recall {id}]'.
|
|
361
|
+
*
|
|
362
|
+
* Only used when `positionedRecallPairs` is true.
|
|
363
|
+
*/
|
|
364
|
+
recallHeaderTemplate?: string;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Per-tool retention limit: keep at most the last N `tool_result` blocks
|
|
368
|
+
* for each tool name. Older results get their content replaced with a
|
|
369
|
+
* brief marker referencing the tool name and how many newer results exist.
|
|
370
|
+
*
|
|
371
|
+
* Two shapes accepted:
|
|
372
|
+
* - `number`: applies as a global default across all tools.
|
|
373
|
+
* - `Record<toolName, number>`: per-tool limit; tools not listed are
|
|
374
|
+
* unlimited.
|
|
375
|
+
*
|
|
376
|
+
* Default: undefined (no pruning). Use a small number (1–5) for tools
|
|
377
|
+
* that produce verbose, mostly-stale output (e.g. file listings, http
|
|
378
|
+
* fetches, log queries).
|
|
379
|
+
*/
|
|
380
|
+
toolResultMaxLastN?: number | Record<string, number>;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Truncate `tool_use` block inputs whose serialized JSON exceeds this
|
|
384
|
+
* many tokens. The truncated input becomes `{ "_truncated": true,
|
|
385
|
+
* "_originalTokens": N }` plus a head slice of the original input
|
|
386
|
+
* keys for context. Default: 0 (no truncation).
|
|
387
|
+
*/
|
|
388
|
+
toolUseInputMaxTokens?: number;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Cap on the number of speculative L1 summaries the strategy will hold
|
|
392
|
+
* (queued + unmerged). When `count(unmerged L1s) + count(queued chunks)`
|
|
393
|
+
* exceeds this cap, `onNewMessage`'s auto-tick is held back. Chunks
|
|
394
|
+
* still form and queue, but compression is deferred until a manual
|
|
395
|
+
* `tick()` or `compile()` triggers it.
|
|
396
|
+
*
|
|
397
|
+
* Default: undefined (no cap; compression fires eagerly on every
|
|
398
|
+
* new message when `autoTickOnNewMessage` is true).
|
|
399
|
+
*/
|
|
400
|
+
maxSpeculativeL1s?: number;
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* When false, the rendering pipeline emits the full ideal context (head
|
|
404
|
+
* window + all selected summaries + all recent messages) without
|
|
405
|
+
* truncating to fit `budget.maxTokens`. The caller's API will reject if
|
|
406
|
+
* the result exceeds the model's context window — the philosophy is
|
|
407
|
+
* "surface the overage rather than silently lose content."
|
|
408
|
+
*
|
|
409
|
+
* Default: true (legacy budget-aware truncation: stops emitting recall
|
|
410
|
+
* pairs and recent messages when the running total exceeds maxTokens).
|
|
411
|
+
*
|
|
412
|
+
* Recommended setting for long-lived agents on large-context models
|
|
413
|
+
* (e.g. opus-4-7 with 1M context): false. The window is generous enough
|
|
414
|
+
* that overflow is rare, and when it does happen you want to know.
|
|
415
|
+
*/
|
|
416
|
+
enforceBudget?: boolean;
|
|
417
|
+
|
|
418
|
+
// --- Adaptive resolution (per docs/adaptive-resolution-design.md) ---
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Enable picker-driven adaptive resolution. When true, `select()` uses
|
|
422
|
+
* the FoldingStrategy + Picker to choose per-message resolution under
|
|
423
|
+
* token-budget pressure rather than the threshold-driven `checkMergeThreshold`
|
|
424
|
+
* path. Default: false (existing hierarchical behavior preserved).
|
|
425
|
+
*/
|
|
426
|
+
adaptiveResolution?: boolean;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Folding strategy name when adaptiveResolution is on. One of:
|
|
430
|
+
* 'flat-profile' (default) — level-equalizing
|
|
431
|
+
* 'oldest-first' — chronological
|
|
432
|
+
* Custom strategies can be plugged in by the host application.
|
|
433
|
+
*/
|
|
434
|
+
foldingStrategy?: 'flat-profile' | 'oldest-first';
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Slack ratio (hysteresis) for the picker. The picker folds until total
|
|
438
|
+
* tokens ≤ budget * (1 - slack), and stays quiet while between slack
|
|
439
|
+
* and budget. Default 0.1.
|
|
440
|
+
*/
|
|
441
|
+
compressionSlackRatio?: number;
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Enable bottom-up speculative pre-production of higher-level summaries.
|
|
445
|
+
* When a new L_k summary is produced, if N siblings exist that would share
|
|
446
|
+
* an L_{k+1} parent, the L_{k+1} is enqueued for production immediately
|
|
447
|
+
* (no picker request needed). Default true when adaptiveResolution is on.
|
|
448
|
+
*/
|
|
449
|
+
speculativeProduction?: boolean;
|
|
184
450
|
}
|
|
185
451
|
|
|
186
452
|
/**
|
|
187
453
|
* Compression level in the hierarchical pyramid.
|
|
454
|
+
*
|
|
455
|
+
* Historically constrained to 1 | 2 | 3. As of the adaptive-resolution design
|
|
456
|
+
* (`docs/adaptive-resolution-design.md`), levels are unbounded: the picker
|
|
457
|
+
* can recursively produce L4, L5, ... as needed. The narrower literal type
|
|
458
|
+
* is kept as `LegacySummaryLevel` for code that still assumes the old shape.
|
|
459
|
+
*/
|
|
460
|
+
export type SummaryLevel = number;
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* The narrow level type used by pre-adaptive-resolution code paths.
|
|
464
|
+
* Prefer `SummaryLevel` for new code.
|
|
188
465
|
*/
|
|
189
|
-
export type
|
|
466
|
+
export type LegacySummaryLevel = 1 | 2 | 3;
|
|
190
467
|
|
|
191
468
|
/**
|
|
192
469
|
* A summary entry in the hierarchical memory pyramid.
|
|
193
470
|
* L1: compressed from raw message chunks.
|
|
194
|
-
*
|
|
195
|
-
* L3: merged from mergeThreshold L2s.
|
|
471
|
+
* L_{k>1}: merged from mergeThreshold L_{k-1}s.
|
|
196
472
|
*/
|
|
197
473
|
export interface SummaryEntry {
|
|
198
474
|
/** Unique ID (e.g., "L1-0", "L2-3") */
|
|
199
475
|
id: string;
|
|
200
|
-
/** Compression level */
|
|
476
|
+
/** Compression level (1, 2, 3, ... — unbounded in the adaptive-resolution design) */
|
|
201
477
|
level: SummaryLevel;
|
|
202
478
|
/** The summary text */
|
|
203
479
|
content: string;
|
|
204
|
-
/** Estimated token count (content.length / 4) */
|
|
480
|
+
/** Estimated token count (content.length / 4 or tokenizer-cached) */
|
|
205
481
|
tokens: number;
|
|
206
|
-
/**
|
|
207
|
-
|
|
208
|
-
|
|
482
|
+
/**
|
|
483
|
+
* Level of the sources: 0 = raw messages, k = L_k summaries.
|
|
484
|
+
* Pre-adaptive code uses 0 | 1 | 2; new code may produce higher values.
|
|
485
|
+
*/
|
|
486
|
+
sourceLevel: number;
|
|
487
|
+
/** IDs of source items (message IDs for L1, summary IDs for L_{k>1}) */
|
|
209
488
|
sourceIds: string[];
|
|
210
489
|
/** Range of original message IDs covered */
|
|
211
490
|
sourceRange: { first: string; last: string };
|
|
212
|
-
/**
|
|
491
|
+
/**
|
|
492
|
+
* The L_{level+1} summary this one is a source for, if produced.
|
|
493
|
+
* Pure archive metadata in the adaptive-resolution design — display
|
|
494
|
+
* decisions live on per-chunk `currentResolution`, not here.
|
|
495
|
+
*/
|
|
496
|
+
parentId?: string;
|
|
497
|
+
/**
|
|
498
|
+
* @deprecated Renamed to `parentId` in the adaptive-resolution design.
|
|
499
|
+
* Kept for read compatibility with chronicles produced by the old
|
|
500
|
+
* threshold-driven path. New writes should set `parentId` only.
|
|
501
|
+
*/
|
|
213
502
|
mergedInto?: string;
|
|
214
503
|
/** Creation timestamp */
|
|
215
504
|
created: number;
|
|
@@ -217,6 +506,37 @@ export interface SummaryEntry {
|
|
|
217
506
|
phaseType?: string;
|
|
218
507
|
}
|
|
219
508
|
|
|
509
|
+
/**
|
|
510
|
+
* Helper: read the parent pointer from a summary, accepting either the
|
|
511
|
+
* new `parentId` field or the deprecated `mergedInto` alias.
|
|
512
|
+
*/
|
|
513
|
+
export function getSummaryParentId(s: SummaryEntry): string | undefined {
|
|
514
|
+
return s.parentId ?? s.mergedInto;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* A range of messages protected from compression. Pins keep a span of raw
|
|
519
|
+
* messages visible at their original position in the rendered context.
|
|
520
|
+
*
|
|
521
|
+
* - `kind: 'pin'` — generic protected range (any first/last span).
|
|
522
|
+
* - `kind: 'document'` — typically a single message containing a body of
|
|
523
|
+
* information the agent wants to retain in full; semantically identical
|
|
524
|
+
* to a single-message pin, distinguished by metadata for tooling.
|
|
525
|
+
*/
|
|
526
|
+
export interface ProtectedRange {
|
|
527
|
+
/** Stable id assigned at pin time. */
|
|
528
|
+
id: string;
|
|
529
|
+
/** First message id of the protected range (inclusive). */
|
|
530
|
+
firstMessageId: string;
|
|
531
|
+
/** Last message id of the protected range (inclusive). */
|
|
532
|
+
lastMessageId: string;
|
|
533
|
+
kind: 'pin' | 'document';
|
|
534
|
+
/** Optional human-readable label. */
|
|
535
|
+
name?: string;
|
|
536
|
+
/** Creation timestamp (ms since epoch). */
|
|
537
|
+
created: number;
|
|
538
|
+
}
|
|
539
|
+
|
|
220
540
|
/**
|
|
221
541
|
* Phase type for knowledge extraction workflows.
|
|
222
542
|
*/
|
|
@@ -279,4 +599,6 @@ Write naturally, as recollection of what you experienced.`,
|
|
|
279
599
|
summaryContextLabel: 'What do you remember from earlier?',
|
|
280
600
|
summaryParticipant: 'Claude',
|
|
281
601
|
maxMessageTokens: 0,
|
|
602
|
+
positionedRecallPairs: true,
|
|
603
|
+
recallHeaderTemplate: '[Recall {id}]',
|
|
282
604
|
};
|