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
package/README.md ADDED
@@ -0,0 +1,530 @@
1
+ # ⚡ Fixo CLI
2
+ > **Autonomous, Free, Multi-Provider LLM Coding Agent CLI**
3
+
4
+ [![Language](https://img.shields.io/badge/Language-TypeScript-blue.svg)](https://www.typescriptlang.org/)
5
+ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
6
+ [![Engine](https://img.shields.io/badge/Engine-Tree--Sitter-orange.svg)](https://tree-sitter.github.io/tree-sitter/)
7
+ [![Status](https://img.shields.io/badge/Status-Production--Ready-brightgreen.svg)]()
8
+
9
+ Fixo CLI is a terminal-based autonomous coding assistant designed to execute complex programming tasks directly in your workspace. Built as a self-correcting agent, it analyzes code using abstract syntax trees (AST), writes implementation plans, edits code files, runs test suites, and iterates until the goal is fully achieved.
10
+
11
+ Fixo CLI integrates seamlessly with **FreeLLMAPI**, automatically load-balancing and failing over across **20+ free LLM providers** (such as Gemini, Groq, SambaNova, Cerebras, and NVIDIA NIM) for zero-cost, state-of-the-art agentic coding.
12
+
13
+ ---
14
+
15
+ ## 📊 Fixo CLI vs. Other Market Leaders
16
+
17
+ Here is how Fixo CLI compares against other prominent terminal and editor-based coding agents:
18
+
19
+ | Feature / Metric | **Fixo CLI** | **Claude Code** | **Aider** | **Cline** |
20
+ | :--- | :--- | :--- | :--- | :--- |
21
+ | **API Cost** | 💰 **100% Free** (via FreeLLMAPI) | 💸 **Paid** (Anthropic API charges) | 💸 **Paid** (Requires personal keys) | 💸 **Paid** (Requires personal keys) |
22
+ | **Multi-Provider Fallback**| 🔄 **Automatic Failover** (No interruptions) | ❌ None (Locked to Anthropic) | ❌ Manual (Requires editing configs) | ❌ Manual (Drops request on 429) |
23
+ | **Workspace Indexing** | 🌳 **AST / Tree-Sitter** (Semantic map) | 🔍 Regex / basic grep | 🗺️ Git/ctags-based map | 🔍 Basic file search |
24
+ | **Autonomy Loops** | 🤖 **Multi-agent / Planning Mode** | 🤖 Agent loops | 💬 Interactive / chat-driven | 💬 Prompt-to-action loops |
25
+ | **Self-Correction** | 🧪 **Built-in test runner & loops** | ❌ Manual trigger | ❌ Requires manual input | ❌ Requires manual input |
26
+ | **No-Card Verification** | ✅ **Yes** (Zero billing required) | ❌ No (Requires credit card) | ❌ No (Requires paid API keys) | ❌ No (Requires paid API keys) |
27
+
28
+ ---
29
+
30
+ ## ⚙️ Architecture & Lifecycle Flow
31
+
32
+ Fixo CLI separates concerns between code understanding (AST parser), task coordination (Planner), and execution (Agent).
33
+
34
+ ```mermaid
35
+ sequenceDiagram
36
+ autonumber
37
+ actor User as Developer
38
+ participant CLI as Fixo CLI
39
+ participant Indexer as AST Workspace Indexer
40
+ participant Planner as Plan Engine
41
+ participant Agent as Autonomous Agent
42
+ participant Proxy as FreeLLMAPI Proxy
43
+ participant LLM as Provider (Groq/Gemini/NIM)
44
+
45
+ User->>CLI: Request task (e.g. "Fix auth bug")
46
+ CLI->>Indexer: Scan repository & generate AST maps
47
+ Indexer-->>CLI: Return semantic codebase layout
48
+ CLI->>Planner: Propose implementation plan
49
+ Planner->>Proxy: Fetch reasoning (smart routing)
50
+ Proxy->>LLM: Try highest ranked provider
51
+ LLM-->>Planner: Return initial plan
52
+ Planner-->>User: Present plan for approval
53
+ User->>CLI: Plan Approved!
54
+ CLI->>Agent: Execute code changes
55
+ loop Iterative Execution
56
+ Agent->>Proxy: Request edit / test run
57
+ Proxy->>LLM: Fallback routing (failover on error)
58
+ LLM-->>Agent: Code output / Command to run
59
+ Agent->>CLI: Apply file changes & execute tests
60
+ end
61
+ Agent-->>User: Task completed successfully!
62
+ ```
63
+
64
+ ---
65
+
66
+ ## 🌟 Key Features
67
+
68
+ * **Autonomous Agent Loop:** Fixo CLI runs an agent loop that defines planning sub-agents, writes files, runs shell commands, reads compiler output, and self-corrects until tests pass.
69
+ * **Workspace AST Indexer:** Uses **Tree-Sitter** to parse JavaScript, TypeScript, Python, and Go codebases, generating a semantic repository map for precise context insertion.
70
+ * **Free Multi-Provider Routing:** Connects to your FreeLLMAPI server to query models like Llama 3.3, Qwen 3, and Gemini 2.5/3.1 without incurring high API costs.
71
+ * **Smart Cooldown & Failover:** The CLI automatically tracks rate-limited providers (429/402/404) and switches to working alternatives in the fallback chain mid-request.
72
+ * **Resilience Stack:** Stream recovery, provider cooldown, context-budget enforcement, and a local telemetry sink work together so the agent stays productive on flaky networks and large codebases. See [Resilience](#-resilience) below.
73
+ * **Built-in Workspace Guard:** Safely manages workspace locks, preventing concurrent file writes and ensuring git safety.
74
+
75
+ ---
76
+
77
+ ## 🛠 Tool Reference (Phase 1–3 + Phase 4)
78
+
79
+ The agent dispatches the following tools through the unified
80
+ `executeTool()` entrypoint in `src/agent/tool-executor.ts`.
81
+ Every call is gated by the granular permission engine
82
+ (`checkPermission`) before any side effect runs. `Mode`
83
+ indicates which execution modes the tool is callable from
84
+ (`PLAN`, `BUILD`, `EXPLORE`, `SCOUT`); tools that mutate the
85
+ workspace are blocked outside `BUILD`.
86
+
87
+ | Tool | Phase | Description | Required args | Mode | Pillar gates | Default permission |
88
+ | :--- | :---: | :--- | :--- | :--- | :--- | :--- |
89
+ | `str_replace` | 1 | Surgical line-level edit with uniqueness check on `find`. Atomic via `applySurgicalReplace`. | `path`, `find`, `replace` | BUILD | Staging + LSP pre-save + workspace guard | `ask` (default-ask) |
90
+ | `glob_files` | 1 | Pattern-based file finder (e.g. `src/**/*.ts`). | `pattern` | EXPLORE, SCOUT, BUILD | Workspace guard | `ask` (default-ask) |
91
+ | `todo_write` / `todo_read` | 2 | Mutable task checklist persisted under `.fixo/`. | `items?` (write) / — (read) | PLAN, BUILD | Workspace guard + staging | `ask` / `allow` |
92
+ | `run_command_async` | 3 | Non-blocking shell execution; returns a job id. | `command`, `cwd?` | BUILD | Command-parser AST + permissions | `ask` (default-ask) |
93
+ | `poll_command_status` | 3 | Poll a previously-spawned async job for status + ring-buffered stdout/stderr. | `id` | BUILD, EXPLORE | n/a (read-only metadata) | `ask` (default-ask) |
94
+ | `kill_command` | 3 | Send `SIGTERM` to a running async job. | `id` | BUILD | Command-parser invariants | `ask` (default-ask) |
95
+ | `spawn_subagent` | 3 | Context-isolated sub-orchestrator with its own conversation budget; inherits parent policy + vault. | `prompt`, `tools?` | PLAN, BUILD | Inherits all four pillars | `ask` |
96
+ | `/mcp` console | 3 | Slash command (`/mcp list`, `/mcp add`, `/mcp restart`) for MCP server management. | — | EXPLORE, BUILD | Config-only (no workspace touch) | n/a |
97
+ | Worktree annotations | 3 | Parsed from assistant text (`[worktree:create branch=x]`, `[worktree:merge branch=x]`, `[worktree:remove path=...]`). Not a tool — a capability the executor extracts post-stream. | n/a (annotation in text) | BUILD | `execFileSync('git', …)` — no shell expansion | `ask` (parsed by the single-agent loop) |
98
+
99
+ ### Predictive Context-Budget Gate (Phase 4)
100
+
101
+ A token-aware predictive gate sits in front of `read_file`. Before
102
+ the byte gate runs, the gate projects the file's token cost via
103
+ `gpt-tokenizer`, adds the current conversation token count, and
104
+ defers the read with a `[Context-Budget Guard]` directive if the
105
+ projected total would exceed `predictiveBudgetPct` of the model's
106
+ input window (default `0.85`). The directive routes the model to
107
+ `extract_symbols` / `extract_imports` / `str_replace` instead of
108
+ reading the full file. Configurable via `preferences.safety.predictiveBudgetPct` —
109
+ set to `1.0` to disable.
110
+
111
+ ---
112
+
113
+ ## 🛡 Resilience
114
+
115
+ Fixo CLI is built for hostile environments: free-tier rate limits, dropped SSE streams, providers that 502 mid-response, and codebases larger than any single context window. The resilience stack is organised into four independent pillars, each of which can be tuned or disabled individually via `~/.fixocli/config.json`.
116
+
117
+ | Pillar | Module | What it does | Default |
118
+ | :--- | :--- | :--- | :--- |
119
+ | **Stream Recovery** | `src/agent/stream-glue.ts` | Detects mid-stream SSE cuts *after* at least one chunk has been yielded, rebuilds the message list with the partial response, and re-issues the request transparently. | `auto` (up to 3 attempts) |
120
+ | **Provider Cooldown** | `src/agent/provider-cooldown.ts` | Tracks per-provider success/failure rates. On 429/5xx, applies an exponential cooldown (30/60/120/240/300s for rate limits, 10/20/40/80/120s for server errors) and steers subsequent requests to healthier providers. | always on |
121
+ | **Context Budgeting** | `src/agent/context-budget.ts` | Counts real BPE tokens (via `gpt-tokenizer`, cl100k / o200k) before every LLM call. When the next request would overflow, runs a tiered trim (prune tool outputs → drop oldest turn-pairs → truncate tool args) and, if still over, marks the conversation for LLM-based compaction. | `auto` at 80% of model window |
122
+ | **Telemetry** | `src/agent/telemetry.ts` | Append-only NDJSON sink at `~/.fixocli/telemetry.jsonl` (rotated at 1 MiB). Emits structured events for retries, cooldowns, stream resumes, context compactions, tool failures, and provider errors. `diagnoseFailures()` reads the recent window and surfaces remediation hints. | `local: on, remote: off` |
123
+
124
+ ### ResilienceConfig schema
125
+
126
+ All four pillars are controlled by the `preferences.resilience` block in your config. Every field has a safe default, so you can omit the entire block if you want the shipped behaviour.
127
+
128
+ ```jsonc
129
+ {
130
+ "preferences": {
131
+ "telemetry": true, // Master switch for *all* telemetry
132
+ "telemetryLocal": true, // Append events to ~/.fixocli/telemetry.jsonl
133
+ "telemetryRemote": false, // POST events to the FreeLLMAPI server (anonymous)
134
+ "resilience": {
135
+ "streamResume": "auto", // "auto" | "never" (kill-switch for stream recovery)
136
+ "maxResumeAttempts": 3, // additional attempts after a mid-stream cut
137
+ "useWithRetry": true, // use the withRetry engine for non-streaming calls
138
+ "contextBudget": "auto", // "auto" | "truncate" | "never" (kill-switch for budget enforcement)
139
+ "contextBudgetRatio": 0.8 // fraction of model window used as the hard cap
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ | Field | Type | Default | Behaviour |
146
+ | :--- | :--- | :--- | :--- |
147
+ | `streamResume` | `"auto" \| "never"` | `"auto"` | When `never`, `chatStream` is called directly and cuts bubble up to the caller as `StreamResumeExhaustedError`. |
148
+ | `maxResumeAttempts` | `number` | `3` | How many additional attempts the resume engine makes after a mid-stream cut. 0 disables resume. |
149
+ | `useWithRetry` | `boolean` | `true` | Toggle the exponential-backoff `withRetry` engine for non-streaming calls. |
150
+ | `contextBudget` | `"auto" \| "truncate" \| "never"` | `"auto"` | `auto` = enforce + LLM-compact; `truncate` = enforce only (no LLM fallback); `never` = skip the enforcer entirely. |
151
+ | `contextBudgetRatio` | `number` (0–1) | `0.8` | Fraction of the model's input window used as the hard cap. 0.8 leaves 20% headroom for the response. |
152
+ | `telemetryLocal` | `boolean` | `true` | Disable to skip the local NDJSON sink while keeping the remote one (if enabled). |
153
+ | `telemetryRemote` | `boolean` | `false` | Opt in to the legacy HTTP poster for anonymous usage stats. |
154
+
155
+ ### Diagnosing a bad session
156
+
157
+ After a session that didn't go well, run the diagnostic from the CLI:
158
+
159
+ ```bash
160
+ fixo --diagnose
161
+ ```
162
+
163
+ Or read the log directly:
164
+
165
+ ```bash
166
+ tail -50 ~/.fixocli/telemetry.jsonl | jq .
167
+ ```
168
+
169
+ `diagnoseFailures()` looks at the last hour by default and reports patterns such as:
170
+ - 3+ retries in the window → likely flaky network or rate-limit
171
+ - provider cooldown → at least one provider is rate-limiting; others are being preferred automatically
172
+ - stream-resume exhaustion → raise `maxResumeAttempts` or check your network
173
+ - repeated tool failures → the same tool has failed 3+ times; check its inputs
174
+ - 5+ provider errors → likely a provider outage
175
+
176
+ See [`docs/RESILIENCE.md`](docs/RESILIENCE.md) for the pillar-by-pillar design notes.
177
+
178
+ ---
179
+
180
+ ## 🛡 Operational Resilience & Enterprise Hardening
181
+
182
+ The Resilience section above covers **uptime** (how Fixo stays
183
+ alive through network noise). This section covers **integrity**
184
+ — how Fixo protects the user's workspace and secrets. The two
185
+ are deliberately orthogonal and live in separate configuration
186
+ blocks:
187
+
188
+ | Concern | Configuration block | Purpose |
189
+ |---|---|---|
190
+ | **Resilience** | `preferences.resilience` | Stream resume, context budgeting, retry engine. |
191
+ | **Safety** | `preferences.safety` | Loop detection, atomic file writes, LSP pre-save, credential vault. |
192
+
193
+ ### Safety configuration schema
194
+
195
+ The full schema lives under `preferences.safety` in
196
+ `~/.fixocli/config.json`. Defaults are safe for interactive
197
+ use; tighten them for CI / unattended runs.
198
+
199
+ ```json
200
+ {
201
+ "preferences": {
202
+ "safety": {
203
+ "atomicStaging": true,
204
+ "stagingTtlMs": 86400000,
205
+ "lspPreSave": "warn",
206
+ "loopTrap": {
207
+ "triggerCount": 3,
208
+ "hardAbortCount": 6,
209
+ "toolResultTailBytes": 1024,
210
+ "maxHistory": 64,
211
+ "enabled": true
212
+ }
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ | Field | Type | Default | Behaviour |
219
+ |---|---|---|---|
220
+ | `atomicStaging` | `boolean` | `true` | Route every file write through the shadow-staging pipeline. When `false`, the executor falls back to the legacy direct-write path (faster, but non-atomic). |
221
+ | `stagingTtlMs` | `number` (ms) | `86400000` (24h) | Staged writes older than this are eligible for auto-GC. |
222
+ | `lspPreSave` | `"off" \| "warn" \| "block"` | `"warn"` | `off` = no-op; `warn` = log diagnostics, allow commit; `block` = throw on any `error`-severity diagnostic and roll back. |
223
+ | `loopTrap.triggerCount` | `number` | `3` | Consecutive equivalent turns that fire the `[Loop-Trap]` directive. |
224
+ | `loopTrap.hardAbortCount` | `number` | `6` | Consecutive equivalent turns that throw `LoopTrapAbortedError`. |
225
+ | `loopTrap.toolResultTailBytes` | `number` (bytes) | `1024` | Length of tool-result tail that is hashed. |
226
+ | `loopTrap.maxHistory` | `number` | `64` | Cap on in-memory detector history. |
227
+ | `loopTrap.enabled` | `boolean` | `true` | Master kill-switch for the detector. |
228
+ | `semanticLoopTrap.enabled` | `boolean` | `true` | Master kill-switch for the *semantic* detector (tracks per-file frequency in a sliding window). |
229
+ | `semanticLoopTrap.windowSize` | `number` | `5` | Width of the sliding window. |
230
+ | `semanticLoopTrap.triggerCount` | `number` | `3` | File accesses inside the window that fire the `[Safety-Alert]` directive. |
231
+ | `semanticLoopTrap.hardAbortCount` | `number` | `6` | File accesses inside the window that throw `SemanticLoopAbortedError` (and roll back any staged writes). |
232
+ | `largeFileGateBytes` | `number` (bytes) | `15360` (15 KiB) | `read_file` returns a `[Context-Budget Guard]` directive when a file exceeds this size. |
233
+ | `largeFileGateLines` | `number` | `350` | Same gate, line-count threshold. |
234
+
235
+ ### Hardening profiles
236
+
237
+ | Profile | `atomicStaging` | `lspPreSave` | `loopTrap` |
238
+ |---|---|---|---|
239
+ | **Interactive dev** *(default)* | `true` | `"warn"` | `3 / 6` |
240
+ | **CI / unattended** | `true` | `"block"` | `2 / 4` |
241
+ | **Trusted fine-tune** | `false` | `"off"` | `5 / 10` |
242
+ | **Benchmarking** | `false` | `"off"` | kill-switch |
243
+
244
+ ### The four safety pillars
245
+
246
+ | # | Pillar | What it stops | Where it lives |
247
+ |---|---|---|---|
248
+ | 1 | **Deterministic Loop-Trap Defenses** (composite + semantic) | An LLM that re-issues equivalent tool calls *or* hammers the same file with varied arguments. | `src/runtime/loop-trap.ts` |
249
+ | 2 | **Atomic Workspace Shadow Staging** | A process kill mid-write leaving the user's file truncated. | `src/runtime/staging.ts` |
250
+ | 3 | **Live Pre-Save LSP Compilation Check** + **Context-Budget Guard** | An LLM writing syntactically valid but semantically broken code, *or* flooding the context window with a single 200KB file. | `src/lsp/lsp-pre-save.ts` + `src/lsp/syntax-fallback.ts` + the large-file gate in `src/agent/tool-executor.ts` |
251
+ | 4 | **Restricted Credential Sandboxing & Redaction** | Direct-provider API keys leaking into a tool result, log line, or model prompt. | `src/runtime/credential-vault.ts` + `src/runtime/redaction.ts` |
252
+
253
+ #### Pillar 1 — Composite *and* semantic loop detection
254
+
255
+ Two detectors run in parallel:
256
+
257
+ - **Composite** (`LoopTrapDetector`) — fingerprints the tool call
258
+ *arguments* and the tail of the tool *result* and trips when
259
+ three consecutive turns hash to the same value. Defends
260
+ against a deterministic "stare at one line" loop.
261
+ - **Semantic** (`SemanticLoopDetector`) — fingerprints the
262
+ *target file path* of every file-mutating tool and trips when
263
+ the same path appears three times inside a sliding 5-turn
264
+ window. Defends against an LLM that varies its search
265
+ arguments (different line numbers, different patterns) but
266
+ keeps hammering the same file.
267
+
268
+ On `triggerCount` the detector injects a `[Safety-Alert]`
269
+ directive into the next system prompt. On `hardAbortCount` it
270
+ throws `SemanticLoopAbortedError`, which the agent catches and
271
+ translates into a clean `AtomicStagingManager.rollbackAll()` so
272
+ no half-edited file survives.
273
+
274
+ #### Pillar 3 — Context-Budget Guard
275
+
276
+ `read_file` is gated by both a byte threshold (`largeFileGateBytes`,
277
+ default 15 KiB) and a line threshold (`largeFileGateLines`,
278
+ default 350). When a file exceeds either, the LLM receives a
279
+ synthetic `[Context-Budget Guard]` directive that points it at
280
+ the new structural pre-scan tools:
281
+
282
+ - `extract_symbols(path)` — top-level declarations only (cap
283
+ 100 per file).
284
+ - `extract_imports(path)` — dependency list only (cap 100 per
285
+ file).
286
+
287
+ Each pre-scan call records the result in the
288
+ `TaskSession.structuralMaps` map so the LLM's later reads can
289
+ prove they were narrowed first.
290
+
291
+ #### Pillar 3 — LSP syntax fallback
292
+
293
+ When no language server (`typescript-language-server`,
294
+ `gopls`, `rust-analyzer`, …) is on the `PATH`, the pre-save
295
+ gate falls back to `syntaxHealthCheck` — a pure-JS
296
+ brace/paren/bracket balance check that runs in microseconds.
297
+ Set `FIXO_LSP_FALLBACK=syntax-only` to force the fallback even
298
+ when a real LSP is available (useful for sandboxed CI). The
299
+ boot sequence also surfaces a one-time warning when neither is
300
+ present.
301
+
302
+ #### Pillar 4 — Three redaction modes
303
+
304
+ `src/runtime/redaction.ts` exposes three helpers for the three
305
+ distinct places redaction is needed:
306
+
307
+ - `stripAnsi(value)` — drop every ANSI escape entirely. Use
308
+ for content piped into another tool that doesn't care about
309
+ colour.
310
+ - `redactAnsi(value)` — replace the `\x1b` byte with the
311
+ printable form `\\x1b` so the message survives a log write /
312
+ telemetry upload without injecting control codes downstream.
313
+ Use for content whose *bytes* must be preserved (file paths,
314
+ error messages).
315
+ - `scrubForLlm(value)` — strip ANSI *and* replace every
316
+ recognised secret pattern with `[REDACTED]`. This is the
317
+ only safe redaction for content heading back into a model
318
+ prompt.
319
+
320
+ ### Automated background garbage collection
321
+
322
+ The staging pipeline writes to `<cwd>/.fixo/staging/<runId>/`
323
+ and would silently bloat the disk if entries were never
324
+ cleaned up. Two sweeps run automatically:
325
+
326
+ - **Per-run GC** — `AtomicStagingManager.gc()` removes entries
327
+ older than `stagingTtlMs` from the current run's directory.
328
+ - **Global GC** — `AtomicStagingManager.garbageCollectAll(cwd, ttlMs)`
329
+ sweeps every `<runId>/` directory. Invoked at the start of
330
+ every `runStreaming` lifecycle (typically < 2 ms) and also
331
+ exposed via the `/fixo gc` slash command for power users.
332
+
333
+ GC is bounded and uses the metadata `createdAt` timestamp
334
+ rather than file mtime, so the TTL is a deterministic policy
335
+ decision rather than a side effect of kernel flush timing.
336
+
337
+ ### Troubleshooting runbooks
338
+
339
+ #### "The agent is stuck in a loop"
340
+
341
+ The `[Loop-Trap]` directive is injected into the system prompt
342
+ after 3 consecutive equivalent turns. The model is expected to
343
+ reconsider its strategy. If it doesn't, the detector hard-
344
+ aborts at 6 turns and the session terminates with
345
+ `LoopTrapAbortedError`.
346
+
347
+ **Diagnosis:**
348
+ ```bash
349
+ tail -100 ~/.fixocli/telemetry.jsonl | jq 'select(.event == "loopTrap")'
350
+ ```
351
+
352
+ **Fix:**
353
+ 1. Read the directive — it tells the LLM *why* the loop is
354
+ happening. Common causes:
355
+ - The test command is failing consistently → fix the test.
356
+ - The patch is being reverted by a pre-commit hook → fix the hook.
357
+ - The agent is confused about file paths → clarify the task.
358
+ 2. Force a manual compaction to drop the noise from the
359
+ conversation history:
360
+ ```bash
361
+ fixo --compact
362
+ ```
363
+ 3. To disable the detector for a single session (debugging
364
+ only), set `loopTrap.enabled` to `false` in
365
+ `~/.fixocli/config.json`.
366
+
367
+ #### "LSP gate blocked my write"
368
+
369
+ `write_file` returns
370
+ `Error: Pre-commit hook rejected: LSP pre-save blocked: N
371
+ error(s) in <path> — <line>:<col> <message>; ...`
372
+
373
+ **Diagnosis:**
374
+ 1. The first 3 error messages in the string are the gate's
375
+ best guess at the root cause.
376
+ 2. Open the file in your editor — the LSP (if installed)
377
+ underlines the offending line.
378
+ 3. Common causes:
379
+ - Missing import → add it.
380
+ - Type mismatch → fix the annotation.
381
+ - Reference to an undeclared identifier → typo.
382
+
383
+ **The original file is preserved on every blocked write.**
384
+ Re-run the agent's suggested fix and try again.
385
+
386
+ **If the gate is over-firing** (a known-good write is being
387
+ rejected), lower the mode from `block` to `warn`:
388
+ ```json
389
+ { "preferences": { "safety": { "lspPreSave": "warn" } } }
390
+ ```
391
+
392
+ #### "A write silently rolled back"
393
+
394
+ The agent said it wrote a file, but the file is unchanged.
395
+
396
+ **Diagnosis:**
397
+ 1. Check the tool result string for
398
+ `PreCommitHookRejectedError` or `StagingPathEscapeError`.
399
+ 2. For `PreCommitHookRejectedError`, the `cause` field is the
400
+ underlying error (`LspPreSaveBlockedError`, or a future
401
+ pre-commit hook). Read `cause.message`.
402
+ 3. For `StagingPathEscapeError`, the target path escapes the
403
+ workspace root — this is a caller bug. Reject and re-prompt.
404
+
405
+ #### "Staging directory is filling my disk"
406
+
407
+ ```bash
408
+ du -sh ~/.fixo/staging
409
+ ```
410
+
411
+ **Fix:**
412
+ 1. The auto-GC runs at the start of every `runStreaming` cycle.
413
+ If the directory is large *during* a session, an LLM is
414
+ staging writes but failing to commit them. Inspect the
415
+ session log for `PreCommitHookRejectedError`.
416
+ 2. Manual sweep (safe; staging is ephemeral):
417
+ ```bash
418
+ rm -rf ~/.fixo/staging/*
419
+ ```
420
+ 3. Lower `stagingTtlMs` to expire entries sooner:
421
+ ```json
422
+ { "preferences": { "safety": { "stagingTtlMs": 3600000 } } }
423
+ ```
424
+
425
+ #### "A direct-provider API key was rejected"
426
+
427
+ ```bash
428
+ fixo providers list
429
+ fixo providers add openai sk-proj-...
430
+ ```
431
+
432
+ The vault is auto-hydrated on the next `getDirectConfig` call,
433
+ so the new key is visible immediately — no restart required.
434
+
435
+ > **Security note:** never paste a real key into a chat
436
+ > message, a GitHub issue, or a tool result. `scrubForLlm`
437
+ > redacts common shapes, but a low-entropy prefix is not
438
+ > guaranteed to match.
439
+
440
+ #### "I need to fully reset the safety layer"
441
+
442
+ ```bash
443
+ # Drop the staging directory (ephemeral)
444
+ rm -rf ~/.fixo/staging
445
+
446
+ # Drop the cached vault singleton (next call re-hydrates)
447
+ fixo providers reset-vault
448
+
449
+ # Reset to safe production defaults
450
+ fixo config reset --section safety
451
+ ```
452
+
453
+ See [`docs/SAFETY.md`](docs/SAFETY.md) for the full threat
454
+ model, design notes, and operator reference.
455
+
456
+ ---
457
+
458
+ ## 🖥️ Dashboard
459
+
460
+ The `Dashboard` (`src/ui/render.ts`) is a typed event bus that
461
+ the agent and any number of subscribers (UI, telemetry, tests)
462
+ can wire into without coupling:
463
+
464
+ - `Dashboard` is a pure state holder. It never touches stdout.
465
+ - `DashboardSubscriber` is a one-method interface; errors
466
+ thrown by a subscriber are counted in
467
+ `Dashboard.subscriberErrors` and never propagate.
468
+ - The default `AnsiRenderer` paints a double-buffered surface
469
+ to the terminal and chooses one of three render modes:
470
+ - `off` — non-TTY (CI, captured pipes). Nothing is written.
471
+ - `single-line` — terminals below 80×24.
472
+ - `dashboard` — full multi-line surface.
473
+
474
+ Wiring an additional subscriber is one line:
475
+
476
+ ```ts
477
+ import { dashboard, AnsiRenderer } from './ui/render.js';
478
+ const renderer = new AnsiRenderer();
479
+ dashboard.subscribe(renderer);
480
+ ```
481
+
482
+ The agent emits `turn-start`, `tool-start`, `tool-finish`,
483
+ `tokens`, `status`, `log`, `mode`, and `done` events. Each
484
+ event is a tagged union so refactors stay type-safe.
485
+
486
+ ---
487
+
488
+ ## 🚀 Getting Started
489
+
490
+ ### 1. Prerequisites
491
+ Ensure you have **Node.js (v18+)** and **npm** installed. Fixo CLI connects to FreeLLMAPI, so you should have a running FreeLLMAPI server or access to a unified proxy endpoint.
492
+
493
+ ### 2. Installation
494
+ Clone the repository and install dependencies:
495
+ ```bash
496
+ git clone https://github.com/Abrar-Akhunji/FIXO_CLI.git
497
+ cd FIXO_CLI
498
+ npm install
499
+ ```
500
+
501
+ ### 3. Build the CLI
502
+ Compile the TypeScript code:
503
+ ```bash
504
+ npm run build
505
+ ```
506
+
507
+ ### 4. Configuration
508
+ Create a `.env` file at the root of your project:
509
+ ```env
510
+ # URL of your FreeLLMAPI instance
511
+ FREELLMAPI_URL=http://localhost:3001
512
+ # Your unified API key (retrieve from FreeLLMAPI Dashboard)
513
+ FREELLMAPI_KEY=your-unified-api-key-here
514
+ ```
515
+
516
+ ### 5. Run the CLI
517
+ Start Fixo CLI in dev mode or link it globally:
518
+ ```bash
519
+ # Run directly
520
+ npm run dev
521
+
522
+ # Or link globally to run 'fixo' from anywhere
523
+ npm link
524
+ fixo
525
+ ```
526
+
527
+ ---
528
+
529
+ ## 📄 License
530
+ This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,108 @@
1
+ /**
2
+ * HTTP client for the FreeLLMAPI proxy server.
3
+ * Supports both regular and streaming (SSE) chat completions.
4
+ * Includes retry with exponential backoff for transient errors.
5
+ */
6
+ import type { ChatMessage, ChatToolDefinition, ChatToolChoice, TokenUsage } from '../shared/types.js';
7
+ export interface ChatOptions {
8
+ tools?: ChatToolDefinition[];
9
+ tool_choice?: ChatToolChoice;
10
+ temperature?: number;
11
+ max_tokens?: number;
12
+ agent_task_type?: 'chat' | 'review' | 'mutation' | 'test-fix' | 'refactor' | 'investigation';
13
+ required_capabilities?: string[];
14
+ }
15
+ export interface ChatResult {
16
+ content: string | null;
17
+ tool_calls: Array<{
18
+ id: string;
19
+ type: 'function';
20
+ function: {
21
+ name: string;
22
+ arguments: string;
23
+ };
24
+ }> | null;
25
+ usage: TokenUsage;
26
+ model: string;
27
+ finish_reason: string | null;
28
+ }
29
+ export interface StreamChunk {
30
+ type: 'content' | 'thinking' | 'tool_call_start' | 'tool_call_delta' | 'done';
31
+ content?: string;
32
+ thinking?: string;
33
+ tool_call?: {
34
+ index: number;
35
+ id?: string;
36
+ function?: {
37
+ name?: string;
38
+ arguments?: string;
39
+ };
40
+ };
41
+ usage?: TokenUsage;
42
+ model?: string;
43
+ finish_reason?: string | null;
44
+ }
45
+ export declare enum ContentType {
46
+ TEXT = "text",
47
+ THINKING = "thinking"
48
+ }
49
+ export interface ContentChunk {
50
+ type: ContentType;
51
+ content: string;
52
+ }
53
+ export declare class ThinkTagParser {
54
+ private OPEN_TAG;
55
+ private CLOSE_TAG;
56
+ private _buffer;
57
+ private _in_think_tag;
58
+ get in_think_mode(): boolean;
59
+ feed(content: string): Generator<ContentChunk>;
60
+ private _parse_outside_think;
61
+ private _parse_inside_think;
62
+ flush(): ContentChunk | null;
63
+ }
64
+ export declare class HttpError extends Error {
65
+ status: number;
66
+ constructor(status: number, message: string);
67
+ }
68
+ export declare class AgentClient {
69
+ private baseUrl;
70
+ private apiKey;
71
+ private verbose;
72
+ constructor(apiKey: string, apiUrl?: string, verbose?: boolean);
73
+ private resolveDirectConfig;
74
+ /**
75
+ * Maps a model id to the provider that will actually serve the
76
+ * request — used as the key for `providerCooldown` tracking. The
77
+ * `freellmapi` sentinel covers the proxy path; everything else
78
+ * routes through a direct provider.
79
+ */
80
+ private getProviderId;
81
+ chat(messages: ChatMessage[], model: string, options?: ChatOptions): Promise<ChatResult>;
82
+ private executeSingleChatStreamAttempt;
83
+ chatStream(messages: ChatMessage[], model: string, options?: ChatOptions): AsyncGenerator<StreamChunk>;
84
+ /**
85
+ * Streaming chat with autonomous mid-stream resume.
86
+ *
87
+ * If the underlying `chatStream` throws *after* at least one chunk
88
+ * has been yielded, the resume engine inspects the partial response,
89
+ * appends a "continue from here" payload to the working message list,
90
+ * and starts a fresh streaming attempt. The consumer sees a single
91
+ * continuous `AsyncGenerator<StreamChunk>` — the resume is invisible.
92
+ *
93
+ * The engine respects:
94
+ * - `maxResumeAttempts` (default 3) — additional attempts beyond
95
+ * this throw `StreamResumeExhaustedError`.
96
+ * - `isMidStreamResumable` — user aborts and 4xx are never resumed.
97
+ * - Cuts inside a tool call — the partial text up to the tool call
98
+ * boundary is preserved, but the call itself cannot be resumed.
99
+ *
100
+ * The method is *additive* and does not change the existing
101
+ * `chatStream` contract. Callers opt in by switching to this entry
102
+ * point (see `SingleAgent.streamResponse`).
103
+ */
104
+ chatStreamWithResume(messages: ChatMessage[], model: string, options?: ChatOptions, maxResumeAttempts?: number): AsyncGenerator<StreamChunk, void, void>;
105
+ getEmbedding(text: string, model?: string): Promise<number[]>;
106
+ ping(): Promise<boolean>;
107
+ }
108
+ //# sourceMappingURL=agent-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-client.d.ts","sourceRoot":"","sources":["../../src/agent/agent-client.ts"],"names":[],"mappings":"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"}