fixo-cli 1.0.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 +201 -0
- package/README.md +530 -0
- package/dist/agent/agent-client.d.ts +108 -0
- package/dist/agent/agent-client.d.ts.map +1 -0
- package/dist/agent/agent-client.js +1247 -0
- package/dist/agent/agent-client.js.map +1 -0
- package/dist/agent/agent-pool.d.ts +20 -0
- package/dist/agent/agent-pool.d.ts.map +1 -0
- package/dist/agent/agent-pool.js +217 -0
- package/dist/agent/agent-pool.js.map +1 -0
- package/dist/agent/background-awareness.d.ts +55 -0
- package/dist/agent/background-awareness.d.ts.map +1 -0
- package/dist/agent/background-awareness.js +104 -0
- package/dist/agent/background-awareness.js.map +1 -0
- package/dist/agent/command-parser.d.ts +33 -0
- package/dist/agent/command-parser.d.ts.map +1 -0
- package/dist/agent/command-parser.js +120 -0
- package/dist/agent/command-parser.js.map +1 -0
- package/dist/agent/context-budget.d.ts +91 -0
- package/dist/agent/context-budget.d.ts.map +1 -0
- package/dist/agent/context-budget.js +219 -0
- package/dist/agent/context-budget.js.map +1 -0
- package/dist/agent/conversation.d.ts +190 -0
- package/dist/agent/conversation.d.ts.map +1 -0
- package/dist/agent/conversation.js +547 -0
- package/dist/agent/conversation.js.map +1 -0
- package/dist/agent/hooks.d.ts +72 -0
- package/dist/agent/hooks.d.ts.map +1 -0
- package/dist/agent/hooks.js +214 -0
- package/dist/agent/hooks.js.map +1 -0
- package/dist/agent/mcp-bridge.d.ts +13 -0
- package/dist/agent/mcp-bridge.d.ts.map +1 -0
- package/dist/agent/mcp-bridge.js +86 -0
- package/dist/agent/mcp-bridge.js.map +1 -0
- package/dist/agent/mcp-client.d.ts +24 -0
- package/dist/agent/mcp-client.d.ts.map +1 -0
- package/dist/agent/mcp-client.js +146 -0
- package/dist/agent/mcp-client.js.map +1 -0
- package/dist/agent/mcp-manager.d.ts +13 -0
- package/dist/agent/mcp-manager.d.ts.map +1 -0
- package/dist/agent/mcp-manager.js +84 -0
- package/dist/agent/mcp-manager.js.map +1 -0
- package/dist/agent/mcp-registry.d.ts +45 -0
- package/dist/agent/mcp-registry.d.ts.map +1 -0
- package/dist/agent/mcp-registry.js +98 -0
- package/dist/agent/mcp-registry.js.map +1 -0
- package/dist/agent/orchestrator.d.ts +14 -0
- package/dist/agent/orchestrator.d.ts.map +1 -0
- package/dist/agent/orchestrator.js +118 -0
- package/dist/agent/orchestrator.js.map +1 -0
- package/dist/agent/parser-adapter.d.ts +120 -0
- package/dist/agent/parser-adapter.d.ts.map +1 -0
- package/dist/agent/parser-adapter.js +265 -0
- package/dist/agent/parser-adapter.js.map +1 -0
- package/dist/agent/parsers/imports.d.ts +11 -0
- package/dist/agent/parsers/imports.d.ts.map +1 -0
- package/dist/agent/parsers/imports.js +94 -0
- package/dist/agent/parsers/imports.js.map +1 -0
- package/dist/agent/parsers/shell.d.ts +23 -0
- package/dist/agent/parsers/shell.d.ts.map +1 -0
- package/dist/agent/parsers/shell.js +200 -0
- package/dist/agent/parsers/shell.js.map +1 -0
- package/dist/agent/parsers/symbols.d.ts +17 -0
- package/dist/agent/parsers/symbols.d.ts.map +1 -0
- package/dist/agent/parsers/symbols.js +103 -0
- package/dist/agent/parsers/symbols.js.map +1 -0
- package/dist/agent/permissions.d.ts +65 -0
- package/dist/agent/permissions.d.ts.map +1 -0
- package/dist/agent/permissions.js +219 -0
- package/dist/agent/permissions.js.map +1 -0
- package/dist/agent/predictive-gate.d.ts +69 -0
- package/dist/agent/predictive-gate.d.ts.map +1 -0
- package/dist/agent/predictive-gate.js +128 -0
- package/dist/agent/predictive-gate.js.map +1 -0
- package/dist/agent/provider-cooldown.d.ts +144 -0
- package/dist/agent/provider-cooldown.d.ts.map +1 -0
- package/dist/agent/provider-cooldown.js +300 -0
- package/dist/agent/provider-cooldown.js.map +1 -0
- package/dist/agent/providers-manager.d.ts +109 -0
- package/dist/agent/providers-manager.d.ts.map +1 -0
- package/dist/agent/providers-manager.js +464 -0
- package/dist/agent/providers-manager.js.map +1 -0
- package/dist/agent/repo-map.d.ts +6 -0
- package/dist/agent/repo-map.d.ts.map +1 -0
- package/dist/agent/repo-map.js +221 -0
- package/dist/agent/repo-map.js.map +1 -0
- package/dist/agent/retry.d.ts +103 -0
- package/dist/agent/retry.d.ts.map +1 -0
- package/dist/agent/retry.js +276 -0
- package/dist/agent/retry.js.map +1 -0
- package/dist/agent/search/index.d.ts +61 -0
- package/dist/agent/search/index.d.ts.map +1 -0
- package/dist/agent/search/index.js +314 -0
- package/dist/agent/search/index.js.map +1 -0
- package/dist/agent/single-agent.d.ts +76 -0
- package/dist/agent/single-agent.d.ts.map +1 -0
- package/dist/agent/single-agent.js +697 -0
- package/dist/agent/single-agent.js.map +1 -0
- package/dist/agent/skills.d.ts +22 -0
- package/dist/agent/skills.d.ts.map +1 -0
- package/dist/agent/skills.js +139 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/stream-glue.d.ts +85 -0
- package/dist/agent/stream-glue.d.ts.map +1 -0
- package/dist/agent/stream-glue.js +120 -0
- package/dist/agent/stream-glue.js.map +1 -0
- package/dist/agent/subagent.d.ts +72 -0
- package/dist/agent/subagent.d.ts.map +1 -0
- package/dist/agent/subagent.js +193 -0
- package/dist/agent/subagent.js.map +1 -0
- package/dist/agent/telemetry.d.ts +192 -0
- package/dist/agent/telemetry.d.ts.map +1 -0
- package/dist/agent/telemetry.js +400 -0
- package/dist/agent/telemetry.js.map +1 -0
- package/dist/agent/tokenizer.d.ts +42 -0
- package/dist/agent/tokenizer.d.ts.map +1 -0
- package/dist/agent/tokenizer.js +107 -0
- package/dist/agent/tokenizer.js.map +1 -0
- package/dist/agent/tool-executor.d.ts +289 -0
- package/dist/agent/tool-executor.d.ts.map +1 -0
- package/dist/agent/tool-executor.js +2519 -0
- package/dist/agent/tool-executor.js.map +1 -0
- package/dist/agent/web-impl.d.ts +2 -0
- package/dist/agent/web-impl.d.ts.map +1 -0
- package/dist/agent/web-impl.js +34 -0
- package/dist/agent/web-impl.js.map +1 -0
- package/dist/agent/web.d.ts +8 -0
- package/dist/agent/web.d.ts.map +1 -0
- package/dist/agent/web.js +8 -0
- package/dist/agent/web.js.map +1 -0
- package/dist/agent/worker-agent.d.ts +27 -0
- package/dist/agent/worker-agent.d.ts.map +1 -0
- package/dist/agent/worker-agent.js +503 -0
- package/dist/agent/worker-agent.js.map +1 -0
- package/dist/config.d.ts +162 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +138 -0
- package/dist/config.js.map +1 -0
- package/dist/context/fixo-md-watcher.d.ts +42 -0
- package/dist/context/fixo-md-watcher.d.ts.map +1 -0
- package/dist/context/fixo-md-watcher.js +126 -0
- package/dist/context/fixo-md-watcher.js.map +1 -0
- package/dist/context/fixo-md.d.ts +50 -0
- package/dist/context/fixo-md.d.ts.map +1 -0
- package/dist/context/fixo-md.js +118 -0
- package/dist/context/fixo-md.js.map +1 -0
- package/dist/context/todo.d.ts +65 -0
- package/dist/context/todo.d.ts.map +1 -0
- package/dist/context/todo.js +194 -0
- package/dist/context/todo.js.map +1 -0
- package/dist/git/git-manager.d.ts +33 -0
- package/dist/git/git-manager.d.ts.map +1 -0
- package/dist/git/git-manager.js +293 -0
- package/dist/git/git-manager.js.map +1 -0
- package/dist/git/git-ops.d.ts +10 -0
- package/dist/git/git-ops.d.ts.map +1 -0
- package/dist/git/git-ops.js +131 -0
- package/dist/git/git-ops.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +352 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +30 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +273 -0
- package/dist/indexer.js.map +1 -0
- package/dist/lsp/lsp-client.d.ts +24 -0
- package/dist/lsp/lsp-client.d.ts.map +1 -0
- package/dist/lsp/lsp-client.js +205 -0
- package/dist/lsp/lsp-client.js.map +1 -0
- package/dist/lsp/lsp-manager.d.ts +17 -0
- package/dist/lsp/lsp-manager.d.ts.map +1 -0
- package/dist/lsp/lsp-manager.js +154 -0
- package/dist/lsp/lsp-manager.js.map +1 -0
- package/dist/lsp/lsp-pre-save.d.ts +137 -0
- package/dist/lsp/lsp-pre-save.d.ts.map +1 -0
- package/dist/lsp/lsp-pre-save.js +245 -0
- package/dist/lsp/lsp-pre-save.js.map +1 -0
- package/dist/lsp/syntax-fallback.d.ts +83 -0
- package/dist/lsp/syntax-fallback.d.ts.map +1 -0
- package/dist/lsp/syntax-fallback.js +275 -0
- package/dist/lsp/syntax-fallback.js.map +1 -0
- package/dist/model-outcomes.d.ts +12 -0
- package/dist/model-outcomes.d.ts.map +1 -0
- package/dist/model-outcomes.js +46 -0
- package/dist/model-outcomes.js.map +1 -0
- package/dist/planner.d.ts +32 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +163 -0
- package/dist/planner.js.map +1 -0
- package/dist/project-memory.d.ts +29 -0
- package/dist/project-memory.d.ts.map +1 -0
- package/dist/project-memory.js +349 -0
- package/dist/project-memory.js.map +1 -0
- package/dist/review.d.ts +2 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +61 -0
- package/dist/review.js.map +1 -0
- package/dist/runtime/background-jobs.d.ts +97 -0
- package/dist/runtime/background-jobs.d.ts.map +1 -0
- package/dist/runtime/background-jobs.js +331 -0
- package/dist/runtime/background-jobs.js.map +1 -0
- package/dist/runtime/credential-vault.d.ts +124 -0
- package/dist/runtime/credential-vault.d.ts.map +1 -0
- package/dist/runtime/credential-vault.js +184 -0
- package/dist/runtime/credential-vault.js.map +1 -0
- package/dist/runtime/loop-trap.d.ts +197 -0
- package/dist/runtime/loop-trap.d.ts.map +1 -0
- package/dist/runtime/loop-trap.js +420 -0
- package/dist/runtime/loop-trap.js.map +1 -0
- package/dist/runtime/policy.d.ts +15 -0
- package/dist/runtime/policy.d.ts.map +1 -0
- package/dist/runtime/policy.js +60 -0
- package/dist/runtime/policy.js.map +1 -0
- package/dist/runtime/redaction.d.ts +66 -0
- package/dist/runtime/redaction.d.ts.map +1 -0
- package/dist/runtime/redaction.js +155 -0
- package/dist/runtime/redaction.js.map +1 -0
- package/dist/runtime/session-snapshots.d.ts +76 -0
- package/dist/runtime/session-snapshots.d.ts.map +1 -0
- package/dist/runtime/session-snapshots.js +166 -0
- package/dist/runtime/session-snapshots.js.map +1 -0
- package/dist/runtime/staging.d.ts +205 -0
- package/dist/runtime/staging.d.ts.map +1 -0
- package/dist/runtime/staging.js +526 -0
- package/dist/runtime/staging.js.map +1 -0
- package/dist/runtime/task-session.d.ts +95 -0
- package/dist/runtime/task-session.d.ts.map +1 -0
- package/dist/runtime/task-session.js +263 -0
- package/dist/runtime/task-session.js.map +1 -0
- package/dist/runtime/worktree.d.ts +55 -0
- package/dist/runtime/worktree.d.ts.map +1 -0
- package/dist/runtime/worktree.js +175 -0
- package/dist/runtime/worktree.js.map +1 -0
- package/dist/setup-wizard.d.ts +8 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +73 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/shared/content.d.ts +43 -0
- package/dist/shared/content.d.ts.map +1 -0
- package/dist/shared/content.js +61 -0
- package/dist/shared/content.js.map +1 -0
- package/dist/shared/types.d.ts +217 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +3 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/test-runner.d.ts +5 -0
- package/dist/test-runner.d.ts.map +1 -0
- package/dist/test-runner.js +42 -0
- package/dist/test-runner.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/ascii.d.ts +23 -0
- package/dist/ui/ascii.d.ts.map +1 -0
- package/dist/ui/ascii.js +45 -0
- package/dist/ui/ascii.js.map +1 -0
- package/dist/ui/colors.d.ts +111 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +166 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/image-attach.d.ts +27 -0
- package/dist/ui/image-attach.d.ts.map +1 -0
- package/dist/ui/image-attach.js +100 -0
- package/dist/ui/image-attach.js.map +1 -0
- package/dist/ui/index.d.ts +18 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +18 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/markdown-stream.d.ts +91 -0
- package/dist/ui/markdown-stream.d.ts.map +1 -0
- package/dist/ui/markdown-stream.js +524 -0
- package/dist/ui/markdown-stream.js.map +1 -0
- package/dist/ui/plan-renderer.d.ts +36 -0
- package/dist/ui/plan-renderer.d.ts.map +1 -0
- package/dist/ui/plan-renderer.js +79 -0
- package/dist/ui/plan-renderer.js.map +1 -0
- package/dist/ui/prompt.d.ts +11 -0
- package/dist/ui/prompt.d.ts.map +1 -0
- package/dist/ui/prompt.js +1960 -0
- package/dist/ui/prompt.js.map +1 -0
- package/dist/ui/render-primitives.d.ts +117 -0
- package/dist/ui/render-primitives.d.ts.map +1 -0
- package/dist/ui/render-primitives.js +322 -0
- package/dist/ui/render-primitives.js.map +1 -0
- package/dist/ui/render.d.ts +133 -0
- package/dist/ui/render.d.ts.map +1 -0
- package/dist/ui/render.js +547 -0
- package/dist/ui/render.js.map +1 -0
- package/dist/ui/session-header.d.ts +30 -0
- package/dist/ui/session-header.d.ts.map +1 -0
- package/dist/ui/session-header.js +74 -0
- package/dist/ui/session-header.js.map +1 -0
- package/dist/workspace-guard.d.ts +68 -0
- package/dist/workspace-guard.d.ts.map +1 -0
- package/dist/workspace-guard.js +168 -0
- package/dist/workspace-guard.js.map +1 -0
- package/dist/workspace-lock.d.ts +27 -0
- package/dist/workspace-lock.d.ts.map +1 -0
- package/dist/workspace-lock.js +95 -0
- package/dist/workspace-lock.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import { WorkspaceGuard } from '../workspace-guard.js';
|
|
4
|
+
import { ParserFactory } from './parser-adapter.js';
|
|
5
|
+
export { ParserFactory };
|
|
6
|
+
/**
|
|
7
|
+
* Parses a shell command string into individual binary and arguments sets.
|
|
8
|
+
*
|
|
9
|
+
* Resolves the active parser via the `ParserFactory` singleton. When the
|
|
10
|
+
* underlying tree-sitter engine is healthy, the AST is used for maximum
|
|
11
|
+
* accuracy. When the WASM is unavailable (architecture mismatch, missing
|
|
12
|
+
* vendor file, etc.) the factory falls back transparently to a pure-JS
|
|
13
|
+
* regex tokenizer — the rest of the safety check pipeline keeps working
|
|
14
|
+
* unchanged.
|
|
15
|
+
*/
|
|
16
|
+
export async function parseShellCommand(command) {
|
|
17
|
+
const parser = await ParserFactory.getParser();
|
|
18
|
+
return parser.parseShellCommand ? parser.parseShellCommand(command) : [];
|
|
19
|
+
}
|
|
20
|
+
const DANGEROUS_MODIFIERS = new Set([
|
|
21
|
+
'rm', 'mv', 'cp', 'mkdir', 'touch', 'chmod', 'chown', 'dd', 'ln', 'rmdir'
|
|
22
|
+
]);
|
|
23
|
+
const DANGEROUS_READERS = new Set([
|
|
24
|
+
'cat', 'less', 'more', 'grep', 'head', 'tail'
|
|
25
|
+
]);
|
|
26
|
+
function unquote(str) {
|
|
27
|
+
if (str.length < 2)
|
|
28
|
+
return str;
|
|
29
|
+
const first = str[0];
|
|
30
|
+
const last = str[str.length - 1];
|
|
31
|
+
if ((first === '"' || first === "'") && first === last) {
|
|
32
|
+
return str.slice(1, -1);
|
|
33
|
+
}
|
|
34
|
+
return str;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Checks whether a shell command is safe to execute based on active path safety rules.
|
|
38
|
+
* Flags modifications outside the workspace root and checks for sensitive file access.
|
|
39
|
+
*/
|
|
40
|
+
export async function isCommandSafe(command, workspaceRoot) {
|
|
41
|
+
const parsed = await parseShellCommand(command);
|
|
42
|
+
const guard = new WorkspaceGuard(workspaceRoot);
|
|
43
|
+
for (const cmd of parsed) {
|
|
44
|
+
const binaryLower = cmd.binary.toLowerCase();
|
|
45
|
+
// Check if the binary itself is a path outside the workspace
|
|
46
|
+
if (binaryLower.startsWith('/') || binaryLower.startsWith('.')) {
|
|
47
|
+
const resolvedBin = path.resolve(workspaceRoot, unquote(binaryLower));
|
|
48
|
+
if (!guard.isInside(resolvedBin)) {
|
|
49
|
+
return {
|
|
50
|
+
safe: false,
|
|
51
|
+
reason: `Attempt to execute an external binary located outside the workspace: ${cmd.binary}`,
|
|
52
|
+
affectedPath: resolvedBin
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const arg of cmd.arguments) {
|
|
57
|
+
if (arg.startsWith('-'))
|
|
58
|
+
continue;
|
|
59
|
+
const cleanArg = unquote(arg);
|
|
60
|
+
const looksLikePath = cleanArg.includes('/') || cleanArg.includes('\\') || cleanArg.includes('.') || cleanArg === '~';
|
|
61
|
+
if (!looksLikePath)
|
|
62
|
+
continue;
|
|
63
|
+
// Expand home paths
|
|
64
|
+
let targetPath = cleanArg;
|
|
65
|
+
if (cleanArg === '~') {
|
|
66
|
+
targetPath = os.homedir();
|
|
67
|
+
}
|
|
68
|
+
else if (cleanArg.startsWith('~/') || cleanArg.startsWith('~\\')) {
|
|
69
|
+
targetPath = path.join(os.homedir(), cleanArg.slice(2));
|
|
70
|
+
}
|
|
71
|
+
const resolved = path.resolve(workspaceRoot, targetPath);
|
|
72
|
+
// Check for workspace escaping
|
|
73
|
+
if (DANGEROUS_MODIFIERS.has(binaryLower)) {
|
|
74
|
+
if (!guard.isInside(resolved)) {
|
|
75
|
+
return {
|
|
76
|
+
safe: false,
|
|
77
|
+
reason: `Command '${cmd.binary}' attempts to write or delete files outside the workspace root`,
|
|
78
|
+
affectedPath: resolved
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Check for sensitive credential files
|
|
83
|
+
const filename = path.basename(resolved).toLowerCase();
|
|
84
|
+
const isSensitive = filename === '.env' ||
|
|
85
|
+
filename.includes('.env.') ||
|
|
86
|
+
filename === 'id_rsa' ||
|
|
87
|
+
filename === 'credentials' ||
|
|
88
|
+
(filename === 'config' && resolved.includes('.aws'));
|
|
89
|
+
if (isSensitive) {
|
|
90
|
+
if (DANGEROUS_MODIFIERS.has(binaryLower)) {
|
|
91
|
+
return {
|
|
92
|
+
safe: false,
|
|
93
|
+
reason: `Command '${cmd.binary}' attempts to modify a sensitive credentials file: ${filename}`,
|
|
94
|
+
affectedPath: resolved
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (DANGEROUS_READERS.has(binaryLower) || binaryLower === 'grep') {
|
|
98
|
+
return {
|
|
99
|
+
safe: false,
|
|
100
|
+
reason: `Command '${cmd.binary}' attempts to read a sensitive credentials file: ${filename}`,
|
|
101
|
+
affectedPath: resolved
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { safe: true };
|
|
108
|
+
}
|
|
109
|
+
// ──── Backwards-compatible exports ───────────────────────────────
|
|
110
|
+
/**
|
|
111
|
+
* @deprecated Direct tree-sitter initialisation is no longer required.
|
|
112
|
+
* `parseShellCommand` now handles initialisation internally via the
|
|
113
|
+
* `ParserFactory` singleton. This export is kept for callers that still
|
|
114
|
+
* need to explicitly warm the parser at startup; it is a no-op once
|
|
115
|
+
* the factory has already initialised.
|
|
116
|
+
*/
|
|
117
|
+
export async function initTreeSitter() {
|
|
118
|
+
await ParserFactory.getParser();
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=command-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-parser.js","sourceRoot":"","sources":["../../src/agent/command-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Budget — proactive context-window enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Pillar 4 of the resilience refactor. Goal: never let the agent send
|
|
5
|
+
* a request that the upstream provider will reject with a 413 / 400
|
|
6
|
+
* "context length exceeded" error. We do this by counting the exact
|
|
7
|
+
* (or near-exact) token cost of the next request right before sending
|
|
8
|
+
* it, and applying a tiered trim strategy if it would overflow.
|
|
9
|
+
*
|
|
10
|
+
* The strategy, in order of severity:
|
|
11
|
+
*
|
|
12
|
+
* 1. `pruneToolOutputs` — drop the body of stale `tool` messages
|
|
13
|
+
* older than the last TAIL turn-pair.
|
|
14
|
+
* 2. `dropOldestTurns` — splice out the oldest user/assistant
|
|
15
|
+
* turn-pairs, preserving at least
|
|
16
|
+
* MIN_MESSAGES_TO_KEEP.
|
|
17
|
+
* 3. `truncateToolArgs` — clip the `arguments` JSON of any
|
|
18
|
+
* remaining tool_calls to a hard cap.
|
|
19
|
+
* 4. `markForCompaction` — give up trimming and return a
|
|
20
|
+
* `markForCompaction: true` flag so the
|
|
21
|
+
* caller can call `ConversationManager.compact()`,
|
|
22
|
+
* which summarises the old turns via an
|
|
23
|
+
* LLM call.
|
|
24
|
+
*
|
|
25
|
+
* The enforcer is intentionally stateless and side-effect-free at the
|
|
26
|
+
* level of *messages*; it returns a new trimmed array and a report
|
|
27
|
+
* describing what was done. `ConversationManager` applies the changes
|
|
28
|
+
* to its own `history` field. This keeps the enforcer pure and easy
|
|
29
|
+
* to unit-test.
|
|
30
|
+
*/
|
|
31
|
+
import type { ChatMessage } from '../shared/types.js';
|
|
32
|
+
/** Severity tiers applied in order, from cheapest to most invasive. */
|
|
33
|
+
export type BudgetAction = 'none' | 'prune-tool-outputs' | 'drop-oldest-turns' | 'truncate-tool-args' | 'mark-for-compaction';
|
|
34
|
+
/** Full report returned by {@link ContextBudgetEnforcer.enforce}. */
|
|
35
|
+
export interface BudgetReport {
|
|
36
|
+
/** Tokens measured before the enforcer ran. */
|
|
37
|
+
readonly tokensBefore: number;
|
|
38
|
+
/** Tokens measured after the enforcer ran. */
|
|
39
|
+
readonly tokensAfter: number;
|
|
40
|
+
/** Ordered list of actions that were applied. */
|
|
41
|
+
readonly actions: BudgetAction[];
|
|
42
|
+
/** True if the caller should trigger LLM-based compaction. */
|
|
43
|
+
readonly markForCompaction: boolean;
|
|
44
|
+
/** True if the final token count is within budget. */
|
|
45
|
+
readonly withinBudget: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** Strategy knob for callers that need to skip the cheaper tiers. */
|
|
48
|
+
export interface BudgetOptions {
|
|
49
|
+
/** Hard cap on the trimmed token count. */
|
|
50
|
+
readonly maxTokens: number;
|
|
51
|
+
/** Model identifier used for BPE encoder selection. */
|
|
52
|
+
readonly model?: string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Number of most-recent messages to leave untouched. Two turn-pairs
|
|
55
|
+
* (user+assistant+user+assistant) are kept verbatim by default. Must
|
|
56
|
+
* be >= 2.
|
|
57
|
+
*/
|
|
58
|
+
readonly tailMessages?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Maximum characters of a tool-call `arguments` JSON to keep after
|
|
61
|
+
* truncation. Defaults to 2,000 — same as the existing heuristic.
|
|
62
|
+
*/
|
|
63
|
+
readonly maxToolArgChars?: number;
|
|
64
|
+
}
|
|
65
|
+
export declare class TokenCounter {
|
|
66
|
+
private readonly model?;
|
|
67
|
+
constructor(model?: string | null | undefined);
|
|
68
|
+
/** Count tokens in a single string. */
|
|
69
|
+
count(text: string): number;
|
|
70
|
+
/** Count tokens in a list of messages (with per-message overhead). */
|
|
71
|
+
countMessages(messages: ReadonlyArray<ChatMessage>): number;
|
|
72
|
+
}
|
|
73
|
+
export declare class ContextBudgetEnforcer {
|
|
74
|
+
private readonly counter;
|
|
75
|
+
constructor(model?: string | null);
|
|
76
|
+
/**
|
|
77
|
+
* Enforce a token budget on a message list, returning a new list and
|
|
78
|
+
* a report describing what was changed.
|
|
79
|
+
*
|
|
80
|
+
* The original `messages` array is NEVER mutated; the result is a
|
|
81
|
+
* deep-enough copy that callers can adopt safely.
|
|
82
|
+
*/
|
|
83
|
+
enforce(messages: ReadonlyArray<ChatMessage>, options: BudgetOptions): {
|
|
84
|
+
messages: ChatMessage[];
|
|
85
|
+
report: BudgetReport;
|
|
86
|
+
};
|
|
87
|
+
private pruneToolOutputs;
|
|
88
|
+
private dropOldestTurns;
|
|
89
|
+
private truncateToolArgs;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=context-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-budget.d.ts","sourceRoot":"","sources":["../../src/agent/context-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,uEAAuE;AACvE,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,oBAAoB,GACpB,mBAAmB,GACnB,oBAAoB,GACpB,qBAAqB,CAAC;AAE1B,qEAAqE;AACrE,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,sDAAsD;IACtD,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAMD,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAN,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,YAAA;IAElD,uCAAuC;IACvC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI3B,sEAAsE;IACtE,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,MAAM;CAG5D;AASD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;gBAE3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG;QACrE,QAAQ,EAAE,WAAW,EAAE,CAAC;QACxB,MAAM,EAAE,YAAY,CAAC;KACtB;IAoED,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,gBAAgB;CAmBzB"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Budget — proactive context-window enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Pillar 4 of the resilience refactor. Goal: never let the agent send
|
|
5
|
+
* a request that the upstream provider will reject with a 413 / 400
|
|
6
|
+
* "context length exceeded" error. We do this by counting the exact
|
|
7
|
+
* (or near-exact) token cost of the next request right before sending
|
|
8
|
+
* it, and applying a tiered trim strategy if it would overflow.
|
|
9
|
+
*
|
|
10
|
+
* The strategy, in order of severity:
|
|
11
|
+
*
|
|
12
|
+
* 1. `pruneToolOutputs` — drop the body of stale `tool` messages
|
|
13
|
+
* older than the last TAIL turn-pair.
|
|
14
|
+
* 2. `dropOldestTurns` — splice out the oldest user/assistant
|
|
15
|
+
* turn-pairs, preserving at least
|
|
16
|
+
* MIN_MESSAGES_TO_KEEP.
|
|
17
|
+
* 3. `truncateToolArgs` — clip the `arguments` JSON of any
|
|
18
|
+
* remaining tool_calls to a hard cap.
|
|
19
|
+
* 4. `markForCompaction` — give up trimming and return a
|
|
20
|
+
* `markForCompaction: true` flag so the
|
|
21
|
+
* caller can call `ConversationManager.compact()`,
|
|
22
|
+
* which summarises the old turns via an
|
|
23
|
+
* LLM call.
|
|
24
|
+
*
|
|
25
|
+
* The enforcer is intentionally stateless and side-effect-free at the
|
|
26
|
+
* level of *messages*; it returns a new trimmed array and a report
|
|
27
|
+
* describing what was done. `ConversationManager` applies the changes
|
|
28
|
+
* to its own `history` field. This keeps the enforcer pure and easy
|
|
29
|
+
* to unit-test.
|
|
30
|
+
*/
|
|
31
|
+
import { countMessagesTokens, countTokens } from './tokenizer.js';
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// TokenCounter — thin façade used by both the enforcer and external tests.
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
export class TokenCounter {
|
|
36
|
+
model;
|
|
37
|
+
constructor(model) {
|
|
38
|
+
this.model = model;
|
|
39
|
+
}
|
|
40
|
+
/** Count tokens in a single string. */
|
|
41
|
+
count(text) {
|
|
42
|
+
return countTokens(text, this.model);
|
|
43
|
+
}
|
|
44
|
+
/** Count tokens in a list of messages (with per-message overhead). */
|
|
45
|
+
countMessages(messages) {
|
|
46
|
+
return countMessagesTokens(messages, this.model);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// ContextBudgetEnforcer
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
const MIN_TAIL_MESSAGES = 2;
|
|
53
|
+
const DEFAULT_MAX_TOOL_ARG_CHARS = 2_000;
|
|
54
|
+
export class ContextBudgetEnforcer {
|
|
55
|
+
counter;
|
|
56
|
+
constructor(model) {
|
|
57
|
+
this.counter = new TokenCounter(model);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Enforce a token budget on a message list, returning a new list and
|
|
61
|
+
* a report describing what was changed.
|
|
62
|
+
*
|
|
63
|
+
* The original `messages` array is NEVER mutated; the result is a
|
|
64
|
+
* deep-enough copy that callers can adopt safely.
|
|
65
|
+
*/
|
|
66
|
+
enforce(messages, options) {
|
|
67
|
+
const tailMessages = Math.max(MIN_TAIL_MESSAGES, options.tailMessages ?? 4);
|
|
68
|
+
const maxToolArgChars = options.maxToolArgChars ?? DEFAULT_MAX_TOOL_ARG_CHARS;
|
|
69
|
+
const actions = [];
|
|
70
|
+
let working = messages.map(cloneMessage);
|
|
71
|
+
const tokensBefore = this.counter.countMessages(messages);
|
|
72
|
+
let tokens = this.counter.countMessages(working);
|
|
73
|
+
if (tokens <= options.maxTokens) {
|
|
74
|
+
return {
|
|
75
|
+
messages: working,
|
|
76
|
+
report: {
|
|
77
|
+
tokensBefore,
|
|
78
|
+
tokensAfter: tokens,
|
|
79
|
+
actions: ['none'],
|
|
80
|
+
markForCompaction: false,
|
|
81
|
+
withinBudget: true,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// Tier 1: prune tool outputs in non-tail messages.
|
|
86
|
+
working = this.pruneToolOutputs(working, tailMessages);
|
|
87
|
+
const tokensAfterTier1 = this.counter.countMessages(working);
|
|
88
|
+
if (tokensAfterTier1 < tokens)
|
|
89
|
+
actions.push('prune-tool-outputs');
|
|
90
|
+
tokens = tokensAfterTier1;
|
|
91
|
+
if (tokens <= options.maxTokens) {
|
|
92
|
+
return finish(working, tokensBefore, tokens, actions);
|
|
93
|
+
}
|
|
94
|
+
// Tier 2: drop oldest turn-pairs.
|
|
95
|
+
const lengthBeforeTier2 = working.length;
|
|
96
|
+
working = this.dropOldestTurns(working, tailMessages);
|
|
97
|
+
if (working.length < lengthBeforeTier2)
|
|
98
|
+
actions.push('drop-oldest-turns');
|
|
99
|
+
tokens = this.counter.countMessages(working);
|
|
100
|
+
if (tokens <= options.maxTokens) {
|
|
101
|
+
return finish(working, tokensBefore, tokens, actions);
|
|
102
|
+
}
|
|
103
|
+
// Tier 3: truncate remaining tool-call arguments.
|
|
104
|
+
const beforeTier3 = JSON.stringify(working);
|
|
105
|
+
working = this.truncateToolArgs(working, maxToolArgChars);
|
|
106
|
+
const afterTier3 = JSON.stringify(working);
|
|
107
|
+
if (afterTier3.length < beforeTier3.length)
|
|
108
|
+
actions.push('truncate-tool-args');
|
|
109
|
+
tokens = this.counter.countMessages(working);
|
|
110
|
+
if (tokens <= options.maxTokens) {
|
|
111
|
+
return finish(working, tokensBefore, tokens, actions);
|
|
112
|
+
}
|
|
113
|
+
// Tier 4: nothing else we can do without an LLM. Tell the caller to
|
|
114
|
+
// compact (summarise) the oldest turns.
|
|
115
|
+
return {
|
|
116
|
+
messages: working,
|
|
117
|
+
report: {
|
|
118
|
+
tokensBefore,
|
|
119
|
+
tokensAfter: tokens,
|
|
120
|
+
actions: [...actions, 'mark-for-compaction'],
|
|
121
|
+
markForCompaction: true,
|
|
122
|
+
withinBudget: false,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// -------------------------------------------------------------------------
|
|
127
|
+
// Strategy tiers
|
|
128
|
+
// -------------------------------------------------------------------------
|
|
129
|
+
pruneToolOutputs(messages, tailMessages) {
|
|
130
|
+
const keepFrom = Math.max(0, messages.length - tailMessages);
|
|
131
|
+
return messages.map((m, i) => {
|
|
132
|
+
if (i >= keepFrom)
|
|
133
|
+
return m;
|
|
134
|
+
if (m.role === 'tool' && m.content && m.content.length > DEFAULT_MAX_TOOL_ARG_CHARS) {
|
|
135
|
+
return {
|
|
136
|
+
...m,
|
|
137
|
+
content: m.content.slice(0, DEFAULT_MAX_TOOL_ARG_CHARS) +
|
|
138
|
+
`\n\n... [pruned: ${m.content.length} → ${DEFAULT_MAX_TOOL_ARG_CHARS} chars]`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return m;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
dropOldestTurns(messages, tailMessages) {
|
|
145
|
+
// We must drop *complete* turn-pairs to keep the conversation
|
|
146
|
+
// coherent. Walk forward from the head, counting turn-pairs
|
|
147
|
+
// (one user + one assistant = one pair). Drop pairs as long as
|
|
148
|
+
// the remaining array is still at least `tailMessages` long.
|
|
149
|
+
if (messages.length <= tailMessages)
|
|
150
|
+
return messages;
|
|
151
|
+
let userCount = 0;
|
|
152
|
+
let dropUntil = 0;
|
|
153
|
+
for (let i = 0; i < messages.length; i++) {
|
|
154
|
+
if (messages[i].role === 'user')
|
|
155
|
+
userCount += 1;
|
|
156
|
+
// Each "user" beyond the first marks the start of a new turn-
|
|
157
|
+
// pair. If we have at least one user and dropping everything up
|
|
158
|
+
// to and including the assistant that follows still leaves the
|
|
159
|
+
// tail intact, we can cut.
|
|
160
|
+
if (userCount >= 1 && i + 1 < messages.length && messages[i + 1].role === 'assistant') {
|
|
161
|
+
// Proposed cut: keep everything from i+2 onward.
|
|
162
|
+
const remaining = messages.length - (i + 2);
|
|
163
|
+
if (remaining >= tailMessages) {
|
|
164
|
+
dropUntil = i + 2;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return dropUntil > 0 ? messages.slice(dropUntil) : messages.slice(-tailMessages);
|
|
172
|
+
}
|
|
173
|
+
truncateToolArgs(messages, maxChars) {
|
|
174
|
+
return messages.map((m) => {
|
|
175
|
+
if (m.role !== 'assistant' || !m.tool_calls)
|
|
176
|
+
return m;
|
|
177
|
+
const toolCalls = m.tool_calls.map((tc) => {
|
|
178
|
+
if (!tc.function || tc.function.arguments.length <= maxChars)
|
|
179
|
+
return tc;
|
|
180
|
+
return {
|
|
181
|
+
...tc,
|
|
182
|
+
function: {
|
|
183
|
+
...tc.function,
|
|
184
|
+
arguments: tc.function.arguments.slice(0, maxChars) + '..."}',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
return { ...m, tool_calls: toolCalls };
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// -------------------------------------------------------------------------
|
|
193
|
+
// Helpers
|
|
194
|
+
// -------------------------------------------------------------------------
|
|
195
|
+
function cloneMessage(m) {
|
|
196
|
+
return {
|
|
197
|
+
...m,
|
|
198
|
+
content: m.content ?? null,
|
|
199
|
+
tool_calls: m.tool_calls
|
|
200
|
+
? m.tool_calls.map((tc) => ({
|
|
201
|
+
...tc,
|
|
202
|
+
function: { ...tc.function },
|
|
203
|
+
}))
|
|
204
|
+
: undefined,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function finish(messages, tokensBefore, tokensAfter, actions) {
|
|
208
|
+
return {
|
|
209
|
+
messages,
|
|
210
|
+
report: {
|
|
211
|
+
tokensBefore,
|
|
212
|
+
tokensAfter,
|
|
213
|
+
actions,
|
|
214
|
+
markForCompaction: false,
|
|
215
|
+
withinBudget: true,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=context-budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-budget.js","sourceRoot":"","sources":["../../src/agent/context-budget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConversationManager — manages multi-turn chat context for the FixO CLI agent.
|
|
3
|
+
*
|
|
4
|
+
* Provides automatic context window management inspired by OpenCode:
|
|
5
|
+
* 1. Tracks estimated token usage per model's context window limit
|
|
6
|
+
* 2. Auto-compacts when the next request would overflow the context
|
|
7
|
+
* 3. Uses a structured summary template to preserve critical information
|
|
8
|
+
* 4. Preserves the N most-recent turns verbatim for continuity
|
|
9
|
+
* 5. Prunes old tool outputs to free space before full compaction
|
|
10
|
+
*/
|
|
11
|
+
import type { ChatMessage } from '../shared/types.js';
|
|
12
|
+
import type { AgentClient } from './agent-client.js';
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the *usable input* context limit for a model identifier.
|
|
15
|
+
* The result is the model's full window minus {@link OUTPUT_TOKEN_RESERVATION}
|
|
16
|
+
* so callers that only care about how much room they have left for input
|
|
17
|
+
* tokens (predictive gate, budget planner) can use the answer directly.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveModelContextLimit(model: string | undefined | null): number;
|
|
20
|
+
export declare class ConversationManager {
|
|
21
|
+
private history;
|
|
22
|
+
private maxTokenBudget;
|
|
23
|
+
private summary;
|
|
24
|
+
private contextLimit;
|
|
25
|
+
private _lastCompactionInfo;
|
|
26
|
+
/**
|
|
27
|
+
* When set (>0), `getTotalTokens()` returns this value instead of
|
|
28
|
+
* summing the history. Used by `--resume` so the restored session
|
|
29
|
+
* shows the same token count the user saw at save time.
|
|
30
|
+
*/
|
|
31
|
+
private tokenOverride;
|
|
32
|
+
constructor(maxTokenBudget?: number);
|
|
33
|
+
/**
|
|
34
|
+
* Auto-configure the context budget based on the model being used.
|
|
35
|
+
* Call this whenever the model changes.
|
|
36
|
+
*/
|
|
37
|
+
setContextLimit(model: string): void;
|
|
38
|
+
getContextLimit(): number;
|
|
39
|
+
/** Returns info about the last compaction (for UX display). */
|
|
40
|
+
getLastCompactionInfo(): {
|
|
41
|
+
messagesBefore: number;
|
|
42
|
+
tokensFreed: number;
|
|
43
|
+
} | null;
|
|
44
|
+
/**
|
|
45
|
+
* Real BPE token count for a piece of text. Uses the OpenAI cl100k
|
|
46
|
+
* encoding by default (good proxy for llama-3, mistral, qwen,
|
|
47
|
+
* deepseek, gemini etc. that the FreeLLMAPI proxy fronts). Pass the
|
|
48
|
+
* model name for an encoder-tuned count.
|
|
49
|
+
*/
|
|
50
|
+
private estimateTokens;
|
|
51
|
+
/**
|
|
52
|
+
* Estimate tokens consumed by a single message, accounting for both its
|
|
53
|
+
* `content` and any attached `tool_calls`.
|
|
54
|
+
*/
|
|
55
|
+
private estimateMessageTokens;
|
|
56
|
+
/**
|
|
57
|
+
* Calculate the total estimated token count across the entire history.
|
|
58
|
+
*/
|
|
59
|
+
getTotalTokens(): number;
|
|
60
|
+
/**
|
|
61
|
+
* Restore the conversation from a previously-saved snapshot. Replaces
|
|
62
|
+
* the entire history, summary, and token override atomically. The
|
|
63
|
+
* `tokens` argument is the value recorded at save time; subsequent
|
|
64
|
+
* reads of `getTotalTokens()` will prefer this override so the
|
|
65
|
+
* resumed session shows the same context-usage meter the user saw
|
|
66
|
+
* when they saved.
|
|
67
|
+
*/
|
|
68
|
+
restoreFromSnapshot(messages: ReadonlyArray<ChatMessage>, summary: string, tokens: number): void;
|
|
69
|
+
/**
|
|
70
|
+
* Estimate the total token count for the NEXT LLM request.
|
|
71
|
+
* This is what actually matters for context overflow detection.
|
|
72
|
+
*/
|
|
73
|
+
estimateNextRequestTokens(systemPrompt: string, userMessage: string): number;
|
|
74
|
+
/**
|
|
75
|
+
* Proactively trim the conversation to fit a token budget, using the
|
|
76
|
+
* tiered {@link ContextBudgetEnforcer} strategy. The `model` argument
|
|
77
|
+
* is forwarded to the BPE encoder so the counts are tuned to the
|
|
78
|
+
* downstream provider.
|
|
79
|
+
*
|
|
80
|
+
* If the enforcer cannot fit the budget even after pruning tool
|
|
81
|
+
* outputs, dropping old turns, and truncating tool arguments, it
|
|
82
|
+
* sets `report.markForCompaction = true`; the caller should then
|
|
83
|
+
* invoke {@link ConversationManager.compact} to summarise the
|
|
84
|
+
* oldest turns via an LLM.
|
|
85
|
+
*
|
|
86
|
+
* Returns a report describing the actions taken and the new total
|
|
87
|
+
* token count.
|
|
88
|
+
*/
|
|
89
|
+
enforceBudget(maxTokens: number, model?: string | null): {
|
|
90
|
+
trimmed: boolean;
|
|
91
|
+
report: {
|
|
92
|
+
tokensBefore: number;
|
|
93
|
+
tokensAfter: number;
|
|
94
|
+
actions: ReadonlyArray<string>;
|
|
95
|
+
markForCompaction: boolean;
|
|
96
|
+
withinBudget: boolean;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Add a user message and the corresponding assistant response as a single
|
|
101
|
+
* conversational turn, then prune if the budget is exceeded.
|
|
102
|
+
*/
|
|
103
|
+
addTurn(userMessage: string, assistantResponse: string): void;
|
|
104
|
+
/**
|
|
105
|
+
* Add a raw {@link ChatMessage} (useful for tool-call results or other
|
|
106
|
+
* non-standard messages), then prune if the budget is exceeded.
|
|
107
|
+
*/
|
|
108
|
+
addMessage(message: ChatMessage): void;
|
|
109
|
+
/**
|
|
110
|
+
* Return all messages for injection into the LLM context.
|
|
111
|
+
* If a compacted summary exists, it is prepended as the first message.
|
|
112
|
+
*/
|
|
113
|
+
getMessages(): ChatMessage[];
|
|
114
|
+
/** Number of complete user/assistant turn pairs in the history. */
|
|
115
|
+
getTurnCount(): number;
|
|
116
|
+
/** Total number of messages in history (excluding summary). */
|
|
117
|
+
getMessageCount(): number;
|
|
118
|
+
/**
|
|
119
|
+
* Remove the oldest user/assistant pairs until the total token estimate
|
|
120
|
+
* fits within {@link maxTokenBudget}.
|
|
121
|
+
*/
|
|
122
|
+
pruneToFitBudget(): void;
|
|
123
|
+
/**
|
|
124
|
+
* Prune large tool outputs in older messages to free context space.
|
|
125
|
+
* Keeps the last TAIL_TURNS * 2 messages untouched.
|
|
126
|
+
*/
|
|
127
|
+
pruneToolOutputs(): number;
|
|
128
|
+
/** Clear all conversation history and summary. */
|
|
129
|
+
clear(): void;
|
|
130
|
+
/** Export a deep copy of the raw history for external persistence. */
|
|
131
|
+
exportHistory(): ChatMessage[];
|
|
132
|
+
/**
|
|
133
|
+
* Import a previously-exported history, replacing the current one.
|
|
134
|
+
* Automatically prunes to fit the current token budget after import.
|
|
135
|
+
*/
|
|
136
|
+
importHistory(messages: ChatMessage[]): void;
|
|
137
|
+
getSummary(): string;
|
|
138
|
+
setSummary(summary: string): void;
|
|
139
|
+
/**
|
|
140
|
+
* Check if the conversation should be compacted before the next request.
|
|
141
|
+
* Uses the estimated next request size vs the model's context limit.
|
|
142
|
+
* If systemPrompt/userMessage are not provided, uses a simpler history-only check.
|
|
143
|
+
*/
|
|
144
|
+
shouldCompact(systemPrompt?: string, userMessage?: string): boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Compact the conversation using a structured summary.
|
|
147
|
+
* - Preserves the last TAIL_TURNS turn-pairs verbatim
|
|
148
|
+
* - Summarizes everything else using the SUMMARY_TEMPLATE
|
|
149
|
+
* - Prunes old tool outputs before summarizing
|
|
150
|
+
*
|
|
151
|
+
* Returns true if compaction succeeded.
|
|
152
|
+
*/
|
|
153
|
+
compact(client: AgentClient, model: string): Promise<boolean>;
|
|
154
|
+
/**
|
|
155
|
+
* Check if an error indicates context window overflow from the provider.
|
|
156
|
+
* If true, the caller should auto-compact and retry.
|
|
157
|
+
*/
|
|
158
|
+
static isContextOverflowError(error: any): boolean;
|
|
159
|
+
}
|
|
160
|
+
export interface SessionData {
|
|
161
|
+
sessionId: string;
|
|
162
|
+
timestamp: string;
|
|
163
|
+
model: string;
|
|
164
|
+
history: ChatMessage[];
|
|
165
|
+
summary: string;
|
|
166
|
+
modifiedFiles: string[];
|
|
167
|
+
tokenUsage: {
|
|
168
|
+
prompt_tokens: number;
|
|
169
|
+
completion_tokens: number;
|
|
170
|
+
total_tokens: number;
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export declare class SessionManager {
|
|
174
|
+
static getSessionsDir(): string;
|
|
175
|
+
static saveSession(conversation: ConversationManager, model: string, modifiedFiles: string[], tokenUsage: {
|
|
176
|
+
prompt_tokens: number;
|
|
177
|
+
completion_tokens: number;
|
|
178
|
+
total_tokens: number;
|
|
179
|
+
}, sessionId?: string): string;
|
|
180
|
+
static listSessions(): Array<{
|
|
181
|
+
sessionId: string;
|
|
182
|
+
timestamp: string;
|
|
183
|
+
model: string;
|
|
184
|
+
messageCount: number;
|
|
185
|
+
summary: string;
|
|
186
|
+
totalTokens: number;
|
|
187
|
+
}>;
|
|
188
|
+
static loadSession(id: string): SessionData;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/agent/conversation.ts"],"names":[],"mappings":"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"}
|