@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,231 @@
|
|
|
1
|
+
import { buildRelevanceScoringInstructions } from "../lfs/memoryParser";
|
|
2
|
+
/** Parse msg_01 / msg_1 / msg1 → 1. Returns null if not a msg_N id. */
|
|
3
|
+
export function parseMsgNumber(id) {
|
|
4
|
+
const m = id.match(/^msg_?0*(\d+)$/i);
|
|
5
|
+
if (!m)
|
|
6
|
+
return null;
|
|
7
|
+
const n = parseInt(m[1], 10);
|
|
8
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
9
|
+
}
|
|
10
|
+
/** User/assistant turns eligible for credit_allocation (excludes traces, tools, archived). */
|
|
11
|
+
export function isScorableMessage(m) {
|
|
12
|
+
if (m.isArchived || m.role === 'system' || m.role === 'tool' || !m.id)
|
|
13
|
+
return false;
|
|
14
|
+
if (m.stepKind === 'thought' || m.stepKind === 'memory')
|
|
15
|
+
return false;
|
|
16
|
+
return m.role === 'user' || m.role === 'assistant';
|
|
17
|
+
}
|
|
18
|
+
export function classifyMessageKind(m) {
|
|
19
|
+
if (m.isArchived)
|
|
20
|
+
return 'archived';
|
|
21
|
+
if (m.role === 'system')
|
|
22
|
+
return 'system';
|
|
23
|
+
if (m.role === 'tool')
|
|
24
|
+
return 'tool';
|
|
25
|
+
if (m.stepKind === 'thought' || m.stepKind === 'memory')
|
|
26
|
+
return 'trace';
|
|
27
|
+
return 'conversation';
|
|
28
|
+
}
|
|
29
|
+
/** Build manifest in strict history array order (oldest → newest). */
|
|
30
|
+
export function buildMessageManifest(history) {
|
|
31
|
+
let conversationalIndex = 0;
|
|
32
|
+
return history.map((m, i) => {
|
|
33
|
+
const scorable = isScorableMessage(m);
|
|
34
|
+
if (scorable)
|
|
35
|
+
conversationalIndex += 1;
|
|
36
|
+
return {
|
|
37
|
+
id: m.id || `unknown_${i}`,
|
|
38
|
+
role: m.role,
|
|
39
|
+
kind: classifyMessageKind(m),
|
|
40
|
+
positionInHistory: i + 1,
|
|
41
|
+
scorable,
|
|
42
|
+
conversationalIndex: scorable ? conversationalIndex : undefined,
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export function collectScorableMessageIds(history) {
|
|
47
|
+
return buildMessageManifest(history)
|
|
48
|
+
.filter((e) => e.scorable)
|
|
49
|
+
.map((e) => e.id);
|
|
50
|
+
}
|
|
51
|
+
/** Map msg number → canonical id (msg_01). One number must not map to two ids. */
|
|
52
|
+
export function buildMsgNumberToIdMap(ids) {
|
|
53
|
+
const map = new Map();
|
|
54
|
+
for (const id of ids) {
|
|
55
|
+
const n = parseMsgNumber(id);
|
|
56
|
+
if (n == null)
|
|
57
|
+
continue;
|
|
58
|
+
if (!map.has(n)) {
|
|
59
|
+
map.set(n, id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return map;
|
|
63
|
+
}
|
|
64
|
+
export function resolveMessageId(rawId, activeIds, numberMap) {
|
|
65
|
+
if (activeIds.has(rawId))
|
|
66
|
+
return rawId;
|
|
67
|
+
const n = parseMsgNumber(rawId);
|
|
68
|
+
if (n != null && numberMap.has(n))
|
|
69
|
+
return numberMap.get(n);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
export function formatMessageForApi(m, entry, totalScorable, isMmuActive) {
|
|
73
|
+
if (isMmuActive === false) {
|
|
74
|
+
return { role: m.role, content: m.content };
|
|
75
|
+
}
|
|
76
|
+
if (entry.kind === 'archived' || entry.kind === 'system') {
|
|
77
|
+
return { role: m.role, content: m.content };
|
|
78
|
+
}
|
|
79
|
+
if (entry.kind === 'tool') {
|
|
80
|
+
return {
|
|
81
|
+
role: 'user',
|
|
82
|
+
content: `[Tool result ID: ${entry.id} | history position #${entry.positionInHistory} | not scorable]\n\n` +
|
|
83
|
+
m.content,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (entry.kind === 'trace') {
|
|
87
|
+
const stepTag = m.agenticStep ? ` | agentic step ${m.agenticStep}` : '';
|
|
88
|
+
return {
|
|
89
|
+
role: m.role,
|
|
90
|
+
content: `[Internal trace ID: ${entry.id} | history #${entry.positionInHistory}${stepTag} | not scorable]\n\n` +
|
|
91
|
+
m.content,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const convIdx = entry.conversationalIndex ?? '?';
|
|
95
|
+
return {
|
|
96
|
+
role: m.role,
|
|
97
|
+
content: `[Message ID: ${entry.id} | conversation #${convIdx} of ${totalScorable} | history #${entry.positionInHistory} | ${entry.role} | oldest→newest order]\n\n` +
|
|
98
|
+
m.content,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export function formatHistoryForModel(history, systemPrompt, opts) {
|
|
102
|
+
const manifest = buildMessageManifest(history);
|
|
103
|
+
const totalScorable = manifest.filter((e) => e.scorable).length;
|
|
104
|
+
const apiMsgs = history.map((m, i) => formatMessageForApi(m, manifest[i], totalScorable, opts?.isMmuActive));
|
|
105
|
+
const mergedMsgs = [];
|
|
106
|
+
let currentArchivedRange = null;
|
|
107
|
+
for (let i = 0; i < apiMsgs.length; i++) {
|
|
108
|
+
const msg = apiMsgs[i];
|
|
109
|
+
const isArchivedPill = history[i].isArchived;
|
|
110
|
+
if (isArchivedPill && opts?.lane === 'optimized') {
|
|
111
|
+
const id = history[i].id;
|
|
112
|
+
// Use clean summary text, stripping remind instructions from it
|
|
113
|
+
const summary = (history[i].archiveSummary || history[i].content)
|
|
114
|
+
.replace(/⚡ SWAPPED TO DISK SUMMARY: /, '')
|
|
115
|
+
.replace(/\. \[Available via tool: remind\(id: '[^']+'\)\]/, '');
|
|
116
|
+
if (!currentArchivedRange) {
|
|
117
|
+
currentArchivedRange = { startId: id, endId: id, items: [{ id, summary }] };
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
currentArchivedRange.endId = id;
|
|
121
|
+
currentArchivedRange.items.push({ id, summary });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
if (currentArchivedRange) {
|
|
126
|
+
const rangeText = currentArchivedRange.startId === currentArchivedRange.endId
|
|
127
|
+
? currentArchivedRange.startId
|
|
128
|
+
: `${currentArchivedRange.startId}-${currentArchivedRange.endId}`;
|
|
129
|
+
let contextText = '';
|
|
130
|
+
const totalItems = currentArchivedRange.items.length;
|
|
131
|
+
if (totalItems <= 8) {
|
|
132
|
+
contextText = currentArchivedRange.items.map(item => `${item.id}: ${item.summary}`).join('; ');
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const olderCount = totalItems - 8;
|
|
136
|
+
const olderStart = currentArchivedRange.items[0].id;
|
|
137
|
+
const olderEnd = currentArchivedRange.items[totalItems - 9].id;
|
|
138
|
+
const olderSummary = `${olderStart}-${olderEnd}: ${olderCount} messages archived (infrastructure, coding, and tests setup)`;
|
|
139
|
+
const recentText = currentArchivedRange.items.slice(totalItems - 8).map(item => `${item.id}: ${item.summary}`).join('; ');
|
|
140
|
+
contextText = `${olderSummary}; ${recentText}`;
|
|
141
|
+
}
|
|
142
|
+
const content = `[📁 Archived Block ${rangeText} | Context: ${contextText} | Available via tool: remind(messageId)]`;
|
|
143
|
+
mergedMsgs.push({ role: 'system', content });
|
|
144
|
+
currentArchivedRange = null;
|
|
145
|
+
}
|
|
146
|
+
mergedMsgs.push(msg);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (currentArchivedRange) {
|
|
150
|
+
const rangeText = currentArchivedRange.startId === currentArchivedRange.endId
|
|
151
|
+
? currentArchivedRange.startId
|
|
152
|
+
: `${currentArchivedRange.startId}-${currentArchivedRange.endId}`;
|
|
153
|
+
let contextText = '';
|
|
154
|
+
const totalItems = currentArchivedRange.items.length;
|
|
155
|
+
if (totalItems <= 8) {
|
|
156
|
+
contextText = currentArchivedRange.items.map(item => `${item.id}: ${item.summary}`).join('; ');
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const olderCount = totalItems - 8;
|
|
160
|
+
const olderStart = currentArchivedRange.items[0].id;
|
|
161
|
+
const olderEnd = currentArchivedRange.items[totalItems - 9].id;
|
|
162
|
+
const olderSummary = `${olderStart}-${olderEnd}: ${olderCount} messages archived (infrastructure, coding, and tests setup)`;
|
|
163
|
+
const recentText = currentArchivedRange.items.slice(totalItems - 8).map(item => `${item.id}: ${item.summary}`).join('; ');
|
|
164
|
+
contextText = `${olderSummary}; ${recentText}`;
|
|
165
|
+
}
|
|
166
|
+
const content = `[📁 Archived Block ${rangeText} | Context: ${contextText} | Available via tool: remind(messageId)]`;
|
|
167
|
+
mergedMsgs.push({ role: 'system', content });
|
|
168
|
+
}
|
|
169
|
+
const result = [
|
|
170
|
+
{ role: 'system', content: systemPrompt },
|
|
171
|
+
...mergedMsgs,
|
|
172
|
+
];
|
|
173
|
+
if (opts?.lane === 'optimized' && opts.isMmuActive !== false) {
|
|
174
|
+
const budget = opts.budget ?? 0;
|
|
175
|
+
const remindClause = opts.remindClause ?? 'Do NOT call tools unless archived disk summaries are present and insufficient.';
|
|
176
|
+
const scoringInstructions = buildRelevanceScoringInstructions({
|
|
177
|
+
budget,
|
|
178
|
+
history,
|
|
179
|
+
});
|
|
180
|
+
const metaContent = `[🚨 SYSTEM MEMORY METADATA & SCORING RULES]
|
|
181
|
+
Active Credit Budget: ${budget} credits for this turn.
|
|
182
|
+
|
|
183
|
+
${scoringInstructions}
|
|
184
|
+
|
|
185
|
+
${remindClause}`;
|
|
186
|
+
if (result.length > 0) {
|
|
187
|
+
const lastMsg = result[result.length - 1];
|
|
188
|
+
if (lastMsg.role === 'user') {
|
|
189
|
+
result.splice(result.length - 1, 0, { role: 'system', content: metaContent });
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
result.push({ role: 'system', content: metaContent });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
/** Agentic loop: history only (system prompt added separately). */
|
|
199
|
+
export function formatAgenticHistoryForModel(history) {
|
|
200
|
+
const manifest = buildMessageManifest(history);
|
|
201
|
+
const totalScorable = manifest.filter((e) => e.scorable).length;
|
|
202
|
+
return history.map((m, i) => formatMessageForApi(m, manifest[i], totalScorable));
|
|
203
|
+
}
|
|
204
|
+
/** Prompt block listing scorable IDs in the same order as the model sees them (oldest first). */
|
|
205
|
+
export function formatScorableIdListForPrompt(history) {
|
|
206
|
+
const hasChunks = history.some(m => m.chunks && m.chunks.length > 0);
|
|
207
|
+
if (hasChunks) {
|
|
208
|
+
const activeChunkIds = [];
|
|
209
|
+
for (const msg of history) {
|
|
210
|
+
if (msg.chunks) {
|
|
211
|
+
for (const chunk of msg.chunks) {
|
|
212
|
+
if (!chunk.isArchived) {
|
|
213
|
+
activeChunkIds.push(chunk.chunkId);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (activeChunkIds.length === 0) {
|
|
219
|
+
return '- (no active scorable chunks yet)';
|
|
220
|
+
}
|
|
221
|
+
return activeChunkIds.map(id => `- ${id}`).join('\n');
|
|
222
|
+
}
|
|
223
|
+
const manifest = buildMessageManifest(history);
|
|
224
|
+
const scorable = manifest.filter((e) => e.scorable);
|
|
225
|
+
if (scorable.length === 0) {
|
|
226
|
+
return '- (no scorable messages yet — use [Message ID: msg_xx] from conversation rows only)';
|
|
227
|
+
}
|
|
228
|
+
return scorable
|
|
229
|
+
.map((e) => `- ${e.id} | conversation #${e.conversationalIndex} of ${scorable.length} | history #${e.positionInHistory} | ${e.role}`)
|
|
230
|
+
.join('\n');
|
|
231
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export { createLfsAgent } from "./agent/createLfsAgent";
|
|
2
|
+
export type { CreateLfsAgentResult } from "./agent/createLfsAgent";
|
|
3
|
+
export { TOOL_LOOP_DEFAULTS } from "./agent/loopConfig";
|
|
4
|
+
export { runLfsTurn } from "./agent/runLfsTurn";
|
|
5
|
+
export { LfsContextAdapter } from "./lfs/contextAdapter";
|
|
6
|
+
export { LFS_CONFIG, LFS_MEMORY_CAP, clampMemoryCapFraction, effectiveMemoryCapFraction, } from "./lfs/config";
|
|
7
|
+
export { resolveLfsRuntimeConfig, resolveLfsRuntimeWindow, clampMemoryPressureFraction, LFS_MEMORY_PRESSURE, } from "./lfs/runtimeConfig";
|
|
8
|
+
export type { LfsRuntimeConfig } from "./lfs/runtimeConfig";
|
|
9
|
+
export { getOrCreateSessionState, clearAllSessions, clearSession, configureSessionStore } from "./lfs/sessionStore";
|
|
10
|
+
export { formatForModelMessages, buildLfsMetadataSystemBlock } from "./history/formatForModel";
|
|
11
|
+
export { formatHistoryForModel } from "./history/messageManifest";
|
|
12
|
+
export { fromDoryCodeMessageV2 } from "./history/dorycodeAdapter";
|
|
13
|
+
export { createToolRegistry } from "./tools/registry";
|
|
14
|
+
export type { ToolRegistryResult } from "./tools/registry";
|
|
15
|
+
export { createRemindTool, buildLfsRemindClause, hydrateLfsTarget } from "./tools/remind";
|
|
16
|
+
export { createSkillLoaderTool, loadSkillMarkdown } from "./tools/skillLoader";
|
|
17
|
+
export type { SkillLoaderOptions } from "./tools/skillLoader";
|
|
18
|
+
export { createAgentSdkProviderRegistry } from "./providers/registry";
|
|
19
|
+
export { createOllamaProvider } from "./providers/ollama";
|
|
20
|
+
export { deriveAdaptiveCoefficients, calculateDynamicMemoryCap } from "./lfs/adaptiveCoefficients";
|
|
21
|
+
export { validateFetchUrl, getFetchUrlAllowlist, MCP_TOOL_RESULT_MAX_CHARS } from "./mcp/config";
|
|
22
|
+
export type { McpClientOptions } from "./mcp/client";
|
|
23
|
+
export { determineExecutionLane } from "./lfs/preflightRouter";
|
|
24
|
+
export type { ExecutionLane, RouterTelemetry } from "./lfs/preflightRouter";
|
|
25
|
+
export { executeParitySwap, compileSkeletalHistory, calculateUtility } from "./lfs/paritySwapper";
|
|
26
|
+
export { chunkMessageTurn, calculateDynamicChunkSize, calculateDynamicFrameLimit, identifyContentType, } from "./lfs/chunkManager";
|
|
27
|
+
export { clearVectorCache, upsertChunkVector, upsertVector, queryChunkVectorCache, queryVectorCache, setChunkArchiveStatus, generateLocalEmbedding, } from "./lfs/vectorDb";
|
|
28
|
+
export type { ChunkRecord } from "./lfs/vectorDb";
|
|
29
|
+
export { applyAgenticStepMemory, applyHydrationDisplacementDecay, applyUserSendDecay, calculateCreditBudget, calculateCreditBudgetV2, calculateTargetMemoryCap, findActiveTurnAnchorId, findPredecessorMessageId, getNormalizedPressure, getPressureRatio, initializeForgetScoresMidConversation, isConversationalMessage, suggestMemoryCapFraction, validateAndApplyCredits, } from "./lfs/memoryEngine";
|
|
30
|
+
export type { MemoryCoefficients } from "./lfs/memoryEngine";
|
|
31
|
+
export type { LfsMessage, LfsLane, MemoryChunk, SessionStoreState, PrepareStepInput, PrepareStepOutput, } from "./lfs/types";
|
|
32
|
+
export type { AdaptiveMessage } from "./lfs/tokenCounter";
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,UAAU,EACV,cAAc,EACd,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACpH,OAAO,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACjG,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAClG,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACL,sBAAsB,EACtB,+BAA+B,EAC/B,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,qCAAqC,EACrC,uBAAuB,EACvB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EACV,UAAU,EACV,OAAO,EACP,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { createLfsAgent } from "./agent/createLfsAgent";
|
|
2
|
+
export { TOOL_LOOP_DEFAULTS } from "./agent/loopConfig";
|
|
3
|
+
export { runLfsTurn } from "./agent/runLfsTurn";
|
|
4
|
+
export { LfsContextAdapter } from "./lfs/contextAdapter";
|
|
5
|
+
export { LFS_CONFIG, LFS_MEMORY_CAP, clampMemoryCapFraction, effectiveMemoryCapFraction, } from "./lfs/config";
|
|
6
|
+
export { resolveLfsRuntimeConfig, resolveLfsRuntimeWindow, clampMemoryPressureFraction, LFS_MEMORY_PRESSURE, } from "./lfs/runtimeConfig";
|
|
7
|
+
export { getOrCreateSessionState, clearAllSessions, clearSession, configureSessionStore } from "./lfs/sessionStore";
|
|
8
|
+
export { formatForModelMessages, buildLfsMetadataSystemBlock } from "./history/formatForModel";
|
|
9
|
+
export { formatHistoryForModel } from "./history/messageManifest";
|
|
10
|
+
export { fromDoryCodeMessageV2 } from "./history/dorycodeAdapter";
|
|
11
|
+
export { createToolRegistry } from "./tools/registry";
|
|
12
|
+
export { createRemindTool, buildLfsRemindClause, hydrateLfsTarget } from "./tools/remind";
|
|
13
|
+
export { createSkillLoaderTool, loadSkillMarkdown } from "./tools/skillLoader";
|
|
14
|
+
export { createAgentSdkProviderRegistry } from "./providers/registry";
|
|
15
|
+
export { createOllamaProvider } from "./providers/ollama";
|
|
16
|
+
export { deriveAdaptiveCoefficients, calculateDynamicMemoryCap } from "./lfs/adaptiveCoefficients";
|
|
17
|
+
export { validateFetchUrl, getFetchUrlAllowlist, MCP_TOOL_RESULT_MAX_CHARS } from "./mcp/config";
|
|
18
|
+
export { determineExecutionLane } from "./lfs/preflightRouter";
|
|
19
|
+
export { executeParitySwap, compileSkeletalHistory, calculateUtility } from "./lfs/paritySwapper";
|
|
20
|
+
export { chunkMessageTurn, calculateDynamicChunkSize, calculateDynamicFrameLimit, identifyContentType, } from "./lfs/chunkManager";
|
|
21
|
+
export { clearVectorCache, upsertChunkVector, upsertVector, queryChunkVectorCache, queryVectorCache, setChunkArchiveStatus, generateLocalEmbedding, } from "./lfs/vectorDb";
|
|
22
|
+
export { applyAgenticStepMemory, applyHydrationDisplacementDecay, applyUserSendDecay, calculateCreditBudget, calculateCreditBudgetV2, calculateTargetMemoryCap, findActiveTurnAnchorId, findPredecessorMessageId, getNormalizedPressure, getPressureRatio, initializeForgetScoresMidConversation, isConversationalMessage, suggestMemoryCapFraction, validateAndApplyCredits, } from "./lfs/memoryEngine";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Use suggestMemoryCapFraction for UI hints only.
|
|
3
|
+
*/
|
|
4
|
+
export declare function calculateDynamicMemoryCap(contextMax: number): number;
|
|
5
|
+
export declare function deriveAdaptiveCoefficients(contextMax: number, options?: {
|
|
6
|
+
memoryCapFraction?: number;
|
|
7
|
+
}): {
|
|
8
|
+
decayBaseline: number;
|
|
9
|
+
alpha: number;
|
|
10
|
+
cMax: number;
|
|
11
|
+
hardBufferCap: number;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=adaptiveCoefficients.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptiveCoefficients.d.ts","sourceRoot":"","sources":["../../src/lfs/adaptiveCoefficients.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,GACvC;IACD,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB,CAOA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { effectiveMemoryCapFraction, LFS_MEMORY_CAP } from './config';
|
|
2
|
+
import { suggestMemoryCapFraction } from './memoryEngine';
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Use suggestMemoryCapFraction for UI hints only.
|
|
5
|
+
*/
|
|
6
|
+
export function calculateDynamicMemoryCap(contextMax) {
|
|
7
|
+
return suggestMemoryCapFraction(contextMax);
|
|
8
|
+
}
|
|
9
|
+
export function deriveAdaptiveCoefficients(contextMax, options) {
|
|
10
|
+
const decayBaseline = Math.min(0.35, Math.max(0.005, 1.2 / (contextMax / 800)));
|
|
11
|
+
const alpha = 0.35;
|
|
12
|
+
const cMax = Math.round(100 * Math.pow(contextMax / 4000, 0.55));
|
|
13
|
+
const uiCap = options?.memoryCapFraction ?? LFS_MEMORY_CAP.DEFAULT;
|
|
14
|
+
const hardBufferCap = effectiveMemoryCapFraction(uiCap);
|
|
15
|
+
return { decayBaseline, alpha, cMax, hardBufferCap };
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compiles a soft attenuation frame from text content.
|
|
3
|
+
* Extracts functions, classes, important variable assignments, keys, and specific configurations,
|
|
4
|
+
* formatted as "Schema: [entry1, entry2, ...]" restricted to a ~25 token budget.
|
|
5
|
+
*
|
|
6
|
+
* @param text - Raw chunk content to summarise.
|
|
7
|
+
* @param maxFrameTokens - Token budget for the frame (default 25).
|
|
8
|
+
* @param stepIndex - Optional positional sequence tag prepended as "[Step N] ".
|
|
9
|
+
* Deducted from the entry budget so the total never exceeds maxFrameTokens.
|
|
10
|
+
*/
|
|
11
|
+
export declare function compileAttenuationFrame(text: string, maxFrameTokens?: number, stepIndex?: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* Asynchronous compilation of attenuation frame.
|
|
14
|
+
*/
|
|
15
|
+
export declare function compileAttenuationFrameAsync(text: string, maxFrameTokens?: number, stepIndex?: number): Promise<string>;
|
|
16
|
+
//# sourceMappingURL=attenuationCompiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attenuationCompiler.d.ts","sourceRoot":"","sources":["../../src/lfs/attenuationCompiler.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,cAAc,SAAK,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA0HR;AAED;;GAEG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,cAAc,SAAK,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { estimateTextTokens } from './tokenCounter';
|
|
2
|
+
/**
|
|
3
|
+
* Compiles a soft attenuation frame from text content.
|
|
4
|
+
* Extracts functions, classes, important variable assignments, keys, and specific configurations,
|
|
5
|
+
* formatted as "Schema: [entry1, entry2, ...]" restricted to a ~25 token budget.
|
|
6
|
+
*
|
|
7
|
+
* @param text - Raw chunk content to summarise.
|
|
8
|
+
* @param maxFrameTokens - Token budget for the frame (default 25).
|
|
9
|
+
* @param stepIndex - Optional positional sequence tag prepended as "[Step N] ".
|
|
10
|
+
* Deducted from the entry budget so the total never exceeds maxFrameTokens.
|
|
11
|
+
*/
|
|
12
|
+
export function compileAttenuationFrame(text, maxFrameTokens = 25, stepIndex) {
|
|
13
|
+
if (!text || !text.trim()) {
|
|
14
|
+
return 'Schema: []';
|
|
15
|
+
}
|
|
16
|
+
const lines = text.split('\n');
|
|
17
|
+
const entriesSet = new Set();
|
|
18
|
+
// Heuristic patterns
|
|
19
|
+
const classRegex = /class\s+([a-zA-Z0-9_]+)/;
|
|
20
|
+
const funcRegexes = [
|
|
21
|
+
/(?:async\s+)?function\s+([a-zA-Z0-9_]+)/,
|
|
22
|
+
/const\s+([a-zA-Z0-9_]+)\s*=\s*(?:\([^)]*\)|[a-zA-Z0-9_]+)\s*=>/,
|
|
23
|
+
/def\s+([a-zA-Z0-9_]+)/,
|
|
24
|
+
/fn\s+([a-zA-Z0-9_]+)/,
|
|
25
|
+
/func\s+([a-zA-Z0-9_]+)/
|
|
26
|
+
];
|
|
27
|
+
const needleRegex = /\b(KEY_[A-Z0-9_]+)\b/;
|
|
28
|
+
const assignmentRegex = /\b([a-zA-Z_][a-zA-Z0-9_-]*)\s*=\s*(.+)/;
|
|
29
|
+
const flagRegex = /--([a-zA-Z0-9_-]+)=([a-zA-Z0-9_-]+)/;
|
|
30
|
+
// Specific patterns for benchmark needles
|
|
31
|
+
const otelRegex = /\b(otel\/opentelemetry-collector-contrib:[a-zA-Z0-9_.-]+)\b/;
|
|
32
|
+
const sentinelRegex = /\b([a-zA-Z0-9_.-]*sentinel[a-zA-Z0-9_.-]*:[0-9]+)\b/;
|
|
33
|
+
for (let line of lines) {
|
|
34
|
+
line = line.trim();
|
|
35
|
+
// Ignore pure comments
|
|
36
|
+
if (line.startsWith('//') || line.startsWith('#') || line.startsWith('/*') || line.startsWith('*')) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// 1. Check for specific needles first (like KEY_X9Y2Z)
|
|
40
|
+
const needleMatch = line.match(needleRegex);
|
|
41
|
+
if (needleMatch) {
|
|
42
|
+
entriesSet.add(needleMatch[1]);
|
|
43
|
+
}
|
|
44
|
+
// 2. Check for OTEL or Redis sentinel strings
|
|
45
|
+
const otelMatch = line.match(otelRegex);
|
|
46
|
+
if (otelMatch) {
|
|
47
|
+
entriesSet.add(otelMatch[1]);
|
|
48
|
+
}
|
|
49
|
+
const sentinelMatch = line.match(sentinelRegex);
|
|
50
|
+
if (sentinelMatch) {
|
|
51
|
+
entriesSet.add(sentinelMatch[1]);
|
|
52
|
+
}
|
|
53
|
+
// 3. Check for command-line flags
|
|
54
|
+
const flagMatch = line.match(flagRegex);
|
|
55
|
+
if (flagMatch) {
|
|
56
|
+
entriesSet.add(`${flagMatch[1]}=${flagMatch[2]}`);
|
|
57
|
+
}
|
|
58
|
+
// 4. Check for classes
|
|
59
|
+
const classMatch = line.match(classRegex);
|
|
60
|
+
if (classMatch) {
|
|
61
|
+
entriesSet.add(`class ${classMatch[1]}`);
|
|
62
|
+
}
|
|
63
|
+
// 5. Check for functions
|
|
64
|
+
for (const reg of funcRegexes) {
|
|
65
|
+
const funcMatch = line.match(reg);
|
|
66
|
+
if (funcMatch) {
|
|
67
|
+
entriesSet.add(`fn ${funcMatch[1]}()`);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// 6. Check for assignments (e.g. PG_POOL_SIZE = 120, x = y)
|
|
72
|
+
const assignMatch = line.match(assignmentRegex);
|
|
73
|
+
if (assignMatch) {
|
|
74
|
+
const lhs = assignMatch[1];
|
|
75
|
+
let rhs = assignMatch[2].trim();
|
|
76
|
+
// Remove trailing semicolon if present
|
|
77
|
+
if (rhs.endsWith(';')) {
|
|
78
|
+
rhs = rhs.slice(0, -1).trim();
|
|
79
|
+
}
|
|
80
|
+
// Check if RHS is a simple literal/value (no complex brackets, reasonably short)
|
|
81
|
+
const isSimpleRhs = rhs.length <= 40 &&
|
|
82
|
+
!rhs.includes('{') &&
|
|
83
|
+
!rhs.includes('}') &&
|
|
84
|
+
!rhs.includes('(') &&
|
|
85
|
+
!rhs.includes(')') &&
|
|
86
|
+
!rhs.includes('[') &&
|
|
87
|
+
!rhs.includes(']');
|
|
88
|
+
if (isSimpleRhs) {
|
|
89
|
+
// Strip quotes around strings
|
|
90
|
+
if ((rhs.startsWith("'") && rhs.endsWith("'")) || (rhs.startsWith('"') && rhs.endsWith('"'))) {
|
|
91
|
+
rhs = rhs.slice(1, -1);
|
|
92
|
+
}
|
|
93
|
+
entriesSet.add(`${lhs}=${rhs}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const prefix = stepIndex != null ? `[Step ${stepIndex}] ` : '';
|
|
98
|
+
const prefixTokens = stepIndex != null ? estimateTextTokens(prefix) : 0;
|
|
99
|
+
const adjustedBudget = Math.max(5, maxFrameTokens - prefixTokens);
|
|
100
|
+
// Build the attenuation frame incrementally to stay within adjustedBudget tokens
|
|
101
|
+
const selectedEntries = [];
|
|
102
|
+
for (const entry of entriesSet) {
|
|
103
|
+
const candidateEntries = [...selectedEntries, entry];
|
|
104
|
+
const candidateFrame = `Schema: [${candidateEntries.join(', ')}]`;
|
|
105
|
+
const tokens = estimateTextTokens(candidateFrame);
|
|
106
|
+
if (tokens > adjustedBudget) {
|
|
107
|
+
// If adding this entry exceeds adjustedBudget tokens, we stop adding more
|
|
108
|
+
if (selectedEntries.length === 0) {
|
|
109
|
+
// Ensure we include at least one entry if possible, even if it barely exceeds adjustedBudget tokens
|
|
110
|
+
selectedEntries.push(entry);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
selectedEntries.push(entry);
|
|
115
|
+
}
|
|
116
|
+
return `${prefix}Schema: [${selectedEntries.join(', ')}]`;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Asynchronous compilation of attenuation frame.
|
|
120
|
+
*/
|
|
121
|
+
export async function compileAttenuationFrameAsync(text, maxFrameTokens = 25, stepIndex) {
|
|
122
|
+
return Promise.resolve(compileAttenuationFrame(text, maxFrameTokens, stepIndex));
|
|
123
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { MemoryChunk } from './tokenCounter';
|
|
2
|
+
export declare function calculateDynamicChunkSize(contextMax: number): number;
|
|
3
|
+
export declare function calculateDynamicFrameLimit(chunkTokens: number): number;
|
|
4
|
+
export declare function chunkMessageTurn(messageId: string, rawContent: string, maxChunkTokens?: number, turnIndex?: number): MemoryChunk[];
|
|
5
|
+
export declare function identifyContentType(content: string): 'code' | 'tool_log' | 'text';
|
|
6
|
+
//# sourceMappingURL=chunkManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunkManager.d.ts","sourceRoot":"","sources":["../../src/lfs/chunkManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGjE,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAMpE;AAED,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAGtE;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,cAAc,SAAM,EACpB,SAAS,SAAI,GACZ,WAAW,EAAE,CA+Ff;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAsCjF"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { estimateTextTokens } from './tokenCounter';
|
|
2
|
+
import { compileAttenuationFrame } from './attenuationCompiler';
|
|
3
|
+
export function calculateDynamicChunkSize(contextMax) {
|
|
4
|
+
if (contextMax <= 10000)
|
|
5
|
+
return 400;
|
|
6
|
+
// Logarithmic scaling bound between 400 and 2048 tokens
|
|
7
|
+
const scaledSize = Math.floor(400 * (1 + Math.log(contextMax / 10000)));
|
|
8
|
+
return Math.min(Math.max(scaledSize, 400), 2048);
|
|
9
|
+
}
|
|
10
|
+
export function calculateDynamicFrameLimit(chunkTokens) {
|
|
11
|
+
const scaled = Math.round(25 + Math.max(0, chunkTokens - 400) * 0.0625);
|
|
12
|
+
return Math.min(Math.max(scaled, 25), 128);
|
|
13
|
+
}
|
|
14
|
+
export function chunkMessageTurn(messageId, rawContent, maxChunkTokens = 400, turnIndex = 0) {
|
|
15
|
+
if (!rawContent || !rawContent.trim()) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
// 1. Split content by code block starts to protect logic units from truncation
|
|
19
|
+
const rawSegments = rawContent.split(/(?=```)/g);
|
|
20
|
+
const segments = [];
|
|
21
|
+
// 2. Further split any segments that are excessively long to prevent token overflow
|
|
22
|
+
for (const seg of rawSegments) {
|
|
23
|
+
if (estimateTextTokens(seg) <= maxChunkTokens) {
|
|
24
|
+
segments.push(seg);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Split by newline to preserve line boundaries
|
|
28
|
+
const lines = seg.split('\n');
|
|
29
|
+
let currentSubSeg = '';
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const lineWithNL = line + '\n';
|
|
32
|
+
if (estimateTextTokens(currentSubSeg + lineWithNL) <= maxChunkTokens) {
|
|
33
|
+
currentSubSeg += lineWithNL;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
if (currentSubSeg) {
|
|
37
|
+
segments.push(currentSubSeg);
|
|
38
|
+
}
|
|
39
|
+
currentSubSeg = lineWithNL;
|
|
40
|
+
// If a single line itself exceeds the max tokens, we slice it by words
|
|
41
|
+
if (estimateTextTokens(currentSubSeg) > maxChunkTokens) {
|
|
42
|
+
const words = currentSubSeg.split(/\s+/);
|
|
43
|
+
let part = '';
|
|
44
|
+
for (const word of words) {
|
|
45
|
+
const proposed = part ? part + ' ' + word : word;
|
|
46
|
+
if (estimateTextTokens(proposed) <= maxChunkTokens) {
|
|
47
|
+
part = proposed;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
if (part)
|
|
51
|
+
segments.push(part);
|
|
52
|
+
part = word;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
currentSubSeg = part;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (currentSubSeg) {
|
|
60
|
+
segments.push(currentSubSeg);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// 3. Group consecutive segments up to maxChunkTokens
|
|
65
|
+
const chunks = [];
|
|
66
|
+
let chunkContent = '';
|
|
67
|
+
let chunkIndex = 0;
|
|
68
|
+
for (const seg of segments) {
|
|
69
|
+
const proposedContent = chunkContent ? chunkContent + '\n' + seg : seg;
|
|
70
|
+
if (estimateTextTokens(proposedContent) <= maxChunkTokens) {
|
|
71
|
+
chunkContent = proposedContent;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
if (chunkContent) {
|
|
75
|
+
const tokenCount = estimateTextTokens(chunkContent);
|
|
76
|
+
const frameLimit = calculateDynamicFrameLimit(tokenCount);
|
|
77
|
+
const chunkType = identifyContentType(chunkContent);
|
|
78
|
+
chunks.push({
|
|
79
|
+
chunkId: `${messageId}_chunk_${chunkIndex++}`,
|
|
80
|
+
parentMessageId: messageId,
|
|
81
|
+
content: chunkContent,
|
|
82
|
+
tokenCount,
|
|
83
|
+
isArchived: false,
|
|
84
|
+
attenuationFrame: compileAttenuationFrame(chunkContent, frameLimit),
|
|
85
|
+
type: chunkType,
|
|
86
|
+
lastReferencedTurn: turnIndex,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
chunkContent = seg;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (chunkContent) {
|
|
93
|
+
const tokenCount = estimateTextTokens(chunkContent);
|
|
94
|
+
const frameLimit = calculateDynamicFrameLimit(tokenCount);
|
|
95
|
+
const chunkType = identifyContentType(chunkContent);
|
|
96
|
+
chunks.push({
|
|
97
|
+
chunkId: `${messageId}_chunk_${chunkIndex++}`,
|
|
98
|
+
parentMessageId: messageId,
|
|
99
|
+
content: chunkContent,
|
|
100
|
+
tokenCount,
|
|
101
|
+
isArchived: false,
|
|
102
|
+
attenuationFrame: compileAttenuationFrame(chunkContent, frameLimit),
|
|
103
|
+
type: chunkType,
|
|
104
|
+
lastReferencedTurn: turnIndex,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return chunks;
|
|
108
|
+
}
|
|
109
|
+
export function identifyContentType(content) {
|
|
110
|
+
const contentLower = content.toLowerCase();
|
|
111
|
+
// Heuristics for terminal tool logs and build outputs
|
|
112
|
+
const logPatterns = [
|
|
113
|
+
/^\s*(?:\$ |> |npm |pnpm |yarn |pip |python3? |git |cargo |docker )/mi,
|
|
114
|
+
/\b(info|warning|warn|error|debug|trace|stderr|stdout)\b/i,
|
|
115
|
+
/error/i, // Catch TypeError, etc. without strict word boundaries
|
|
116
|
+
/\b(tests? passed|failed|assertions?|spec|test suite|coverage)\b/i,
|
|
117
|
+
/\bat\s+.*:[0-9]+:[0-9]+/i, // JS stack trace line
|
|
118
|
+
/traceback \(most recent call last\):/i, // Python traceback
|
|
119
|
+
/\b(untracked files:|changes not staged for commit:|changes to be committed:)\b/i, // Git status
|
|
120
|
+
/\b(package\.json|node_modules|pnpm-lock\.yaml|package-lock\.json)\b/i,
|
|
121
|
+
/\b(resolved |installed |fetched |downloaded |compiling |building |bundled )\b/i,
|
|
122
|
+
];
|
|
123
|
+
let matches = 0;
|
|
124
|
+
for (const pattern of logPatterns) {
|
|
125
|
+
if (pattern.test(content)) {
|
|
126
|
+
matches++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (matches >= 2 || (matches >= 1 && contentLower.includes('stack_trace_line_dump'))) {
|
|
130
|
+
return 'tool_log';
|
|
131
|
+
}
|
|
132
|
+
// Heuristics for code files
|
|
133
|
+
const codePatterns = [
|
|
134
|
+
/\b(function|const|let|var|class|import|export|require|return|def|fn|impl|struct|enum|public|private)\b/,
|
|
135
|
+
/[\{\}\[\]\(\)=;:,\.\!\?\-\+\*\/&\|<>]/
|
|
136
|
+
];
|
|
137
|
+
const isCode = codePatterns.every(p => p.test(content));
|
|
138
|
+
if (isCode && !contentLower.includes('terminal') && !contentLower.includes('command line')) {
|
|
139
|
+
return 'code';
|
|
140
|
+
}
|
|
141
|
+
return 'text';
|
|
142
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const LFS_CONFIG: {
|
|
2
|
+
BYPASS_THRESHOLD: number;
|
|
3
|
+
PREFLIGHT_PASS_THRU_THRESHOLD: number;
|
|
4
|
+
DECAY_BASELINE_FACTOR: number;
|
|
5
|
+
ALPHA_MASS_PENALTY: number;
|
|
6
|
+
/** When UI memory cap is 100%, effective eviction cap uses this fraction of contextMax. */
|
|
7
|
+
PHYSICAL_FIREBREAK: number;
|
|
8
|
+
NATIVE_CONTEXT_FRACTION: number;
|
|
9
|
+
LFS_API_SEND_FRACTION: number;
|
|
10
|
+
OUTPUT_RESERVE_FRACTION: number;
|
|
11
|
+
OUTPUT_RESERVE_FLOOR: number;
|
|
12
|
+
OUTPUT_RESERVE_CEILING: number;
|
|
13
|
+
};
|
|
14
|
+
export declare const LFS_MEMORY_CAP: {
|
|
15
|
+
readonly DEFAULT: 0.7;
|
|
16
|
+
readonly MIN: 0.5;
|
|
17
|
+
readonly MAX: 0.99;
|
|
18
|
+
};
|
|
19
|
+
export declare function clampMemoryCapFraction(fraction: number): number;
|
|
20
|
+
/** UI 100% maps to PHYSICAL_FIREBREAK so provider wrappers never hit hard truncation. */
|
|
21
|
+
export declare function effectiveMemoryCapFraction(uiFraction: number): number;
|
|
22
|
+
export declare function computeOutputReserveTokens(contextMax: number): number;
|
|
23
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lfs/config.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU;;;;;IAKrB,2FAA2F;;;;;;;CAO5F,CAAC;AAEF,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,yFAAyF;AACzF,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAOrE;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAMrE"}
|