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.
Files changed (303) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +530 -0
  3. package/dist/agent/agent-client.d.ts +108 -0
  4. package/dist/agent/agent-client.d.ts.map +1 -0
  5. package/dist/agent/agent-client.js +1247 -0
  6. package/dist/agent/agent-client.js.map +1 -0
  7. package/dist/agent/agent-pool.d.ts +20 -0
  8. package/dist/agent/agent-pool.d.ts.map +1 -0
  9. package/dist/agent/agent-pool.js +217 -0
  10. package/dist/agent/agent-pool.js.map +1 -0
  11. package/dist/agent/background-awareness.d.ts +55 -0
  12. package/dist/agent/background-awareness.d.ts.map +1 -0
  13. package/dist/agent/background-awareness.js +104 -0
  14. package/dist/agent/background-awareness.js.map +1 -0
  15. package/dist/agent/command-parser.d.ts +33 -0
  16. package/dist/agent/command-parser.d.ts.map +1 -0
  17. package/dist/agent/command-parser.js +120 -0
  18. package/dist/agent/command-parser.js.map +1 -0
  19. package/dist/agent/context-budget.d.ts +91 -0
  20. package/dist/agent/context-budget.d.ts.map +1 -0
  21. package/dist/agent/context-budget.js +219 -0
  22. package/dist/agent/context-budget.js.map +1 -0
  23. package/dist/agent/conversation.d.ts +190 -0
  24. package/dist/agent/conversation.d.ts.map +1 -0
  25. package/dist/agent/conversation.js +547 -0
  26. package/dist/agent/conversation.js.map +1 -0
  27. package/dist/agent/hooks.d.ts +72 -0
  28. package/dist/agent/hooks.d.ts.map +1 -0
  29. package/dist/agent/hooks.js +214 -0
  30. package/dist/agent/hooks.js.map +1 -0
  31. package/dist/agent/mcp-bridge.d.ts +13 -0
  32. package/dist/agent/mcp-bridge.d.ts.map +1 -0
  33. package/dist/agent/mcp-bridge.js +86 -0
  34. package/dist/agent/mcp-bridge.js.map +1 -0
  35. package/dist/agent/mcp-client.d.ts +24 -0
  36. package/dist/agent/mcp-client.d.ts.map +1 -0
  37. package/dist/agent/mcp-client.js +146 -0
  38. package/dist/agent/mcp-client.js.map +1 -0
  39. package/dist/agent/mcp-manager.d.ts +13 -0
  40. package/dist/agent/mcp-manager.d.ts.map +1 -0
  41. package/dist/agent/mcp-manager.js +84 -0
  42. package/dist/agent/mcp-manager.js.map +1 -0
  43. package/dist/agent/mcp-registry.d.ts +45 -0
  44. package/dist/agent/mcp-registry.d.ts.map +1 -0
  45. package/dist/agent/mcp-registry.js +98 -0
  46. package/dist/agent/mcp-registry.js.map +1 -0
  47. package/dist/agent/orchestrator.d.ts +14 -0
  48. package/dist/agent/orchestrator.d.ts.map +1 -0
  49. package/dist/agent/orchestrator.js +118 -0
  50. package/dist/agent/orchestrator.js.map +1 -0
  51. package/dist/agent/parser-adapter.d.ts +120 -0
  52. package/dist/agent/parser-adapter.d.ts.map +1 -0
  53. package/dist/agent/parser-adapter.js +265 -0
  54. package/dist/agent/parser-adapter.js.map +1 -0
  55. package/dist/agent/parsers/imports.d.ts +11 -0
  56. package/dist/agent/parsers/imports.d.ts.map +1 -0
  57. package/dist/agent/parsers/imports.js +94 -0
  58. package/dist/agent/parsers/imports.js.map +1 -0
  59. package/dist/agent/parsers/shell.d.ts +23 -0
  60. package/dist/agent/parsers/shell.d.ts.map +1 -0
  61. package/dist/agent/parsers/shell.js +200 -0
  62. package/dist/agent/parsers/shell.js.map +1 -0
  63. package/dist/agent/parsers/symbols.d.ts +17 -0
  64. package/dist/agent/parsers/symbols.d.ts.map +1 -0
  65. package/dist/agent/parsers/symbols.js +103 -0
  66. package/dist/agent/parsers/symbols.js.map +1 -0
  67. package/dist/agent/permissions.d.ts +65 -0
  68. package/dist/agent/permissions.d.ts.map +1 -0
  69. package/dist/agent/permissions.js +219 -0
  70. package/dist/agent/permissions.js.map +1 -0
  71. package/dist/agent/predictive-gate.d.ts +69 -0
  72. package/dist/agent/predictive-gate.d.ts.map +1 -0
  73. package/dist/agent/predictive-gate.js +128 -0
  74. package/dist/agent/predictive-gate.js.map +1 -0
  75. package/dist/agent/provider-cooldown.d.ts +144 -0
  76. package/dist/agent/provider-cooldown.d.ts.map +1 -0
  77. package/dist/agent/provider-cooldown.js +300 -0
  78. package/dist/agent/provider-cooldown.js.map +1 -0
  79. package/dist/agent/providers-manager.d.ts +109 -0
  80. package/dist/agent/providers-manager.d.ts.map +1 -0
  81. package/dist/agent/providers-manager.js +464 -0
  82. package/dist/agent/providers-manager.js.map +1 -0
  83. package/dist/agent/repo-map.d.ts +6 -0
  84. package/dist/agent/repo-map.d.ts.map +1 -0
  85. package/dist/agent/repo-map.js +221 -0
  86. package/dist/agent/repo-map.js.map +1 -0
  87. package/dist/agent/retry.d.ts +103 -0
  88. package/dist/agent/retry.d.ts.map +1 -0
  89. package/dist/agent/retry.js +276 -0
  90. package/dist/agent/retry.js.map +1 -0
  91. package/dist/agent/search/index.d.ts +61 -0
  92. package/dist/agent/search/index.d.ts.map +1 -0
  93. package/dist/agent/search/index.js +314 -0
  94. package/dist/agent/search/index.js.map +1 -0
  95. package/dist/agent/single-agent.d.ts +76 -0
  96. package/dist/agent/single-agent.d.ts.map +1 -0
  97. package/dist/agent/single-agent.js +697 -0
  98. package/dist/agent/single-agent.js.map +1 -0
  99. package/dist/agent/skills.d.ts +22 -0
  100. package/dist/agent/skills.d.ts.map +1 -0
  101. package/dist/agent/skills.js +139 -0
  102. package/dist/agent/skills.js.map +1 -0
  103. package/dist/agent/stream-glue.d.ts +85 -0
  104. package/dist/agent/stream-glue.d.ts.map +1 -0
  105. package/dist/agent/stream-glue.js +120 -0
  106. package/dist/agent/stream-glue.js.map +1 -0
  107. package/dist/agent/subagent.d.ts +72 -0
  108. package/dist/agent/subagent.d.ts.map +1 -0
  109. package/dist/agent/subagent.js +193 -0
  110. package/dist/agent/subagent.js.map +1 -0
  111. package/dist/agent/telemetry.d.ts +192 -0
  112. package/dist/agent/telemetry.d.ts.map +1 -0
  113. package/dist/agent/telemetry.js +400 -0
  114. package/dist/agent/telemetry.js.map +1 -0
  115. package/dist/agent/tokenizer.d.ts +42 -0
  116. package/dist/agent/tokenizer.d.ts.map +1 -0
  117. package/dist/agent/tokenizer.js +107 -0
  118. package/dist/agent/tokenizer.js.map +1 -0
  119. package/dist/agent/tool-executor.d.ts +289 -0
  120. package/dist/agent/tool-executor.d.ts.map +1 -0
  121. package/dist/agent/tool-executor.js +2519 -0
  122. package/dist/agent/tool-executor.js.map +1 -0
  123. package/dist/agent/web-impl.d.ts +2 -0
  124. package/dist/agent/web-impl.d.ts.map +1 -0
  125. package/dist/agent/web-impl.js +34 -0
  126. package/dist/agent/web-impl.js.map +1 -0
  127. package/dist/agent/web.d.ts +8 -0
  128. package/dist/agent/web.d.ts.map +1 -0
  129. package/dist/agent/web.js +8 -0
  130. package/dist/agent/web.js.map +1 -0
  131. package/dist/agent/worker-agent.d.ts +27 -0
  132. package/dist/agent/worker-agent.d.ts.map +1 -0
  133. package/dist/agent/worker-agent.js +503 -0
  134. package/dist/agent/worker-agent.js.map +1 -0
  135. package/dist/config.d.ts +162 -0
  136. package/dist/config.d.ts.map +1 -0
  137. package/dist/config.js +138 -0
  138. package/dist/config.js.map +1 -0
  139. package/dist/context/fixo-md-watcher.d.ts +42 -0
  140. package/dist/context/fixo-md-watcher.d.ts.map +1 -0
  141. package/dist/context/fixo-md-watcher.js +126 -0
  142. package/dist/context/fixo-md-watcher.js.map +1 -0
  143. package/dist/context/fixo-md.d.ts +50 -0
  144. package/dist/context/fixo-md.d.ts.map +1 -0
  145. package/dist/context/fixo-md.js +118 -0
  146. package/dist/context/fixo-md.js.map +1 -0
  147. package/dist/context/todo.d.ts +65 -0
  148. package/dist/context/todo.d.ts.map +1 -0
  149. package/dist/context/todo.js +194 -0
  150. package/dist/context/todo.js.map +1 -0
  151. package/dist/git/git-manager.d.ts +33 -0
  152. package/dist/git/git-manager.d.ts.map +1 -0
  153. package/dist/git/git-manager.js +293 -0
  154. package/dist/git/git-manager.js.map +1 -0
  155. package/dist/git/git-ops.d.ts +10 -0
  156. package/dist/git/git-ops.d.ts.map +1 -0
  157. package/dist/git/git-ops.js +131 -0
  158. package/dist/git/git-ops.js.map +1 -0
  159. package/dist/index.d.ts +3 -0
  160. package/dist/index.d.ts.map +1 -0
  161. package/dist/index.js +352 -0
  162. package/dist/index.js.map +1 -0
  163. package/dist/indexer.d.ts +30 -0
  164. package/dist/indexer.d.ts.map +1 -0
  165. package/dist/indexer.js +273 -0
  166. package/dist/indexer.js.map +1 -0
  167. package/dist/lsp/lsp-client.d.ts +24 -0
  168. package/dist/lsp/lsp-client.d.ts.map +1 -0
  169. package/dist/lsp/lsp-client.js +205 -0
  170. package/dist/lsp/lsp-client.js.map +1 -0
  171. package/dist/lsp/lsp-manager.d.ts +17 -0
  172. package/dist/lsp/lsp-manager.d.ts.map +1 -0
  173. package/dist/lsp/lsp-manager.js +154 -0
  174. package/dist/lsp/lsp-manager.js.map +1 -0
  175. package/dist/lsp/lsp-pre-save.d.ts +137 -0
  176. package/dist/lsp/lsp-pre-save.d.ts.map +1 -0
  177. package/dist/lsp/lsp-pre-save.js +245 -0
  178. package/dist/lsp/lsp-pre-save.js.map +1 -0
  179. package/dist/lsp/syntax-fallback.d.ts +83 -0
  180. package/dist/lsp/syntax-fallback.d.ts.map +1 -0
  181. package/dist/lsp/syntax-fallback.js +275 -0
  182. package/dist/lsp/syntax-fallback.js.map +1 -0
  183. package/dist/model-outcomes.d.ts +12 -0
  184. package/dist/model-outcomes.d.ts.map +1 -0
  185. package/dist/model-outcomes.js +46 -0
  186. package/dist/model-outcomes.js.map +1 -0
  187. package/dist/planner.d.ts +32 -0
  188. package/dist/planner.d.ts.map +1 -0
  189. package/dist/planner.js +163 -0
  190. package/dist/planner.js.map +1 -0
  191. package/dist/project-memory.d.ts +29 -0
  192. package/dist/project-memory.d.ts.map +1 -0
  193. package/dist/project-memory.js +349 -0
  194. package/dist/project-memory.js.map +1 -0
  195. package/dist/review.d.ts +2 -0
  196. package/dist/review.d.ts.map +1 -0
  197. package/dist/review.js +61 -0
  198. package/dist/review.js.map +1 -0
  199. package/dist/runtime/background-jobs.d.ts +97 -0
  200. package/dist/runtime/background-jobs.d.ts.map +1 -0
  201. package/dist/runtime/background-jobs.js +331 -0
  202. package/dist/runtime/background-jobs.js.map +1 -0
  203. package/dist/runtime/credential-vault.d.ts +124 -0
  204. package/dist/runtime/credential-vault.d.ts.map +1 -0
  205. package/dist/runtime/credential-vault.js +184 -0
  206. package/dist/runtime/credential-vault.js.map +1 -0
  207. package/dist/runtime/loop-trap.d.ts +197 -0
  208. package/dist/runtime/loop-trap.d.ts.map +1 -0
  209. package/dist/runtime/loop-trap.js +420 -0
  210. package/dist/runtime/loop-trap.js.map +1 -0
  211. package/dist/runtime/policy.d.ts +15 -0
  212. package/dist/runtime/policy.d.ts.map +1 -0
  213. package/dist/runtime/policy.js +60 -0
  214. package/dist/runtime/policy.js.map +1 -0
  215. package/dist/runtime/redaction.d.ts +66 -0
  216. package/dist/runtime/redaction.d.ts.map +1 -0
  217. package/dist/runtime/redaction.js +155 -0
  218. package/dist/runtime/redaction.js.map +1 -0
  219. package/dist/runtime/session-snapshots.d.ts +76 -0
  220. package/dist/runtime/session-snapshots.d.ts.map +1 -0
  221. package/dist/runtime/session-snapshots.js +166 -0
  222. package/dist/runtime/session-snapshots.js.map +1 -0
  223. package/dist/runtime/staging.d.ts +205 -0
  224. package/dist/runtime/staging.d.ts.map +1 -0
  225. package/dist/runtime/staging.js +526 -0
  226. package/dist/runtime/staging.js.map +1 -0
  227. package/dist/runtime/task-session.d.ts +95 -0
  228. package/dist/runtime/task-session.d.ts.map +1 -0
  229. package/dist/runtime/task-session.js +263 -0
  230. package/dist/runtime/task-session.js.map +1 -0
  231. package/dist/runtime/worktree.d.ts +55 -0
  232. package/dist/runtime/worktree.d.ts.map +1 -0
  233. package/dist/runtime/worktree.js +175 -0
  234. package/dist/runtime/worktree.js.map +1 -0
  235. package/dist/setup-wizard.d.ts +8 -0
  236. package/dist/setup-wizard.d.ts.map +1 -0
  237. package/dist/setup-wizard.js +73 -0
  238. package/dist/setup-wizard.js.map +1 -0
  239. package/dist/shared/content.d.ts +43 -0
  240. package/dist/shared/content.d.ts.map +1 -0
  241. package/dist/shared/content.js +61 -0
  242. package/dist/shared/content.js.map +1 -0
  243. package/dist/shared/types.d.ts +217 -0
  244. package/dist/shared/types.d.ts.map +1 -0
  245. package/dist/shared/types.js +3 -0
  246. package/dist/shared/types.js.map +1 -0
  247. package/dist/test-runner.d.ts +5 -0
  248. package/dist/test-runner.d.ts.map +1 -0
  249. package/dist/test-runner.js +42 -0
  250. package/dist/test-runner.js.map +1 -0
  251. package/dist/types.d.ts +85 -0
  252. package/dist/types.d.ts.map +1 -0
  253. package/dist/types.js +2 -0
  254. package/dist/types.js.map +1 -0
  255. package/dist/ui/ascii.d.ts +23 -0
  256. package/dist/ui/ascii.d.ts.map +1 -0
  257. package/dist/ui/ascii.js +45 -0
  258. package/dist/ui/ascii.js.map +1 -0
  259. package/dist/ui/colors.d.ts +111 -0
  260. package/dist/ui/colors.d.ts.map +1 -0
  261. package/dist/ui/colors.js +166 -0
  262. package/dist/ui/colors.js.map +1 -0
  263. package/dist/ui/image-attach.d.ts +27 -0
  264. package/dist/ui/image-attach.d.ts.map +1 -0
  265. package/dist/ui/image-attach.js +100 -0
  266. package/dist/ui/image-attach.js.map +1 -0
  267. package/dist/ui/index.d.ts +18 -0
  268. package/dist/ui/index.d.ts.map +1 -0
  269. package/dist/ui/index.js +18 -0
  270. package/dist/ui/index.js.map +1 -0
  271. package/dist/ui/markdown-stream.d.ts +91 -0
  272. package/dist/ui/markdown-stream.d.ts.map +1 -0
  273. package/dist/ui/markdown-stream.js +524 -0
  274. package/dist/ui/markdown-stream.js.map +1 -0
  275. package/dist/ui/plan-renderer.d.ts +36 -0
  276. package/dist/ui/plan-renderer.d.ts.map +1 -0
  277. package/dist/ui/plan-renderer.js +79 -0
  278. package/dist/ui/plan-renderer.js.map +1 -0
  279. package/dist/ui/prompt.d.ts +11 -0
  280. package/dist/ui/prompt.d.ts.map +1 -0
  281. package/dist/ui/prompt.js +1960 -0
  282. package/dist/ui/prompt.js.map +1 -0
  283. package/dist/ui/render-primitives.d.ts +117 -0
  284. package/dist/ui/render-primitives.d.ts.map +1 -0
  285. package/dist/ui/render-primitives.js +322 -0
  286. package/dist/ui/render-primitives.js.map +1 -0
  287. package/dist/ui/render.d.ts +133 -0
  288. package/dist/ui/render.d.ts.map +1 -0
  289. package/dist/ui/render.js +547 -0
  290. package/dist/ui/render.js.map +1 -0
  291. package/dist/ui/session-header.d.ts +30 -0
  292. package/dist/ui/session-header.d.ts.map +1 -0
  293. package/dist/ui/session-header.js +74 -0
  294. package/dist/ui/session-header.js.map +1 -0
  295. package/dist/workspace-guard.d.ts +68 -0
  296. package/dist/workspace-guard.d.ts.map +1 -0
  297. package/dist/workspace-guard.js +168 -0
  298. package/dist/workspace-guard.js.map +1 -0
  299. package/dist/workspace-lock.d.ts +27 -0
  300. package/dist/workspace-lock.d.ts.map +1 -0
  301. package/dist/workspace-lock.js +95 -0
  302. package/dist/workspace-lock.js.map +1 -0
  303. 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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA+ClE,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;AAE9E,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,KAAqB;QAArB,UAAK,GAAL,KAAK,CAAgB;IAAG,CAAC;IAEtD,uCAAuC;IACvC,KAAK,CAAC,IAAY;QAChB,OAAO,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,sEAAsE;IACtE,aAAa,CAAC,QAAoC;QAChD,OAAO,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACnD,CAAC;CACF;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,0BAA0B,GAAG,KAAK,CAAC;AAEzC,MAAM,OAAO,qBAAqB;IACf,OAAO,CAAe;IAEvC,YAAY,KAAqB;QAC/B,IAAI,CAAC,OAAO,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,QAAoC,EAAE,OAAsB;QAIlE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC;QAC5E,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,0BAA0B,CAAC;QAC9E,MAAM,OAAO,GAAmB,EAAE,CAAC;QAEnC,IAAI,OAAO,GAAkB,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC1D,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAEjD,IAAI,MAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO;gBACL,QAAQ,EAAE,OAAO;gBACjB,MAAM,EAAE;oBACN,YAAY;oBACZ,WAAW,EAAE,MAAM;oBACnB,OAAO,EAAE,CAAC,MAAM,CAAC;oBACjB,iBAAiB,EAAE,KAAK;oBACxB,YAAY,EAAE,IAAI;iBACnB;aACF,CAAC;QACJ,CAAC;QAED,mDAAmD;QACnD,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACvD,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC7D,IAAI,gBAAgB,GAAG,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAClE,MAAM,GAAG,gBAAgB,CAAC;QAC1B,IAAI,MAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACxD,CAAC;QAED,kCAAkC;QAClC,MAAM,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;QACzC,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACtD,IAAI,OAAO,CAAC,MAAM,GAAG,iBAAiB;YAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC1E,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,MAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACxD,CAAC;QAED,kDAAkD;QAClD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,UAAU,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAC/E,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,MAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACxD,CAAC;QAED,oEAAoE;QACpE,wCAAwC;QACxC,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE;gBACN,YAAY;gBACZ,WAAW,EAAE,MAAM;gBACnB,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,qBAAqB,CAAC;gBAC5C,iBAAiB,EAAE,IAAI;gBACvB,YAAY,EAAE,KAAK;aACpB;SACF,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,iBAAiB;IACjB,4EAA4E;IAEpE,gBAAgB,CACtB,QAAuB,EACvB,YAAoB;QAEpB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC;QAC7D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC3B,IAAI,CAAC,IAAI,QAAQ;gBAAE,OAAO,CAAC,CAAC;YAC5B,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,0BAA0B,EAAE,CAAC;gBACpF,OAAO;oBACL,GAAG,CAAC;oBACJ,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,0BAA0B,CAAC;wBACrD,oBAAoB,CAAC,CAAC,OAAO,CAAC,MAAM,MAAM,0BAA0B,SAAS;iBAChF,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,eAAe,CACrB,QAAuB,EACvB,YAAoB;QAEpB,8DAA8D;QAC9D,4DAA4D;QAC5D,+DAA+D;QAC/D,6DAA6D;QAC7D,IAAI,QAAQ,CAAC,MAAM,IAAI,YAAY;YAAE,OAAO,QAAQ,CAAC;QACrD,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM;gBAAE,SAAS,IAAI,CAAC,CAAC;YAChD,8DAA8D;YAC9D,gEAAgE;YAChE,+DAA+D;YAC/D,2BAA2B;YAC3B,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACtF,iDAAiD;gBACjD,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC5C,IAAI,SAAS,IAAI,YAAY,EAAE,CAAC;oBAC9B,SAAS,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACN,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;IACnF,CAAC;IAEO,gBAAgB,CACtB,QAAuB,EACvB,QAAgB;QAEhB,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACxB,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,CAAC,UAAU;gBAAE,OAAO,CAAC,CAAC;YACtD,MAAM,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;gBACxC,IAAI,CAAC,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,IAAI,QAAQ;oBAAE,OAAO,EAAE,CAAC;gBACxE,OAAO;oBACL,GAAG,EAAE;oBACL,QAAQ,EAAE;wBACR,GAAG,EAAE,CAAC,QAAQ;wBACd,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,OAAO;qBAC9D;iBACF,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED,4EAA4E;AAC5E,UAAU;AACV,4EAA4E;AAE5E,SAAS,YAAY,CAAC,CAAc;IAClC,OAAO;QACL,GAAG,CAAC;QACJ,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI;QAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;YACtB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBACxB,GAAG,EAAE;gBACL,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE;aAC7B,CAAC,CAAC;YACL,CAAC,CAAC,SAAS;KACd,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CACb,QAAuB,EACvB,YAAoB,EACpB,WAAmB,EACnB,OAAuB;IAEvB,OAAO;QACL,QAAQ;QACR,MAAM,EAAE;YACN,YAAY;YACZ,WAAW;YACX,OAAO;YACP,iBAAiB,EAAE,KAAK;YACxB,YAAY,EAAE,IAAI;SACnB;KACF,CAAC;AACJ,CAAC"}
@@ -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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"}