@dory-agentic/dory-agentic-sdk 0.2.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/LICENSE +21 -0
- package/README.md +331 -0
- package/dist/agent/createLfsAgent.d.ts +27 -0
- package/dist/agent/createLfsAgent.d.ts.map +1 -0
- package/dist/agent/createLfsAgent.js +51 -0
- package/dist/agent/loopConfig.d.ts +6 -0
- package/dist/agent/loopConfig.d.ts.map +1 -0
- package/dist/agent/loopConfig.js +5 -0
- package/dist/agent/runLfsTurn.d.ts +10 -0
- package/dist/agent/runLfsTurn.d.ts.map +1 -0
- package/dist/agent/runLfsTurn.js +20 -0
- package/dist/history/dorycodeAdapter.d.ts +16 -0
- package/dist/history/dorycodeAdapter.d.ts.map +1 -0
- package/dist/history/dorycodeAdapter.js +18 -0
- package/dist/history/formatForModel.d.ts +19 -0
- package/dist/history/formatForModel.d.ts.map +1 -0
- package/dist/history/formatForModel.js +22 -0
- package/dist/history/lanes.d.ts +3 -0
- package/dist/history/lanes.d.ts.map +1 -0
- package/dist/history/lanes.js +3 -0
- package/dist/history/messageManifest.d.ts +45 -0
- package/dist/history/messageManifest.d.ts.map +1 -0
- package/dist/history/messageManifest.js +231 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/lfs/adaptiveCoefficients.d.ts +13 -0
- package/dist/lfs/adaptiveCoefficients.d.ts.map +1 -0
- package/dist/lfs/adaptiveCoefficients.js +16 -0
- package/dist/lfs/attenuationCompiler.d.ts +16 -0
- package/dist/lfs/attenuationCompiler.d.ts.map +1 -0
- package/dist/lfs/attenuationCompiler.js +123 -0
- package/dist/lfs/chunkManager.d.ts +6 -0
- package/dist/lfs/chunkManager.d.ts.map +1 -0
- package/dist/lfs/chunkManager.js +142 -0
- package/dist/lfs/config.d.ts +23 -0
- package/dist/lfs/config.d.ts.map +1 -0
- package/dist/lfs/config.js +34 -0
- package/dist/lfs/contextAdapter.d.ts +5 -0
- package/dist/lfs/contextAdapter.d.ts.map +1 -0
- package/dist/lfs/contextAdapter.js +161 -0
- package/dist/lfs/memoryEngine.d.ts +108 -0
- package/dist/lfs/memoryEngine.d.ts.map +1 -0
- package/dist/lfs/memoryEngine.js +322 -0
- package/dist/lfs/memoryParser.d.ts +121 -0
- package/dist/lfs/memoryParser.d.ts.map +1 -0
- package/dist/lfs/memoryParser.js +743 -0
- package/dist/lfs/paritySwapper.d.ts +20 -0
- package/dist/lfs/paritySwapper.d.ts.map +1 -0
- package/dist/lfs/paritySwapper.js +317 -0
- package/dist/lfs/preflightRouter.d.ts +14 -0
- package/dist/lfs/preflightRouter.d.ts.map +1 -0
- package/dist/lfs/preflightRouter.js +24 -0
- package/dist/lfs/runtimeConfig.d.ts +31 -0
- package/dist/lfs/runtimeConfig.d.ts.map +1 -0
- package/dist/lfs/runtimeConfig.js +40 -0
- package/dist/lfs/sessionStore.d.ts +14 -0
- package/dist/lfs/sessionStore.d.ts.map +1 -0
- package/dist/lfs/sessionStore.js +52 -0
- package/dist/lfs/tokenCounter.d.ts +90 -0
- package/dist/lfs/tokenCounter.d.ts.map +1 -0
- package/dist/lfs/tokenCounter.js +26 -0
- package/dist/lfs/types.d.ts +44 -0
- package/dist/lfs/types.d.ts.map +1 -0
- package/dist/lfs/types.js +1 -0
- package/dist/lfs/vectorDb.d.ts +19 -0
- package/dist/lfs/vectorDb.d.ts.map +1 -0
- package/dist/lfs/vectorDb.js +158 -0
- package/dist/mcp/client.d.ts +23 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +60 -0
- package/dist/mcp/config.d.ts +10 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +72 -0
- package/dist/providers/ollama.d.ts +2 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +7 -0
- package/dist/providers/registry.d.ts +5 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +9 -0
- package/dist/tools/registry.d.ts +14 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +26 -0
- package/dist/tools/remind.d.ts +15 -0
- package/dist/tools/remind.d.ts.map +1 -0
- package/dist/tools/remind.js +53 -0
- package/dist/tools/skillLoader.d.ts +12 -0
- package/dist/tools/skillLoader.d.ts.map +1 -0
- package/dist/tools/skillLoader.js +38 -0
- package/package.json +66 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AdaptiveMessage, MemoryChunk } from './tokenCounter';
|
|
2
|
+
export declare function calculateUtility(similarity: number, agentWeight: number, beta?: number): number;
|
|
3
|
+
/**
|
|
4
|
+
* Executes a proportional parity swap across all active and archived chunks.
|
|
5
|
+
* Governed by the utility equation: U = beta * S_rel + (1 - beta) * W_agent.
|
|
6
|
+
* Evicts the lowest-utility chunks to maintain context memory below the 70% cap.
|
|
7
|
+
*/
|
|
8
|
+
export declare function executeParitySwap(params: {
|
|
9
|
+
history: AdaptiveMessage[];
|
|
10
|
+
longTermMemory: Record<string, MemoryChunk>;
|
|
11
|
+
query: string;
|
|
12
|
+
agentWeights: Record<string, number>;
|
|
13
|
+
contextMax: number;
|
|
14
|
+
hardBufferCap: number;
|
|
15
|
+
beta?: number;
|
|
16
|
+
/** Current turn index for CODE_ASSET temporal decay (Sprint 2). */
|
|
17
|
+
currentTurnIndex?: number;
|
|
18
|
+
}): Promise<AdaptiveMessage[]>;
|
|
19
|
+
export declare function compileSkeletalHistory(chunks: MemoryChunk[]): string;
|
|
20
|
+
//# sourceMappingURL=paritySwapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paritySwapper.d.ts","sourceRoot":"","sources":["../../src/lfs/paritySwapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAsB,MAAM,gBAAgB,CAAC;AAIlF,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,MAAM,CAE5F;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CA2V7B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAqBpE"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { estimateTextTokens } from './tokenCounter';
|
|
2
|
+
import { queryChunkVectorCache, setChunkArchiveStatus } from './vectorDb';
|
|
3
|
+
import { extractHydrationBeaconVariables } from './memoryParser';
|
|
4
|
+
export function calculateUtility(similarity, agentWeight, beta = 0.5) {
|
|
5
|
+
return (beta * similarity) + ((1.0 - beta) * agentWeight);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Executes a proportional parity swap across all active and archived chunks.
|
|
9
|
+
* Governed by the utility equation: U = beta * S_rel + (1 - beta) * W_agent.
|
|
10
|
+
* Evicts the lowest-utility chunks to maintain context memory below the 70% cap.
|
|
11
|
+
*/
|
|
12
|
+
export async function executeParitySwap(params) {
|
|
13
|
+
const { history, longTermMemory, query, agentWeights, contextMax, hardBufferCap, beta = 0.5, currentTurnIndex = 0, } = params;
|
|
14
|
+
// 1. Gather all chunks in the system
|
|
15
|
+
const allChunksMap = {};
|
|
16
|
+
for (const msg of history) {
|
|
17
|
+
if (msg.chunks) {
|
|
18
|
+
for (const chunk of msg.chunks) {
|
|
19
|
+
allChunksMap[chunk.chunkId] = chunk;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
for (const chunk of Object.values(longTermMemory)) {
|
|
24
|
+
allChunksMap[chunk.chunkId] = chunk;
|
|
25
|
+
}
|
|
26
|
+
const allChunks = Object.values(allChunksMap);
|
|
27
|
+
if (allChunks.length === 0)
|
|
28
|
+
return history;
|
|
29
|
+
// 2. Fetch semantic similarity scores from the vector cache
|
|
30
|
+
const vectorMatches = await queryChunkVectorCache(query, 100);
|
|
31
|
+
const similarityScores = {};
|
|
32
|
+
for (const match of vectorMatches) {
|
|
33
|
+
similarityScores[match.chunkId] = match.score;
|
|
34
|
+
}
|
|
35
|
+
// 3. Compute utility and update forgetScore for all chunks
|
|
36
|
+
const chunkUtilities = {};
|
|
37
|
+
for (const chunk of allChunks) {
|
|
38
|
+
const sim = similarityScores[chunk.chunkId] || 0.0;
|
|
39
|
+
const weight = agentWeights[chunk.chunkId] || 0.0;
|
|
40
|
+
let utility = calculateUtility(sim, weight, beta);
|
|
41
|
+
const msgIndex = history.findIndex((m) => m.id === chunk.parentMessageId);
|
|
42
|
+
const messageDistance = msgIndex !== -1 ? history.length - 1 - msgIndex : 99;
|
|
43
|
+
// LFS v4.6 — Heterogeneous Temporal Decay:
|
|
44
|
+
// CODE_ASSET (type='code') gets a 5× protection boost if referenced within 3 turns,
|
|
45
|
+
// then fades exponentially as the agent moves to a different task domain.
|
|
46
|
+
if (chunk.type === 'code') {
|
|
47
|
+
const turnsSinceRef = currentTurnIndex - (chunk.lastReferencedTurn ?? 0);
|
|
48
|
+
if (turnsSinceRef <= 3) {
|
|
49
|
+
utility = Math.max(utility * 5.0, 0.35); // Shield: actively-used CODE_ASSET
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
utility *= Math.exp(-0.4 * (turnsSinceRef - 3)); // Exponential task-window fade
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (messageDistance <= 4) {
|
|
56
|
+
// Progressive baseline for non-code chunks (unchanged from v4.5)
|
|
57
|
+
utility = Math.max(utility, 0.35);
|
|
58
|
+
}
|
|
59
|
+
// Penalize TOOL_LOG chunks − evict them first under memory pressure
|
|
60
|
+
if (chunk.type === 'tool_log') {
|
|
61
|
+
utility -= 0.20;
|
|
62
|
+
}
|
|
63
|
+
chunkUtilities[chunk.chunkId] = utility;
|
|
64
|
+
chunk.forgetScore = Number((1.0 - utility).toFixed(4));
|
|
65
|
+
}
|
|
66
|
+
const topMatches = vectorMatches.slice(0, 5);
|
|
67
|
+
const topMatchIds = new Set(topMatches.map((m) => m.chunkId));
|
|
68
|
+
const queryLower = query.toLowerCase();
|
|
69
|
+
const phase1Shortcuts = ['msg_01', 'msg_02', 'msg_03', 'msg_04', 'msg_05', 'msg_06', 'msg_07', 'msg_08', 'msg_09', 'msg_10'];
|
|
70
|
+
const isPhase1Query = queryLower.includes('phase 1') ||
|
|
71
|
+
queryLower.includes('infrastructure configuration') ||
|
|
72
|
+
queryLower.includes('infrastructure baseline') ||
|
|
73
|
+
queryLower.includes('initial setup');
|
|
74
|
+
const initialHydrateIds = new Set();
|
|
75
|
+
for (const chunk of allChunks) {
|
|
76
|
+
const isTopVectorMatch = topMatchIds.has(chunk.chunkId);
|
|
77
|
+
let isKeywordMatch = false;
|
|
78
|
+
if (isPhase1Query && phase1Shortcuts.includes(chunk.parentMessageId)) {
|
|
79
|
+
const contentLower = chunk.content.toLowerCase();
|
|
80
|
+
if (contentLower.includes('pg_pool_size') ||
|
|
81
|
+
contentLower.includes('sentinel') ||
|
|
82
|
+
contentLower.includes('max-old-space-size') ||
|
|
83
|
+
contentLower.includes('opentelemetry-collector') ||
|
|
84
|
+
contentLower.includes('otel/')) {
|
|
85
|
+
isKeywordMatch = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// 🚨 LFS v4.2 BM25-style keyword fallback boost to prevent needle dilution:
|
|
89
|
+
if (!isKeywordMatch) {
|
|
90
|
+
const contentLower = chunk.content.toLowerCase();
|
|
91
|
+
// RULER NIAH tasks matching:
|
|
92
|
+
if ((queryLower.includes('encryption key') && contentLower.includes('encryption key')) ||
|
|
93
|
+
(queryLower.includes('secret key') && contentLower.includes('secret key')) ||
|
|
94
|
+
(queryLower.includes('security configuration') && contentLower.includes('security configuration'))) {
|
|
95
|
+
isKeywordMatch = true;
|
|
96
|
+
}
|
|
97
|
+
// RULER VT tasks matching:
|
|
98
|
+
const varNameMatch = queryLower.match(/"([a-zA-Z0-9_]+)"/);
|
|
99
|
+
if (varNameMatch) {
|
|
100
|
+
const varName = varNameMatch[1];
|
|
101
|
+
if (contentLower.includes(`${varName} =`) || contentLower.includes(`${varName}=`)) {
|
|
102
|
+
isKeywordMatch = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (isTopVectorMatch || isKeywordMatch) {
|
|
107
|
+
initialHydrateIds.add(chunk.chunkId);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Multi-hop variable tracking hydration
|
|
111
|
+
const wantedVariables = new Set();
|
|
112
|
+
const extractTargetVariables = (text) => {
|
|
113
|
+
const targets = [];
|
|
114
|
+
const regexes = [
|
|
115
|
+
/variable\s+["']?([a-zA-Z_][a-zA-Z0-9_]*)["']?/gi,
|
|
116
|
+
/assigned\s+to\s+([a-zA-Z_][a-zA-Z0-9_]*)/gi,
|
|
117
|
+
/value\s+of\s+([a-zA-Z_][a-zA-Z0-9_]*)/gi
|
|
118
|
+
];
|
|
119
|
+
for (const r of regexes) {
|
|
120
|
+
let match;
|
|
121
|
+
while ((match = r.exec(text)) !== null) {
|
|
122
|
+
targets.push(match[1]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return targets;
|
|
126
|
+
};
|
|
127
|
+
const extractAssignments = (text) => {
|
|
128
|
+
const list = [];
|
|
129
|
+
const regex = /\b([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*([a-zA-Z0-9_]+)\b/g;
|
|
130
|
+
let match;
|
|
131
|
+
while ((match = regex.exec(text)) !== null) {
|
|
132
|
+
list.push({ lhs: match[1], rhs: match[2] });
|
|
133
|
+
}
|
|
134
|
+
return list;
|
|
135
|
+
};
|
|
136
|
+
// Add query variables
|
|
137
|
+
const queryVars = extractTargetVariables(query);
|
|
138
|
+
for (const v of queryVars) {
|
|
139
|
+
wantedVariables.add(v);
|
|
140
|
+
}
|
|
141
|
+
// Add variables from initially hydrated chunks
|
|
142
|
+
for (const chunkId of initialHydrateIds) {
|
|
143
|
+
const chunk = allChunksMap[chunkId];
|
|
144
|
+
if (chunk) {
|
|
145
|
+
const assigns = extractAssignments(chunk.content);
|
|
146
|
+
for (const a of assigns) {
|
|
147
|
+
wantedVariables.add(a.lhs);
|
|
148
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(a.rhs) && isNaN(Number(a.rhs))) {
|
|
149
|
+
wantedVariables.add(a.rhs);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Transitive hydration search
|
|
155
|
+
let addedNewVar = true;
|
|
156
|
+
const transitivelyHydratedIds = new Set();
|
|
157
|
+
while (addedNewVar) {
|
|
158
|
+
addedNewVar = false;
|
|
159
|
+
for (const chunk of allChunks) {
|
|
160
|
+
if (initialHydrateIds.has(chunk.chunkId) || transitivelyHydratedIds.has(chunk.chunkId)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const assigns = extractAssignments(chunk.content);
|
|
164
|
+
for (const a of assigns) {
|
|
165
|
+
if (wantedVariables.has(a.lhs)) {
|
|
166
|
+
transitivelyHydratedIds.add(chunk.chunkId);
|
|
167
|
+
console.log(`🔮 [TRANSITIVE HYDRATE] Marking chunk ${chunk.chunkId} for hydration (defines variable "${a.lhs}").`);
|
|
168
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(a.rhs) && isNaN(Number(a.rhs))) {
|
|
169
|
+
if (!wantedVariables.has(a.rhs)) {
|
|
170
|
+
wantedVariables.add(a.rhs);
|
|
171
|
+
addedNewVar = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const allHydrateIds = new Set([...initialHydrateIds, ...transitivelyHydratedIds]);
|
|
179
|
+
// ── Unified Budget-Enforcing Hydration & Eviction ──────────────────────────
|
|
180
|
+
const activeAnchorMsg = history[history.length - 1]; // Current turn reply is protected
|
|
181
|
+
const protectedMessageIds = new Set();
|
|
182
|
+
if (activeAnchorMsg) {
|
|
183
|
+
protectedMessageIds.add(activeAnchorMsg.id);
|
|
184
|
+
}
|
|
185
|
+
if (history.length >= 2) {
|
|
186
|
+
protectedMessageIds.add(history[history.length - 2].id); // User prompt is protected
|
|
187
|
+
}
|
|
188
|
+
// Ensure all chunks from protected messages are paged in
|
|
189
|
+
for (const chunk of allChunks) {
|
|
190
|
+
if (protectedMessageIds.has(chunk.parentMessageId)) {
|
|
191
|
+
if (chunk.isArchived) {
|
|
192
|
+
chunk.isArchived = false;
|
|
193
|
+
chunk.forgetScore = 0.0;
|
|
194
|
+
setChunkArchiveStatus(chunk.chunkId, false);
|
|
195
|
+
console.log(`🔮 [HYDRATE CHUNK] Hydrated chunk ${chunk.chunkId} back into active memory.`);
|
|
196
|
+
delete longTermMemory[chunk.chunkId];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// Calculate system overheads and target budget capacity
|
|
201
|
+
const msgFormatOverhead = history.length * 4;
|
|
202
|
+
const systemPromptOverhead = 350;
|
|
203
|
+
const hydrationBeaconText = extractHydrationBeaconVariables(history, query);
|
|
204
|
+
const hydrationBeaconOverhead = hydrationBeaconText ? estimateTextTokens(hydrationBeaconText) : 0;
|
|
205
|
+
const totalSystemOverhead = msgFormatOverhead + systemPromptOverhead + hydrationBeaconOverhead + 50;
|
|
206
|
+
const targetCap = (contextMax * hardBufferCap) - totalSystemOverhead;
|
|
207
|
+
// Calculate active tokens from protected messages first (since they are absolute/non-negotiable)
|
|
208
|
+
let activeTokens = 0;
|
|
209
|
+
for (const msg of history) {
|
|
210
|
+
if (protectedMessageIds.has(msg.id)) {
|
|
211
|
+
if (msg.chunks) {
|
|
212
|
+
for (const chunk of msg.chunks) {
|
|
213
|
+
activeTokens += chunk.tokenCount;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
activeTokens += msg.tokenCount || estimateTextTokens(msg.content);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else if (!msg.chunks && !msg.isArchived) {
|
|
221
|
+
// Non-chunked messages that aren't archived (like system/other prompts)
|
|
222
|
+
activeTokens += msg.tokenCount || estimateTextTokens(msg.content);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Non-protected scorable chunks sorted by utility descending (highest utility first)
|
|
226
|
+
const scorableChunks = allChunks.filter((c) => !protectedMessageIds.has(c.parentMessageId));
|
|
227
|
+
scorableChunks.sort((a, b) => chunkUtilities[b.chunkId] - chunkUtilities[a.chunkId]);
|
|
228
|
+
// Log hard cap pressure check if total projected active exceeds targetCap
|
|
229
|
+
let projectedActive = activeTokens;
|
|
230
|
+
for (const chunk of scorableChunks) {
|
|
231
|
+
const isCandidateActive = !chunk.isArchived || allHydrateIds.has(chunk.chunkId);
|
|
232
|
+
if (isCandidateActive) {
|
|
233
|
+
projectedActive += chunk.tokenCount;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
const breadcrumb = chunk.type === 'tool_log'
|
|
237
|
+
? `\n[📁 Attenuated Tool Log (ID: ${chunk.chunkId}): Evicted to free memory]\n`
|
|
238
|
+
: `\n[📁 Attenuated (ID: ${chunk.chunkId}): ${chunk.attenuationFrame || 'No schema metadata cached'}]\n`;
|
|
239
|
+
projectedActive += estimateTextTokens(breadcrumb);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Budget allocation pass
|
|
243
|
+
for (const chunk of scorableChunks) {
|
|
244
|
+
const isCandidateActive = !chunk.isArchived || allHydrateIds.has(chunk.chunkId);
|
|
245
|
+
const breadcrumbSize = estimateTextTokens(chunk.type === 'tool_log'
|
|
246
|
+
? `\n[📁 Attenuated Tool Log (ID: ${chunk.chunkId}): Evicted to free memory]\n`
|
|
247
|
+
: `\n[📁 Attenuated (ID: ${chunk.chunkId}): ${chunk.attenuationFrame || 'No schema metadata cached'}]\n`);
|
|
248
|
+
if (isCandidateActive && (activeTokens + chunk.tokenCount <= targetCap)) {
|
|
249
|
+
// Safe to hydrate/keep active within the budget limit
|
|
250
|
+
if (chunk.isArchived) {
|
|
251
|
+
chunk.isArchived = false;
|
|
252
|
+
chunk.forgetScore = 0.0;
|
|
253
|
+
chunk.lastReferencedTurn = currentTurnIndex; // Stamp hydration turn for decay tracking
|
|
254
|
+
setChunkArchiveStatus(chunk.chunkId, false);
|
|
255
|
+
console.log(`🔮 [HYDRATE CHUNK] Hydrated chunk ${chunk.chunkId} back into active memory.`);
|
|
256
|
+
delete longTermMemory[chunk.chunkId];
|
|
257
|
+
}
|
|
258
|
+
activeTokens += chunk.tokenCount;
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
// Must evict to disk or remain archived
|
|
262
|
+
if (!chunk.isArchived) {
|
|
263
|
+
chunk.isArchived = true;
|
|
264
|
+
setChunkArchiveStatus(chunk.chunkId, true);
|
|
265
|
+
longTermMemory[chunk.chunkId] = chunk;
|
|
266
|
+
console.log(`💾 [EVICT CHUNK] Evicted chunk ${chunk.chunkId} to disk. Utility: ${chunkUtilities[chunk.chunkId].toFixed(4)}`);
|
|
267
|
+
}
|
|
268
|
+
activeTokens += breadcrumbSize;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Compile history content and metrics per message
|
|
272
|
+
const updatedHistory = history.map((msg) => {
|
|
273
|
+
if (msg.chunks) {
|
|
274
|
+
const updatedChunks = msg.chunks.map((c) => allChunksMap[c.chunkId] || c);
|
|
275
|
+
const hasActiveChunk = updatedChunks.some((c) => !c.isArchived);
|
|
276
|
+
const compiledContent = compileSkeletalHistory(updatedChunks);
|
|
277
|
+
const originalContent = updatedChunks.map((c) => c.content).join('\n');
|
|
278
|
+
const archiveSummary = msg.archiveSummary || (originalContent.length > 60 ? originalContent.slice(0, 60).trim() + '...' : originalContent);
|
|
279
|
+
return {
|
|
280
|
+
...msg,
|
|
281
|
+
chunks: updatedChunks,
|
|
282
|
+
isArchived: !hasActiveChunk,
|
|
283
|
+
content: compiledContent,
|
|
284
|
+
archiveSummary,
|
|
285
|
+
tokenCount: updatedChunks.reduce((sum, c) => sum +
|
|
286
|
+
(c.isArchived
|
|
287
|
+
? estimateTextTokens(c.type === 'tool_log'
|
|
288
|
+
? `\n[📁 Attenuated Tool Log (ID: ${c.chunkId}): Evicted to free memory]\n`
|
|
289
|
+
: `\n[📁 Attenuated (ID: ${c.chunkId}): ${c.attenuationFrame || 'No schema metadata cached'}]\n`)
|
|
290
|
+
: c.tokenCount), 0),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return msg;
|
|
294
|
+
});
|
|
295
|
+
return updatedHistory;
|
|
296
|
+
}
|
|
297
|
+
export function compileSkeletalHistory(chunks) {
|
|
298
|
+
// 🚨 CRITICAL FIX: Sort chunks chronologically by parent ID and chunk order
|
|
299
|
+
// This guarantees that 'x = 120' ALWAYS appears before 'z = x', which appears before 'x = 30'
|
|
300
|
+
const chronologicalChunks = [...chunks].sort((a, b) => {
|
|
301
|
+
if (a.parentMessageId !== b.parentMessageId) {
|
|
302
|
+
return a.parentMessageId.localeCompare(b.parentMessageId);
|
|
303
|
+
}
|
|
304
|
+
return a.chunkId.localeCompare(b.chunkId, undefined, { numeric: true });
|
|
305
|
+
});
|
|
306
|
+
return chronologicalChunks
|
|
307
|
+
.map((c) => {
|
|
308
|
+
if (c.isArchived) {
|
|
309
|
+
if (c.type === 'tool_log') {
|
|
310
|
+
return `\n[📁 Attenuated Tool Log (ID: ${c.chunkId}): Evicted to free memory]\n`;
|
|
311
|
+
}
|
|
312
|
+
return `\n[📁 Attenuated (ID: ${c.chunkId}): ${c.attenuationFrame || 'No schema metadata cached'}]\n`;
|
|
313
|
+
}
|
|
314
|
+
return c.content;
|
|
315
|
+
})
|
|
316
|
+
.join('\n');
|
|
317
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ExecutionLane = 'PASS_THRU' | 'ELASTIC_SWAP';
|
|
2
|
+
export interface RouterTelemetry {
|
|
3
|
+
activeHistoryTokens: number;
|
|
4
|
+
incomingTextLength: string | number;
|
|
5
|
+
contextMax: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Pre-Flight Router that decides which execution lane should be selected for the current turn.
|
|
9
|
+
* Uses Cumulative Context Velocity Gating.
|
|
10
|
+
* @param metrics session metrics containing activeHistoryTokens, incomingTextLength, and contextMax
|
|
11
|
+
* @returns 'PASS_THRU' or 'ELASTIC_SWAP'
|
|
12
|
+
*/
|
|
13
|
+
export declare function determineExecutionLane(metrics: RouterTelemetry): ExecutionLane;
|
|
14
|
+
//# sourceMappingURL=preflightRouter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflightRouter.d.ts","sourceRoot":"","sources":["../../src/lfs/preflightRouter.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,cAAc,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,eAAe,GAAG,aAAa,CAkB9E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { estimateTextTokens } from './tokenCounter';
|
|
2
|
+
import { LFS_CONFIG } from './config';
|
|
3
|
+
/**
|
|
4
|
+
* Pre-Flight Router that decides which execution lane should be selected for the current turn.
|
|
5
|
+
* Uses Cumulative Context Velocity Gating.
|
|
6
|
+
* @param metrics session metrics containing activeHistoryTokens, incomingTextLength, and contextMax
|
|
7
|
+
* @returns 'PASS_THRU' or 'ELASTIC_SWAP'
|
|
8
|
+
*/
|
|
9
|
+
export function determineExecutionLane(metrics) {
|
|
10
|
+
// 1. Calculate the raw symbol-aware token estimation for the incoming payload
|
|
11
|
+
// Safely handle both direct prompt content (string) and character length count (number)
|
|
12
|
+
const estimatedIncomingTokens = typeof metrics.incomingTextLength === 'number'
|
|
13
|
+
? Math.ceil(metrics.incomingTextLength / 4)
|
|
14
|
+
: estimateTextTokens(metrics.incomingTextLength);
|
|
15
|
+
// 2. Compute total projected spatial weight
|
|
16
|
+
const totalProjectedTokens = metrics.activeHistoryTokens + estimatedIncomingTokens;
|
|
17
|
+
// 3. Volumetric Gating Policy
|
|
18
|
+
// If the total window space remains below 50% capacity, bypass the MMU entirely.
|
|
19
|
+
if (totalProjectedTokens < metrics.contextMax * LFS_CONFIG.PREFLIGHT_PASS_THRU_THRESHOLD) {
|
|
20
|
+
return 'PASS_THRU';
|
|
21
|
+
}
|
|
22
|
+
// Engage LFS Virtual Memory Swap arrays if token density threatens performance thresholds
|
|
23
|
+
return 'ELASTIC_SWAP';
|
|
24
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MemoryCoefficients } from "./memoryEngine";
|
|
2
|
+
export { LFS_MEMORY_CAP as LFS_MEMORY_PRESSURE } from "./config";
|
|
3
|
+
export type LfsRuntimeConfig = {
|
|
4
|
+
contextMax: number;
|
|
5
|
+
memoryCapFraction: number;
|
|
6
|
+
effectiveMemoryCapFraction: number;
|
|
7
|
+
targetMemoryCapTokens: number;
|
|
8
|
+
bypassThresholdTokens: number;
|
|
9
|
+
preflightPassThruTokens: number;
|
|
10
|
+
outputReserveTokens: number;
|
|
11
|
+
nativeSendBudgetTokens: number;
|
|
12
|
+
lfsApiSendBudgetTokens: number;
|
|
13
|
+
suggestedMemoryCapFraction: number;
|
|
14
|
+
coeffs: MemoryCoefficients;
|
|
15
|
+
};
|
|
16
|
+
export declare function resolveLfsRuntimeConfig(input: {
|
|
17
|
+
contextMax: number;
|
|
18
|
+
memoryCapFraction?: number;
|
|
19
|
+
}): LfsRuntimeConfig;
|
|
20
|
+
/** @deprecated Use resolveLfsRuntimeConfig */
|
|
21
|
+
export declare function resolveLfsRuntimeWindow(input: {
|
|
22
|
+
contextMax: number;
|
|
23
|
+
memoryCapFraction?: number;
|
|
24
|
+
}): {
|
|
25
|
+
contextMax: number;
|
|
26
|
+
memoryCapFraction: number;
|
|
27
|
+
targetMemoryCapTokens: number;
|
|
28
|
+
preflightPassThruTokens: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function clampMemoryPressureFraction(fraction: number): number;
|
|
31
|
+
//# sourceMappingURL=runtimeConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtimeConfig.d.ts","sourceRoot":"","sources":["../../src/lfs/runtimeConfig.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAUzD,OAAO,EAAE,cAAc,IAAI,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEjE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,0BAA0B,EAAE,MAAM,CAAC;IACnC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,uBAAuB,EAAE,MAAM,CAAC;IAChC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,0BAA0B,EAAE,MAAM,CAAC;IACnC,MAAM,EAAE,kBAAkB,CAAC;CAC5B,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,gBAAgB,CAwBnB;AAED,8CAA8C;AAC9C,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;;;;;EAQA;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { deriveAdaptiveCoefficients } from "./adaptiveCoefficients";
|
|
2
|
+
import { suggestMemoryCapFraction } from "./memoryEngine";
|
|
3
|
+
import { clampMemoryCapFraction, computeOutputReserveTokens, effectiveMemoryCapFraction, LFS_CONFIG, LFS_MEMORY_CAP, } from "./config";
|
|
4
|
+
export { LFS_MEMORY_CAP as LFS_MEMORY_PRESSURE } from "./config";
|
|
5
|
+
export function resolveLfsRuntimeConfig(input) {
|
|
6
|
+
const contextMax = Math.max(1, Math.floor(input.contextMax));
|
|
7
|
+
const requestedMemoryCapFraction = input.memoryCapFraction ?? LFS_MEMORY_CAP.DEFAULT;
|
|
8
|
+
const memoryCapFraction = clampMemoryCapFraction(requestedMemoryCapFraction);
|
|
9
|
+
const effectiveCapFraction = effectiveMemoryCapFraction(requestedMemoryCapFraction);
|
|
10
|
+
const targetMemoryCapTokens = Math.floor(contextMax * effectiveCapFraction);
|
|
11
|
+
const coeffs = deriveAdaptiveCoefficients(contextMax, {
|
|
12
|
+
memoryCapFraction: effectiveCapFraction,
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
contextMax,
|
|
16
|
+
memoryCapFraction,
|
|
17
|
+
effectiveMemoryCapFraction: effectiveCapFraction,
|
|
18
|
+
targetMemoryCapTokens,
|
|
19
|
+
bypassThresholdTokens: Math.floor(contextMax * LFS_CONFIG.BYPASS_THRESHOLD),
|
|
20
|
+
preflightPassThruTokens: Math.floor(contextMax * LFS_CONFIG.PREFLIGHT_PASS_THRU_THRESHOLD),
|
|
21
|
+
outputReserveTokens: computeOutputReserveTokens(contextMax),
|
|
22
|
+
nativeSendBudgetTokens: Math.floor(contextMax * LFS_CONFIG.NATIVE_CONTEXT_FRACTION),
|
|
23
|
+
lfsApiSendBudgetTokens: Math.floor(contextMax * LFS_CONFIG.LFS_API_SEND_FRACTION),
|
|
24
|
+
suggestedMemoryCapFraction: suggestMemoryCapFraction(contextMax),
|
|
25
|
+
coeffs: { ...coeffs, contextMax },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** @deprecated Use resolveLfsRuntimeConfig */
|
|
29
|
+
export function resolveLfsRuntimeWindow(input) {
|
|
30
|
+
const runtime = resolveLfsRuntimeConfig(input);
|
|
31
|
+
return {
|
|
32
|
+
contextMax: runtime.contextMax,
|
|
33
|
+
memoryCapFraction: runtime.effectiveMemoryCapFraction,
|
|
34
|
+
targetMemoryCapTokens: runtime.targetMemoryCapTokens,
|
|
35
|
+
preflightPassThruTokens: runtime.preflightPassThruTokens,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function clampMemoryPressureFraction(fraction) {
|
|
39
|
+
return clampMemoryCapFraction(fraction);
|
|
40
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SessionStoreState } from "./types";
|
|
2
|
+
import type { AdaptiveMessage } from "./tokenCounter";
|
|
3
|
+
export declare function configureSessionStore(options: {
|
|
4
|
+
maxSessions?: number;
|
|
5
|
+
}): void;
|
|
6
|
+
export declare function getOrCreateSessionState(params: {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
contextMax: number;
|
|
9
|
+
memoryCapFraction?: number;
|
|
10
|
+
}): SessionStoreState;
|
|
11
|
+
export declare function updateSessionHistory(sessionId: string, history: AdaptiveMessage[]): void;
|
|
12
|
+
export declare function clearSession(sessionId: string): void;
|
|
13
|
+
export declare function clearAllSessions(): void;
|
|
14
|
+
//# sourceMappingURL=sessionStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionStore.d.ts","sourceRoot":"","sources":["../../src/lfs/sessionStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAOtD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAI7E;AAUD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,iBAAiB,CAwBpB;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAIxF;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { LFS_MEMORY_CAP } from "./config";
|
|
2
|
+
const SESSION_CACHE = new Map();
|
|
3
|
+
const DEFAULT_MAX_SESSIONS = 256;
|
|
4
|
+
let maxSessions = DEFAULT_MAX_SESSIONS;
|
|
5
|
+
export function configureSessionStore(options) {
|
|
6
|
+
if (options.maxSessions != null) {
|
|
7
|
+
maxSessions = Math.max(1, options.maxSessions);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function evictIfNeeded() {
|
|
11
|
+
while (SESSION_CACHE.size > maxSessions) {
|
|
12
|
+
const firstKey = SESSION_CACHE.keys().next().value;
|
|
13
|
+
if (firstKey == null)
|
|
14
|
+
break;
|
|
15
|
+
SESSION_CACHE.delete(firstKey);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function getOrCreateSessionState(params) {
|
|
19
|
+
const existing = SESSION_CACHE.get(params.sessionId);
|
|
20
|
+
if (existing) {
|
|
21
|
+
existing.contextMax = params.contextMax;
|
|
22
|
+
if (params.memoryCapFraction != null) {
|
|
23
|
+
existing.memoryCapFraction = params.memoryCapFraction;
|
|
24
|
+
}
|
|
25
|
+
return existing;
|
|
26
|
+
}
|
|
27
|
+
evictIfNeeded();
|
|
28
|
+
const created = {
|
|
29
|
+
sessionId: params.sessionId,
|
|
30
|
+
fullHistory: [],
|
|
31
|
+
longTermMemory: {},
|
|
32
|
+
forgetScores: {},
|
|
33
|
+
agentWeights: {},
|
|
34
|
+
contextMax: params.contextMax,
|
|
35
|
+
memoryCapFraction: params.memoryCapFraction ?? LFS_MEMORY_CAP.DEFAULT,
|
|
36
|
+
turnIndex: 0,
|
|
37
|
+
};
|
|
38
|
+
SESSION_CACHE.set(params.sessionId, created);
|
|
39
|
+
return created;
|
|
40
|
+
}
|
|
41
|
+
export function updateSessionHistory(sessionId, history) {
|
|
42
|
+
const state = SESSION_CACHE.get(sessionId);
|
|
43
|
+
if (!state)
|
|
44
|
+
return;
|
|
45
|
+
state.fullHistory = history;
|
|
46
|
+
}
|
|
47
|
+
export function clearSession(sessionId) {
|
|
48
|
+
SESSION_CACHE.delete(sessionId);
|
|
49
|
+
}
|
|
50
|
+
export function clearAllSessions() {
|
|
51
|
+
SESSION_CACHE.clear();
|
|
52
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export interface MemoryChunk {
|
|
2
|
+
chunkId: string;
|
|
3
|
+
parentMessageId: string;
|
|
4
|
+
content: string;
|
|
5
|
+
tokenCount: number;
|
|
6
|
+
isArchived: boolean;
|
|
7
|
+
forgetScore?: number;
|
|
8
|
+
attenuationFrame?: string;
|
|
9
|
+
type?: 'code' | 'tool_log' | 'text';
|
|
10
|
+
/** Turn index when this chunk was last cited or hydrated — drives CODE_ASSET temporal decay. */
|
|
11
|
+
lastReferencedTurn?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface AdaptiveMessage {
|
|
14
|
+
id: string;
|
|
15
|
+
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
16
|
+
content: string;
|
|
17
|
+
tokenCount: number;
|
|
18
|
+
forgetScore?: number;
|
|
19
|
+
isArchived: boolean;
|
|
20
|
+
archiveSummary?: string;
|
|
21
|
+
isOptimizing?: boolean;
|
|
22
|
+
isPulsingInUI?: boolean;
|
|
23
|
+
thinking?: string;
|
|
24
|
+
chunks?: MemoryChunk[];
|
|
25
|
+
referencedIds?: string[];
|
|
26
|
+
creditsAllocated?: number;
|
|
27
|
+
decayApplied?: number;
|
|
28
|
+
memoryDebug?: TurnMemorySnapshot;
|
|
29
|
+
contextShift?: ContextShift;
|
|
30
|
+
/** Internal agentic-loop step (1-based) within a single user turn. */
|
|
31
|
+
agenticStep?: number;
|
|
32
|
+
stepKind?: 'thought' | 'tool' | 'memory' | 'final';
|
|
33
|
+
turnsAtLimit?: number;
|
|
34
|
+
hydrationGraceTurns?: number;
|
|
35
|
+
}
|
|
36
|
+
/** Immutable memory table captured when an assistant reply was finalized. */
|
|
37
|
+
export interface MemorySnapshotRow {
|
|
38
|
+
id: string;
|
|
39
|
+
role: string;
|
|
40
|
+
tokens: number;
|
|
41
|
+
forgetScore: number;
|
|
42
|
+
decayApplied: number;
|
|
43
|
+
creditsRequested: number;
|
|
44
|
+
creditsApplied: number;
|
|
45
|
+
/** Raw % from agent credit_allocation (before normalization). */
|
|
46
|
+
relevanceWeight: number;
|
|
47
|
+
/** Normalized % after backend scales agent values to sum 100. */
|
|
48
|
+
agentPercentNormalized: number;
|
|
49
|
+
/** Same as agentPercentNormalized (share of turn relevance). */
|
|
50
|
+
budgetSharePercent: number;
|
|
51
|
+
status: 'CITED' | 'SHIELDED' | 'DECAYING' | 'DANGER' | 'EVICTED' | 'FRESH';
|
|
52
|
+
}
|
|
53
|
+
export type ContextShift = 'continue' | 'new_topic';
|
|
54
|
+
export interface TurnMemorySnapshot {
|
|
55
|
+
turnBudget: number;
|
|
56
|
+
contextShift: ContextShift;
|
|
57
|
+
/** Credits actually applied this turn (sum of scaled allocation). */
|
|
58
|
+
creditsConsumed: number;
|
|
59
|
+
/** Raw relevance % from agent credit_allocation (should sum to ~100). */
|
|
60
|
+
agentAllocation: AllocationMap;
|
|
61
|
+
/** Normalized % (always sums to 100 when scoring is active). */
|
|
62
|
+
effectiveAllocation: AllocationMap;
|
|
63
|
+
/** Message IDs in credit_allocation that were not in active history. */
|
|
64
|
+
invalidAgentIds?: string[];
|
|
65
|
+
usedFallback: boolean;
|
|
66
|
+
scaledAllocation: AllocationMap;
|
|
67
|
+
totalRequested: number;
|
|
68
|
+
scaleApplied: number;
|
|
69
|
+
rows: MemorySnapshotRow[];
|
|
70
|
+
}
|
|
71
|
+
export type AllocationMap = Record<string, number>;
|
|
72
|
+
export interface TelemetryMetrics {
|
|
73
|
+
turn: number;
|
|
74
|
+
rawTokenTotal: number;
|
|
75
|
+
optimizedTokenTotal: number;
|
|
76
|
+
rawLatencySeconds: number;
|
|
77
|
+
optimizedLatencySeconds: number;
|
|
78
|
+
currentCreditBudget: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Estimates the token count of a single text string using a word-based multiplier (1.3 tokens per word).
|
|
82
|
+
* Sanitizes spaces and returns at least 1 token if content is present.
|
|
83
|
+
*/
|
|
84
|
+
export declare function estimateTextTokens(text: string): number;
|
|
85
|
+
/**
|
|
86
|
+
* Estimates total token allocation for a set of messages.
|
|
87
|
+
* Includes a minor constant overhead per message (e.g. 4 tokens) to account for LLM role metadata formatting.
|
|
88
|
+
*/
|
|
89
|
+
export declare function estimateMessagesTokens(messages: AdaptiveMessage[]): number;
|
|
90
|
+
//# sourceMappingURL=tokenCounter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenCounter.d.ts","sourceRoot":"","sources":["../../src/lfs/tokenCounter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Estimates the token count of a single text string using a word-based multiplier (1.3 tokens per word).
|
|
3
|
+
* Sanitizes spaces and returns at least 1 token if content is present.
|
|
4
|
+
*/
|
|
5
|
+
export function estimateTextTokens(text) {
|
|
6
|
+
if (!text || !text.trim())
|
|
7
|
+
return 0;
|
|
8
|
+
// 1. Calculate traditional whitespace-separated words
|
|
9
|
+
const wordCount = text.trim().split(/\s+/).length;
|
|
10
|
+
// 2. Extract code-specific structural symbols that BPE encoders treat as individual tokens
|
|
11
|
+
const symbolCount = (text.match(/[\{\}\[\]\(\)=;:,\.\!\?\-\+\*\/&\|<>]/g) || []).length;
|
|
12
|
+
// 3. Compensate for long alphanumeric strings common in code (hashes, paths, tokens)
|
|
13
|
+
const longStrings = (text.match(/[a-zA-Z0-9_-]{10,}/g) || []).length * 2;
|
|
14
|
+
return Math.ceil((wordCount + symbolCount + longStrings) * 1.85);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Estimates total token allocation for a set of messages.
|
|
18
|
+
* Includes a minor constant overhead per message (e.g. 4 tokens) to account for LLM role metadata formatting.
|
|
19
|
+
*/
|
|
20
|
+
export function estimateMessagesTokens(messages) {
|
|
21
|
+
return messages.reduce((acc, msg) => {
|
|
22
|
+
// 4 tokens overhead per message for the prompt format structure (role, formatting)
|
|
23
|
+
const count = (msg.tokenCount && msg.tokenCount > 0) ? msg.tokenCount : estimateTextTokens(msg.content);
|
|
24
|
+
return acc + count + 4;
|
|
25
|
+
}, 0);
|
|
26
|
+
}
|