savant-code 0.0.17 → 0.0.19

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 (1591) hide show
  1. package/.agents/skills/coding-csharp/SKILL.md +82 -0
  2. package/.agents/skills/coding-go/SKILL.md +76 -0
  3. package/.agents/skills/coding-java/SKILL.md +79 -0
  4. package/.agents/skills/coding-python/SKILL.md +87 -0
  5. package/.agents/skills/coding-rust/SKILL.md +83 -0
  6. package/.agents/skills/coding-typescript/SKILL.md +90 -0
  7. package/.agents/skills/release-workflow/SKILL.md +69 -0
  8. package/.bun-version +1 -0
  9. package/.commandcode/settings.json +11 -0
  10. package/.commandcode/taste/workflow/taste.md +6 -0
  11. package/.env.example +95 -0
  12. package/.gitattributes +43 -0
  13. package/.githooks/pre-push +35 -0
  14. package/.github/workflows/build-release-binaries.yml +163 -0
  15. package/.markdownlint.json +42 -0
  16. package/.markdownlintignore +21 -0
  17. package/.prettierignore +9 -0
  18. package/.prettierrc +23 -0
  19. package/.savantignore +3 -0
  20. package/AGENTS.md +97 -0
  21. package/ARCHITECTURE.md +241 -0
  22. package/CHANGELOG.md +2631 -0
  23. package/CONTRIBUTING.md +88 -0
  24. package/ECHO-freebuff.md +22 -0
  25. package/ECHO.md +722 -0
  26. package/FREEREADME.md +35 -0
  27. package/LEARNINGS.md +345 -0
  28. package/NOTICE +15 -0
  29. package/README.md +550 -348
  30. package/README.zh-CN.md +479 -0
  31. package/SECURITY.md +8 -0
  32. package/VERSION +1 -0
  33. package/WINDOWS.md +238 -0
  34. package/agents/README.md +41 -0
  35. package/agents/base-chat.ts +44 -0
  36. package/agents/basher.ts +107 -0
  37. package/agents/browser-use/browser-use.test.ts +211 -0
  38. package/agents/browser-use/browser-use.ts +259 -0
  39. package/agents/constants.ts +1 -0
  40. package/agents/context-pruner.ts +1076 -0
  41. package/agents/detective/detective.ts +168 -0
  42. package/agents/editor/best-of-n/best-of-n-selector2.ts +148 -0
  43. package/agents/editor/best-of-n/editor-implementor-gpt-5.ts +7 -0
  44. package/agents/editor/best-of-n/editor-implementor-opus.ts +7 -0
  45. package/agents/editor/best-of-n/editor-implementor.ts +196 -0
  46. package/agents/editor/best-of-n/editor-multi-prompt.ts +265 -0
  47. package/agents/file-explorer/directory-lister.ts +92 -0
  48. package/agents/file-explorer/glob-matcher.ts +96 -0
  49. package/agents/forge/forge.ts +146 -0
  50. package/agents/librarian/librarian.test.ts +289 -0
  51. package/agents/librarian/librarian.ts +158 -0
  52. package/agents/package.json +11 -0
  53. package/agents/recorder/recorder.ts +104 -0
  54. package/agents/researcher/researcher-docs.ts +32 -0
  55. package/agents/researcher/researcher-web.ts +40 -0
  56. package/agents/savant/savant-analyze.ts +11 -0
  57. package/agents/savant/savant-free-deepseek-flash.ts +13 -0
  58. package/agents/savant/savant-free-deepseek.ts +13 -0
  59. package/agents/savant/savant-free-evals.ts +9 -0
  60. package/agents/savant/savant-free-glm.ts +13 -0
  61. package/agents/savant/savant-free-kimi.ts +13 -0
  62. package/agents/savant/savant-free-mimo-pro.ts +13 -0
  63. package/agents/savant/savant-free-mimo.ts +13 -0
  64. package/agents/savant/savant-free-minimax-m3.ts +13 -0
  65. package/agents/savant/savant-free.ts +9 -0
  66. package/agents/savant/savant-scaffold.ts +15 -0
  67. package/agents/savant/savant.ts +706 -0
  68. package/agents/scout/scout.ts +305 -0
  69. package/agents/scribe/scribe.ts +54 -0
  70. package/agents/thinker/thinker-gemini.ts +26 -0
  71. package/agents/thinker/thinker-gpt.ts +21 -0
  72. package/agents/thinker/thinker-with-files-gemini.ts +66 -0
  73. package/agents/thinker/thinker.ts +99 -0
  74. package/agents/tmux-cli.ts +724 -0
  75. package/agents/tsconfig.json +12 -0
  76. package/agents/types/agent-definition.ts +1 -0
  77. package/agents/types/secret-agent-definition.ts +103 -0
  78. package/agents/types/tools.ts +465 -0
  79. package/agents/types/util-types.ts +167 -0
  80. package/agents/verifier/verifier.ts +80 -0
  81. package/art/savant-redesign.md +7 -0
  82. package/art/savant-text-graf.md +5 -0
  83. package/art/savant_ascii.md +88 -0
  84. package/assets/banner.png +0 -0
  85. package/bun.lock +1979 -0
  86. package/bunfig.toml +7 -0
  87. package/cli/README.md +111 -0
  88. package/cli/bunfig.toml +2 -0
  89. package/cli/cli_exit +1 -0
  90. package/cli/package.json +66 -0
  91. package/cli/release/README.md +348 -0
  92. package/{index.js → cli/release/index.js} +0 -0
  93. package/cli/release/package.json +56 -0
  94. package/cli/release-core/README.md +20 -0
  95. package/cli/release-core/package.json +4 -0
  96. package/cli/release-core/prepare-package.js +31 -0
  97. package/cli/release-staging/README.md +70 -0
  98. package/cli/release-staging/index.js +42 -0
  99. package/cli/release-staging/package.json +43 -0
  100. package/cli/scripts/build-binary.ts +469 -0
  101. package/cli/scripts/clean.ts +16 -0
  102. package/cli/scripts/prebuild-agents.ts +250 -0
  103. package/cli/scripts/release.ts +103 -0
  104. package/cli/scripts/smoke-binary.ts +280 -0
  105. package/cli/scripts/test-sdk-file-hooks.sh +87 -0
  106. package/cli/scripts/validate-cli-with-tmux.sh +43 -0
  107. package/cli/src/__tests__/README.md +256 -0
  108. package/cli/src/__tests__/bash-mode.test.ts +442 -0
  109. package/cli/src/__tests__/cli-args.test.ts +241 -0
  110. package/cli/src/__tests__/e2e/first-time-login.test.ts +173 -0
  111. package/cli/src/__tests__/e2e/logout-relogin-flow.test.ts +110 -0
  112. package/cli/src/__tests__/e2e/returning-user-auth.test.ts +111 -0
  113. package/cli/src/__tests__/e2e-cli.test.ts +185 -0
  114. package/cli/src/__tests__/helpers/mock-api-client.ts +64 -0
  115. package/cli/src/__tests__/helpers/terminal-watchdog-fixture.ts +50 -0
  116. package/cli/src/__tests__/home-directory-detection.test.ts +213 -0
  117. package/cli/src/__tests__/integration/agent-toolnames-validation.test.ts +63 -0
  118. package/cli/src/__tests__/integration/api-integration.test.ts +289 -0
  119. package/cli/src/__tests__/integration/credentials-storage.test.ts +453 -0
  120. package/cli/src/__tests__/integration/local-agents.test.ts +1187 -0
  121. package/cli/src/__tests__/integration/login-flow-instrumentation.test.ts +146 -0
  122. package/cli/src/__tests__/integration/login-polling-working.test.ts +287 -0
  123. package/cli/src/__tests__/integration/usage-refresh-on-completion.test.ts +177 -0
  124. package/cli/src/__tests__/integration-tmux.test.ts +186 -0
  125. package/cli/src/__tests__/mocks/hover-toggle-controller.ts +71 -0
  126. package/cli/src/__tests__/path-completion.test.ts +243 -0
  127. package/cli/src/__tests__/project-files-chat-id.test.ts +34 -0
  128. package/cli/src/__tests__/release/proxy-http-get.test.ts +670 -0
  129. package/cli/src/__tests__/release/wrapper-safety.test.ts +177 -0
  130. package/cli/src/__tests__/rerender-perf.integration.test.ts +283 -0
  131. package/cli/src/__tests__/terminal-reset-sequences.test.ts +36 -0
  132. package/cli/src/__tests__/terminal-watchdog.test.ts +114 -0
  133. package/cli/src/__tests__/test-utils.ts +266 -0
  134. package/cli/src/__tests__/tmux-poc.ts +150 -0
  135. package/cli/src/__tests__/unit/agent-mode-toggle.test.ts +138 -0
  136. package/cli/src/__tests__/unit/copy-button.test.tsx +188 -0
  137. package/cli/src/__tests__/unit/create-run-config.test.ts +77 -0
  138. package/cli/src/__tests__/unit/publish-confirmation.test.ts +70 -0
  139. package/cli/src/__tests__/unit/segmented-control.test.ts +167 -0
  140. package/cli/src/__tests__/utils/env.test.ts +210 -0
  141. package/cli/src/__tests__/utils/project-picker.test.ts +39 -0
  142. package/cli/src/agents/bundled-agents.generated.d.ts +14 -0
  143. package/cli/src/agents/bundled-agents.generated.ts +1962 -0
  144. package/cli/src/app.tsx +383 -0
  145. package/cli/src/chat.tsx +2008 -0
  146. package/cli/src/cli-args.ts +183 -0
  147. package/cli/src/commands/__tests__/bash-command.test.ts +449 -0
  148. package/cli/src/commands/__tests__/command-args.test.ts +325 -0
  149. package/cli/src/commands/__tests__/copy-conversation.test.ts +240 -0
  150. package/cli/src/commands/__tests__/dev-command.test.ts +128 -0
  151. package/cli/src/commands/__tests__/diagnostics-command.test.ts +35 -0
  152. package/cli/src/commands/__tests__/image.test.ts +99 -0
  153. package/cli/src/commands/__tests__/init.test.ts +479 -0
  154. package/cli/src/commands/__tests__/loop-command.test.ts +89 -0
  155. package/cli/src/commands/__tests__/new-command-rotates-chat.test.ts +87 -0
  156. package/cli/src/commands/__tests__/permissions-command.test.ts +93 -0
  157. package/cli/src/commands/__tests__/process-diagnostics.test.ts +54 -0
  158. package/cli/src/commands/__tests__/prompt-builders.test.ts +69 -0
  159. package/cli/src/commands/__tests__/registry-gating.test.ts +125 -0
  160. package/cli/src/commands/__tests__/rewind-command.test.ts +227 -0
  161. package/cli/src/commands/__tests__/router-connect-chatgpt.test.ts +72 -0
  162. package/cli/src/commands/__tests__/router-input.test.ts +302 -0
  163. package/cli/src/commands/__tests__/router-provider-setup.test.ts +180 -0
  164. package/cli/src/commands/__tests__/savant-free-command-aliases.test.ts +56 -0
  165. package/cli/src/commands/ads.ts +45 -0
  166. package/cli/src/commands/command-registry.ts +1112 -0
  167. package/cli/src/commands/copy-conversation.ts +386 -0
  168. package/cli/src/commands/goal.ts +87 -0
  169. package/cli/src/commands/health-command.ts +61 -0
  170. package/cli/src/commands/help.ts +13 -0
  171. package/cli/src/commands/image.ts +20 -0
  172. package/cli/src/commands/init.ts +121 -0
  173. package/cli/src/commands/loop.ts +182 -0
  174. package/cli/src/commands/process-diagnostics.ts +120 -0
  175. package/cli/src/commands/prompt-builders.ts +140 -0
  176. package/cli/src/commands/publish.ts +261 -0
  177. package/cli/src/commands/rewind.ts +130 -0
  178. package/cli/src/commands/router-utils.ts +80 -0
  179. package/cli/src/commands/router.ts +589 -0
  180. package/cli/src/commands/telemetry.ts +68 -0
  181. package/cli/src/commands/usage.ts +26 -0
  182. package/cli/src/components/__tests__/ad-banner.test.tsx +84 -0
  183. package/cli/src/components/__tests__/block-helpers.test.tsx +139 -0
  184. package/cli/src/components/__tests__/clickable.test.tsx +39 -0
  185. package/cli/src/components/__tests__/grid-layout.integration.test.tsx +365 -0
  186. package/cli/src/components/__tests__/grid-layout.test.tsx +1033 -0
  187. package/cli/src/components/__tests__/markdown-content.test.tsx +163 -0
  188. package/cli/src/components/__tests__/message-block.completion.test.tsx +104 -0
  189. package/cli/src/components/__tests__/message-block.streaming.test.tsx +78 -0
  190. package/cli/src/components/__tests__/message-with-agents.test.tsx +571 -0
  191. package/cli/src/components/__tests__/multiline-input.test.tsx +1129 -0
  192. package/cli/src/components/__tests__/selectable-list.test.ts +232 -0
  193. package/cli/src/components/__tests__/shimmer-text.test.tsx +32 -0
  194. package/cli/src/components/__tests__/status-indicator.test.tsx +184 -0
  195. package/cli/src/components/__tests__/user-error-banner.test.tsx +100 -0
  196. package/cli/src/components/ad-banner.tsx +351 -0
  197. package/cli/src/components/agent-checklist.tsx +385 -0
  198. package/cli/src/components/agent-mode-toggle.tsx +294 -0
  199. package/cli/src/components/ask-user/__tests__/multiple-choice-form.test.ts +432 -0
  200. package/cli/src/components/ask-user/__tests__/validation.test.ts +213 -0
  201. package/cli/src/components/ask-user/components/accordion-question.tsx +153 -0
  202. package/cli/src/components/ask-user/components/custom-answer-input.tsx +66 -0
  203. package/cli/src/components/ask-user/components/options-list.tsx +139 -0
  204. package/cli/src/components/ask-user/components/question-header.tsx +80 -0
  205. package/cli/src/components/ask-user/components/question-option.tsx +94 -0
  206. package/cli/src/components/ask-user/constants.ts +35 -0
  207. package/cli/src/components/ask-user/index.tsx +660 -0
  208. package/cli/src/components/ask-user/utils/validation.ts +85 -0
  209. package/cli/src/components/attachment-card.tsx +67 -0
  210. package/cli/src/components/blocks/agent-block-grid.tsx +58 -0
  211. package/cli/src/components/blocks/agent-branch-item.tsx +230 -0
  212. package/cli/src/components/blocks/agent-branch-wrapper.tsx +524 -0
  213. package/cli/src/components/blocks/agent-list-branch.tsx +77 -0
  214. package/cli/src/components/blocks/ask-user-branch.tsx +98 -0
  215. package/cli/src/components/blocks/block-helpers.tsx +257 -0
  216. package/cli/src/components/blocks/blocks-renderer.tsx +296 -0
  217. package/cli/src/components/blocks/content-with-markdown.tsx +34 -0
  218. package/cli/src/components/blocks/copy-button.tsx +106 -0
  219. package/cli/src/components/blocks/copyable-block.tsx +47 -0
  220. package/cli/src/components/blocks/image-block.tsx +128 -0
  221. package/cli/src/components/blocks/implementor-row.tsx +453 -0
  222. package/cli/src/components/blocks/markdown-content.tsx +360 -0
  223. package/cli/src/components/blocks/markdown-renderables.tsx +347 -0
  224. package/cli/src/components/blocks/single-block.tsx +210 -0
  225. package/cli/src/components/blocks/thinking-block.tsx +77 -0
  226. package/cli/src/components/blocks/tool-block-group.tsx +72 -0
  227. package/cli/src/components/blocks/tool-branch.tsx +196 -0
  228. package/cli/src/components/blocks/user-content-copy.tsx +171 -0
  229. package/cli/src/components/bottom-banner.tsx +145 -0
  230. package/cli/src/components/build-mode-buttons.tsx +110 -0
  231. package/cli/src/components/button.tsx +75 -0
  232. package/cli/src/components/chat-header.tsx +11 -0
  233. package/cli/src/components/chat-history-screen.tsx +417 -0
  234. package/cli/src/components/chat-input-bar.tsx +535 -0
  235. package/cli/src/components/chatgpt-connect-banner.tsx +234 -0
  236. package/cli/src/components/clickable.tsx +149 -0
  237. package/cli/src/components/collapse-button.tsx +36 -0
  238. package/cli/src/components/command-palette.tsx +166 -0
  239. package/cli/src/components/copy-button.tsx +219 -0
  240. package/cli/src/components/dialog.tsx +111 -0
  241. package/cli/src/components/elapsed-timer.tsx +56 -0
  242. package/cli/src/components/error-boundary.tsx +63 -0
  243. package/cli/src/components/feedback-container.tsx +206 -0
  244. package/cli/src/components/feedback-icon-button.tsx +89 -0
  245. package/cli/src/components/feedback-input-mode.tsx +352 -0
  246. package/cli/src/components/file-attachment-card.tsx +89 -0
  247. package/cli/src/components/grid-layout.tsx +83 -0
  248. package/cli/src/components/help-banner.tsx +136 -0
  249. package/cli/src/components/highlighted-text.tsx +52 -0
  250. package/cli/src/components/image-card.tsx +167 -0
  251. package/cli/src/components/image-thumbnail.tsx +112 -0
  252. package/cli/src/components/input-cursor.tsx +77 -0
  253. package/cli/src/components/input-mode-banner.tsx +70 -0
  254. package/cli/src/components/load-previous-button.tsx +48 -0
  255. package/cli/src/components/login-modal.tsx +450 -0
  256. package/cli/src/components/message-block.tsx +337 -0
  257. package/cli/src/components/message-footer.tsx +269 -0
  258. package/cli/src/components/message-with-agents.tsx +594 -0
  259. package/cli/src/components/mode-divider.tsx +43 -0
  260. package/cli/src/components/model-picker.tsx +371 -0
  261. package/cli/src/components/multiline-input.tsx +1276 -0
  262. package/cli/src/components/out-of-credits-banner.tsx +155 -0
  263. package/cli/src/components/pending-attachments-banner.tsx +125 -0
  264. package/cli/src/components/pending-bash-message.tsx +57 -0
  265. package/cli/src/components/progress-bar.tsx +81 -0
  266. package/cli/src/components/project-picker-screen.tsx +469 -0
  267. package/cli/src/components/provider-picker.tsx +139 -0
  268. package/cli/src/components/publish-confirmation.tsx +514 -0
  269. package/cli/src/components/publish-container.tsx +737 -0
  270. package/cli/src/components/raised-pill.tsx +89 -0
  271. package/cli/src/components/renderers/plan-box.tsx +70 -0
  272. package/cli/src/components/review-screen.tsx +114 -0
  273. package/cli/src/components/rewind-picker.tsx +276 -0
  274. package/cli/src/components/right-sidebar.tsx +296 -0
  275. package/cli/src/components/savant-free-active-session-summary.tsx +63 -0
  276. package/cli/src/components/savant-free-landing-screen.tsx +756 -0
  277. package/cli/src/components/savant-free-model-selector.tsx +815 -0
  278. package/cli/src/components/savant-free-referral-banner.tsx +423 -0
  279. package/cli/src/components/savant-free-superseded-screen.tsx +62 -0
  280. package/cli/src/components/savant-ui/animation/pulse.tsx +26 -0
  281. package/cli/src/components/savant-ui/animation/typewriter.tsx +40 -0
  282. package/cli/src/components/savant-ui/branding.tsx +59 -0
  283. package/cli/src/components/savant-ui/data-display/badge.tsx +52 -0
  284. package/cli/src/components/savant-ui/data-display/code-block.tsx +55 -0
  285. package/cli/src/components/savant-ui/data-display/key-value.tsx +34 -0
  286. package/cli/src/components/savant-ui/data-display/sparkline.tsx +42 -0
  287. package/cli/src/components/savant-ui/data-display/timeline.tsx +44 -0
  288. package/cli/src/components/savant-ui/data-display/tree-view.tsx +55 -0
  289. package/cli/src/components/savant-ui/echo/__tests__/sidebar-collapse.test.tsx +90 -0
  290. package/cli/src/components/savant-ui/echo/agent-stack.tsx +72 -0
  291. package/cli/src/components/savant-ui/echo/agent-status.tsx +66 -0
  292. package/cli/src/components/savant-ui/echo/fid-card.tsx +89 -0
  293. package/cli/src/components/savant-ui/echo/fid-list.tsx +68 -0
  294. package/cli/src/components/savant-ui/echo/loop-status-panel.tsx +64 -0
  295. package/cli/src/components/savant-ui/echo/perfection-loop.tsx +118 -0
  296. package/cli/src/components/savant-ui/echo/phase-indicator.tsx +68 -0
  297. package/cli/src/components/savant-ui/echo/phase-info.ts +72 -0
  298. package/cli/src/components/savant-ui/echo/token-meter.tsx +39 -0
  299. package/cli/src/components/savant-ui/feedback/alert.tsx +45 -0
  300. package/cli/src/components/savant-ui/feedback/cost-tracker.tsx +54 -0
  301. package/cli/src/components/savant-ui/feedback/progress-bar.tsx +71 -0
  302. package/cli/src/components/savant-ui/feedback/spinner.tsx +40 -0
  303. package/cli/src/components/savant-ui/icon-theme-keys.ts +67 -0
  304. package/cli/src/components/savant-ui/icon.tsx +53 -0
  305. package/cli/src/components/savant-ui/index.ts +75 -0
  306. package/cli/src/components/savant-ui/input/select.tsx +26 -0
  307. package/cli/src/components/savant-ui/input/toggle.tsx +28 -0
  308. package/cli/src/components/savant-ui/layout/grid.tsx +52 -0
  309. package/cli/src/components/savant-ui/layout/header.tsx +28 -0
  310. package/cli/src/components/savant-ui/navigation/stepper.tsx +41 -0
  311. package/cli/src/components/savant-ui/primitives/key-hint.tsx +45 -0
  312. package/cli/src/components/savant-ui/primitives/key-value-row.tsx +41 -0
  313. package/cli/src/components/savant-ui/primitives/panel.tsx +42 -0
  314. package/cli/src/components/savant-ui/primitives/separator.tsx +34 -0
  315. package/cli/src/components/savant-ui/primitives/sidebar-section.tsx +64 -0
  316. package/cli/src/components/savant-ui/primitives/spacer.tsx +13 -0
  317. package/cli/src/components/savant-ui/primitives/stack.tsx +35 -0
  318. package/cli/src/components/savant-ui/theme.ts +45 -0
  319. package/cli/src/components/scroll-to-bottom-button.tsx +34 -0
  320. package/cli/src/components/segmented-control.tsx +199 -0
  321. package/cli/src/components/selectable-list.tsx +249 -0
  322. package/cli/src/components/selected-chips.tsx +86 -0
  323. package/cli/src/components/separator.tsx +40 -0
  324. package/cli/src/components/session-ended-banner.tsx +183 -0
  325. package/cli/src/components/shimmer-text.tsx +254 -0
  326. package/cli/src/components/status-bar.tsx +279 -0
  327. package/cli/src/components/subscription-limit-banner.tsx +269 -0
  328. package/cli/src/components/suggested-prompts.tsx +184 -0
  329. package/cli/src/components/suggestion-menu.tsx +219 -0
  330. package/cli/src/components/terminal-command-display.tsx +152 -0
  331. package/cli/src/components/terminal-link.tsx +97 -0
  332. package/cli/src/components/text-attachment-card.tsx +77 -0
  333. package/cli/src/components/thinking.tsx +108 -0
  334. package/cli/src/components/toast.tsx +109 -0
  335. package/cli/src/components/tools/__tests__/apply-patch.test.tsx +97 -0
  336. package/cli/src/components/tools/__tests__/code-search.test.tsx +45 -0
  337. package/cli/src/components/tools/__tests__/gravity-index.test.ts +76 -0
  338. package/cli/src/components/tools/__tests__/render-ui.test.tsx +556 -0
  339. package/cli/src/components/tools/__tests__/run-terminal-command.test.ts +274 -0
  340. package/cli/src/components/tools/__tests__/tool-call-item.test.tsx +73 -0
  341. package/cli/src/components/tools/apply-patch.tsx +101 -0
  342. package/cli/src/components/tools/code-search.tsx +50 -0
  343. package/cli/src/components/tools/composio.tsx +140 -0
  344. package/cli/src/components/tools/diff-viewer.tsx +21 -0
  345. package/cli/src/components/tools/glob.tsx +66 -0
  346. package/cli/src/components/tools/gravity-index.tsx +80 -0
  347. package/cli/src/components/tools/list-directory.tsx +66 -0
  348. package/cli/src/components/tools/read-docs.tsx +36 -0
  349. package/cli/src/components/tools/read-files.tsx +97 -0
  350. package/cli/src/components/tools/read-subtree.tsx +40 -0
  351. package/cli/src/components/tools/read-url.tsx +32 -0
  352. package/cli/src/components/tools/registry.ts +131 -0
  353. package/cli/src/components/tools/render-ui.tsx +466 -0
  354. package/cli/src/components/tools/run-terminal-command.tsx +86 -0
  355. package/cli/src/components/tools/skill.tsx +26 -0
  356. package/cli/src/components/tools/str-replace.tsx +77 -0
  357. package/cli/src/components/tools/suggest-followups.tsx +373 -0
  358. package/cli/src/components/tools/task-completed.tsx +16 -0
  359. package/cli/src/components/tools/tool-call-item.tsx +163 -0
  360. package/cli/src/components/tools/types.ts +100 -0
  361. package/cli/src/components/tools/web-search.tsx +32 -0
  362. package/cli/src/components/tools/write-file.tsx +20 -0
  363. package/cli/src/components/tools/write-todos.tsx +105 -0
  364. package/cli/src/components/top-banner.tsx +187 -0
  365. package/cli/src/components/usage-banner.tsx +296 -0
  366. package/cli/src/components/user-error-banner.tsx +54 -0
  367. package/cli/src/components/validation-error-popover.tsx +191 -0
  368. package/cli/src/data/slash-commands.ts +306 -0
  369. package/cli/src/hooks/__tests__/holds-live-savant-free-slot.test.ts +49 -0
  370. package/cli/src/hooks/__tests__/run-outcome.test.ts +178 -0
  371. package/cli/src/hooks/__tests__/session-fetch-signal.test.ts +45 -0
  372. package/cli/src/hooks/__tests__/use-activity-query.test.ts +810 -0
  373. package/cli/src/hooks/__tests__/use-ask-user-bridge.test.ts +175 -0
  374. package/cli/src/hooks/__tests__/use-auth-query.test.ts +21 -0
  375. package/cli/src/hooks/__tests__/use-chat-messages-collapse.test.ts +96 -0
  376. package/cli/src/hooks/__tests__/use-connection-status.test.ts +112 -0
  377. package/cli/src/hooks/__tests__/use-directory-browser.test.ts +215 -0
  378. package/cli/src/hooks/__tests__/use-gravity-ad.test.ts +73 -0
  379. package/cli/src/hooks/__tests__/use-grid-layout.test.ts +346 -0
  380. package/cli/src/hooks/__tests__/use-input-history.test.ts +704 -0
  381. package/cli/src/hooks/__tests__/use-loop-scheduler.test.ts +247 -0
  382. package/cli/src/hooks/__tests__/use-path-tab-completion.test.ts +324 -0
  383. package/cli/src/hooks/__tests__/use-queue-controls.test.ts +68 -0
  384. package/cli/src/hooks/__tests__/use-searchable-list.test.ts +364 -0
  385. package/cli/src/hooks/__tests__/use-send-message-timer.test.ts +146 -0
  386. package/cli/src/hooks/__tests__/use-suggestion-engine-mention.test.ts +364 -0
  387. package/cli/src/hooks/__tests__/use-suggestion-engine.test.ts +485 -0
  388. package/cli/src/hooks/__tests__/use-terminal-focus.test.ts +66 -0
  389. package/cli/src/hooks/__tests__/use-terminal-layout.test.ts +675 -0
  390. package/cli/src/hooks/__tests__/use-timeout.test.ts +330 -0
  391. package/cli/src/hooks/__tests__/use-usage-query.test.ts +526 -0
  392. package/cli/src/hooks/__tests__/use-user-details-query.test.ts +225 -0
  393. package/cli/src/hooks/helpers/__tests__/send-message.test.ts +1851 -0
  394. package/cli/src/hooks/helpers/send-message.ts +553 -0
  395. package/cli/src/hooks/run-outcome.ts +64 -0
  396. package/cli/src/hooks/stream-state.ts +106 -0
  397. package/cli/src/hooks/use-activity-query.ts +696 -0
  398. package/cli/src/hooks/use-agent-validation.ts +80 -0
  399. package/cli/src/hooks/use-ask-user-bridge.ts +102 -0
  400. package/cli/src/hooks/use-auth-query.ts +261 -0
  401. package/cli/src/hooks/use-auth-state.ts +155 -0
  402. package/cli/src/hooks/use-chat-input.ts +87 -0
  403. package/cli/src/hooks/use-chat-keyboard.ts +325 -0
  404. package/cli/src/hooks/use-chat-messages.ts +273 -0
  405. package/cli/src/hooks/use-chat-state.ts +219 -0
  406. package/cli/src/hooks/use-chat-streaming.ts +270 -0
  407. package/cli/src/hooks/use-chat-ui.ts +136 -0
  408. package/cli/src/hooks/use-clipboard.ts +154 -0
  409. package/cli/src/hooks/use-connection-status.ts +156 -0
  410. package/cli/src/hooks/use-directory-browser.ts +88 -0
  411. package/cli/src/hooks/use-elapsed-time.ts +116 -0
  412. package/cli/src/hooks/use-event.ts +36 -0
  413. package/cli/src/hooks/use-exit-handler.ts +108 -0
  414. package/cli/src/hooks/use-fetch-login-url.ts +65 -0
  415. package/cli/src/hooks/use-fids.ts +48 -0
  416. package/cli/src/hooks/use-fingerprint.ts +64 -0
  417. package/cli/src/hooks/use-gravity-ad.ts +688 -0
  418. package/cli/src/hooks/use-grid-layout.ts +78 -0
  419. package/cli/src/hooks/use-input-history.ts +173 -0
  420. package/cli/src/hooks/use-login-keyboard-handlers.ts +74 -0
  421. package/cli/src/hooks/use-login-polling.ts +120 -0
  422. package/cli/src/hooks/use-logo.tsx +178 -0
  423. package/cli/src/hooks/use-loop-scheduler.ts +481 -0
  424. package/cli/src/hooks/use-message-queue.ts +301 -0
  425. package/cli/src/hooks/use-now.ts +20 -0
  426. package/cli/src/hooks/use-path-tab-completion.ts +88 -0
  427. package/cli/src/hooks/use-publish-mutation.ts +60 -0
  428. package/cli/src/hooks/use-queue-controls.ts +70 -0
  429. package/cli/src/hooks/use-queue-ui.ts +55 -0
  430. package/cli/src/hooks/use-savant-free-ctrl-c-exit.ts +23 -0
  431. package/cli/src/hooks/use-savant-free-session-progress.ts +34 -0
  432. package/cli/src/hooks/use-savant-free-session.ts +679 -0
  433. package/cli/src/hooks/use-savant-free-streak-query.ts +72 -0
  434. package/cli/src/hooks/use-scaffold-confirm.ts +62 -0
  435. package/cli/src/hooks/use-scaffold-revert-subscriber.ts +66 -0
  436. package/cli/src/hooks/use-scroll-management.ts +182 -0
  437. package/cli/src/hooks/use-searchable-list.ts +99 -0
  438. package/cli/src/hooks/use-send-message.ts +1038 -0
  439. package/cli/src/hooks/use-sheen-animation.tsx +88 -0
  440. package/cli/src/hooks/use-subscription-query.ts +106 -0
  441. package/cli/src/hooks/use-suggestion-engine.ts +793 -0
  442. package/cli/src/hooks/use-terminal-breakpoints.ts +77 -0
  443. package/cli/src/hooks/use-terminal-dimensions.ts +50 -0
  444. package/cli/src/hooks/use-terminal-focus.ts +146 -0
  445. package/cli/src/hooks/use-terminal-layout.ts +189 -0
  446. package/cli/src/hooks/use-theme.tsx +140 -0
  447. package/cli/src/hooks/use-timeout.ts +88 -0
  448. package/cli/src/hooks/use-toast.ts +114 -0
  449. package/cli/src/hooks/use-update-preference.ts +77 -0
  450. package/cli/src/hooks/use-usage-monitor.ts +59 -0
  451. package/cli/src/hooks/use-usage-query.ts +138 -0
  452. package/cli/src/hooks/use-user-details-query.ts +104 -0
  453. package/cli/src/hooks/use-why-did-you-update.ts +178 -0
  454. package/cli/src/index.tsx +484 -0
  455. package/cli/src/init/__tests__/init-direnv.test.ts +597 -0
  456. package/cli/src/init/init-app.ts +54 -0
  457. package/cli/src/init/init-direnv.ts +133 -0
  458. package/cli/src/login/constants.ts +71 -0
  459. package/cli/src/login/login-flow.ts +224 -0
  460. package/cli/src/login/plain-login.ts +107 -0
  461. package/cli/src/login/utils.ts +183 -0
  462. package/cli/src/native/ripgrep.ts +82 -0
  463. package/cli/src/polyfills/bun-strip-ansi.ts +12 -0
  464. package/cli/src/pre-init/load-dev-env.ts +98 -0
  465. package/cli/src/pre-init/tree-sitter-wasm.ts +98 -0
  466. package/cli/src/project-files.ts +102 -0
  467. package/cli/src/state/__tests__/chat-store-focus.test.ts +22 -0
  468. package/cli/src/state/__tests__/feedback-store.test.ts +260 -0
  469. package/cli/src/state/chat-history-store.ts +27 -0
  470. package/cli/src/state/chat-store.ts +791 -0
  471. package/cli/src/state/feedback-store.ts +162 -0
  472. package/cli/src/state/gateway-catalog-store.ts +45 -0
  473. package/cli/src/state/login-store.ts +150 -0
  474. package/cli/src/state/message-block-store.ts +143 -0
  475. package/cli/src/state/model-picker-store.ts +67 -0
  476. package/cli/src/state/provider-picker-store.ts +53 -0
  477. package/cli/src/state/publish-store.ts +141 -0
  478. package/cli/src/state/review-store.ts +24 -0
  479. package/cli/src/state/rewind-picker-store.ts +74 -0
  480. package/cli/src/state/savant-free-model-store.ts +59 -0
  481. package/cli/src/state/savant-free-session-store.ts +32 -0
  482. package/cli/src/test-setup.ts +20 -0
  483. package/cli/src/testing/env.ts +46 -0
  484. package/cli/src/types/chat-state.ts +15 -0
  485. package/cli/src/types/chat.ts +242 -0
  486. package/cli/src/types/contracts/send-message.ts +16 -0
  487. package/cli/src/types/env.ts +107 -0
  488. package/cli/src/types/function-params.ts +35 -0
  489. package/cli/src/types/opentui-augmentation.d.ts +11 -0
  490. package/cli/src/types/react19-compat.d.ts +19 -0
  491. package/cli/src/types/savant-free-session.ts +17 -0
  492. package/cli/src/types/store.ts +112 -0
  493. package/cli/src/types/theme-system.ts +194 -0
  494. package/cli/src/types/utils.ts +3 -0
  495. package/cli/src/utils/__tests__/activity-tracker.test.ts +440 -0
  496. package/cli/src/utils/__tests__/agent-display.test.ts +139 -0
  497. package/cli/src/utils/__tests__/analytics-client.test.ts +311 -0
  498. package/cli/src/utils/__tests__/anonymous-id.test.ts +74 -0
  499. package/cli/src/utils/__tests__/arrays.test.ts +264 -0
  500. package/cli/src/utils/__tests__/bash-context-processor.test.ts +38 -0
  501. package/cli/src/utils/__tests__/block-processor.test.ts +690 -0
  502. package/cli/src/utils/__tests__/chat-history.test.ts +247 -0
  503. package/cli/src/utils/__tests__/chat-input-key-intercept.test.ts +40 -0
  504. package/cli/src/utils/__tests__/chat-layout.test.ts +32 -0
  505. package/cli/src/utils/__tests__/chat-meta.test.ts +160 -0
  506. package/cli/src/utils/__tests__/chatgpt-oauth.test.ts +50 -0
  507. package/cli/src/utils/__tests__/clipboard.test.ts +998 -0
  508. package/cli/src/utils/__tests__/code-search-summary.test.ts +84 -0
  509. package/cli/src/utils/__tests__/collapse-helpers.test.ts +1272 -0
  510. package/cli/src/utils/__tests__/error-handling.test.ts +611 -0
  511. package/cli/src/utils/__tests__/feedback-helpers.test.ts +452 -0
  512. package/cli/src/utils/__tests__/feedback-submission.test.ts +26 -0
  513. package/cli/src/utils/__tests__/fetch-usage.test.ts +355 -0
  514. package/cli/src/utils/__tests__/fingerprint.test.ts +147 -0
  515. package/cli/src/utils/__tests__/format-elapsed-time.test.ts +103 -0
  516. package/cli/src/utils/__tests__/format-timeout.test.ts +87 -0
  517. package/cli/src/utils/__tests__/image-dimensions.test.ts +243 -0
  518. package/cli/src/utils/__tests__/image-processor.test.ts +250 -0
  519. package/cli/src/utils/__tests__/implementor-helpers.test.ts +1457 -0
  520. package/cli/src/utils/__tests__/keyboard-actions.test.ts +686 -0
  521. package/cli/src/utils/__tests__/layout-helpers.test.ts +36 -0
  522. package/cli/src/utils/__tests__/lazy-response-ads.test.ts +167 -0
  523. package/cli/src/utils/__tests__/logger-sanitize-secrets.test.ts +80 -0
  524. package/cli/src/utils/__tests__/markdown-renderer.test.tsx +518 -0
  525. package/cli/src/utils/__tests__/markdown-streaming.test.ts +64 -0
  526. package/cli/src/utils/__tests__/message-block-helpers.test.ts +1197 -0
  527. package/cli/src/utils/__tests__/message-updater.test.ts +659 -0
  528. package/cli/src/utils/__tests__/ollama-onboarding.test.ts +239 -0
  529. package/cli/src/utils/__tests__/openrouter-models.test.ts +295 -0
  530. package/cli/src/utils/__tests__/osc-timeout-scenarios.test.ts +172 -0
  531. package/cli/src/utils/__tests__/pending-attachments.test.ts +278 -0
  532. package/cli/src/utils/__tests__/provider-setup.test.ts +183 -0
  533. package/cli/src/utils/__tests__/run-state-storage.test.ts +741 -0
  534. package/cli/src/utils/__tests__/savant-code-api.test.ts +634 -0
  535. package/cli/src/utils/__tests__/savant-free-instance-owner.test.ts +69 -0
  536. package/cli/src/utils/__tests__/savant-free-model-navigation.test.ts +104 -0
  537. package/cli/src/utils/__tests__/savant-free-premium-reset.test.ts +81 -0
  538. package/cli/src/utils/__tests__/savant-free-referral-cache.test.ts +67 -0
  539. package/cli/src/utils/__tests__/savant-free-session-display.test.ts +23 -0
  540. package/cli/src/utils/__tests__/savant-free-streak-line.test.ts +76 -0
  541. package/cli/src/utils/__tests__/sdk-event-handlers.test.ts +615 -0
  542. package/cli/src/utils/__tests__/send-message-helpers.test.ts +1875 -0
  543. package/cli/src/utils/__tests__/send-message-timer.test.ts +68 -0
  544. package/cli/src/utils/__tests__/settings.test.ts +138 -0
  545. package/cli/src/utils/__tests__/strings.test.ts +210 -0
  546. package/cli/src/utils/__tests__/syntax-theme.test.ts +91 -0
  547. package/cli/src/utils/__tests__/terminal-color-detection.test.ts +425 -0
  548. package/cli/src/utils/__tests__/terminal-enter-detection.test.ts +77 -0
  549. package/cli/src/utils/__tests__/text-layout.test.ts +82 -0
  550. package/cli/src/utils/__tests__/think-tag-parser.test.ts +159 -0
  551. package/cli/src/utils/__tests__/trace-writer.test.ts +193 -0
  552. package/cli/src/utils/__tests__/trim-chat-logs.test.ts +79 -0
  553. package/cli/src/utils/__tests__/usage-banner-state.test.ts +298 -0
  554. package/cli/src/utils/__tests__/validation-error-formatting.test.ts +126 -0
  555. package/cli/src/utils/__tests__/write-file-atomic.test.ts +130 -0
  556. package/cli/src/utils/active-run.ts +22 -0
  557. package/cli/src/utils/activity-tracker.ts +66 -0
  558. package/cli/src/utils/agent-display.ts +87 -0
  559. package/cli/src/utils/agent-helpers.ts +47 -0
  560. package/cli/src/utils/ai-message-id.ts +12 -0
  561. package/cli/src/utils/analytics.ts +407 -0
  562. package/cli/src/utils/anonymous-id.ts +87 -0
  563. package/cli/src/utils/arrays.ts +117 -0
  564. package/cli/src/utils/auth.ts +258 -0
  565. package/cli/src/utils/bash-context-processor.ts +47 -0
  566. package/cli/src/utils/bash-messages.ts +108 -0
  567. package/cli/src/utils/block-margins.ts +35 -0
  568. package/cli/src/utils/block-operations.ts +548 -0
  569. package/cli/src/utils/block-processor.ts +212 -0
  570. package/cli/src/utils/chat-history.ts +265 -0
  571. package/cli/src/utils/chat-input-key-intercept.ts +52 -0
  572. package/cli/src/utils/chat-layout.ts +44 -0
  573. package/cli/src/utils/chat-meta.ts +101 -0
  574. package/cli/src/utils/chat-scroll-accel.ts +151 -0
  575. package/cli/src/utils/chatgpt-oauth.ts +350 -0
  576. package/cli/src/utils/clipboard-image.ts +610 -0
  577. package/cli/src/utils/clipboard.ts +307 -0
  578. package/cli/src/utils/code-search-summary.ts +69 -0
  579. package/cli/src/utils/collapse-helpers.ts +265 -0
  580. package/cli/src/utils/config-dir.ts +30 -0
  581. package/cli/src/utils/constants.ts +159 -0
  582. package/cli/src/utils/create-event-handler-state.ts +79 -0
  583. package/cli/src/utils/create-run-config.ts +160 -0
  584. package/cli/src/utils/db-storage.ts +254 -0
  585. package/cli/src/utils/detect-shell.ts +112 -0
  586. package/cli/src/utils/directory-browser.ts +74 -0
  587. package/cli/src/utils/engagement.ts +55 -0
  588. package/cli/src/utils/env.ts +109 -0
  589. package/cli/src/utils/error-handling.ts +246 -0
  590. package/cli/src/utils/error-messages.ts +84 -0
  591. package/cli/src/utils/feedback-helpers.ts +115 -0
  592. package/cli/src/utils/feedback-submission.ts +24 -0
  593. package/cli/src/utils/fetch-usage.ts +75 -0
  594. package/cli/src/utils/fid-loader.ts +135 -0
  595. package/cli/src/utils/fingerprint.ts +263 -0
  596. package/cli/src/utils/finish-logic.ts +154 -0
  597. package/cli/src/utils/format-elapsed-time.ts +33 -0
  598. package/cli/src/utils/format-session-units.ts +6 -0
  599. package/cli/src/utils/format-timeout.ts +28 -0
  600. package/cli/src/utils/format-validation-errors-for-message.ts +85 -0
  601. package/cli/src/utils/git.ts +17 -0
  602. package/cli/src/utils/glyphs.ts +188 -0
  603. package/cli/src/utils/helpers.ts +47 -0
  604. package/cli/src/utils/image-display.ts +73 -0
  605. package/cli/src/utils/image-handler.ts +336 -0
  606. package/cli/src/utils/image-processor.ts +133 -0
  607. package/cli/src/utils/image-thumbnail.ts +83 -0
  608. package/cli/src/utils/implementor-helpers.ts +817 -0
  609. package/cli/src/utils/input-modes.ts +196 -0
  610. package/cli/src/utils/keyboard-actions.ts +394 -0
  611. package/cli/src/utils/keypad-keys.ts +47 -0
  612. package/cli/src/utils/layout-helpers.ts +36 -0
  613. package/cli/src/utils/lazy-response-ads.ts +1 -0
  614. package/cli/src/utils/loading-phrases.ts +68 -0
  615. package/cli/src/utils/local-agent-registry.ts +611 -0
  616. package/cli/src/utils/log-shipper.ts +116 -0
  617. package/cli/src/utils/logger.ts +435 -0
  618. package/cli/src/utils/markdown-renderer.tsx +1315 -0
  619. package/cli/src/utils/math.ts +3 -0
  620. package/cli/src/utils/message-block-helpers.ts +667 -0
  621. package/cli/src/utils/message-history.ts +153 -0
  622. package/cli/src/utils/message-tree-utils.ts +67 -0
  623. package/cli/src/utils/message-updater.ts +279 -0
  624. package/cli/src/utils/ollama-onboarding.ts +215 -0
  625. package/cli/src/utils/open-file.ts +144 -0
  626. package/cli/src/utils/open-url.ts +63 -0
  627. package/cli/src/utils/openrouter-models.ts +945 -0
  628. package/cli/src/utils/path-completion.ts +111 -0
  629. package/cli/src/utils/path-helpers.ts +38 -0
  630. package/cli/src/utils/pending-attachments.ts +363 -0
  631. package/cli/src/utils/post-processing.ts +99 -0
  632. package/cli/src/utils/project-picker.ts +12 -0
  633. package/cli/src/utils/provider-setup.ts +259 -0
  634. package/cli/src/utils/recent-projects.ts +156 -0
  635. package/cli/src/utils/renderer-cleanup.ts +142 -0
  636. package/cli/src/utils/response-ad-positions.ts +1 -0
  637. package/cli/src/utils/rewind.ts +175 -0
  638. package/cli/src/utils/run-state-storage.ts +480 -0
  639. package/cli/src/utils/savant-code-api.ts +686 -0
  640. package/cli/src/utils/savant-code-client.ts +217 -0
  641. package/cli/src/utils/savant-free-agent-selection.ts +12 -0
  642. package/cli/src/utils/savant-free-exit.ts +24 -0
  643. package/cli/src/utils/savant-free-instance-owner.ts +66 -0
  644. package/cli/src/utils/savant-free-model-navigation.ts +46 -0
  645. package/cli/src/utils/savant-free-premium-reset.ts +52 -0
  646. package/cli/src/utils/savant-free-referral-cache.ts +35 -0
  647. package/cli/src/utils/savant-free-session-display.ts +21 -0
  648. package/cli/src/utils/savant-free-streak-line.ts +67 -0
  649. package/cli/src/utils/sdk-event-handlers.ts +587 -0
  650. package/cli/src/utils/send-message-helpers.ts +199 -0
  651. package/cli/src/utils/send-message-timer.ts +110 -0
  652. package/cli/src/utils/settings.ts +388 -0
  653. package/cli/src/utils/skill-registry.ts +100 -0
  654. package/cli/src/utils/spawn-agent-matcher.ts +56 -0
  655. package/cli/src/utils/status-indicator-state.ts +100 -0
  656. package/cli/src/utils/stream-chunk-processor.ts +65 -0
  657. package/cli/src/utils/strings.ts +272 -0
  658. package/cli/src/utils/subscription.ts +31 -0
  659. package/cli/src/utils/syntax-highlighter.tsx +19 -0
  660. package/cli/src/utils/syntax-theme.ts +131 -0
  661. package/cli/src/utils/terminal-color-detection.ts +472 -0
  662. package/cli/src/utils/terminal-enter-detection.ts +63 -0
  663. package/cli/src/utils/terminal-images.ts +223 -0
  664. package/cli/src/utils/terminal-reset-sequences.ts +33 -0
  665. package/cli/src/utils/terminal-title.ts +113 -0
  666. package/cli/src/utils/terminal-watchdog.ts +246 -0
  667. package/cli/src/utils/text-layout.ts +153 -0
  668. package/cli/src/utils/theme-config.ts +143 -0
  669. package/cli/src/utils/theme-system.ts +1265 -0
  670. package/cli/src/utils/think-tag-parser.ts +105 -0
  671. package/cli/src/utils/trace-writer.ts +147 -0
  672. package/cli/src/utils/ui-constants.ts +69 -0
  673. package/cli/src/utils/usage-banner-state.ts +146 -0
  674. package/cli/src/utils/validation-error-formatting.ts +92 -0
  675. package/cli/src/utils/validation-error-helpers.ts +19 -0
  676. package/cli/src/utils/version.ts +23 -0
  677. package/cli/src/utils/word-wrap-utils.ts +53 -0
  678. package/cli/src/utils/write-file-atomic.ts +82 -0
  679. package/cli/src/utils/yield-to-event-loop.ts +9 -0
  680. package/cli/tsconfig.json +26 -0
  681. package/coding-standards/csharp.md +77 -0
  682. package/coding-standards/go.md +71 -0
  683. package/coding-standards/java.md +74 -0
  684. package/coding-standards/python.md +82 -0
  685. package/coding-standards/rust.md +78 -0
  686. package/coding-standards/typescript.md +85 -0
  687. package/coding-standards/x402.md +227 -0
  688. package/common/README.md +41 -0
  689. package/common/common_exit +1 -0
  690. package/common/package.json +45 -0
  691. package/common/src/__tests__/agent-validation.test.ts +861 -0
  692. package/common/src/__tests__/agents.test.ts +16 -0
  693. package/common/src/__tests__/dynamic-agent-template-schema.test.ts +420 -0
  694. package/common/src/__tests__/env-ci.test.ts +167 -0
  695. package/common/src/__tests__/env-process.test.ts +145 -0
  696. package/common/src/__tests__/free-agents.test.ts +269 -0
  697. package/common/src/__tests__/handlesteps-parsing.test.ts +247 -0
  698. package/common/src/__tests__/model-config.test.ts +41 -0
  699. package/common/src/__tests__/project-file-tree.test.ts +82 -0
  700. package/common/src/__tests__/response-ad-positions.test.ts +78 -0
  701. package/common/src/__tests__/savant-free-models.test.ts +460 -0
  702. package/common/src/__tests__/savant-free-referral-tiers.test.ts +122 -0
  703. package/common/src/__tests__/user-state.test.ts +30 -0
  704. package/common/src/actions.ts +215 -0
  705. package/common/src/analytics-core.ts +73 -0
  706. package/common/src/analytics.ts +96 -0
  707. package/common/src/api-keys/constants.ts +26 -0
  708. package/common/src/browser-actions.ts +414 -0
  709. package/common/src/constants/agents.ts +250 -0
  710. package/common/src/constants/analytics-events.ts +318 -0
  711. package/common/src/constants/anthropic.ts +72 -0
  712. package/common/src/constants/byok.ts +2 -0
  713. package/common/src/constants/chatgpt-oauth.ts +85 -0
  714. package/common/src/constants/composio.ts +34 -0
  715. package/common/src/constants/feedback.ts +18 -0
  716. package/common/src/constants/free-agents.ts +347 -0
  717. package/common/src/constants/gemini.ts +6 -0
  718. package/common/src/constants/grant-priorities.ts +13 -0
  719. package/common/src/constants/hosts.ts +6 -0
  720. package/common/src/constants/images.ts +53 -0
  721. package/common/src/constants/index.ts +8 -0
  722. package/common/src/constants/knowledge.ts +46 -0
  723. package/common/src/constants/limits.ts +23 -0
  724. package/common/src/constants/model-config.ts +469 -0
  725. package/common/src/constants/paths.ts +69 -0
  726. package/common/src/constants/savant-code-config.ts +12 -0
  727. package/common/src/constants/savant-free-gemini-thinker.ts +30 -0
  728. package/common/src/constants/savant-free-models.ts +855 -0
  729. package/common/src/constants/savant-free-referral-tiers.ts +169 -0
  730. package/common/src/constants/skills.ts +60 -0
  731. package/common/src/constants/subscription-plans.ts +54 -0
  732. package/common/src/constants/ui.ts +25 -0
  733. package/common/src/env-ci.ts +36 -0
  734. package/common/src/env-process.ts +95 -0
  735. package/common/src/env-schema.ts +42 -0
  736. package/common/src/env.ts +69 -0
  737. package/common/src/mcp/client.ts +308 -0
  738. package/common/src/old-constants.ts +10 -0
  739. package/common/src/project-file-tree.ts +361 -0
  740. package/common/src/reddit-capi.ts +175 -0
  741. package/common/src/schemas/feedback.ts +57 -0
  742. package/common/src/schemas/logs.ts +66 -0
  743. package/common/src/templates/agent-validation.ts +436 -0
  744. package/common/src/templates/initial-agents-dir/LICENSE +202 -0
  745. package/common/src/templates/initial-agents-dir/README.md +314 -0
  746. package/common/src/templates/initial-agents-dir/examples/01-basic-diff-reviewer.ts +17 -0
  747. package/common/src/templates/initial-agents-dir/examples/02-intermediate-git-committer.ts +78 -0
  748. package/common/src/templates/initial-agents-dir/examples/03-advanced-file-explorer.ts +76 -0
  749. package/common/src/templates/initial-agents-dir/my-custom-agent.ts +40 -0
  750. package/common/src/templates/initial-agents-dir/package.json +6 -0
  751. package/common/src/templates/initial-agents-dir/skills/README.md +66 -0
  752. package/common/src/templates/initial-agents-dir/skills/example-skill/SKILL.md +29 -0
  753. package/common/src/templates/initial-agents-dir/types/agent-definition.ts +484 -0
  754. package/common/src/templates/initial-agents-dir/types/tools.ts +452 -0
  755. package/common/src/templates/initial-agents-dir/types/util-types.ts +167 -0
  756. package/common/src/testing/TESTING_PATTERNS.md +353 -0
  757. package/common/src/testing/errors.ts +33 -0
  758. package/common/src/testing/fixtures/agent-runtime.ts +325 -0
  759. package/common/src/testing/impl/agent-runtime.ts +6 -0
  760. package/common/src/testing/index.ts +84 -0
  761. package/common/src/testing/mock-modules.ts +54 -0
  762. package/common/src/testing/mock-types.ts +123 -0
  763. package/common/src/testing/mocks/analytics.ts +262 -0
  764. package/common/src/testing/mocks/child-process.ts +93 -0
  765. package/common/src/testing/mocks/crypto.ts +218 -0
  766. package/common/src/testing/mocks/database.ts +337 -0
  767. package/common/src/testing/mocks/filesystem.ts +166 -0
  768. package/common/src/testing/mocks/index.ts +83 -0
  769. package/common/src/testing/mocks/logger.ts +136 -0
  770. package/common/src/testing/mocks/stream.ts +314 -0
  771. package/common/src/testing/mocks/timers.ts +132 -0
  772. package/common/src/testing/mocks/tree-sitter.ts +127 -0
  773. package/common/src/testing/setup.ts +282 -0
  774. package/common/src/testing-env-ci.ts +15 -0
  775. package/common/src/testing-env-process.ts +78 -0
  776. package/common/src/tools/__tests__/compile-tool-definitions.test.ts +34 -0
  777. package/common/src/tools/__tests__/thought-session.test.ts +256 -0
  778. package/common/src/tools/compile-tool-definitions.ts +194 -0
  779. package/common/src/tools/constants.ts +132 -0
  780. package/common/src/tools/list.ts +193 -0
  781. package/common/src/tools/params/__tests__/coerce-to-array.test.ts +237 -0
  782. package/common/src/tools/params/__tests__/sequential-thinking-coercion.test.ts +98 -0
  783. package/common/src/tools/params/tool/add-message.ts +36 -0
  784. package/common/src/tools/params/tool/add-subgoal.ts +57 -0
  785. package/common/src/tools/params/tool/apply-patch.ts +110 -0
  786. package/common/src/tools/params/tool/ask-user.ts +181 -0
  787. package/common/src/tools/params/tool/browser-logs.ts +85 -0
  788. package/common/src/tools/params/tool/code-search.ts +144 -0
  789. package/common/src/tools/params/tool/composio.ts +131 -0
  790. package/common/src/tools/params/tool/create-plan.ts +80 -0
  791. package/common/src/tools/params/tool/end-turn.ts +54 -0
  792. package/common/src/tools/params/tool/find-files.ts +60 -0
  793. package/common/src/tools/params/tool/glob.ts +74 -0
  794. package/common/src/tools/params/tool/gravity-index.ts +92 -0
  795. package/common/src/tools/params/tool/list-directory.ts +58 -0
  796. package/common/src/tools/params/tool/lookup-agent-info.ts +37 -0
  797. package/common/src/tools/params/tool/propose-str-replace.ts +103 -0
  798. package/common/src/tools/params/tool/propose-write-file.ts +71 -0
  799. package/common/src/tools/params/tool/read-docs.ts +85 -0
  800. package/common/src/tools/params/tool/read-files.ts +61 -0
  801. package/common/src/tools/params/tool/read-subtree.ts +83 -0
  802. package/common/src/tools/params/tool/read-url.ts +81 -0
  803. package/common/src/tools/params/tool/render-ui.ts +203 -0
  804. package/common/src/tools/params/tool/run-file-change-hooks.ts +57 -0
  805. package/common/src/tools/params/tool/run-readonly-command.ts +76 -0
  806. package/common/src/tools/params/tool/run-terminal-command.ts +185 -0
  807. package/common/src/tools/params/tool/sequential-thinking.ts +110 -0
  808. package/common/src/tools/params/tool/set-messages.ts +42 -0
  809. package/common/src/tools/params/tool/set-output.ts +62 -0
  810. package/common/src/tools/params/tool/set-scaffold-complete.ts +51 -0
  811. package/common/src/tools/params/tool/skill.ts +56 -0
  812. package/common/src/tools/params/tool/spawn-agent-inline.ts +57 -0
  813. package/common/src/tools/params/tool/spawn-agents.ts +154 -0
  814. package/common/src/tools/params/tool/str-replace.ts +107 -0
  815. package/common/src/tools/params/tool/suggest-followups.ts +94 -0
  816. package/common/src/tools/params/tool/task-completed.ts +58 -0
  817. package/common/src/tools/params/tool/think-deeply.ts +53 -0
  818. package/common/src/tools/params/tool/transition-phase.ts +57 -0
  819. package/common/src/tools/params/tool/update-subgoal.ts +89 -0
  820. package/common/src/tools/params/tool/web-search.ts +73 -0
  821. package/common/src/tools/params/tool/write-file.ts +71 -0
  822. package/common/src/tools/params/tool/write-todos.ts +67 -0
  823. package/common/src/tools/params/utils.ts +152 -0
  824. package/common/src/tools/safety-registry.ts +274 -0
  825. package/common/src/tools/safety.ts +42 -0
  826. package/common/src/tools/sequential-thinking.ts +340 -0
  827. package/common/src/tools/utils.ts +25 -0
  828. package/common/src/types/__tests__/dynamic-agent-template.test.ts +20 -0
  829. package/common/src/types/agent-template.ts +223 -0
  830. package/common/src/types/api/agents/publish.ts +63 -0
  831. package/common/src/types/contracts/agent-runtime.ts +84 -0
  832. package/common/src/types/contracts/analytics.ts +12 -0
  833. package/common/src/types/contracts/bigquery.ts +55 -0
  834. package/common/src/types/contracts/billing.ts +46 -0
  835. package/common/src/types/contracts/client.ts +52 -0
  836. package/common/src/types/contracts/database.ts +104 -0
  837. package/common/src/types/contracts/env.ts +203 -0
  838. package/common/src/types/contracts/llm.ts +183 -0
  839. package/common/src/types/contracts/logger.ts +29 -0
  840. package/common/src/types/contracts/logs.ts +34 -0
  841. package/common/src/types/contracts/trace.ts +21 -0
  842. package/common/src/types/dynamic-agent-template.ts +290 -0
  843. package/common/src/types/filesystem.ts +10 -0
  844. package/common/src/types/function-params.ts +35 -0
  845. package/common/src/types/grant.ts +20 -0
  846. package/common/src/types/gravity-index.ts +170 -0
  847. package/common/src/types/json.ts +26 -0
  848. package/common/src/types/mcp.ts +26 -0
  849. package/common/src/types/messages/content-part.ts +59 -0
  850. package/common/src/types/messages/data-content.ts +14 -0
  851. package/common/src/types/messages/provider-metadata.ts +10 -0
  852. package/common/src/types/messages/savant-code-message.ts +55 -0
  853. package/common/src/types/organization.ts +118 -0
  854. package/common/src/types/print-mode.ts +167 -0
  855. package/common/src/types/publisher.ts +67 -0
  856. package/common/src/types/savant-free-session.ts +295 -0
  857. package/common/src/types/savant-free-streak.ts +6 -0
  858. package/common/src/types/session-state.ts +237 -0
  859. package/common/src/types/skill.ts +56 -0
  860. package/common/src/types/source.ts +11 -0
  861. package/common/src/types/spawn.ts +13 -0
  862. package/common/src/types/subscription.ts +68 -0
  863. package/common/src/types/usage.ts +16 -0
  864. package/common/src/types/util.ts +3 -0
  865. package/common/src/util/__tests__/analytics-dispatcher.test.ts +145 -0
  866. package/common/src/util/__tests__/analytics-log.test.ts +110 -0
  867. package/common/src/util/__tests__/analytics-sampling.test.ts +131 -0
  868. package/common/src/util/__tests__/axiom-only-log.test.ts +64 -0
  869. package/common/src/util/__tests__/engagement-tracker.test.ts +115 -0
  870. package/common/src/util/__tests__/error-abort.test.ts +822 -0
  871. package/common/src/util/__tests__/error-api-details.test.ts +190 -0
  872. package/common/src/util/__tests__/format-code-search.test.ts +60 -0
  873. package/common/src/util/__tests__/log-mirror.test.ts +31 -0
  874. package/common/src/util/__tests__/messages.test.ts +1118 -0
  875. package/common/src/util/__tests__/partial-json-delta.test.ts +505 -0
  876. package/common/src/util/__tests__/paths.test.ts +289 -0
  877. package/common/src/util/__tests__/promise.test.ts +334 -0
  878. package/common/src/util/__tests__/protocol-config.test.ts +119 -0
  879. package/common/src/util/__tests__/rate-limit.test.ts +28 -0
  880. package/common/src/util/__tests__/reddit-savant-free-retention.test.ts +120 -0
  881. package/common/src/util/__tests__/savant-free-streak.test.ts +142 -0
  882. package/common/src/util/__tests__/saxy.test.ts +1008 -0
  883. package/common/src/util/__tests__/string.test.ts +238 -0
  884. package/common/src/util/__tests__/zoned-time.test.ts +88 -0
  885. package/common/src/util/agent-file-utils.ts +110 -0
  886. package/common/src/util/agent-id-parsing.ts +147 -0
  887. package/common/src/util/agent-name-normalization.ts +39 -0
  888. package/common/src/util/analytics-dispatcher.ts +81 -0
  889. package/common/src/util/analytics-log.ts +84 -0
  890. package/common/src/util/analytics-sampling.ts +225 -0
  891. package/common/src/util/array.ts +63 -0
  892. package/common/src/util/axiom-only-log.ts +79 -0
  893. package/common/src/util/cache-debug.ts +191 -0
  894. package/common/src/util/credentials.ts +12 -0
  895. package/common/src/util/currency.ts +25 -0
  896. package/common/src/util/dates.ts +81 -0
  897. package/common/src/util/engagement-tracker.ts +133 -0
  898. package/common/src/util/error.ts +580 -0
  899. package/common/src/util/file.ts +363 -0
  900. package/common/src/util/format-code-search.ts +115 -0
  901. package/common/src/util/lazy-response-ads.ts +93 -0
  902. package/common/src/util/log-data.ts +57 -0
  903. package/common/src/util/log-mirror.ts +34 -0
  904. package/common/src/util/lru-cache.ts +67 -0
  905. package/common/src/util/messages.ts +600 -0
  906. package/common/src/util/model-utils.ts +25 -0
  907. package/common/src/util/object.ts +137 -0
  908. package/common/src/util/param-helpers.ts +49 -0
  909. package/common/src/util/partial-json-delta.ts +100 -0
  910. package/common/src/util/paths.ts +194 -0
  911. package/common/src/util/promise.ts +87 -0
  912. package/common/src/util/protocol-config.ts +141 -0
  913. package/common/src/util/random.ts +15 -0
  914. package/common/src/util/rate-limit.ts +56 -0
  915. package/common/src/util/reddit-capi-events.ts +40 -0
  916. package/common/src/util/reddit-savant-free-retention.ts +69 -0
  917. package/common/src/util/response-ad-positions.ts +54 -0
  918. package/common/src/util/savant-free-privacy.ts +78 -0
  919. package/common/src/util/savant-free-streak.ts +153 -0
  920. package/common/src/util/saxy.ts +739 -0
  921. package/common/src/util/skills.ts +32 -0
  922. package/common/src/util/stop-sequence.ts +60 -0
  923. package/common/src/util/string.ts +419 -0
  924. package/common/src/util/system-info.ts +51 -0
  925. package/common/src/util/type-narrowing.ts +125 -0
  926. package/common/src/util/xml.ts +17 -0
  927. package/common/src/util/zod-schema.ts +80 -0
  928. package/common/src/util/zoned-time.ts +136 -0
  929. package/common/src/utils/ask-user-bridge.ts +47 -0
  930. package/common/tsconfig.json +8 -0
  931. package/database.db +0 -0
  932. package/dev/LEARNINGS.md +309 -0
  933. package/dev/fids/.gitkeep +0 -0
  934. package/dev/fids/archive/.gitkeep +0 -0
  935. package/dev/fids/archive/FID-2026-0802-008-sdk-package-audit-client-run-run-state.md +238 -0
  936. package/dev/fids/archive/FID-2026-0803-001-echo-enforcement-layer-drift.md +404 -0
  937. package/dev/fids/archive/FID-2026-0803-002-llm-providers-database-audit.md +348 -0
  938. package/dev/fids/archive/FID-2026-0803-003-sdk-impl-common-util-audit.md +277 -0
  939. package/dev/fids/archive/FID-2026-0803-004-checkpoint-rewind.md +181 -0
  940. package/dev/fids/archive/FID-2026-0803-005-quality-scan-hygiene-fixes.md +265 -0
  941. package/dev/fids/archive/FID-2026-0803-006-code-map-audit-hygiene.md +243 -0
  942. package/dev/fids/archive/FID-2026-0803-007-evals-benchmark-audit-hygiene.md +296 -0
  943. package/dev/fids/archive/FID-2026-0803-009-echo-enforcement-doc-drift.md +112 -0
  944. package/dev/fids/archive/FID-2026-0803-010-database-llm-providers-low-fixes.md +255 -0
  945. package/dev/fids/archive/FID-2026-0803-011-build-artifact-hygiene.md +169 -0
  946. package/dev/fids/archive/FID-2026-0803-012-release-readiness-audit.md +162 -0
  947. package/dev/fids/archive/FID-2026-0803-013-agent-roster-over-reporting.md +146 -0
  948. package/dev/fids/archive/FID-2026-0803-014-freebuff-to-savant-rebrand-sweep.md +185 -0
  949. package/dev/nova/reports/2026-08-02-top-25-ai-inference-providers-competitive-intel.md +213 -0
  950. package/dev/nova/specs/echo-v0.1.2-freebuff.md +391 -0
  951. package/dev/nova/specs/goal-loop-feature-spec.md +245 -0
  952. package/dev/nova/specs/launch-strategy-research-prompt.md +88 -0
  953. package/dev/releases/README.md +50 -0
  954. package/dev/scratchpad/.gitkeep +0 -0
  955. package/dev/session-summaries/.gitkeep +0 -0
  956. package/dev/session-summaries/2026-07-16-1255.md +128 -0
  957. package/dev/session-summaries/2026-07-17-1000.md +64 -0
  958. package/dev/session-summaries/2026-07-19-eslint-zero-tolerance-cleanup.md +145 -0
  959. package/dev/session-summaries/2026-07-19-fid-026-debugging-and-rename.md +79 -0
  960. package/dev/session-summaries/2026-07-19-fid-026-phase-b-rebrand.md +76 -0
  961. package/dev/session-summaries/2026-07-19-fid-027-clean-break.md +47 -0
  962. package/dev/session-summaries/2026-07-19-pre-push-house-cleaning.md +100 -0
  963. package/dev/session-summaries/2026-07-19-v0.0.2-release-session.md +229 -0
  964. package/dev/session-summaries/2026-07-20-1805-fid-loop-closure.md +95 -0
  965. package/dev/session-summaries/2026-07-22-1800.md +52 -0
  966. package/dev/session-summaries/2026-07-23-fsm-optimization.md +88 -0
  967. package/dev/session-summaries/2026-07-25-0000.md +80 -0
  968. package/dev/session-summaries/2026-07-25-1200-context-compaction.md +154 -0
  969. package/dev/session-summaries/2026-07-25-1600-layer4-reactive-compact.md +96 -0
  970. package/dev/session-summaries/2026-07-25-1700-dev-folder-audit.md +161 -0
  971. package/dev/session-summaries/2026-07-25-2000-benchmark-v2-filters.md +147 -0
  972. package/dev/session-summaries/2026-07-25-2000.md +77 -0
  973. package/dev/session-summaries/2026-07-27-0000.md +63 -0
  974. package/dev/session-summaries/2026-07-28-history-capture-handoff.md +206 -0
  975. package/dev/session-summaries/2026-07-31-freebuff-echo-compliance-remediation.md +156 -0
  976. package/dev/session-summaries/2026-07-31-pre-launch-optimization-execution.md +46 -0
  977. package/dev/session-summaries/2026-0721-1800.md +44 -0
  978. package/dev/session-summaries/2026-08-01-sidebar-folded-startup.md +159 -0
  979. package/dev/session-summaries/2026-08-02-0.0.15-release-closeout.md +32 -0
  980. package/dev/session-summaries/2026-08-02-repository-hygiene.md +51 -0
  981. package/dev/session-summaries/2026-08-03-agent-roster-fix-and-ready-check-closeout.md +73 -0
  982. package/dev/session-summaries/2026-08-03-build-artifact-hygiene-closeout.md +40 -0
  983. package/dev/session-summaries/2026-08-03-code-map-audit-closeout.md +44 -0
  984. package/dev/session-summaries/2026-08-03-database-llm-providers-low-fixes-closeout.md +47 -0
  985. package/dev/session-summaries/2026-08-03-echo-enforcement-doc-drift-closeout.md +43 -0
  986. package/dev/session-summaries/2026-08-03-evals-benchmark-audit-closeout.md +50 -0
  987. package/dev/session-summaries/2026-08-03-quality-scan-hygiene-fixes-closeout.md +57 -0
  988. package/dev/session-summaries/2026-08-03-quality-session-checkpoint-rewind-closeout.md +56 -0
  989. package/dev/session-summaries/2026-08-03-release-readiness-audit-closeout.md +60 -0
  990. package/dev/test-prompts/archive/0.0.2-final-pass.md +209 -0
  991. package/dev/test-prompts/archive/agent-capabilities-full-test.md +361 -0
  992. package/dev/test-prompts/archive/agent-capabilities-test.md +471 -0
  993. package/dev/test-prompts/archive/comprehensive-az-system-test-v1.md +441 -0
  994. package/dev/test-prompts/archive/comprehensive-az-system-test-v2-report.md +377 -0
  995. package/dev/test-prompts/archive/comprehensive-az-system-test-v2.md +544 -0
  996. package/dev/test-prompts/archive/comprehensive-az-system-test.md +396 -0
  997. package/dev/test-prompts/archive/comprehensive-az-test-final.md +900 -0
  998. package/dev/test-prompts/archive/comprehensive-az-test-v11.md +141 -0
  999. package/dev/test-prompts/archive/comprehensive-az-test-v12.md +29 -0
  1000. package/dev/test-prompts/archive/comprehensive-az-test-v6.md +631 -0
  1001. package/dev/test-prompts/archive/comprehensive-az-test-v7.md +625 -0
  1002. package/dev/test-prompts/archive/fid-007-ability-confirmation.md +248 -0
  1003. package/dev/test-prompts/archive/fid-2026-0801-006-thinker-tool-call-boundary.md +86 -0
  1004. package/dev/test-prompts/archive/fid-2026-0801-007-child-tool-set-fallback-cli.md +101 -0
  1005. package/dev/test-prompts/archive/fid-2026-0801-008-provider-tool-call-accumulation-cli.md +111 -0
  1006. package/dev/test-prompts/archive/fid-2026-0801-012-thinker-state-output-cli.md +134 -0
  1007. package/dev/test-prompts/archive/gemini-deep-research-sidebar-highlight.md +598 -0
  1008. package/dev/test-prompts/archive/goal-loop-cli-test.md +142 -0
  1009. package/dev/test-prompts/archive/rebrand-qa.md +258 -0
  1010. package/dev/test-prompts/archive/release-az-test-fid-085.md +306 -0
  1011. package/dev/test-prompts/archive/release-az-test-fid-087.md +219 -0
  1012. package/dev/test-prompts/archive/release-az-test-fid-2026-0726-001.md +270 -0
  1013. package/dev/test-prompts/archive/release-az-test-fid-2026-0728-008.md +213 -0
  1014. package/dev/test-prompts/archive/release-az-test-fid-2026-0728-launch-tracks.md +253 -0
  1015. package/dev/test-prompts/archive/release-az-test-fid-2026-0731-pre-launch.md +102 -0
  1016. package/docs/AI Coding Agents Market Research.md +516 -0
  1017. package/docs/Agent Harness Feature Pairing Research.md +166 -0
  1018. package/docs/CLI Agent Inference Backend Research.md +446 -0
  1019. package/docs/Codebuff Rebranding And Migration Plan.md +418 -0
  1020. package/docs/ECHO-EVOLUTION-ARCH.md +77 -0
  1021. package/docs/Harness Engineering for Coding Agents Research.md +196 -0
  1022. package/docs/Launch Plan Review and Optimization.md +178 -0
  1023. package/docs/OpenTUI Sidebar Bug Fix.md +245 -0
  1024. package/docs/SAVANT-VERSIONING.md +31 -0
  1025. package/docs/Savant Code Launch Strategy.md +270 -0
  1026. package/docs/Savant-Code Business And Backend Research.md +222 -0
  1027. package/docs/agents-and-tools.md +217 -0
  1028. package/docs/cloudflare-llms-full.md +9623 -0
  1029. package/docs/design/OpenTUI Terminal Visualization Guide.md +471 -0
  1030. package/docs/design/database-architecture.md +217 -0
  1031. package/docs/design/deep-research-report.md +295 -0
  1032. package/docs/design/thinker-sequentialthinking-regression-diagnostic.md +366 -0
  1033. package/docs/discord-server-design.md +167 -0
  1034. package/docs/gravity-integration-starter.md +202 -0
  1035. package/docs/launch/hn-first-comment.md +44 -0
  1036. package/docs/launch/hn-post.md +35 -0
  1037. package/docs/launch/incident-response.md +88 -0
  1038. package/docs/launch/landing/index.html +233 -0
  1039. package/docs/launch/mastodon-thread.md +52 -0
  1040. package/docs/launch/newsletter-pitch.md +49 -0
  1041. package/docs/launch/twitter-thread.md +62 -0
  1042. package/docs/privacy.md +186 -0
  1043. package/docs/reports/Savant-Code Benchmark Specification.md +206 -0
  1044. package/docs/reports/Thinker Agent Architecture Research.md +284 -0
  1045. package/docs/reports/adoptable-features-2026-07-25.md +604 -0
  1046. package/docs/reports/adoptable-features-master.md +480 -0
  1047. package/docs/reports/codebuff-discord-feedback.md +43 -0
  1048. package/docs/reports/feature-parity-report.md +810 -0
  1049. package/docs/reports/repos/AionUi.md +75 -0
  1050. package/docs/reports/repos/OpenHands.md +76 -0
  1051. package/docs/reports/repos/SWE-agent.md +82 -0
  1052. package/docs/reports/repos/agno.md +89 -0
  1053. package/docs/reports/repos/aider.md +74 -0
  1054. package/docs/reports/repos/cline.md +83 -0
  1055. package/docs/reports/repos/codex.md +83 -0
  1056. package/docs/reports/repos/gemini-cli.md +92 -0
  1057. package/docs/reports/repos/goose.md +65 -0
  1058. package/docs/reports/repos/gpt-pilot.md +52 -0
  1059. package/docs/reports/repos/hermes-agent.md +101 -0
  1060. package/docs/reports/repos/kilocode.md +79 -0
  1061. package/docs/reports/repos/openclaude.md +99 -0
  1062. package/docs/reports/repos/openclaw.md +100 -0
  1063. package/docs/reports/repos/opencode-dev.md +78 -0
  1064. package/docs/reports/repos/zero.md +123 -0
  1065. package/docs/reports/savant-code-benchmark-v2-2026-08-03.md +76 -0
  1066. package/docs/savant-code-modes.md +338 -0
  1067. package/docs/testing.md +51 -0
  1068. package/docs/visual-mockup-neon-slate.txt +41 -0
  1069. package/eslint.config.js +211 -0
  1070. package/evals/README.md +45 -0
  1071. package/evals/benchmark/README.md +449 -0
  1072. package/evals/benchmark/agent-runner.ts +217 -0
  1073. package/evals/benchmark/analyze-task-scores.ts +496 -0
  1074. package/evals/benchmark/eval-codebuff-hard.json +3343 -0
  1075. package/evals/benchmark/eval-codebuff.json +3193 -0
  1076. package/evals/benchmark/eval-codebuff2.json +2494 -0
  1077. package/evals/benchmark/eval-manifold-hard.json +2525 -0
  1078. package/evals/benchmark/eval-manifold.json +1675 -0
  1079. package/evals/benchmark/eval-manifold2.json +1945 -0
  1080. package/evals/benchmark/eval-plane-hard.json +4372 -0
  1081. package/evals/benchmark/eval-plane.json +2028 -0
  1082. package/evals/benchmark/eval-plane2.json +3517 -0
  1083. package/evals/benchmark/eval-saleor-hard.json +6081 -0
  1084. package/evals/benchmark/eval-saleor.json +1829 -0
  1085. package/evals/benchmark/eval-saleor2.json +3273 -0
  1086. package/evals/benchmark/eval-task-generator.ts +158 -0
  1087. package/evals/benchmark/filter-supplemental-files.ts +225 -0
  1088. package/evals/benchmark/format-output.ts +215 -0
  1089. package/evals/benchmark/gen-evals.ts +294 -0
  1090. package/evals/benchmark/gen-repo-eval.ts +71 -0
  1091. package/evals/benchmark/judge.ts +308 -0
  1092. package/evals/benchmark/lessons-extractor.ts +247 -0
  1093. package/evals/benchmark/main-hard-tasks.ts +49 -0
  1094. package/evals/benchmark/main-single-eval.ts +23 -0
  1095. package/evals/benchmark/main.ts +29 -0
  1096. package/evals/benchmark/meta-analyzer.ts +329 -0
  1097. package/evals/benchmark/pick-commits.ts +627 -0
  1098. package/evals/benchmark/run-benchmark.ts +657 -0
  1099. package/evals/benchmark/runners/claude.ts +176 -0
  1100. package/evals/benchmark/runners/codex.ts +143 -0
  1101. package/evals/benchmark/runners/index.ts +4 -0
  1102. package/evals/benchmark/runners/opencode.ts +253 -0
  1103. package/evals/benchmark/runners/runner.ts +13 -0
  1104. package/evals/benchmark/runners/savant.ts +148 -0
  1105. package/evals/benchmark/setup-test-repo.ts +314 -0
  1106. package/evals/benchmark/trace-analyzer.ts +257 -0
  1107. package/evals/benchmark/trace-utils.ts +80 -0
  1108. package/evals/benchmark/types.ts +83 -0
  1109. package/evals/bunfig.toml +4 -0
  1110. package/evals/logger.ts +87 -0
  1111. package/evals/package.json +31 -0
  1112. package/evals/subagents/test-repo-utils.ts +131 -0
  1113. package/evals/tsconfig.json +13 -0
  1114. package/evals/v2/README.md +149 -0
  1115. package/evals/v2/reports/report.json +261 -0
  1116. package/evals/v2/reports/report.md +15 -0
  1117. package/evals/v2/schema/task.schema.json +172 -0
  1118. package/evals/v2/src/cli.ts +216 -0
  1119. package/evals/v2/src/golden.ts +73 -0
  1120. package/evals/v2/src/harness.ts +293 -0
  1121. package/evals/v2/src/metrics.ts +326 -0
  1122. package/evals/v2/src/registry.ts +179 -0
  1123. package/evals/v2/src/reports.ts +126 -0
  1124. package/evals/v2/src/runner.ts +143 -0
  1125. package/evals/v2/src/runners/savant.ts +105 -0
  1126. package/evals/v2/src/sandbox.ts +42 -0
  1127. package/evals/v2/src/sandboxes/docker.ts +49 -0
  1128. package/evals/v2/src/sandboxes/tempdir.ts +120 -0
  1129. package/evals/v2/src/schema.ts +83 -0
  1130. package/evals/v2/src/trace.ts +186 -0
  1131. package/evals/v2/src/verify.ts +199 -0
  1132. package/evals/v2/tasks/error_recovery/env-fault/calculator.js +11 -0
  1133. package/evals/v2/tasks/error_recovery/env-fault/calculator.test.js +15 -0
  1134. package/evals/v2/tasks/error_recovery/env-fault/golden.patch +15 -0
  1135. package/evals/v2/tasks/error_recovery/env-fault/task.yaml +20 -0
  1136. package/evals/v2/tasks/multi_agent_orchestration/options-contract/app.js +5 -0
  1137. package/evals/v2/tasks/multi_agent_orchestration/options-contract/app.test.js +7 -0
  1138. package/evals/v2/tasks/multi_agent_orchestration/options-contract/golden.patch +19 -0
  1139. package/evals/v2/tasks/multi_agent_orchestration/options-contract/greet.js +3 -0
  1140. package/evals/v2/tasks/multi_agent_orchestration/options-contract/orchestration.test.js +30 -0
  1141. package/evals/v2/tasks/multi_agent_orchestration/options-contract/task.yaml +29 -0
  1142. package/evals/v2/tasks/pure_coding/add-fix/add.js +3 -0
  1143. package/evals/v2/tasks/pure_coding/add-fix/add.test.js +11 -0
  1144. package/evals/v2/tasks/pure_coding/add-fix/golden.patch +7 -0
  1145. package/evals/v2/tasks/pure_coding/add-fix/task.yaml +20 -0
  1146. package/evals/v2/tasks/pure_coding/rename-greet/app.js +5 -0
  1147. package/evals/v2/tasks/pure_coding/rename-greet/app.test.js +32 -0
  1148. package/evals/v2/tasks/pure_coding/rename-greet/golden.patch +19 -0
  1149. package/evals/v2/tasks/pure_coding/rename-greet/greet.js +5 -0
  1150. package/evals/v2/tasks/pure_coding/rename-greet/task.yaml +21 -0
  1151. package/evals/v2/tests/golden.test.ts +104 -0
  1152. package/evals/v2/tests/harness.test.ts +204 -0
  1153. package/evals/v2/tests/metrics.test.ts +353 -0
  1154. package/evals/v2/tests/registry.test.ts +210 -0
  1155. package/evals/v2/tests/reports.test.ts +218 -0
  1156. package/evals/v2/tests/savant-runner.test.ts +218 -0
  1157. package/evals/v2/tests/schema.test.ts +127 -0
  1158. package/evals/v2/tests/tempdir-sandbox.test.ts +49 -0
  1159. package/evals/v2/tests/trace.test.ts +76 -0
  1160. package/evals/v2/tests/verify.test.ts +224 -0
  1161. package/license +202 -0
  1162. package/package.json +71 -44
  1163. package/packages/agent-runtime/README.md +46 -0
  1164. package/packages/agent-runtime/agent_exit +1 -0
  1165. package/packages/agent-runtime/bunfig.toml +2 -0
  1166. package/packages/agent-runtime/package.json +34 -0
  1167. package/packages/agent-runtime/src/__tests__/apply-patch-tool.test.ts +202 -0
  1168. package/packages/agent-runtime/src/__tests__/cost-aggregation.test.ts +397 -0
  1169. package/packages/agent-runtime/src/__tests__/generate-diffs-prompt.test.ts +107 -0
  1170. package/packages/agent-runtime/src/__tests__/get-file-reading-updates.test.ts +59 -0
  1171. package/packages/agent-runtime/src/__tests__/gravity-index-tool.test.ts +501 -0
  1172. package/packages/agent-runtime/src/__tests__/loop-agent-steps.test.ts +1512 -0
  1173. package/packages/agent-runtime/src/__tests__/main-prompt.test.ts +452 -0
  1174. package/packages/agent-runtime/src/__tests__/n-parameter.test.ts +984 -0
  1175. package/packages/agent-runtime/src/__tests__/process-file-block.test.ts +196 -0
  1176. package/packages/agent-runtime/src/__tests__/process-str-replace.test.ts +512 -0
  1177. package/packages/agent-runtime/src/__tests__/programmatic-tool-authorization.test.ts +219 -0
  1178. package/packages/agent-runtime/src/__tests__/prompt-caching-subagents.test.ts +749 -0
  1179. package/packages/agent-runtime/src/__tests__/prompts-schema-handling.test.ts +454 -0
  1180. package/packages/agent-runtime/src/__tests__/propose-tools.test.ts +824 -0
  1181. package/packages/agent-runtime/src/__tests__/read-docs-tool.test.ts +394 -0
  1182. package/packages/agent-runtime/src/__tests__/run-agent-step-prefill.test.ts +161 -0
  1183. package/packages/agent-runtime/src/__tests__/run-agent-step-tools.test.ts +612 -0
  1184. package/packages/agent-runtime/src/__tests__/run-programmatic-step.test.ts +1743 -0
  1185. package/packages/agent-runtime/src/__tests__/sandbox-generator.test.ts +154 -0
  1186. package/packages/agent-runtime/src/__tests__/spawn-agents-image-content.test.ts +354 -0
  1187. package/packages/agent-runtime/src/__tests__/spawn-agents-message-history.test.ts +280 -0
  1188. package/packages/agent-runtime/src/__tests__/spawn-agents-permissions.test.ts +664 -0
  1189. package/packages/agent-runtime/src/__tests__/stream-parser-abort.test.ts +250 -0
  1190. package/packages/agent-runtime/src/__tests__/stream-parser-reasoning.test.ts +194 -0
  1191. package/packages/agent-runtime/src/__tests__/subagent-streaming.test.ts +230 -0
  1192. package/packages/agent-runtime/src/__tests__/test-utils.ts +96 -0
  1193. package/packages/agent-runtime/src/__tests__/thinker-convergence-gate.test.ts +242 -0
  1194. package/packages/agent-runtime/src/__tests__/to-token-count-input-schema.test.ts +81 -0
  1195. package/packages/agent-runtime/src/__tests__/tool-executor-sandbox.test.ts +167 -0
  1196. package/packages/agent-runtime/src/__tests__/tool-stream-parser.test.ts +682 -0
  1197. package/packages/agent-runtime/src/__tests__/tool-validation-error.test.ts +1380 -0
  1198. package/packages/agent-runtime/src/__tests__/web-search-tool.test.ts +402 -0
  1199. package/packages/agent-runtime/src/__tests__/xml-tool-result-ordering.test.ts +264 -0
  1200. package/packages/agent-runtime/src/constants.ts +12 -0
  1201. package/packages/agent-runtime/src/context-compactor.ts +425 -0
  1202. package/packages/agent-runtime/src/find-files/__tests__/request-files-prompt.test.ts +190 -0
  1203. package/packages/agent-runtime/src/find-files/custom-file-picker-config.ts +61 -0
  1204. package/packages/agent-runtime/src/find-files/request-files-prompt.ts +462 -0
  1205. package/packages/agent-runtime/src/generate-diffs-prompt.ts +38 -0
  1206. package/packages/agent-runtime/src/get-file-reading-updates.ts +27 -0
  1207. package/packages/agent-runtime/src/llm-api/__tests__/gemini-with-fallbacks.test.ts +267 -0
  1208. package/packages/agent-runtime/src/llm-api/__tests__/serper-api.test.ts +297 -0
  1209. package/packages/agent-runtime/src/llm-api/claude.ts +7 -0
  1210. package/packages/agent-runtime/src/llm-api/context7-api.ts +290 -0
  1211. package/packages/agent-runtime/src/llm-api/gemini-with-fallbacks.ts +104 -0
  1212. package/packages/agent-runtime/src/llm-api/savant-code-web-api.ts +370 -0
  1213. package/packages/agent-runtime/src/llm-api/serper-api.ts +193 -0
  1214. package/packages/agent-runtime/src/main-prompt.ts +251 -0
  1215. package/packages/agent-runtime/src/mcp-constants.ts +12 -0
  1216. package/packages/agent-runtime/src/mcp.ts +83 -0
  1217. package/packages/agent-runtime/src/process-file-block.ts +118 -0
  1218. package/packages/agent-runtime/src/process-str-replace.ts +213 -0
  1219. package/packages/agent-runtime/src/prompt-agent-stream.ts +119 -0
  1220. package/packages/agent-runtime/src/run-agent-step.ts +1632 -0
  1221. package/packages/agent-runtime/src/run-programmatic-step.ts +633 -0
  1222. package/packages/agent-runtime/src/system-prompt/prompts.ts +215 -0
  1223. package/packages/agent-runtime/src/system-prompt/search-system-prompt.ts +89 -0
  1224. package/packages/agent-runtime/src/system-prompt/truncate-file-tree.ts +414 -0
  1225. package/packages/agent-runtime/src/templates/README.md +160 -0
  1226. package/packages/agent-runtime/src/templates/__tests__/agent-registry.test.ts +489 -0
  1227. package/packages/agent-runtime/src/templates/__tests__/strings.test.ts +465 -0
  1228. package/packages/agent-runtime/src/templates/agent-registry.ts +122 -0
  1229. package/packages/agent-runtime/src/templates/prompts.ts +189 -0
  1230. package/packages/agent-runtime/src/templates/strings.ts +292 -0
  1231. package/packages/agent-runtime/src/templates/types.ts +61 -0
  1232. package/packages/agent-runtime/src/tool-stream-parser.ts +260 -0
  1233. package/packages/agent-runtime/src/tools/filter-tool-set.ts +18 -0
  1234. package/packages/agent-runtime/src/tools/handlers/__tests__/glob.test.ts +331 -0
  1235. package/packages/agent-runtime/src/tools/handlers/__tests__/read-subtree.test.ts +346 -0
  1236. package/packages/agent-runtime/src/tools/handlers/__tests__/run-readonly-command.test.ts +284 -0
  1237. package/packages/agent-runtime/src/tools/handlers/handler-function-type.ts +77 -0
  1238. package/packages/agent-runtime/src/tools/handlers/list.ts +105 -0
  1239. package/packages/agent-runtime/src/tools/handlers/tool/__tests__/checkpoint-store.test.ts +394 -0
  1240. package/packages/agent-runtime/src/tools/handlers/tool/__tests__/skill.test.ts +98 -0
  1241. package/packages/agent-runtime/src/tools/handlers/tool/__tests__/write-file.test.ts +24 -0
  1242. package/packages/agent-runtime/src/tools/handlers/tool/add-message.ts +37 -0
  1243. package/packages/agent-runtime/src/tools/handlers/tool/add-subgoal.ts +30 -0
  1244. package/packages/agent-runtime/src/tools/handlers/tool/apply-patch.ts +120 -0
  1245. package/packages/agent-runtime/src/tools/handlers/tool/ask-user.ts +25 -0
  1246. package/packages/agent-runtime/src/tools/handlers/tool/browser-logs.ts +21 -0
  1247. package/packages/agent-runtime/src/tools/handlers/tool/checkpoint-store.ts +378 -0
  1248. package/packages/agent-runtime/src/tools/handlers/tool/code-search.ts +21 -0
  1249. package/packages/agent-runtime/src/tools/handlers/tool/composio.ts +52 -0
  1250. package/packages/agent-runtime/src/tools/handlers/tool/create-plan.ts +63 -0
  1251. package/packages/agent-runtime/src/tools/handlers/tool/end-turn.ts +15 -0
  1252. package/packages/agent-runtime/src/tools/handlers/tool/find-files.ts +161 -0
  1253. package/packages/agent-runtime/src/tools/handlers/tool/glob.ts +22 -0
  1254. package/packages/agent-runtime/src/tools/handlers/tool/gravity-index.ts +230 -0
  1255. package/packages/agent-runtime/src/tools/handlers/tool/list-directory.ts +22 -0
  1256. package/packages/agent-runtime/src/tools/handlers/tool/lookup-agent-info.ts +105 -0
  1257. package/packages/agent-runtime/src/tools/handlers/tool/propose-str-replace.ts +111 -0
  1258. package/packages/agent-runtime/src/tools/handlers/tool/propose-write-file.ts +89 -0
  1259. package/packages/agent-runtime/src/tools/handlers/tool/proposed-content-store.ts +64 -0
  1260. package/packages/agent-runtime/src/tools/handlers/tool/read-docs.ts +160 -0
  1261. package/packages/agent-runtime/src/tools/handlers/tool/read-files.ts +43 -0
  1262. package/packages/agent-runtime/src/tools/handlers/tool/read-subtree.ts +198 -0
  1263. package/packages/agent-runtime/src/tools/handlers/tool/read-url.ts +21 -0
  1264. package/packages/agent-runtime/src/tools/handlers/tool/render-ui.ts +15 -0
  1265. package/packages/agent-runtime/src/tools/handlers/tool/run-file-change-hooks.ts +20 -0
  1266. package/packages/agent-runtime/src/tools/handlers/tool/run-readonly-command.ts +215 -0
  1267. package/packages/agent-runtime/src/tools/handlers/tool/run-terminal-command.ts +33 -0
  1268. package/packages/agent-runtime/src/tools/handlers/tool/sequential-thinking.ts +53 -0
  1269. package/packages/agent-runtime/src/tools/handlers/tool/set-messages.ts +19 -0
  1270. package/packages/agent-runtime/src/tools/handlers/tool/set-output.ts +104 -0
  1271. package/packages/agent-runtime/src/tools/handlers/tool/set-scaffold-complete.ts +32 -0
  1272. package/packages/agent-runtime/src/tools/handlers/tool/skill.ts +146 -0
  1273. package/packages/agent-runtime/src/tools/handlers/tool/spawn-agent-inline.ts +149 -0
  1274. package/packages/agent-runtime/src/tools/handlers/tool/spawn-agent-utils.ts +436 -0
  1275. package/packages/agent-runtime/src/tools/handlers/tool/spawn-agents.ts +286 -0
  1276. package/packages/agent-runtime/src/tools/handlers/tool/str-replace.ts +135 -0
  1277. package/packages/agent-runtime/src/tools/handlers/tool/suggest-followups.ts +20 -0
  1278. package/packages/agent-runtime/src/tools/handlers/tool/task-completed.ts +15 -0
  1279. package/packages/agent-runtime/src/tools/handlers/tool/think-deeply.ts +25 -0
  1280. package/packages/agent-runtime/src/tools/handlers/tool/transition-phase.ts +160 -0
  1281. package/packages/agent-runtime/src/tools/handlers/tool/update-subgoal.ts +48 -0
  1282. package/packages/agent-runtime/src/tools/handlers/tool/web-search.ts +151 -0
  1283. package/packages/agent-runtime/src/tools/handlers/tool/write-file.ts +281 -0
  1284. package/packages/agent-runtime/src/tools/handlers/tool/write-todos.ts +19 -0
  1285. package/packages/agent-runtime/src/tools/prompts.ts +233 -0
  1286. package/packages/agent-runtime/src/tools/sandbox/__tests__/engine.test.ts +169 -0
  1287. package/packages/agent-runtime/src/tools/sandbox/__tests__/shell-denylist.test.ts +104 -0
  1288. package/packages/agent-runtime/src/tools/sandbox/engine.ts +109 -0
  1289. package/packages/agent-runtime/src/tools/sandbox/index.ts +14 -0
  1290. package/packages/agent-runtime/src/tools/sandbox/shell-denylist.ts +119 -0
  1291. package/packages/agent-runtime/src/tools/stream-parser.ts +407 -0
  1292. package/packages/agent-runtime/src/tools/thinker-convergence-gate.ts +162 -0
  1293. package/packages/agent-runtime/src/tools/thought-session-store.ts +43 -0
  1294. package/packages/agent-runtime/src/tools/tool-executor.ts +1098 -0
  1295. package/packages/agent-runtime/src/util/__tests__/messages.test.ts +995 -0
  1296. package/packages/agent-runtime/src/util/__tests__/parse-tool-calls-from-text.test.ts +363 -0
  1297. package/packages/agent-runtime/src/util/__tests__/simplify-tool-results.test.ts +382 -0
  1298. package/packages/agent-runtime/src/util/__tests__/stream-xml-parser.test.ts +285 -0
  1299. package/packages/agent-runtime/src/util/__tests__/think-tags.test.ts +49 -0
  1300. package/packages/agent-runtime/src/util/__tests__/token-counter.test.ts +121 -0
  1301. package/packages/agent-runtime/src/util/activity-tracking.ts +252 -0
  1302. package/packages/agent-runtime/src/util/agent-output.ts +102 -0
  1303. package/packages/agent-runtime/src/util/cache-debug.ts +330 -0
  1304. package/packages/agent-runtime/src/util/format-value.ts +16 -0
  1305. package/packages/agent-runtime/src/util/messages.ts +461 -0
  1306. package/packages/agent-runtime/src/util/parse-tool-calls-from-text.ts +122 -0
  1307. package/packages/agent-runtime/src/util/render-read-files-result.ts +18 -0
  1308. package/packages/agent-runtime/src/util/simplify-tool-results.ts +61 -0
  1309. package/packages/agent-runtime/src/util/stream-xml-parser.ts +259 -0
  1310. package/packages/agent-runtime/src/util/think-tags.ts +33 -0
  1311. package/packages/agent-runtime/src/util/token-counter.ts +136 -0
  1312. package/packages/agent-runtime/tsconfig.json +8 -0
  1313. package/packages/code-map/README.md +43 -0
  1314. package/packages/code-map/__tests__/integration.test.ts +278 -0
  1315. package/packages/code-map/__tests__/languages.test.ts +240 -0
  1316. package/packages/code-map/__tests__/parse.test.ts +640 -0
  1317. package/packages/code-map/__tests__/test-langs/test.c +42 -0
  1318. package/packages/code-map/__tests__/test-langs/test.cpp +34 -0
  1319. package/packages/code-map/__tests__/test-langs/test.cs +34 -0
  1320. package/packages/code-map/__tests__/test-langs/test.go +27 -0
  1321. package/packages/code-map/__tests__/test-langs/test.java +31 -0
  1322. package/packages/code-map/__tests__/test-langs/test.js +50 -0
  1323. package/packages/code-map/__tests__/test-langs/test.php +34 -0
  1324. package/packages/code-map/__tests__/test-langs/test.py +25 -0
  1325. package/packages/code-map/__tests__/test-langs/test.rb +28 -0
  1326. package/packages/code-map/__tests__/test-langs/test.rs +28 -0
  1327. package/packages/code-map/__tests__/test-langs/test.ts +31 -0
  1328. package/packages/code-map/package.json +33 -0
  1329. package/packages/code-map/src/index.ts +4 -0
  1330. package/packages/code-map/src/init-node.ts +185 -0
  1331. package/packages/code-map/src/languages.ts +344 -0
  1332. package/packages/code-map/src/parse.ts +410 -0
  1333. package/packages/code-map/src/tree-sitter-queries/readme.md +24 -0
  1334. package/packages/code-map/src/tree-sitter-queries/tree-sitter-c-tags.scm +16 -0
  1335. package/packages/code-map/src/tree-sitter-queries/tree-sitter-c_sharp-tags.scm +23 -0
  1336. package/packages/code-map/src/tree-sitter-queries/tree-sitter-cpp-tags.scm +29 -0
  1337. package/packages/code-map/src/tree-sitter-queries/tree-sitter-go-tags.scm +26 -0
  1338. package/packages/code-map/src/tree-sitter-queries/tree-sitter-java-tags.scm +19 -0
  1339. package/packages/code-map/src/tree-sitter-queries/tree-sitter-javascript-tags.scm +16 -0
  1340. package/packages/code-map/src/tree-sitter-queries/tree-sitter-php-tags.scm +23 -0
  1341. package/packages/code-map/src/tree-sitter-queries/tree-sitter-python-tags.scm +12 -0
  1342. package/packages/code-map/src/tree-sitter-queries/tree-sitter-ruby-tags.scm +58 -0
  1343. package/packages/code-map/src/tree-sitter-queries/tree-sitter-rust-tags.scm +26 -0
  1344. package/packages/code-map/src/tree-sitter-queries/tree-sitter-typescript-tags.scm +22 -0
  1345. package/packages/code-map/src/types.ts +11 -0
  1346. package/packages/code-map/src/utils.ts +12 -0
  1347. package/packages/code-map/tsconfig.json +10 -0
  1348. package/packages/database/README.md +43 -0
  1349. package/packages/database/package.json +28 -0
  1350. package/packages/database/src/__tests__/service.test.ts +167 -0
  1351. package/packages/database/src/index.ts +146 -0
  1352. package/packages/database/src/service.ts +342 -0
  1353. package/packages/database/tsconfig.json +9 -0
  1354. package/packages/llm-providers/README.md +42 -0
  1355. package/packages/llm-providers/package.json +42 -0
  1356. package/packages/llm-providers/src/ollama/__tests__/detect.test.ts +59 -0
  1357. package/packages/llm-providers/src/ollama/detect.ts +113 -0
  1358. package/packages/llm-providers/src/ollama/index.ts +2 -0
  1359. package/packages/llm-providers/src/openai-compatible/chat/convert-to-openai-compatible-chat-messages.test.ts +855 -0
  1360. package/packages/llm-providers/src/openai-compatible/chat/convert-to-openai-compatible-chat-messages.ts +177 -0
  1361. package/packages/llm-providers/src/openai-compatible/chat/get-response-metadata.ts +15 -0
  1362. package/packages/llm-providers/src/openai-compatible/chat/map-openai-compatible-finish-reason.ts +19 -0
  1363. package/packages/llm-providers/src/openai-compatible/chat/openai-compatible-api-types.ts +60 -0
  1364. package/packages/llm-providers/src/openai-compatible/chat/openai-compatible-chat-language-model.test.ts +549 -0
  1365. package/packages/llm-providers/src/openai-compatible/chat/openai-compatible-chat-language-model.ts +474 -0
  1366. package/packages/llm-providers/src/openai-compatible/chat/openai-compatible-chat-options.ts +25 -0
  1367. package/packages/llm-providers/src/openai-compatible/chat/openai-compatible-metadata-extractor.ts +49 -0
  1368. package/packages/llm-providers/src/openai-compatible/chat/openai-compatible-prepare-tools.ts +162 -0
  1369. package/packages/llm-providers/src/openai-compatible/chat/stream-transform.test.ts +106 -0
  1370. package/packages/llm-providers/src/openai-compatible/chat/stream-transform.ts +585 -0
  1371. package/packages/llm-providers/src/openai-compatible/completion/convert-to-openai-compatible-completion-prompt.ts +98 -0
  1372. package/packages/llm-providers/src/openai-compatible/completion/openai-compatible-completion-language-model.test.ts +173 -0
  1373. package/packages/llm-providers/src/openai-compatible/completion/openai-compatible-completion-language-model.ts +414 -0
  1374. package/packages/llm-providers/src/openai-compatible/completion/openai-compatible-completion-options.ts +33 -0
  1375. package/packages/llm-providers/src/openai-compatible/embedding/openai-compatible-embedding-model.ts +143 -0
  1376. package/packages/llm-providers/src/openai-compatible/embedding/openai-compatible-embedding-options.ts +21 -0
  1377. package/packages/llm-providers/src/openai-compatible/image/openai-compatible-image-model.ts +129 -0
  1378. package/packages/llm-providers/src/openai-compatible/image/openai-compatible-image-settings.ts +1 -0
  1379. package/packages/llm-providers/src/openai-compatible/index.ts +27 -0
  1380. package/packages/llm-providers/src/openai-compatible/openai-compatible-error.ts +34 -0
  1381. package/packages/llm-providers/src/openai-compatible/openai-compatible-provider.ts +174 -0
  1382. package/packages/llm-providers/src/openai-compatible/version.ts +5 -0
  1383. package/packages/llm-providers/tsconfig.json +9 -0
  1384. package/protocol.config.yaml +113 -0
  1385. package/savant-free/README.md +73 -0
  1386. package/savant-free/SPEC.md +385 -0
  1387. package/savant-free/cli/build.ts +49 -0
  1388. package/savant-free/cli/release/README.md +55 -0
  1389. package/savant-free/cli/release/index.js +37 -0
  1390. package/savant-free/cli/release/package.json +43 -0
  1391. package/savant-free/cli/release.ts +128 -0
  1392. package/savant-free/cli/smoke-test.test.ts +257 -0
  1393. package/savant-free/e2e/README.md +173 -0
  1394. package/savant-free/e2e/agent/savant-free-tester.ts +52 -0
  1395. package/savant-free/e2e/tests/ads-behavior.e2e.test.ts +51 -0
  1396. package/savant-free/e2e/tests/agent-startup.e2e.test.ts +61 -0
  1397. package/savant-free/e2e/tests/code-edit.e2e.test.ts +78 -0
  1398. package/savant-free/e2e/tests/help-command.e2e.test.ts +97 -0
  1399. package/savant-free/e2e/tests/knowledge-file.e2e.test.ts +66 -0
  1400. package/savant-free/e2e/tests/slash-commands.e2e.test.ts +119 -0
  1401. package/savant-free/e2e/tests/startup.e2e.test.ts +56 -0
  1402. package/savant-free/e2e/tests/terminal-command.e2e.test.ts +71 -0
  1403. package/savant-free/e2e/tests/version.e2e.test.ts +50 -0
  1404. package/savant-free/e2e/utils/binary-helpers.ts +24 -0
  1405. package/savant-free/e2e/utils/index.ts +17 -0
  1406. package/savant-free/e2e/utils/savant-free-session.ts +229 -0
  1407. package/savant-free/e2e/utils/tmux-custom-tools.ts +156 -0
  1408. package/savant-free/e2e/utils/tmux-helpers.ts +79 -0
  1409. package/savant-free/package.json +20 -0
  1410. package/scripts/eslint-rules/__fixtures__/debug-ast.mjs +20 -0
  1411. package/scripts/eslint-rules/__fixtures__/probe.mjs +41 -0
  1412. package/scripts/eslint-rules/__fixtures__/probe2.mjs +43 -0
  1413. package/scripts/eslint-rules/__fixtures__/rule-test.mjs +44 -0
  1414. package/scripts/eslint-rules/__fixtures__/rule-test2.mjs +27 -0
  1415. package/scripts/eslint-rules/__fixtures__/unknown-bad.ts +21 -0
  1416. package/scripts/eslint-rules/__fixtures__/unknown-good.ts +35 -0
  1417. package/scripts/eslint-rules/no-unknown-in-signatures.js +141 -0
  1418. package/scripts/release.py +360 -0
  1419. package/scripts/run-az-test.sh +1150 -0
  1420. package/scripts/sync-agents.py +337 -0
  1421. package/scripts/tmux/README.md +359 -0
  1422. package/scripts/tmux/package.json +9 -0
  1423. package/scripts/tmux/tmux-capture.sh +231 -0
  1424. package/scripts/tmux/tmux-cli.sh +160 -0
  1425. package/scripts/tmux/tmux-env.sh +34 -0
  1426. package/scripts/tmux/tmux-send.sh +339 -0
  1427. package/scripts/tmux/tmux-start.sh +264 -0
  1428. package/scripts/tmux/tmux-stop.sh +157 -0
  1429. package/scripts/tmux/tmux-viewer/README.md +254 -0
  1430. package/scripts/tmux/tmux-viewer/components/session-viewer.tsx +556 -0
  1431. package/scripts/tmux/tmux-viewer/components/theme.ts +54 -0
  1432. package/scripts/tmux/tmux-viewer/gif-encoder-2.d.ts +28 -0
  1433. package/scripts/tmux/tmux-viewer/gif-exporter.ts +278 -0
  1434. package/scripts/tmux/tmux-viewer/index.tsx +237 -0
  1435. package/scripts/tmux/tmux-viewer/package.json +9 -0
  1436. package/scripts/tmux/tmux-viewer/session-loader.ts +234 -0
  1437. package/scripts/tmux/tmux-viewer/tsconfig.json +9 -0
  1438. package/scripts/tmux/tmux-viewer/types.ts +76 -0
  1439. package/sdk/CHANGELOG.md +126 -0
  1440. package/sdk/PUBLISHING.md +55 -0
  1441. package/sdk/README.md +334 -0
  1442. package/sdk/bunfig.toml +6 -0
  1443. package/sdk/e2e/README.md +158 -0
  1444. package/sdk/e2e/custom-agents/api-integration-agent.e2e.test.ts +145 -0
  1445. package/sdk/e2e/custom-agents/apply-patch-tool.e2e.test.ts +62 -0
  1446. package/sdk/e2e/custom-agents/database-query-agent.e2e.test.ts +135 -0
  1447. package/sdk/e2e/custom-agents/weather-agent.e2e.test.ts +120 -0
  1448. package/sdk/e2e/examples/code-explainer.example.ts +62 -0
  1449. package/sdk/e2e/examples/code-reviewer.example.ts +52 -0
  1450. package/sdk/e2e/examples/commit-message-generator.example.ts +61 -0
  1451. package/sdk/e2e/examples/sdk-lint.example.ts +65 -0
  1452. package/sdk/e2e/examples/sdk-refactor.example.ts +63 -0
  1453. package/sdk/e2e/examples/sdk-test-gen.example.ts +62 -0
  1454. package/sdk/e2e/features/knowledge-files.e2e.test.ts +82 -0
  1455. package/sdk/e2e/features/max-agent-steps.e2e.test.ts +65 -0
  1456. package/sdk/e2e/features/project-files.e2e.test.ts +80 -0
  1457. package/sdk/e2e/integration/connection-check.integration.test.ts +33 -0
  1458. package/sdk/e2e/integration/event-ordering.integration.test.ts +186 -0
  1459. package/sdk/e2e/integration/event-types.integration.test.ts +183 -0
  1460. package/sdk/e2e/integration/stream-chunks.integration.test.ts +191 -0
  1461. package/sdk/e2e/streaming/concurrent-streams.e2e.test.ts +164 -0
  1462. package/sdk/e2e/streaming/subagent-streaming.e2e.test.ts +158 -0
  1463. package/sdk/e2e/utils/__tests__/event-collector.test.ts +297 -0
  1464. package/sdk/e2e/utils/e2e-mocks.ts +458 -0
  1465. package/sdk/e2e/utils/event-collector.ts +145 -0
  1466. package/sdk/e2e/utils/get-api-key.ts +76 -0
  1467. package/sdk/e2e/utils/index.ts +3 -0
  1468. package/sdk/e2e/utils/test-fixtures.ts +196 -0
  1469. package/sdk/e2e/workflows/error-recovery.e2e.test.ts +112 -0
  1470. package/sdk/e2e/workflows/multi-turn-conversation.e2e.test.ts +132 -0
  1471. package/sdk/examples/readme-example-1.ts +34 -0
  1472. package/sdk/examples/readme-example-2.ts +76 -0
  1473. package/sdk/package.json +82 -0
  1474. package/sdk/scripts/build.ts +349 -0
  1475. package/sdk/scripts/fetch-ripgrep.ts +172 -0
  1476. package/sdk/scripts/publish.ts +51 -0
  1477. package/sdk/scripts/release.js +100 -0
  1478. package/sdk/scripts/verify.ts +204 -0
  1479. package/sdk/sdk_exit +1 -0
  1480. package/sdk/smoke-test-dist.ts +248 -0
  1481. package/sdk/src/__tests__/apply-overrides-resume.test.ts +83 -0
  1482. package/sdk/src/__tests__/apply-patch.test.ts +438 -0
  1483. package/sdk/src/__tests__/build-file-tree.test.ts +31 -0
  1484. package/sdk/src/__tests__/change-file.test.ts +212 -0
  1485. package/sdk/src/__tests__/client.test.ts +142 -0
  1486. package/sdk/src/__tests__/clone-session-state.test.ts +158 -0
  1487. package/sdk/src/__tests__/code-search.test.ts +896 -0
  1488. package/sdk/src/__tests__/composio.test.ts +86 -0
  1489. package/sdk/src/__tests__/credentials.test.ts +344 -0
  1490. package/sdk/src/__tests__/database.test.ts +153 -0
  1491. package/sdk/src/__tests__/env.test.ts +137 -0
  1492. package/sdk/src/__tests__/error-utils.test.ts +253 -0
  1493. package/sdk/src/__tests__/fixtures/windows-stubborn-grandchild.ts +26 -0
  1494. package/sdk/src/__tests__/initial-session-state.test.ts +447 -0
  1495. package/sdk/src/__tests__/knowledge-file-selection.test.ts +335 -0
  1496. package/sdk/src/__tests__/load-agents.test.ts +935 -0
  1497. package/sdk/src/__tests__/load-mcp-config.test.ts +285 -0
  1498. package/sdk/src/__tests__/load-skills.test.ts +325 -0
  1499. package/sdk/src/__tests__/model-provider.test.ts +44 -0
  1500. package/sdk/src/__tests__/path-utils.test.ts +66 -0
  1501. package/sdk/src/__tests__/read-files.test.ts +573 -0
  1502. package/sdk/src/__tests__/read-url.test.ts +371 -0
  1503. package/sdk/src/__tests__/researcher-web.integration.test.ts +130 -0
  1504. package/sdk/src/__tests__/run-cancellation.test.ts +1321 -0
  1505. package/sdk/src/__tests__/run-error-preserves-history.test.ts +335 -0
  1506. package/sdk/src/__tests__/run-event-dispatch.test.ts +80 -0
  1507. package/sdk/src/__tests__/run-file-filter.test.ts +530 -0
  1508. package/sdk/src/__tests__/run-handle-event.test.ts +142 -0
  1509. package/sdk/src/__tests__/run-mcp-tool-filter.test.ts +124 -0
  1510. package/sdk/src/__tests__/run-state-child-process.test.ts +72 -0
  1511. package/sdk/src/__tests__/run-terminal-command.test.ts +236 -0
  1512. package/sdk/src/__tests__/run.integration.test.ts +170 -0
  1513. package/sdk/src/__tests__/user-knowledge-files.test.ts +361 -0
  1514. package/sdk/src/__tests__/validate-agents.test.ts +943 -0
  1515. package/sdk/src/agents/load-agents.ts +339 -0
  1516. package/sdk/src/agents/load-mcp-config.ts +275 -0
  1517. package/sdk/src/client.ts +87 -0
  1518. package/sdk/src/composio.ts +78 -0
  1519. package/sdk/src/constants.ts +29 -0
  1520. package/sdk/src/credentials.ts +303 -0
  1521. package/sdk/src/custom-tool.ts +66 -0
  1522. package/sdk/src/env.ts +116 -0
  1523. package/sdk/src/error-utils.ts +127 -0
  1524. package/sdk/src/impl/__tests__/llm-chatgpt-oauth-policy.test.ts +67 -0
  1525. package/sdk/src/impl/__tests__/llm-native-tool-call.test.ts +38 -0
  1526. package/sdk/src/impl/__tests__/llm-stream-yielded-content.test.ts +118 -0
  1527. package/sdk/src/impl/__tests__/model-provider-free-mode.test.ts +166 -0
  1528. package/sdk/src/impl/__tests__/prompt-result.test.ts +210 -0
  1529. package/sdk/src/impl/__tests__/provider-options-metadata.test.ts +67 -0
  1530. package/sdk/src/impl/agent-runtime.ts +159 -0
  1531. package/sdk/src/impl/chatgpt-backend-fetch.ts +588 -0
  1532. package/sdk/src/impl/database.ts +516 -0
  1533. package/sdk/src/impl/llm.ts +852 -0
  1534. package/sdk/src/impl/model-provider.ts +638 -0
  1535. package/sdk/src/impl/openrouter-key-resolver.ts +78 -0
  1536. package/sdk/src/index.ts +133 -0
  1537. package/sdk/src/native/ripgrep.ts +141 -0
  1538. package/sdk/src/retry-config.ts +51 -0
  1539. package/sdk/src/run-state.ts +897 -0
  1540. package/sdk/src/run.ts +1222 -0
  1541. package/sdk/src/skills/load-skills.ts +253 -0
  1542. package/sdk/src/testing/env.ts +19 -0
  1543. package/sdk/src/tools/apply-patch.ts +690 -0
  1544. package/sdk/src/tools/change-file.ts +155 -0
  1545. package/sdk/src/tools/code-search.ts +547 -0
  1546. package/sdk/src/tools/glob.ts +58 -0
  1547. package/sdk/src/tools/index.ts +19 -0
  1548. package/sdk/src/tools/list-directory.ts +52 -0
  1549. package/sdk/src/tools/path-utils.ts +101 -0
  1550. package/sdk/src/tools/read-files.ts +110 -0
  1551. package/sdk/src/tools/read-url.ts +469 -0
  1552. package/sdk/src/tools/run-file-change-hooks.ts +22 -0
  1553. package/sdk/src/tools/run-terminal-command.ts +483 -0
  1554. package/sdk/src/tools/ssrf.ts +120 -0
  1555. package/sdk/src/types/env.ts +40 -0
  1556. package/sdk/src/utils/logger.ts +15 -0
  1557. package/sdk/src/validate-agents.ts +157 -0
  1558. package/sdk/test/cjs-compatibility/package-lock.json +423 -0
  1559. package/sdk/test/cjs-compatibility/package.json +21 -0
  1560. package/sdk/test/cjs-compatibility/test-imports.js +77 -0
  1561. package/sdk/test/cjs-compatibility/test-types.ts +54 -0
  1562. package/sdk/test/cjs-compatibility/tsconfig.json +17 -0
  1563. package/sdk/test/esm-compatibility/package-lock.json +423 -0
  1564. package/sdk/test/esm-compatibility/package.json +21 -0
  1565. package/sdk/test/esm-compatibility/test-imports.js +83 -0
  1566. package/sdk/test/esm-compatibility/test-types.ts +59 -0
  1567. package/sdk/test/esm-compatibility/tsconfig.json +17 -0
  1568. package/sdk/test/ripgrep-bundling/package-lock.json +423 -0
  1569. package/sdk/test/ripgrep-bundling/package.json +21 -0
  1570. package/sdk/test/ripgrep-bundling/test-ripgrep-types.ts +132 -0
  1571. package/sdk/test/ripgrep-bundling/test-ripgrep.js +257 -0
  1572. package/sdk/test/ripgrep-bundling/tsconfig.json +15 -0
  1573. package/sdk/test/setup-env.ts +54 -0
  1574. package/sdk/test/test-sdk.ts +25 -0
  1575. package/sdk/test/tree-sitter-queries/package-lock.json +47 -0
  1576. package/sdk/test/tree-sitter-queries/package.json +12 -0
  1577. package/sdk/test/tree-sitter-queries/test-query-files.js +256 -0
  1578. package/sdk/tsconfig.build.json +32 -0
  1579. package/sdk/tsconfig.json +27 -0
  1580. package/sdk/vendor/ripgrep/arm64-darwin/rg +0 -0
  1581. package/sdk/vendor/ripgrep/arm64-linux/rg +0 -0
  1582. package/sdk/vendor/ripgrep/x64-darwin/rg +0 -0
  1583. package/sdk/vendor/ripgrep/x64-linux/rg +0 -0
  1584. package/sdk/vendor/ripgrep/x64-win32/rg.exe +0 -0
  1585. package/templates/FID-TEMPLATE.md +125 -0
  1586. package/templates/README-TEMPLATE.md +87 -0
  1587. package/templates/SESSION-SUMMARY.md +135 -0
  1588. package/tsconfig.base.json +18 -0
  1589. package/tsconfig.json +27 -0
  1590. /package/{http.js → cli/release-core/http.js} +0 -0
  1591. /package/{launcher.js → cli/release-core/launcher.js} +0 -0
@@ -0,0 +1,2008 @@
1
+ import { AnalyticsEvent } from '@savant-code/common/constants/analytics-events'
2
+ import {
3
+ useCallback,
4
+ useEffect,
5
+ useLayoutEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from 'react'
10
+ import { useShallow } from 'zustand/react/shallow'
11
+
12
+ import { getAdsEnabled } from './commands/ads'
13
+ import { getCheckpointDir } from './commands/rewind'
14
+ import { routeUserPrompt, addBashMessageToHistory } from './commands/router'
15
+ import { SingleAdBanner } from './components/ad-banner'
16
+ import { ChatHeader } from './components/chat-header'
17
+ import { ChatInputBar } from './components/chat-input-bar'
18
+ import { LoadPreviousButton } from './components/load-previous-button'
19
+ import { MessageWithAgents } from './components/message-with-agents'
20
+ import { ModelPicker } from './components/model-picker'
21
+ import { areCreditsRestored } from './components/out-of-credits-banner'
22
+ import { PendingBashMessage } from './components/pending-bash-message'
23
+ import { ProviderPicker } from './components/provider-picker'
24
+ import { ReviewScreen } from './components/review-screen'
25
+ import { RewindPicker } from './components/rewind-picker'
26
+ import { RightSidebar } from './components/right-sidebar'
27
+ import { SavantFreeActiveSessionSummary } from './components/savant-free-active-session-summary'
28
+ import { SessionEndedBanner } from './components/session-ended-banner'
29
+ import { StatusBar } from './components/status-bar'
30
+ import {
31
+ SuggestedPrompts,
32
+ DEFAULT_SUGGESTED_PROMPTS,
33
+ type SuggestedPromptSelection,
34
+ } from './components/suggested-prompts'
35
+ import { TopBanner } from './components/top-banner'
36
+ import { getSlashCommandsWithSkills } from './data/slash-commands'
37
+ import { createLoopRunHandler } from './hooks/run-outcome'
38
+ import { useAgentValidation } from './hooks/use-agent-validation'
39
+ import { useAskUserBridge } from './hooks/use-ask-user-bridge'
40
+ import { useChatInput } from './hooks/use-chat-input'
41
+ import {
42
+ useChatKeyboard,
43
+ type ChatKeyboardHandlers,
44
+ } from './hooks/use-chat-keyboard'
45
+ import { useChatMessages } from './hooks/use-chat-messages'
46
+ import { useChatState } from './hooks/use-chat-state'
47
+ import { useChatStreaming } from './hooks/use-chat-streaming'
48
+ import { useChatUI } from './hooks/use-chat-ui'
49
+ import { useClipboard } from './hooks/use-clipboard'
50
+ import { useEvent } from './hooks/use-event'
51
+ import { useGravityAd } from './hooks/use-gravity-ad'
52
+ import { useInputHistory } from './hooks/use-input-history'
53
+ import { useLoopScheduler } from './hooks/use-loop-scheduler'
54
+ import { usePublishMutation } from './hooks/use-publish-mutation'
55
+ import { returnToSavantFreeLanding } from './hooks/use-savant-free-session'
56
+ import { useScaffoldRevertSubscriber } from './hooks/use-scaffold-revert-subscriber'
57
+ import { useSendMessage } from './hooks/use-send-message'
58
+ import { useSubscriptionQuery } from './hooks/use-subscription-query'
59
+ import { useSuggestionEngine } from './hooks/use-suggestion-engine'
60
+ import { useUsageMonitor } from './hooks/use-usage-monitor'
61
+ import { WEBSITE_URL } from './login/constants'
62
+ import { getProjectRoot, tryGetProjectRoot } from './project-files'
63
+ import { useChatHistoryStore } from './state/chat-history-store'
64
+ import { useChatStore } from './state/chat-store'
65
+ import { useFeedbackStore } from './state/feedback-store'
66
+ import { useGatewayCatalogStore } from './state/gateway-catalog-store'
67
+ import { useMessageBlockStore } from './state/message-block-store'
68
+ import { useModelPickerStore } from './state/model-picker-store'
69
+ import { useProviderPickerStore } from './state/provider-picker-store'
70
+ import { usePublishStore } from './state/publish-store'
71
+ import { useReviewStore } from './state/review-store'
72
+ import { useRewindPickerStore } from './state/rewind-picker-store'
73
+ import { useSavantFreeModelStore } from './state/savant-free-model-store'
74
+ import { reportActivity } from './utils/activity-tracker'
75
+ import { trackEvent } from './utils/analytics'
76
+ import { showClipboardMessage } from './utils/clipboard'
77
+ import { readClipboardImage } from './utils/clipboard-image'
78
+ import { END_SESSION_MESSAGE, IS_SAVANT_FREE } from './utils/constants'
79
+ import { getInputModeConfig } from './utils/input-modes'
80
+ import {
81
+ type ChatKeyboardState,
82
+ createDefaultChatKeyboardState,
83
+ } from './utils/keyboard-actions'
84
+ import { loadLocalAgents } from './utils/local-agent-registry'
85
+ import { logger } from './utils/logger'
86
+ import { getSystemMessage } from './utils/message-history'
87
+ import { safeOpen } from './utils/open-url'
88
+ import { resolveContextWindowForModel } from './utils/openrouter-models'
89
+ import { formatCwd } from './utils/path-helpers'
90
+ import {
91
+ addClipboardPlaceholder,
92
+ addPendingFileFromPath,
93
+ addPendingImageFromFile,
94
+ validateAndAddImage,
95
+ } from './utils/pending-attachments'
96
+ import {
97
+ beginProviderSetup,
98
+ getMissingProviderSetup,
99
+ getProviderSetupGuidance,
100
+ getProviderSetupInfo,
101
+ } from './utils/provider-setup'
102
+ import { executeRewind } from './utils/rewind'
103
+ import {
104
+ hasSubmittedFirstPrompt,
105
+ loadSavantCodeModelPreference,
106
+ markFirstPromptSubmitted,
107
+ saveSavantCodeModelPreference,
108
+ saveSavantCodeModelProviderPreference,
109
+ } from './utils/settings'
110
+ import { getLoadedSkills } from './utils/skill-registry'
111
+ import {
112
+ getStatusIndicatorState,
113
+ type AuthStatus,
114
+ } from './utils/status-indicator-state'
115
+ import { createPasteHandler } from './utils/strings'
116
+ import { setTerminalTitle } from './utils/terminal-title'
117
+ import { computeInputLayoutMetrics } from './utils/text-layout'
118
+
119
+ import type { CommandResult } from './commands/command-registry'
120
+ import type { MultilineInputHandle } from './components/multiline-input'
121
+ import type { MatchedSlashCommand } from './hooks/use-suggestion-engine'
122
+ import type { RewindMode } from './state/rewind-picker-store'
123
+ import type { SavantFreeSession } from './types/savant-free-session'
124
+ import type { User } from './utils/auth'
125
+ import type { AgentMode } from './utils/constants'
126
+ import type { OpenRouterModel } from './utils/openrouter-models'
127
+ import type { ProviderSetupName } from './utils/provider-setup'
128
+ import type { PermissionMode } from './utils/settings'
129
+ import type { BoxRenderable, ScrollBoxRenderable } from '@opentui/core'
130
+ import type { FeedbackCategory } from '@savant-code/common/constants/feedback'
131
+ import type { FileTreeNode } from '@savant-code/common/util/file'
132
+ import type { TurnSummary } from '@savant-code/sdk'
133
+ import type { UseMutationResult } from '@tanstack/react-query'
134
+ import type { Dispatch, SetStateAction } from 'react'
135
+
136
+ // FID-007 S2: sidebar tool list — hoisted (stable identity so RightSidebar's
137
+ // React.memo can skip) and cross-checked against
138
+ // common/src/tools/constants.ts. The previous inline list named tools that do
139
+ // NOT exist in the executor allowlist (read_file, search_files, bash).
140
+ const SIDEBAR_TOOLS_AVAILABLE = [
141
+ 'read_files',
142
+ 'code_search',
143
+ 'apply_patch',
144
+ 'run_terminal_command',
145
+ ]
146
+
147
+ // FID-007 D7: hoisted static style objects so the render loop stops
148
+ // allocating fresh style identities every render/tick.
149
+ const CHAT_ROOT_STYLE = {
150
+ flexDirection: 'row', // Horizontal split: chat + sidebar
151
+ gap: 0,
152
+ flexGrow: 1,
153
+ } as const
154
+
155
+ const HEADER_BOX_STYLE = { flexDirection: 'column' } as const
156
+
157
+ const SCROLLBOX_STYLE = {
158
+ flexGrow: 1,
159
+ rootOptions: {
160
+ flexGrow: 1,
161
+ padding: 0,
162
+ gap: 0,
163
+ flexDirection: 'row',
164
+ shouldFill: true,
165
+ backgroundColor: 'transparent',
166
+ },
167
+ wrapperOptions: {
168
+ flexGrow: 1,
169
+ border: false,
170
+ shouldFill: true,
171
+ backgroundColor: 'transparent',
172
+ flexDirection: 'column',
173
+ },
174
+ contentOptions: {
175
+ flexDirection: 'column',
176
+ gap: 0,
177
+ shouldFill: true,
178
+ justifyContent: 'flex-end',
179
+ backgroundColor: 'transparent',
180
+ paddingLeft: 1,
181
+ paddingRight: 2,
182
+ },
183
+ } as const
184
+
185
+ const BOTTOM_BOX_STYLE = {
186
+ flexShrink: 0,
187
+ backgroundColor: 'transparent',
188
+ } as const
189
+
190
+ export const Chat = ({
191
+ initialPrompt,
192
+ agentId,
193
+ fileTree,
194
+ inputRef,
195
+ setIsAuthenticated,
196
+ setUser,
197
+ logoutMutation,
198
+ continueChat,
199
+ continueChatId,
200
+ authStatus,
201
+ initialMode,
202
+ initialPermissionMode,
203
+ gitRoot,
204
+ onSwitchToGitRoot,
205
+ savantFreeSession,
206
+ }: {
207
+ initialPrompt: string | null
208
+ agentId?: string
209
+ fileTree: FileTreeNode[]
210
+ inputRef: React.MutableRefObject<MultilineInputHandle | null>
211
+ setIsAuthenticated: Dispatch<SetStateAction<boolean | null>>
212
+ setUser: Dispatch<SetStateAction<User | null>>
213
+ logoutMutation: UseMutationResult<boolean, Error, void, unknown>
214
+ continueChat: boolean
215
+ continueChatId?: string
216
+ authStatus: AuthStatus
217
+ initialMode?: AgentMode
218
+ initialPermissionMode?: PermissionMode
219
+ gitRoot?: string | null
220
+ onSwitchToGitRoot?: () => void
221
+ savantFreeSession: SavantFreeSession | null
222
+ }) => {
223
+ const [forceFileOnlyMentions, setForceFileOnlyMentions] = useState(false)
224
+ const headerRef = useRef<BoxRenderable | null>(null)
225
+ const [isHeaderVisible, setIsHeaderVisible] = useState(true)
226
+
227
+ // First-time onboarding: show clickable starter prompts until the user
228
+ // submits their first prompt ever (persisted in settings). SavantFree only.
229
+ const [showSuggestedPrompts, setShowSuggestedPrompts] = useState(
230
+ () => IS_SAVANT_FREE && !hasSubmittedFirstPrompt(),
231
+ )
232
+
233
+ const { validate: validateAgents } = useAgentValidation()
234
+
235
+ // Subscribe to ask_user bridge to trigger form display
236
+ useAskUserBridge()
237
+
238
+ // Auto-revert from SCAFFOLD to EDIT when the orchestrator declares the
239
+ // scaffold complete via the set_scaffold_complete tool.
240
+ useScaffoldRevertSubscriber()
241
+
242
+ // Monitor usage data and auto-show banner when thresholds are crossed
243
+ useUsageMonitor()
244
+
245
+ // Get chat state from extracted hook
246
+ const {
247
+ inputValue,
248
+ cursorPosition,
249
+ lastEditDueToNav,
250
+ setInputValue,
251
+ inputFocused,
252
+ setInputFocused,
253
+ slashSelectedIndex,
254
+ setSlashSelectedIndex,
255
+ agentSelectedIndex,
256
+ setAgentSelectedIndex,
257
+ focusedAgentId,
258
+ setFocusedAgentId,
259
+ messages,
260
+ setMessages,
261
+ agentMode,
262
+ setAgentMode,
263
+ toggleAgentMode,
264
+ isRetrying,
265
+ pendingBashMessages,
266
+ refs: {
267
+ activeAgentStreamsRef,
268
+ isChainInProgressRef,
269
+ activeSubagentsRef,
270
+ abortControllerRef,
271
+ sendMessageRef,
272
+ },
273
+ } = useChatState()
274
+
275
+ const providerGuidanceShownRef = useRef(false)
276
+ useEffect(() => {
277
+ // FID-007 U1: provider guidance is non-free builds only — SavantFree
278
+ // reaches inference via its own gateway, `/provider` is not registered
279
+ // there, and instructing free users to run it would produce
280
+ // "Command not found".
281
+ if (IS_SAVANT_FREE) return
282
+ if (providerGuidanceShownRef.current || messages.length !== 0) return
283
+
284
+ const missingProvider = getMissingProviderSetup()
285
+ if (!missingProvider) return
286
+
287
+ providerGuidanceShownRef.current = true
288
+ setMessages((prev) => [
289
+ ...prev,
290
+ getSystemMessage(getProviderSetupGuidance(missingProvider)),
291
+ ])
292
+ }, [messages.length, setMessages])
293
+
294
+ // Sidebar data from chat-store
295
+ const contextTokensUsed = useChatStore((s) => s.contextTokensUsed)
296
+ const contextTokensMax = useChatStore((s) => s.contextTokensMax)
297
+ // FID-007 S1: reactive selector — the previous getState() read during
298
+ // render was non-reactive (stale until some unrelated state forced a
299
+ // re-render).
300
+ const fsmPhase = useChatStore((s) => s.fsmPhase)
301
+ const toolsUsed = useChatStore((s) => s.toolsUsed)
302
+ const toolHistory = useChatStore((s) => s.toolHistory)
303
+ const filesChanged = useChatStore((s) => s.filesChanged)
304
+ const agentStack = useChatStore((s) => s.agentStack)
305
+ const sessionCost = useChatStore((s) => s.sessionCost)
306
+ const updateContextTokensMax = useChatStore((s) => s.updateContextTokensMax)
307
+ const sidebarModel = useSavantFreeModelStore((s) => s.selectedModel)
308
+
309
+ // Interactive /model picker overlay state.
310
+ const modelPickerOpen = useModelPickerStore((s) => s.isOpen)
311
+ const modelPickerQuery = useModelPickerStore((s) => s.query)
312
+ const modelPickerModels = useModelPickerStore((s) => s.models)
313
+ const modelPickerSelectedIndex = useModelPickerStore((s) => s.selectedIndex)
314
+ const setModelPickerQuery = useModelPickerStore((s) => s.setQuery)
315
+ const setModelPickerSelectedIndex = useModelPickerStore(
316
+ (s) => s.setSelectedIndex,
317
+ )
318
+ const closeModelPicker = useModelPickerStore((s) => s.close)
319
+
320
+ // Interactive /provider picker overlay state.
321
+ const providerPickerOpen = useProviderPickerStore((s) => s.isOpen)
322
+ const providerPickerProviders = useProviderPickerStore((s) => s.providers)
323
+ const providerPickerSelectedIndex = useProviderPickerStore(
324
+ (s) => s.selectedIndex,
325
+ )
326
+ const setProviderPickerSelectedIndex = useProviderPickerStore(
327
+ (s) => s.setSelectedIndex,
328
+ )
329
+ const closeProviderPicker = useProviderPickerStore((s) => s.close)
330
+
331
+ // Interactive /rewind picker overlay state (FID-2026-0803-004).
332
+ const rewindPickerOpen = useRewindPickerStore((s) => s.isOpen)
333
+ const rewindPickerTurns = useRewindPickerStore((s) => s.turns)
334
+ const rewindPickerSelectedIndex = useRewindPickerStore((s) => s.selectedIndex)
335
+ const rewindPickerStage = useRewindPickerStore((s) => s.stage)
336
+ const rewindPickerMode = useRewindPickerStore((s) => s.mode)
337
+ const setRewindPickerSelectedIndex = useRewindPickerStore(
338
+ (s) => s.setSelectedIndex,
339
+ )
340
+ const setRewindPickerStage = useRewindPickerStore((s) => s.setStage)
341
+ const setRewindPickerMode = useRewindPickerStore((s) => s.setMode)
342
+ const closeRewindPicker = useRewindPickerStore((s) => s.close)
343
+
344
+ const { statusMessage } = useClipboard()
345
+
346
+ // Fetch subscription data early - needed for session credits tracking and ad gating
347
+ const { data: subscriptionData } = useSubscriptionQuery({
348
+ refetchInterval: 60 * 1000,
349
+ })
350
+ const hasSubscription = subscriptionData?.hasSubscription ?? false
351
+
352
+ const {
353
+ ads,
354
+ responseAds,
355
+ requestResponseAds,
356
+ recordClick,
357
+ recordImpression,
358
+ } = useGravityAd({
359
+ enabled: IS_SAVANT_FREE || !hasSubscription,
360
+ provider: 'gravity',
361
+ inline: true,
362
+ surface: 'cli_chat',
363
+ // Lazily fill a four-ad pool, then repeat it for later transcript slots.
364
+ inlinePlacementId: 'CLI-Chat-Inline',
365
+ // Keep the rotating above-input slot separate for reporting continuity.
366
+ slotPlacementId: 'Single-Ad-Unit-1',
367
+ })
368
+ const showInlineAds = IS_SAVANT_FREE || getAdsEnabled()
369
+
370
+ // Stable identities so the message-block callbacks (set once) always call
371
+ // the latest recorder from the hook.
372
+ const handleAdClick = useEvent(recordClick)
373
+ const handleAdImpression = useEvent(recordImpression)
374
+ const handleResponseAdsNeeded = useEvent(requestResponseAds)
375
+
376
+ // While the model or provider picker overlay is open, blur the text input
377
+ // so keystrokes route to the picker, not the input.
378
+ useEffect(() => {
379
+ if (modelPickerOpen || providerPickerOpen) {
380
+ setInputFocused(false)
381
+ }
382
+ }, [modelPickerOpen, providerPickerOpen, setInputFocused])
383
+
384
+ // FID-2026-0723-062: keep the sidebar context-token cap in sync with the
385
+ // active model. This fires on initial render (restored preference), when the
386
+ // model changes, and when the gateway catalog finishes loading asynchronously.
387
+ const gatewayCatalogLoadedAt = useGatewayCatalogStore((s) => s.lastLoadedAt)
388
+ useEffect(() => {
389
+ if (sidebarModel) {
390
+ const maxTokens = resolveContextWindowForModel(sidebarModel)
391
+ updateContextTokensMax(maxTokens)
392
+ }
393
+ }, [sidebarModel, updateContextTokensMax, gatewayCatalogLoadedAt])
394
+
395
+ // Commit a /rewind picker selection (FID-2026-0803-004): execute the chosen
396
+ // restore mode against the selected turn's checkpoint and report in-chat.
397
+ const handleRewindPickerConfirm = useCallback(
398
+ (turn: TurnSummary, mode: RewindMode) => {
399
+ closeRewindPicker()
400
+ const projectRoot = tryGetProjectRoot() ?? getProjectRoot()
401
+ const checkpointDir = getCheckpointDir()
402
+ const message = executeRewind({
403
+ checkpointDir,
404
+ projectRoot,
405
+ turnId: turn.turnId,
406
+ mode,
407
+ setMessages,
408
+ })
409
+ setMessages((prev) => [...prev, getSystemMessage(message)])
410
+ setInputFocused(true)
411
+ inputRef.current?.focus()
412
+ },
413
+ [closeRewindPicker, setMessages, setInputFocused, inputRef],
414
+ )
415
+
416
+ // Commit a provider pick: enter providerSetup mode for the chosen provider.
417
+ const handleProviderPickerSelect = useCallback(
418
+ (provider: ProviderSetupName) => {
419
+ closeProviderPicker()
420
+ beginProviderSetup(provider)
421
+ const info = getProviderSetupInfo(provider)
422
+ if (info) {
423
+ useChatStore.getState().setInputMode('providerSetup')
424
+ setInputFocused(true)
425
+ inputRef.current?.focus()
426
+ setMessages((prev) => [
427
+ ...prev,
428
+ getSystemMessage(
429
+ `${info.label} selected. Enter your API key below. It will be masked and stored locally in credentials.json. Environment variables take precedence.`,
430
+ ),
431
+ ])
432
+ }
433
+ },
434
+ [closeProviderPicker, setInputFocused, inputRef, setMessages],
435
+ )
436
+
437
+ // Commit a model pick: persist the override, confirm in-chat, and close.
438
+ const handleModelPickerSelect = useCallback(
439
+ (model: OpenRouterModel) => {
440
+ saveSavantCodeModelPreference(model.id)
441
+ saveSavantCodeModelProviderPreference(model.provider ?? 'openrouter')
442
+ useSavantFreeModelStore.getState().switchModel(model.id)
443
+ const current = loadSavantCodeModelPreference()
444
+ setMessages((prev) => [
445
+ ...prev,
446
+ getSystemMessage(
447
+ current
448
+ ? `Model switched to: ${current}`
449
+ : `Model switched to: ${model.id}`,
450
+ ),
451
+ ])
452
+ closeModelPicker()
453
+ setInputFocused(true)
454
+ inputRef.current?.focus()
455
+ },
456
+ [
457
+ saveSavantCodeModelPreference,
458
+ saveSavantCodeModelProviderPreference,
459
+ loadSavantCodeModelPreference,
460
+ setMessages,
461
+ closeModelPicker,
462
+ setInputFocused,
463
+ inputRef,
464
+ ],
465
+ )
466
+
467
+ // Set initial mode from CLI flag on mount
468
+ useEffect(() => {
469
+ if (initialMode) {
470
+ setAgentMode(initialMode)
471
+ }
472
+ }, [initialMode, setAgentMode])
473
+
474
+ // Set initial permission mode from CLI flag on mount (CLI takes precedence
475
+ // over saved setting because it is an explicit per-launch override).
476
+ useEffect(() => {
477
+ if (initialPermissionMode) {
478
+ useChatStore.getState().setPermissionMode(initialPermissionMode)
479
+ }
480
+ }, [initialPermissionMode])
481
+
482
+ // Use extracted chat messages hook for message tree and pagination
483
+ const {
484
+ messageTree,
485
+ visibleTopLevelMessages,
486
+ hiddenMessageCount,
487
+ handleCollapseToggle,
488
+ isUserCollapsing,
489
+ handleLoadPreviousMessages,
490
+ handleToggleAll,
491
+ } = useChatMessages({ messages, setMessages })
492
+
493
+ // Use extracted UI hook for scroll, terminal dimensions, and theme
494
+ const {
495
+ scrollRef,
496
+ scrollToLatest,
497
+ scrollUp,
498
+ scrollDown,
499
+ appliedScrollboxProps,
500
+ isAtBottom,
501
+ hasOverflow,
502
+ terminalWidth,
503
+ terminalHeight,
504
+ separatorWidth,
505
+ messageAvailableWidth,
506
+ isCompactHeight,
507
+ isNarrowWidth,
508
+ theme,
509
+ markdownPalette,
510
+ } = useChatUI({ messages, isUserCollapsing })
511
+
512
+ const updateHeaderVisibility = useCallback(() => {
513
+ const header = headerRef.current
514
+ const viewport = scrollRef.current?.viewport
515
+ if (!header || !viewport) return
516
+
517
+ const headerTop = header.screenY
518
+ const headerBottom = headerTop + header.height
519
+ const viewportTop = viewport.screenY
520
+ const viewportBottom = viewportTop + viewport.height
521
+ const visible = headerTop < viewportBottom && headerBottom > viewportTop
522
+ setIsHeaderVisible((current) => (current === visible ? current : visible))
523
+ }, [scrollRef])
524
+
525
+ useEffect(() => {
526
+ const scrollbox = scrollRef.current
527
+ if (!scrollbox) return
528
+
529
+ const timeoutId = setTimeout(updateHeaderVisibility, 0)
530
+ scrollbox.verticalScrollBar.on('change', updateHeaderVisibility)
531
+ return () => {
532
+ clearTimeout(timeoutId)
533
+ scrollbox.verticalScrollBar.off('change', updateHeaderVisibility)
534
+ }
535
+ }, [scrollRef, updateHeaderVisibility])
536
+
537
+ useEffect(() => {
538
+ const timeoutId = setTimeout(updateHeaderVisibility, 0)
539
+ return () => clearTimeout(timeoutId)
540
+ }, [messages, terminalHeight, terminalWidth, updateHeaderVisibility])
541
+
542
+ const localAgents = useMemo(() => loadLocalAgents(agentMode), [agentMode])
543
+ const inputMode = useChatStore((state) => state.inputMode)
544
+ const setInputMode = useChatStore((state) => state.setInputMode)
545
+ const askUserState = useChatStore((state) => state.askUserState)
546
+ const adsEnabled = useChatStore((state) => state.adsEnabled)
547
+
548
+ // Get loaded skills for slash commands
549
+ const loadedSkills = useMemo(() => getLoadedSkills(), [])
550
+
551
+ // Filter slash commands based on current ads state - only show the option that changes state
552
+ // Hide both ads commands entirely for subscribers
553
+ // Also merge in skill commands
554
+ const filteredSlashCommands = useMemo(() => {
555
+ const allCommands = getSlashCommandsWithSkills(loadedSkills)
556
+ return allCommands.filter((cmd) => {
557
+ if (cmd.id === 'ads:enable') return !hasSubscription && !adsEnabled
558
+ if (cmd.id === 'ads:disable') return !hasSubscription && adsEnabled
559
+ return true
560
+ })
561
+ // FID-007 P1: deps are the reactive ads store slice (updated by the ads
562
+ // commands) — NOT inputValue, whose only effect was to rebuild the command
563
+ // list and clear the suggestion-engine cache on every keystroke.
564
+ }, [loadedSkills, hasSubscription, adsEnabled])
565
+
566
+ const {
567
+ slashContext,
568
+ mentionContext,
569
+ slashMatches,
570
+ agentMatches,
571
+ fileMatches,
572
+ slashSuggestionItems,
573
+ agentSuggestionItems,
574
+ fileSuggestionItems,
575
+ } = useSuggestionEngine({
576
+ disableAgentSuggestions: forceFileOnlyMentions || inputMode !== 'default',
577
+ inputValue: inputMode === 'bash' ? '' : inputValue,
578
+ cursorPosition,
579
+ slashCommands: filteredSlashCommands,
580
+ localAgents,
581
+ fileTree,
582
+ currentAgentMode: agentMode,
583
+ })
584
+
585
+ useEffect(() => {
586
+ if (!mentionContext.active) {
587
+ setForceFileOnlyMentions(false)
588
+ }
589
+ }, [mentionContext.active])
590
+
591
+ // Track when slash menu is activated
592
+ const prevSlashActiveRef = useRef(false)
593
+ useEffect(() => {
594
+ if (slashContext.active && !prevSlashActiveRef.current) {
595
+ trackEvent(AnalyticsEvent.SLASH_MENU_ACTIVATED, {
596
+ queryLength: slashContext.query.length,
597
+ matchCount: slashMatches.length,
598
+ inputLength: inputValue.length,
599
+ })
600
+ }
601
+ prevSlashActiveRef.current = slashContext.active
602
+ }, [
603
+ slashContext.active,
604
+ slashContext.query,
605
+ slashMatches.length,
606
+ inputValue.length,
607
+ ])
608
+
609
+ // Reset suggestion menu indexes when context changes
610
+ useEffect(() => {
611
+ if (!slashContext.active) {
612
+ setSlashSelectedIndex(0)
613
+ return
614
+ }
615
+ setSlashSelectedIndex(0)
616
+ }, [slashContext.active, slashContext.query, setSlashSelectedIndex])
617
+
618
+ useEffect(() => {
619
+ if (slashMatches.length > 0 && slashSelectedIndex >= slashMatches.length) {
620
+ setSlashSelectedIndex(slashMatches.length - 1)
621
+ }
622
+ if (slashMatches.length === 0 && slashSelectedIndex !== 0) {
623
+ setSlashSelectedIndex(0)
624
+ }
625
+ }, [slashMatches.length, slashSelectedIndex, setSlashSelectedIndex])
626
+
627
+ useEffect(() => {
628
+ if (!mentionContext.active) {
629
+ setAgentSelectedIndex(0)
630
+ return
631
+ }
632
+ setAgentSelectedIndex(0)
633
+ }, [mentionContext.active, mentionContext.query, setAgentSelectedIndex])
634
+
635
+ useEffect(() => {
636
+ const totalMatches = agentMatches.length + fileMatches.length
637
+ if (totalMatches > 0 && agentSelectedIndex >= totalMatches) {
638
+ setAgentSelectedIndex(totalMatches - 1)
639
+ }
640
+ if (totalMatches === 0 && agentSelectedIndex !== 0) {
641
+ setAgentSelectedIndex(0)
642
+ }
643
+ }, [
644
+ agentMatches.length,
645
+ fileMatches.length,
646
+ agentSelectedIndex,
647
+ setAgentSelectedIndex,
648
+ ])
649
+
650
+ const openFileMenuWithTab = useCallback((): boolean => {
651
+ const safeCursor = Math.max(0, Math.min(cursorPosition, inputValue.length))
652
+
653
+ let wordStart = safeCursor
654
+ while (wordStart > 0 && !/\s/.test(inputValue[wordStart - 1]!)) {
655
+ wordStart--
656
+ }
657
+ // FID-007 D4: report whether a word precedes the cursor so the keyboard
658
+ // handler can fall through when there is nothing to complete (previously
659
+ // this scan was duplicated inline in onOpenFileMenuWithTab).
660
+ if (wordStart >= safeCursor) return false
661
+
662
+ const before = inputValue.slice(0, wordStart)
663
+ const wordAtCursor = inputValue.slice(wordStart, safeCursor)
664
+ const after = inputValue.slice(safeCursor)
665
+ const mentionWord = wordAtCursor.startsWith('@')
666
+ ? wordAtCursor
667
+ : `@${wordAtCursor}`
668
+
669
+ const text = `${before}${mentionWord}${after}`
670
+ const nextCursor = before.length + mentionWord.length
671
+
672
+ setInputValue({
673
+ text,
674
+ cursorPosition: nextCursor,
675
+ lastEditDueToNav: false,
676
+ })
677
+ setForceFileOnlyMentions(true)
678
+ return true
679
+ }, [cursorPosition, inputValue, setInputValue])
680
+
681
+ const { saveToHistory, navigateUp, navigateDown, resetHistoryNavigation } =
682
+ useInputHistory(inputValue, setInputValue, { inputMode, setInputMode })
683
+
684
+ // Use extracted streaming hook for connection, timer, queue, and exit handling
685
+ const {
686
+ isConnected,
687
+ showReconnectionMessage,
688
+ mainAgentTimer,
689
+ timerStartTime,
690
+ streamStatus,
691
+ isWaitingForResponse,
692
+ isStreaming,
693
+ setStreamStatus,
694
+ queuedMessages,
695
+ queuePaused,
696
+ streamMessageIdRef,
697
+ addToQueue,
698
+ addToQueueFront,
699
+ stopStreaming,
700
+ setCanProcessQueue,
701
+ pauseQueue,
702
+ resumeQueue,
703
+ clearQueue,
704
+ isQueuePausedRef,
705
+ isProcessingQueueRef,
706
+ queuedCount,
707
+ shouldShowQueuePreview,
708
+ queuePreviewTitle,
709
+ pausedQueueText,
710
+ inputPlaceholder,
711
+ handleCtrlC,
712
+ ensureQueueActiveBeforeSubmit,
713
+ nextCtrlCWillExit,
714
+ } = useChatStreaming({
715
+ agentMode,
716
+ inputValue,
717
+ setInputValue,
718
+ terminalWidth,
719
+ separatorWidth,
720
+ isChainInProgressRef,
721
+ activeAgentStreamsRef,
722
+ sendMessageRef,
723
+ })
724
+
725
+ // When streaming completes, flush any pending bash commands into history (ghost mode only)
726
+ // Non-ghost mode commands are already in history and will be cleared when user sends next message
727
+ useEffect(() => {
728
+ if (
729
+ !isStreaming &&
730
+ !streamMessageIdRef.current &&
731
+ !isChainInProgressRef.current &&
732
+ pendingBashMessages.length > 0
733
+ ) {
734
+ // Only flush ghost mode commands (those not already added to history) to UI
735
+ const ghostModeMessages = pendingBashMessages.filter(
736
+ (msg) => !msg.isRunning && !msg.addedToHistory,
737
+ )
738
+
739
+ // Add ghost mode messages to UI history
740
+ for (const msg of ghostModeMessages) {
741
+ addBashMessageToHistory({
742
+ command: msg.command,
743
+ stdout: msg.stdout,
744
+ stderr: msg.stderr ?? null,
745
+ exitCode: msg.exitCode,
746
+ cwd: msg.cwd || process.cwd(),
747
+ setMessages,
748
+ })
749
+ }
750
+
751
+ // Mark ghost mode messages as added to history (so they don't show as ghost UI)
752
+ // but keep them in pendingBashMessages so they get sent to LLM with next user message
753
+ if (ghostModeMessages.length > 0) {
754
+ const ghostIds = new Set(ghostModeMessages.map((m) => m.id))
755
+ useChatStore.setState((state) => ({
756
+ pendingBashMessages: state.pendingBashMessages.map((m) =>
757
+ ghostIds.has(m.id) ? { ...m, addedToHistory: true } : m,
758
+ ),
759
+ }))
760
+ }
761
+ }
762
+ }, [isStreaming, pendingBashMessages, setMessages])
763
+
764
+ const { sendMessage, clearMessages } = useSendMessage({
765
+ inputRef,
766
+ activeSubagentsRef,
767
+ isChainInProgressRef,
768
+ setStreamStatus,
769
+ setCanProcessQueue,
770
+ abortControllerRef,
771
+ agentId,
772
+ onBeforeMessageSend: validateAgents,
773
+ mainAgentTimer,
774
+ scrollToLatest,
775
+ onTimerEvent: () => {},
776
+ isQueuePausedRef,
777
+ isProcessingQueueRef,
778
+ resumeQueue,
779
+ requeueMessageAtFront: addToQueueFront,
780
+ continueChat,
781
+ continueChatId,
782
+ subscriptionData,
783
+ })
784
+
785
+ sendMessageRef.current = sendMessage
786
+
787
+ // FID-2026-0726-001: mount the loop scheduler so /loop cadence actually
788
+ // recurs. The callback re-submits the loop prompt using the current agentMode.
789
+ useLoopScheduler(
790
+ useCallback(createLoopRunHandler(sendMessage, agentMode), [
791
+ sendMessage,
792
+ agentMode,
793
+ ]),
794
+ )
795
+
796
+ const onSubmitPrompt = useEvent(
797
+ async (
798
+ content: string,
799
+ mode: AgentMode,
800
+ options?: { preserveInputValue?: boolean },
801
+ ) => {
802
+ ensureQueueActiveBeforeSubmit()
803
+
804
+ const preserveInput = options?.preserveInputValue === true
805
+ const previousInputValue = preserveInput
806
+ ? (() => {
807
+ const {
808
+ inputValue: text,
809
+ cursorPosition,
810
+ lastEditDueToNav,
811
+ } = useChatStore.getState()
812
+ return { text, cursorPosition, lastEditDueToNav }
813
+ })()
814
+ : null
815
+
816
+ // Preserve attachments if needed (inline logic to avoid abstraction overhead)
817
+ const preservedAttachments = preserveInput
818
+ ? (() => {
819
+ const items = useChatStore.getState().pendingAttachments
820
+ if (items.length > 0) {
821
+ useChatStore.getState().clearPendingAttachments()
822
+ return [...items]
823
+ }
824
+ return null
825
+ })()
826
+ : null
827
+
828
+ try {
829
+ const result = await routeUserPrompt({
830
+ abortControllerRef,
831
+ agentMode: mode,
832
+ inputRef,
833
+ inputValue: content,
834
+ isChainInProgressRef,
835
+ isStreaming,
836
+ logoutMutation,
837
+ streamMessageIdRef,
838
+ addToQueue,
839
+ clearMessages,
840
+ saveToHistory,
841
+ scrollToLatest,
842
+ sendMessage,
843
+ setCanProcessQueue,
844
+ setInputFocused,
845
+ setInputValue,
846
+ setIsAuthenticated,
847
+ setMessages,
848
+ setUser,
849
+ stopStreaming,
850
+ })
851
+
852
+ return result
853
+ } finally {
854
+ if (previousInputValue) {
855
+ setInputValue({
856
+ text: previousInputValue.text,
857
+ cursorPosition: previousInputValue.cursorPosition,
858
+ lastEditDueToNav: previousInputValue.lastEditDueToNav,
859
+ })
860
+ }
861
+
862
+ // Restore attachments if they were preserved and none have been added since
863
+ if (
864
+ preservedAttachments &&
865
+ useChatStore.getState().pendingAttachments.length === 0
866
+ ) {
867
+ useChatStore.setState((state) => {
868
+ state.pendingAttachments = preservedAttachments
869
+ })
870
+ }
871
+ }
872
+ },
873
+ )
874
+
875
+ // Retire onboarding suggested prompts only after the user submits a prompt.
876
+ // Provider guidance is a system message and must not count as submission.
877
+ // FID-007 D6: memoize the O(n) scan — it was re-run over the whole
878
+ // transcript on every render.
879
+ const hasSubmittedPrompt = useMemo(
880
+ () => messages.some((message) => message.variant === 'user'),
881
+ [messages],
882
+ )
883
+ useEffect(() => {
884
+ if (showSuggestedPrompts && hasSubmittedPrompt) {
885
+ markFirstPromptSubmitted()
886
+ setShowSuggestedPrompts(false)
887
+ }
888
+ }, [showSuggestedPrompts, hasSubmittedPrompt])
889
+
890
+ // Submit a suggested onboarding prompt as if the user had typed and sent it
891
+ const handleSelectSuggestedPrompt = useEvent(
892
+ (prompt: string, selection: SuggestedPromptSelection) => {
893
+ trackEvent(AnalyticsEvent.SUGGESTED_PROMPT_CLICKED, {
894
+ label: selection.label,
895
+ index: selection.index,
896
+ promptLength: prompt.length,
897
+ agentMode,
898
+ })
899
+ onSubmitPrompt(prompt, agentMode).catch((error) => {
900
+ logger.error({ error }, '[suggested-prompt] Failed to submit prompt')
901
+ showClipboardMessage('Failed to send prompt', { durationMs: 3000 })
902
+ })
903
+ },
904
+ )
905
+
906
+ // Handle followup suggestion clicks
907
+ useEffect(() => {
908
+ const handleFollowupClick = (event: Event) => {
909
+ const customEvent = event as CustomEvent<{
910
+ prompt: string
911
+ index: number
912
+ toolCallId: string
913
+ }>
914
+ const { prompt, index, toolCallId } = customEvent.detail
915
+
916
+ logger.info(
917
+ { promptLength: prompt.length, index, toolCallId, agentMode },
918
+ '[followup-click] Followup clicked',
919
+ )
920
+
921
+ // Track analytics event
922
+ trackEvent(AnalyticsEvent.FOLLOWUP_CLICKED, {
923
+ promptLength: prompt.length,
924
+ index,
925
+ agentMode,
926
+ })
927
+
928
+ // Mark this followup as clicked (persisted per toolCallId)
929
+ useChatStore.getState().markFollowupClicked(toolCallId, index)
930
+
931
+ // Send the followup prompt directly, preserving the user's current input
932
+ onSubmitPrompt(prompt, agentMode, {
933
+ preserveInputValue: true,
934
+ })
935
+ .then((result) => {
936
+ logger.info(
937
+ { hasResult: !!result },
938
+ '[followup-click] onSubmitPrompt completed',
939
+ )
940
+ })
941
+ .catch((error) => {
942
+ logger.error(
943
+ { error },
944
+ '[followup-click] onSubmitPrompt failed with error',
945
+ )
946
+ showClipboardMessage('Failed to send followup', { durationMs: 3000 })
947
+ })
948
+ }
949
+
950
+ globalThis.addEventListener(
951
+ 'savant-code:send-followup',
952
+ handleFollowupClick,
953
+ )
954
+ return () => {
955
+ globalThis.removeEventListener(
956
+ 'savant-code:send-followup',
957
+ handleFollowupClick,
958
+ )
959
+ }
960
+ }, [onSubmitPrompt, agentMode])
961
+
962
+ // handleSlashItemClick is defined later after feedback/publish stores are available
963
+
964
+ // FID-007 D3: single mention select-and-replace helper shared by the click
965
+ // handler and the two keyboard menu handlers (previously 3 copies).
966
+ const selectMentionAt = useCallback(
967
+ (index: number): boolean => {
968
+ if (mentionContext.startIndex < 0) return false
969
+
970
+ let replacement: string
971
+ if (index < agentMatches.length) {
972
+ const selected = agentMatches[index]
973
+ if (!selected) return false
974
+ replacement = `@${selected.id} `
975
+ } else {
976
+ const fileIndex = index - agentMatches.length
977
+ const selectedFile = fileMatches[fileIndex]
978
+ if (!selectedFile) return false
979
+ replacement = `@${selectedFile.filePath} `
980
+ }
981
+ const before = inputValue.slice(0, mentionContext.startIndex)
982
+ const after = inputValue.slice(
983
+ mentionContext.startIndex + 1 + mentionContext.query.length,
984
+ )
985
+ setInputValue({
986
+ text: before + replacement + after,
987
+ cursorPosition: before.length + replacement.length,
988
+ lastEditDueToNav: false,
989
+ })
990
+ setAgentSelectedIndex(0)
991
+ return true
992
+ },
993
+ [
994
+ mentionContext,
995
+ agentMatches,
996
+ fileMatches,
997
+ inputValue,
998
+ setInputValue,
999
+ setAgentSelectedIndex,
1000
+ ],
1001
+ )
1002
+
1003
+ const handleMentionItemClick = useCallback(
1004
+ (index: number) => {
1005
+ selectMentionAt(index)
1006
+ },
1007
+ [selectMentionAt],
1008
+ )
1009
+
1010
+ const { inputWidth, handleBuildFast, handleBuildMax, handleBuildLite } =
1011
+ useChatInput({
1012
+ setInputValue,
1013
+ agentMode,
1014
+ setAgentMode,
1015
+ separatorWidth,
1016
+ initialPrompt,
1017
+ onSubmitPrompt,
1018
+ isCompactHeight,
1019
+ isNarrowWidth,
1020
+ })
1021
+
1022
+ const {
1023
+ feedbackMode,
1024
+ feedbackText,
1025
+ openFeedbackForMessage,
1026
+ closeFeedback,
1027
+ saveCurrentInput,
1028
+ restoreSavedInput,
1029
+ setFeedbackText,
1030
+ } = useFeedbackStore(
1031
+ useShallow((state) => ({
1032
+ feedbackMode: state.feedbackMode,
1033
+ feedbackText: state.feedbackText,
1034
+ openFeedbackForMessage: state.openFeedbackForMessage,
1035
+ closeFeedback: state.closeFeedback,
1036
+ saveCurrentInput: state.saveCurrentInput,
1037
+ restoreSavedInput: state.restoreSavedInput,
1038
+ setFeedbackText: state.setFeedbackText,
1039
+ })),
1040
+ )
1041
+
1042
+ const { publishMode, openPublishMode, closePublish, preSelectAgents } =
1043
+ usePublishStore(
1044
+ useShallow((state) => ({
1045
+ publishMode: state.publishMode,
1046
+ openPublishMode: state.openPublishMode,
1047
+ closePublish: state.closePublish,
1048
+ preSelectAgents: state.preSelectAgents,
1049
+ })),
1050
+ )
1051
+
1052
+ const { reviewMode, closeReviewScreen } = useReviewStore(
1053
+ useShallow((state) => ({
1054
+ reviewMode: state.reviewMode,
1055
+ closeReviewScreen: state.closeReviewScreen,
1056
+ })),
1057
+ )
1058
+
1059
+ const publishMutation = usePublishMutation()
1060
+
1061
+ const handleCommandResult = useCallback(
1062
+ (result?: CommandResult) => {
1063
+ if (!result) return
1064
+
1065
+ if (result.openFeedbackMode) {
1066
+ // Save the feedback text that was set by the command handler before opening feedback mode
1067
+ const { feedbackText, feedbackCursor } = useFeedbackStore.getState()
1068
+ saveCurrentInput('', 0)
1069
+ openFeedbackForMessage(null)
1070
+ // Restore the prefilled text after openFeedbackForMessage resets it
1071
+ if (feedbackText) {
1072
+ useFeedbackStore.getState().setFeedbackText(feedbackText)
1073
+ useFeedbackStore.getState().setFeedbackCursor(feedbackCursor)
1074
+ }
1075
+ }
1076
+
1077
+ if (result.openPublishMode) {
1078
+ if (result.preSelectAgents && result.preSelectAgents.length > 0) {
1079
+ // preSelectAgents already sets publishMode: true, so don't call openPublishMode
1080
+ // which would reset the selectedAgentIds
1081
+ preSelectAgents(result.preSelectAgents)
1082
+ } else {
1083
+ openPublishMode()
1084
+ }
1085
+ }
1086
+
1087
+ if (result.openChatHistory) {
1088
+ useChatHistoryStore.getState().openChatHistory()
1089
+ }
1090
+
1091
+ if (result.openReviewScreen) {
1092
+ useReviewStore.getState().openReviewScreen()
1093
+ }
1094
+ },
1095
+ [
1096
+ saveCurrentInput,
1097
+ openFeedbackForMessage,
1098
+ openPublishMode,
1099
+ preSelectAgents,
1100
+ ],
1101
+ )
1102
+
1103
+ // Helper to apply insertText for slash commands - returns true if handled
1104
+ const applySlashInsertText = useCallback(
1105
+ (selected: MatchedSlashCommand): boolean => {
1106
+ if (selected.insertText != null && slashContext.startIndex >= 0) {
1107
+ const before = inputValue.slice(0, slashContext.startIndex)
1108
+ const after = inputValue.slice(
1109
+ slashContext.startIndex + 1 + slashContext.query.length,
1110
+ )
1111
+ setInputValue({
1112
+ text: before + selected.insertText + after,
1113
+ cursorPosition: before.length + selected.insertText.length,
1114
+ lastEditDueToNav: false,
1115
+ })
1116
+ setSlashSelectedIndex(0)
1117
+ return true
1118
+ }
1119
+ return false
1120
+ },
1121
+ [slashContext, inputValue, setInputValue, setSlashSelectedIndex],
1122
+ )
1123
+
1124
+ // Click handler for slash menu items - executes command or inserts text
1125
+ // FID-007 D3/E1: single slash execute helper (insertText handling, selection
1126
+ // reset, and error surfacing) shared by the click handler and keyboard menu.
1127
+ const executeSlashCommand = useCallback(
1128
+ async (selected: MatchedSlashCommand | undefined): Promise<void> => {
1129
+ if (!selected) return
1130
+
1131
+ // If the command has insertText, insert it instead of executing
1132
+ if (applySlashInsertText(selected)) return
1133
+
1134
+ setSlashSelectedIndex(0)
1135
+ try {
1136
+ const result = await onSubmitPrompt(`/${selected.id}`, agentMode)
1137
+ handleCommandResult(result)
1138
+ } catch (error) {
1139
+ logger.error({ error }, '[slash] Failed to execute command')
1140
+ showClipboardMessage('Failed to execute command', { durationMs: 3000 })
1141
+ }
1142
+ },
1143
+ [
1144
+ applySlashInsertText,
1145
+ setSlashSelectedIndex,
1146
+ onSubmitPrompt,
1147
+ agentMode,
1148
+ handleCommandResult,
1149
+ ],
1150
+ )
1151
+
1152
+ const handleSlashItemClick = useCallback(
1153
+ async (index: number) => {
1154
+ await executeSlashCommand(slashMatches[index])
1155
+ },
1156
+ [slashMatches, executeSlashCommand],
1157
+ )
1158
+
1159
+ const inputValueRef = useRef(inputValue)
1160
+ const cursorPositionRef = useRef(cursorPosition)
1161
+ useEffect(() => {
1162
+ inputValueRef.current = inputValue
1163
+ }, [inputValue])
1164
+
1165
+ // Report activity on input changes for ad rotation (debounced via separate effect)
1166
+ const lastReportedActivityRef = useRef<number>(0)
1167
+ useEffect(() => {
1168
+ const now = Date.now()
1169
+ // Throttle to max once per second to avoid excessive calls
1170
+ if (now - lastReportedActivityRef.current > 1000) {
1171
+ lastReportedActivityRef.current = now
1172
+ reportActivity()
1173
+ }
1174
+ }, [inputValue])
1175
+ useEffect(() => {
1176
+ cursorPositionRef.current = cursorPosition
1177
+ }, [cursorPosition])
1178
+
1179
+ const handleOpenFeedbackForMessage = useCallback(
1180
+ (
1181
+ id: string | null,
1182
+ options?: {
1183
+ category?: FeedbackCategory
1184
+ footerMessage?: string
1185
+ errors?: Array<{ id: string; message: string }>
1186
+ },
1187
+ ) => {
1188
+ saveCurrentInput(inputValueRef.current, cursorPositionRef.current)
1189
+ openFeedbackForMessage(id, options)
1190
+ },
1191
+ [saveCurrentInput, openFeedbackForMessage],
1192
+ )
1193
+
1194
+ const handleMessageFeedback = useCallback(
1195
+ (
1196
+ id: string,
1197
+ options?: {
1198
+ category?: FeedbackCategory
1199
+ footerMessage?: string
1200
+ errors?: Array<{ id: string; message: string }>
1201
+ },
1202
+ ) => {
1203
+ handleOpenFeedbackForMessage(id, options)
1204
+ },
1205
+ [handleOpenFeedbackForMessage],
1206
+ )
1207
+
1208
+ const handleExitFeedback = useCallback(() => {
1209
+ const { value, cursor } = restoreSavedInput()
1210
+ setInputValue({
1211
+ text: value,
1212
+ cursorPosition: cursor,
1213
+ lastEditDueToNav: false,
1214
+ })
1215
+ setInputFocused(true)
1216
+ resetHistoryNavigation()
1217
+ }, [
1218
+ restoreSavedInput,
1219
+ setInputValue,
1220
+ setInputFocused,
1221
+ resetHistoryNavigation,
1222
+ ])
1223
+
1224
+ const handleCloseFeedback = useCallback(() => {
1225
+ closeFeedback()
1226
+ handleExitFeedback()
1227
+ }, [closeFeedback, handleExitFeedback])
1228
+
1229
+ const handleExitPublish = useCallback(() => {
1230
+ closePublish()
1231
+ setInputFocused(true)
1232
+ }, [closePublish, setInputFocused])
1233
+
1234
+ const handleReviewOptionSelect = useCallback(
1235
+ (reviewText: string) => {
1236
+ closeReviewScreen()
1237
+ setInputFocused(true)
1238
+ // Submit the review request
1239
+ onSubmitPrompt(reviewText, agentMode)
1240
+ .then((result) => handleCommandResult(result))
1241
+ .catch((error) => {
1242
+ logger.error({ error }, '[review] Failed to submit review prompt')
1243
+ showClipboardMessage('Failed to send review request', {
1244
+ durationMs: 3000,
1245
+ })
1246
+ })
1247
+ },
1248
+ [
1249
+ closeReviewScreen,
1250
+ setInputFocused,
1251
+ onSubmitPrompt,
1252
+ agentMode,
1253
+ handleCommandResult,
1254
+ ],
1255
+ )
1256
+
1257
+ const handleCloseReviewScreen = useCallback(() => {
1258
+ closeReviewScreen()
1259
+ setInputFocused(true)
1260
+ }, [closeReviewScreen, setInputFocused])
1261
+
1262
+ const handleReviewCustom = useCallback(() => {
1263
+ closeReviewScreen()
1264
+ setInputMode('review')
1265
+ setInputFocused(true)
1266
+ }, [closeReviewScreen, setInputMode, setInputFocused])
1267
+
1268
+ const handlePublish = useCallback(
1269
+ async (agentIds: string[]) => {
1270
+ await publishMutation.mutateAsync(agentIds)
1271
+ },
1272
+ [publishMutation],
1273
+ )
1274
+
1275
+ // Ensure bracketed paste events target the active chat input
1276
+ useEffect(() => {
1277
+ if (feedbackMode) {
1278
+ inputRef.current?.focus()
1279
+ return
1280
+ }
1281
+ if (!askUserState) {
1282
+ inputRef.current?.focus()
1283
+ }
1284
+ }, [feedbackMode, askUserState, inputRef])
1285
+
1286
+ const handleSubmit = useCallback(async () => {
1287
+ // Report activity for ad rotation
1288
+ reportActivity()
1289
+ // Update terminal title with truncated user input
1290
+ if (inputValue.trim()) {
1291
+ setTerminalTitle(inputValue)
1292
+ }
1293
+ // FID-007 E1: surface submit failures instead of an unhandled rejection.
1294
+ try {
1295
+ const result = await onSubmitPrompt(inputValue, agentMode)
1296
+ handleCommandResult(result)
1297
+ } catch (error) {
1298
+ logger.error({ error }, '[submit] Failed to submit prompt')
1299
+ showClipboardMessage('Failed to send message', { durationMs: 3000 })
1300
+ }
1301
+ }, [onSubmitPrompt, inputValue, agentMode, handleCommandResult])
1302
+
1303
+ const totalMentionMatches = agentMatches.length + fileMatches.length
1304
+ const historyNavUpEnabled =
1305
+ lastEditDueToNav ||
1306
+ (cursorPosition === 0 &&
1307
+ ((slashContext.active && slashSelectedIndex === 0) ||
1308
+ (mentionContext.active && agentSelectedIndex === 0) ||
1309
+ (!slashContext.active && !mentionContext.active)))
1310
+ const historyNavDownEnabled =
1311
+ lastEditDueToNav ||
1312
+ (cursorPosition === inputValue.length &&
1313
+ ((slashContext.active &&
1314
+ slashSelectedIndex === slashMatches.length - 1) ||
1315
+ (mentionContext.active &&
1316
+ agentSelectedIndex === totalMentionMatches - 1) ||
1317
+ (!slashContext.active && !mentionContext.active)))
1318
+
1319
+ // Build keyboard state from store values
1320
+ const chatKeyboardState: ChatKeyboardState = useMemo(
1321
+ () => ({
1322
+ ...createDefaultChatKeyboardState(),
1323
+ inputMode,
1324
+ inputValue: feedbackMode ? feedbackText : inputValue,
1325
+ cursorPosition,
1326
+ isStreaming,
1327
+ isWaitingForResponse,
1328
+ feedbackMode,
1329
+ focusedAgentId,
1330
+ slashMenuActive: slashContext.active,
1331
+ mentionMenuActive: mentionContext.active,
1332
+ slashSelectedIndex,
1333
+ agentSelectedIndex,
1334
+ slashMatchesLength: slashMatches.length,
1335
+ totalMentionMatches: agentMatches.length + fileMatches.length,
1336
+ disableSlashSuggestions:
1337
+ getInputModeConfig(inputMode).disableSlashSuggestions,
1338
+ modelPickerOpen,
1339
+ historyNavUpEnabled,
1340
+ historyNavDownEnabled,
1341
+ nextCtrlCWillExit,
1342
+ queuePaused,
1343
+ queuedCount,
1344
+ }),
1345
+ [
1346
+ inputMode,
1347
+ inputValue,
1348
+ feedbackText,
1349
+ cursorPosition,
1350
+ isStreaming,
1351
+ isWaitingForResponse,
1352
+ feedbackMode,
1353
+ focusedAgentId,
1354
+ slashContext.active,
1355
+ mentionContext.active,
1356
+ slashSelectedIndex,
1357
+ agentSelectedIndex,
1358
+ slashMatches.length,
1359
+ agentMatches.length,
1360
+ fileMatches.length,
1361
+ historyNavUpEnabled,
1362
+ historyNavDownEnabled,
1363
+ nextCtrlCWillExit,
1364
+ queuePaused,
1365
+ queuedCount,
1366
+ ],
1367
+ )
1368
+
1369
+ // Keyboard handlers
1370
+ const chatKeyboardHandlers: ChatKeyboardHandlers = useMemo(
1371
+ () => ({
1372
+ onExitInputMode: () => setInputMode('default'),
1373
+ onExitFeedbackMode: handleCloseFeedback,
1374
+ onClearFeedbackInput: () => {
1375
+ setFeedbackText('')
1376
+ useFeedbackStore.getState().setFeedbackCursor(0)
1377
+ },
1378
+ onClearInput: () =>
1379
+ setInputValue({ text: '', cursorPosition: 0, lastEditDueToNav: false }),
1380
+ onBackspaceExitMode: () => setInputMode('default'),
1381
+ onInterruptStream: () => {
1382
+ abortControllerRef.current?.abort()
1383
+ if (queuedMessages.length > 0) {
1384
+ pauseQueue()
1385
+ }
1386
+ },
1387
+ onSlashMenuDown: () => setSlashSelectedIndex((prev) => prev + 1),
1388
+ onSlashMenuUp: () => setSlashSelectedIndex((prev) => prev - 1),
1389
+ onSlashMenuSelect: async () => {
1390
+ await executeSlashCommand(
1391
+ slashMatches[slashSelectedIndex] || slashMatches[0],
1392
+ )
1393
+ },
1394
+ onSlashMenuComplete: () => {
1395
+ // Complete the word without executing - same as clicking on the item
1396
+ const selected = slashMatches[slashSelectedIndex] || slashMatches[0]
1397
+ if (!selected || slashContext.startIndex < 0) return
1398
+
1399
+ // If the command has insertText, insert it instead of the command
1400
+ if (applySlashInsertText(selected)) return
1401
+
1402
+ const before = inputValue.slice(0, slashContext.startIndex)
1403
+ const after = inputValue.slice(
1404
+ slashContext.startIndex + 1 + slashContext.query.length,
1405
+ )
1406
+ const replacement = `/${selected.id} `
1407
+ setInputValue({
1408
+ text: before + replacement + after,
1409
+ cursorPosition: before.length + replacement.length,
1410
+ lastEditDueToNav: false,
1411
+ })
1412
+ setSlashSelectedIndex(0)
1413
+ },
1414
+ onMentionMenuDown: () => setAgentSelectedIndex((prev) => prev + 1),
1415
+ onMentionMenuUp: () => setAgentSelectedIndex((prev) => prev - 1),
1416
+ onMentionMenuTab: () => {
1417
+ const totalMatches = agentMatches.length + fileMatches.length
1418
+ setAgentSelectedIndex((prev) => (prev + 1) % totalMatches)
1419
+ },
1420
+ onMentionMenuShiftTab: () => {
1421
+ const totalMatches = agentMatches.length + fileMatches.length
1422
+ setAgentSelectedIndex(
1423
+ (prev) => (totalMatches + prev - 1) % totalMatches,
1424
+ )
1425
+ },
1426
+ onMentionMenuSelect: () => {
1427
+ // Try current selection, fall back to first item
1428
+ selectMentionAt(agentSelectedIndex) || selectMentionAt(0)
1429
+ },
1430
+ onMentionMenuComplete: () => {
1431
+ // Complete the word without executing - same as select for mentions
1432
+ selectMentionAt(agentSelectedIndex) || selectMentionAt(0)
1433
+ },
1434
+ onOpenFileMenuWithTab: () => openFileMenuWithTab(),
1435
+ onHistoryUp: navigateUp,
1436
+ onHistoryDown: navigateDown,
1437
+ onToggleAgentMode: toggleAgentMode,
1438
+ onUnfocusAgent: () => {
1439
+ setFocusedAgentId(null)
1440
+ setInputFocused(true)
1441
+ inputRef.current?.focus()
1442
+ },
1443
+ onClearQueue: clearQueue,
1444
+ onExitAppWarning: () => handleCtrlC(),
1445
+ onExitApp: () => handleCtrlC(),
1446
+ onBashHistoryUp: navigateUp,
1447
+ onBashHistoryDown: navigateDown,
1448
+ onPasteImage: () => {
1449
+ const placeholderPath = addClipboardPlaceholder()
1450
+
1451
+ // Process the image in the background
1452
+ setTimeout(() => {
1453
+ const result = readClipboardImage()
1454
+ if (!result.success || !result.imagePath) {
1455
+ useChatStore.getState().removePendingImage(placeholderPath)
1456
+ showClipboardMessage(result.error || 'Failed to paste image', {
1457
+ durationMs: 3000,
1458
+ })
1459
+ return
1460
+ }
1461
+
1462
+ const cwd = getProjectRoot() ?? process.cwd()
1463
+ addPendingImageFromFile(result.imagePath, cwd, placeholderPath).catch(
1464
+ (error) => {
1465
+ logger.error({ error }, 'Failed to add pending image from file')
1466
+ showClipboardMessage('Failed to add image', { durationMs: 3000 })
1467
+ },
1468
+ )
1469
+ }, 0)
1470
+ },
1471
+ onPasteImagePath: (imagePath: string) => {
1472
+ const cwd = getProjectRoot() ?? process.cwd()
1473
+ validateAndAddImage(imagePath, cwd).catch((error) => {
1474
+ logger.error({ error, imagePath }, 'Failed to validate and add image')
1475
+ showClipboardMessage('Failed to add image', { durationMs: 3000 })
1476
+ })
1477
+ },
1478
+ onPasteFilePath: (filePath: string, isDirectory: boolean) => {
1479
+ addPendingFileFromPath(filePath, isDirectory)
1480
+ },
1481
+ onPasteText: (text: string) => {
1482
+ setInputValue((prev) => {
1483
+ const before = prev.text.slice(0, prev.cursorPosition)
1484
+ const after = prev.text.slice(prev.cursorPosition)
1485
+ return {
1486
+ text: before + text + after,
1487
+ cursorPosition: before.length + text.length,
1488
+ lastEditDueToNav: false,
1489
+ }
1490
+ })
1491
+ },
1492
+ onScrollUp: scrollUp,
1493
+ onScrollDown: scrollDown,
1494
+ onToggleAll: handleToggleAll,
1495
+ onOpenBuyCredits: () => {
1496
+ // If credits have been restored, just return to default mode
1497
+ if (areCreditsRestored()) {
1498
+ setInputMode('default')
1499
+ return
1500
+ }
1501
+ // Otherwise open the buy credits page
1502
+ safeOpen(WEBSITE_URL + '/usage')
1503
+ },
1504
+ }),
1505
+ [
1506
+ setInputMode,
1507
+ handleCloseFeedback,
1508
+ setFeedbackText,
1509
+ setInputValue,
1510
+ abortControllerRef,
1511
+ queuedMessages.length,
1512
+ pauseQueue,
1513
+ setSlashSelectedIndex,
1514
+ slashMatches,
1515
+ slashSelectedIndex,
1516
+ slashContext,
1517
+ inputValue,
1518
+ applySlashInsertText,
1519
+ onSubmitPrompt,
1520
+ agentMode,
1521
+ handleCommandResult,
1522
+ setAgentSelectedIndex,
1523
+ agentSelectedIndex,
1524
+ selectMentionAt,
1525
+ openFileMenuWithTab,
1526
+ navigateUp,
1527
+ navigateDown,
1528
+ toggleAgentMode,
1529
+ setFocusedAgentId,
1530
+ setInputFocused,
1531
+ inputRef,
1532
+ handleCtrlC,
1533
+ clearQueue,
1534
+ scrollUp,
1535
+ scrollDown,
1536
+ handleToggleAll,
1537
+ ],
1538
+ )
1539
+
1540
+ // Use the chat keyboard hook
1541
+ useChatKeyboard({
1542
+ state: chatKeyboardState,
1543
+ handlers: chatKeyboardHandlers,
1544
+ // Disable the global keyboard dispatcher while the model or provider picker
1545
+ // overlay is open so its own useKeyboard handler has exclusive control.
1546
+ disabled:
1547
+ askUserState !== null ||
1548
+ reviewMode ||
1549
+ modelPickerOpen ||
1550
+ providerPickerOpen,
1551
+ })
1552
+
1553
+ // Sync message block context to zustand store for child components
1554
+ const setMessageBlockContext = useMessageBlockStore(
1555
+ (state) => state.setContext,
1556
+ )
1557
+ const setMessageBlockCallbacks = useMessageBlockStore(
1558
+ (state) => state.setCallbacks,
1559
+ )
1560
+
1561
+ // Update context when values change - useLayoutEffect ensures synchronous updates
1562
+ // to prevent message loss during rapid streaming (race condition fix)
1563
+ useLayoutEffect(() => {
1564
+ setMessageBlockContext({
1565
+ theme,
1566
+ markdownPalette,
1567
+ messageTree,
1568
+ isWaitingForResponse,
1569
+ timerStartTime,
1570
+ availableWidth: messageAvailableWidth,
1571
+ responseAds: showInlineAds ? responseAds : {},
1572
+ })
1573
+ }, [
1574
+ theme,
1575
+ markdownPalette,
1576
+ messageTree,
1577
+ isWaitingForResponse,
1578
+ timerStartTime,
1579
+ messageAvailableWidth,
1580
+ responseAds,
1581
+ showInlineAds,
1582
+ setMessageBlockContext,
1583
+ ])
1584
+
1585
+ // Update callbacks once (they're stable)
1586
+ useEffect(() => {
1587
+ setMessageBlockCallbacks({
1588
+ onToggleCollapsed: handleCollapseToggle,
1589
+ onBuildFast: handleBuildFast,
1590
+ onBuildMax: handleBuildMax,
1591
+ onBuildLite: handleBuildLite,
1592
+ onFeedback: handleMessageFeedback,
1593
+ onCloseFeedback: handleCloseFeedback,
1594
+ onAdClick: handleAdClick,
1595
+ onAdImpression: handleAdImpression,
1596
+ onResponseAdsNeeded: handleResponseAdsNeeded,
1597
+ })
1598
+ }, [
1599
+ handleCollapseToggle,
1600
+ handleBuildFast,
1601
+ handleBuildMax,
1602
+ handleBuildLite,
1603
+ handleMessageFeedback,
1604
+ handleCloseFeedback,
1605
+ handleAdClick,
1606
+ handleAdImpression,
1607
+ handleResponseAdsNeeded,
1608
+ setMessageBlockCallbacks,
1609
+ ])
1610
+
1611
+ const modeConfig = getInputModeConfig(inputMode)
1612
+ const hasSlashSuggestions =
1613
+ slashContext.active &&
1614
+ slashSuggestionItems.length > 0 &&
1615
+ !modeConfig.disableSlashSuggestions
1616
+ const hasMentionSuggestions =
1617
+ !slashContext.active &&
1618
+ mentionContext.active &&
1619
+ (agentSuggestionItems.length > 0 || fileSuggestionItems.length > 0)
1620
+ const hasSuggestionMenu = hasSlashSuggestions || hasMentionSuggestions
1621
+
1622
+ // Show first-time onboarding starter prompts only on a pristine, idle,
1623
+ // empty-input default-mode chat — and never while a menu/overlay is up.
1624
+ const showOnboardingPrompts =
1625
+ showSuggestedPrompts &&
1626
+ messages.length === 0 &&
1627
+ inputValue.length === 0 &&
1628
+ inputMode === 'default' &&
1629
+ !hasSuggestionMenu &&
1630
+ !isStreaming &&
1631
+ !isWaitingForResponse &&
1632
+ !feedbackMode &&
1633
+ !publishMode &&
1634
+ !reviewMode &&
1635
+ askUserState === null
1636
+
1637
+ // Fire a one-time impression so we can measure onboarding-prompt usage
1638
+ // (click-through = SUGGESTED_PROMPT_CLICKED / SUGGESTED_PROMPT_SHOWN).
1639
+ const suggestedPromptsShownRef = useRef(false)
1640
+ useEffect(() => {
1641
+ if (showOnboardingPrompts && !suggestedPromptsShownRef.current) {
1642
+ suggestedPromptsShownRef.current = true
1643
+ trackEvent(AnalyticsEvent.SUGGESTED_PROMPT_SHOWN, {
1644
+ count: isCompactHeight
1645
+ ? Math.min(2, DEFAULT_SUGGESTED_PROMPTS.length)
1646
+ : DEFAULT_SUGGESTED_PROMPTS.length,
1647
+ isCompactHeight,
1648
+ })
1649
+ }
1650
+ }, [showOnboardingPrompts, isCompactHeight])
1651
+
1652
+ const inputLayoutMetrics = useMemo(() => {
1653
+ // In bash mode, layout is based on the actual input (no ! prefix needed)
1654
+ const text = inputValue ?? ''
1655
+ const layoutContent = text.length > 0 ? text : ' '
1656
+ const safeCursor = Math.max(
1657
+ 0,
1658
+ Math.min(cursorPosition, layoutContent.length),
1659
+ )
1660
+ const cursorProbe =
1661
+ safeCursor >= layoutContent.length
1662
+ ? layoutContent
1663
+ : layoutContent.slice(0, safeCursor)
1664
+ const cols = Math.max(1, inputWidth)
1665
+ return computeInputLayoutMetrics({
1666
+ layoutContent,
1667
+ cursorProbe,
1668
+ cols,
1669
+ maxHeight: Math.floor(terminalHeight / 2),
1670
+ })
1671
+ }, [inputValue, cursorPosition, inputWidth, terminalHeight])
1672
+ const isMultilineInput = inputLayoutMetrics.heightLines > 1
1673
+ const shouldCenterInputVertically = !hasSuggestionMenu && !isMultilineInput
1674
+ const statusIndicatorState = getStatusIndicatorState({
1675
+ statusMessage,
1676
+ streamStatus,
1677
+ nextCtrlCWillExit,
1678
+ isConnected,
1679
+ authStatus,
1680
+ showReconnectionMessage,
1681
+ isRetrying,
1682
+ isAskUserActive: askUserState !== null,
1683
+ })
1684
+ const hasStatusIndicatorContent = statusIndicatorState.kind !== 'idle'
1685
+
1686
+ // Auto-show subscription limit banner when rate limit becomes active
1687
+ const subscriptionLimitShownRef = useRef(false)
1688
+ const subscriptionRateLimit = subscriptionData?.hasSubscription
1689
+ ? subscriptionData.rateLimit
1690
+ : undefined
1691
+ const fallbackToALaCarte = subscriptionData?.fallbackToALaCarte ?? false
1692
+ useEffect(() => {
1693
+ const isLimited = subscriptionRateLimit?.limited === true
1694
+ if (isLimited && !subscriptionLimitShownRef.current) {
1695
+ subscriptionLimitShownRef.current = true
1696
+ // Skip showing the banner if user prefers to always fall back to a-la-carte
1697
+ if (!fallbackToALaCarte) {
1698
+ useChatStore.getState().setInputMode('subscriptionLimit')
1699
+ }
1700
+ } else if (!isLimited) {
1701
+ subscriptionLimitShownRef.current = false
1702
+ if (useChatStore.getState().inputMode === 'subscriptionLimit') {
1703
+ useChatStore.getState().setInputMode('default')
1704
+ }
1705
+ }
1706
+ }, [subscriptionRateLimit?.limited, fallbackToALaCarte])
1707
+
1708
+ const inputBoxTitle = useMemo(() => {
1709
+ const segments: string[] = []
1710
+
1711
+ if (queuePreviewTitle) {
1712
+ segments.push(queuePreviewTitle)
1713
+ } else if (pausedQueueText) {
1714
+ segments.push(`⏸ ${pausedQueueText}`)
1715
+ }
1716
+
1717
+ if (segments.length === 0) {
1718
+ return undefined
1719
+ }
1720
+
1721
+ return ` ${segments.join(' ')} `
1722
+ }, [queuePreviewTitle, pausedQueueText])
1723
+
1724
+ const hasActiveSavantFreeSession =
1725
+ IS_SAVANT_FREE && savantFreeSession?.status === 'active'
1726
+ const isSavantFreeSessionOver =
1727
+ IS_SAVANT_FREE && savantFreeSession?.status === 'ended'
1728
+ const shouldShowStatusLine =
1729
+ !feedbackMode &&
1730
+ (hasStatusIndicatorContent ||
1731
+ shouldShowQueuePreview ||
1732
+ !isAtBottom ||
1733
+ hasActiveSavantFreeSession)
1734
+
1735
+ // Track mouse movement for ad activity (throttled)
1736
+ const lastMouseActivityRef = useRef<number>(0)
1737
+ const handleMouseActivity = useCallback(() => {
1738
+ const now = Date.now()
1739
+ // Throttle to max once per second
1740
+ if (now - lastMouseActivityRef.current > 1000) {
1741
+ lastMouseActivityRef.current = now
1742
+ reportActivity()
1743
+ }
1744
+ }, [])
1745
+
1746
+ // FID-2026-0722-045: Hide the right sidebar on narrow terminals so the
1747
+ // chat area keeps enough width to remain usable. The sidebar is 40 cols
1748
+ // and supplemental; below this threshold the left column needs the space.
1749
+ const SIDEBAR_MIN_TERMINAL_WIDTH = 100
1750
+
1751
+ const projectRootDisplay = formatCwd(getProjectRoot())
1752
+ const showSidebar = terminalWidth >= SIDEBAR_MIN_TERMINAL_WIDTH
1753
+ const directoryMaxWidth = showSidebar ? terminalWidth - 42 : terminalWidth - 2
1754
+ const directoryDisplay =
1755
+ projectRootDisplay.length > directoryMaxWidth && directoryMaxWidth > 10
1756
+ ? `…${projectRootDisplay.slice(-directoryMaxWidth + 1)}`
1757
+ : projectRootDisplay
1758
+
1759
+ return (
1760
+ <box
1761
+ onMouseMove={handleMouseActivity}
1762
+ focusable={false}
1763
+ style={CHAT_ROOT_STYLE}
1764
+ >
1765
+ {/* Left column: chat content + bottom section */}
1766
+ <box
1767
+ focusable={false}
1768
+ style={{
1769
+ flexDirection: 'column',
1770
+ flexGrow: 1,
1771
+ gap: 0,
1772
+ borderStyle: 'single',
1773
+ borderColor: theme.border,
1774
+ }}
1775
+ >
1776
+ {/* Pinned header — outside scrollbox so it stays at top */}
1777
+ <box
1778
+ ref={headerRef as React.Ref<BoxRenderable>}
1779
+ style={HEADER_BOX_STYLE}
1780
+ focusable={false}
1781
+ >
1782
+ <ChatHeader
1783
+ projectRoot={getProjectRoot()}
1784
+ animationEnabled={isHeaderVisible && inputFocused}
1785
+ />
1786
+ </box>
1787
+
1788
+ <scrollbox
1789
+ ref={scrollRef as React.Ref<ScrollBoxRenderable>}
1790
+ stickyScroll
1791
+ stickyStart="bottom"
1792
+ scrollX={false}
1793
+ scrollbarOptions={{ visible: false }}
1794
+ verticalScrollbarOptions={{
1795
+ visible: !isStreaming && !isWaitingForResponse && hasOverflow,
1796
+ trackOptions: { width: 1 },
1797
+ }}
1798
+ {...appliedScrollboxProps}
1799
+ style={SCROLLBOX_STYLE}
1800
+ >
1801
+ <TopBanner gitRoot={gitRoot} onSwitchToGitRoot={onSwitchToGitRoot} />
1802
+
1803
+ {IS_SAVANT_FREE && (
1804
+ <SavantFreeActiveSessionSummary session={savantFreeSession} />
1805
+ )}
1806
+ {hiddenMessageCount > 0 && (
1807
+ <LoadPreviousButton
1808
+ hiddenCount={hiddenMessageCount}
1809
+ onLoadMore={handleLoadPreviousMessages}
1810
+ />
1811
+ )}
1812
+ {visibleTopLevelMessages.map((message, idx) => (
1813
+ <MessageWithAgents
1814
+ key={message.id}
1815
+ message={message}
1816
+ depth={0}
1817
+ isLastMessage={idx === visibleTopLevelMessages.length - 1}
1818
+ availableWidth={messageAvailableWidth}
1819
+ />
1820
+ ))}
1821
+ {/* Pending bash messages as ghost messages (only show those not already in history) */}
1822
+ {pendingBashMessages
1823
+ .filter((msg) => !msg.addedToHistory)
1824
+ .map((msg) => (
1825
+ <PendingBashMessage
1826
+ key={`pending-bash-${msg.id}`}
1827
+ message={msg}
1828
+ />
1829
+ ))}
1830
+ </scrollbox>
1831
+
1832
+ <box focusable={false} style={BOTTOM_BOX_STYLE}>
1833
+ {showOnboardingPrompts && !reviewMode && !isSavantFreeSessionOver && (
1834
+ <SuggestedPrompts
1835
+ onSelect={handleSelectSuggestedPrompt}
1836
+ maxItems={isCompactHeight ? 2 : undefined}
1837
+ />
1838
+ )}
1839
+
1840
+ {shouldShowStatusLine && (
1841
+ <StatusBar
1842
+ timerStartTime={timerStartTime}
1843
+ isAtBottom={isAtBottom}
1844
+ scrollToLatest={scrollToLatest}
1845
+ statusIndicatorState={statusIndicatorState}
1846
+ onStop={chatKeyboardHandlers.onInterruptStream}
1847
+ onEndSession={() => {
1848
+ setMessages((prev) => [
1849
+ ...prev,
1850
+ getSystemMessage(END_SESSION_MESSAGE),
1851
+ ])
1852
+ returnToSavantFreeLanding({ resetChat: true }).catch(() => {})
1853
+ }}
1854
+ savantFreeSession={savantFreeSession}
1855
+ />
1856
+ )}
1857
+
1858
+ {ads?.[0] && showInlineAds && (
1859
+ <SingleAdBanner
1860
+ ad={ads[0]}
1861
+ onClick={recordClick}
1862
+ onImpression={recordImpression}
1863
+ />
1864
+ )}
1865
+
1866
+ {reviewMode ? (
1867
+ // Review and ask_user take precedence over the session-ended banner:
1868
+ // during the grace window the agent may still be asking to run tools
1869
+ // or asking the user a question, and those approvals/answers must be
1870
+ // reachable for the run to finish — otherwise the agent hangs
1871
+ // waiting for input that can never be given.
1872
+ <ReviewScreen
1873
+ onSelectOption={handleReviewOptionSelect}
1874
+ onCustom={handleReviewCustom}
1875
+ onCancel={handleCloseReviewScreen}
1876
+ />
1877
+ ) : isSavantFreeSessionOver && !askUserState ? (
1878
+ <SessionEndedBanner
1879
+ isStreaming={isStreaming || isWaitingForResponse}
1880
+ />
1881
+ ) : (
1882
+ <>
1883
+ {modelPickerOpen && (
1884
+ <ModelPicker
1885
+ models={modelPickerModels}
1886
+ query={modelPickerQuery}
1887
+ selectedIndex={modelPickerSelectedIndex}
1888
+ onQueryChange={setModelPickerQuery}
1889
+ onSelectIndex={setModelPickerSelectedIndex}
1890
+ onSelect={handleModelPickerSelect}
1891
+ onClose={closeModelPicker}
1892
+ />
1893
+ )}
1894
+ {providerPickerOpen && (
1895
+ <ProviderPicker
1896
+ providers={providerPickerProviders}
1897
+ selectedIndex={providerPickerSelectedIndex}
1898
+ onSelectIndex={setProviderPickerSelectedIndex}
1899
+ onSelect={handleProviderPickerSelect}
1900
+ onClose={closeProviderPicker}
1901
+ />
1902
+ )}
1903
+ {rewindPickerOpen && (
1904
+ <RewindPicker
1905
+ turns={rewindPickerTurns}
1906
+ selectedIndex={rewindPickerSelectedIndex}
1907
+ stage={rewindPickerStage}
1908
+ mode={rewindPickerMode}
1909
+ onSelectIndex={setRewindPickerSelectedIndex}
1910
+ onSetStage={setRewindPickerStage}
1911
+ onSetMode={setRewindPickerMode}
1912
+ onConfirm={handleRewindPickerConfirm}
1913
+ onClose={closeRewindPicker}
1914
+ />
1915
+ )}
1916
+ <box flexDirection="row" paddingLeft={1} focusable={false}>
1917
+ <text fg={theme.muted} wrapMode="none" selectable={false}>
1918
+ {`cwd: ${directoryDisplay}`}
1919
+ </text>
1920
+ </box>
1921
+ <ChatInputBar
1922
+ inputValue={inputValue}
1923
+ cursorPosition={cursorPosition}
1924
+ setInputValue={setInputValue}
1925
+ inputFocused={inputFocused}
1926
+ inputRef={inputRef}
1927
+ inputPlaceholder={inputPlaceholder}
1928
+ lastEditDueToNav={lastEditDueToNav}
1929
+ agentMode={agentMode}
1930
+ toggleAgentMode={toggleAgentMode}
1931
+ setAgentMode={setAgentMode}
1932
+ hasSlashSuggestions={hasSlashSuggestions}
1933
+ hasMentionSuggestions={hasMentionSuggestions}
1934
+ hasSuggestionMenu={hasSuggestionMenu}
1935
+ slashSuggestionItems={slashSuggestionItems}
1936
+ agentSuggestionItems={agentSuggestionItems}
1937
+ fileSuggestionItems={fileSuggestionItems}
1938
+ slashSelectedIndex={slashSelectedIndex}
1939
+ agentSelectedIndex={agentSelectedIndex}
1940
+ onSlashItemClick={handleSlashItemClick}
1941
+ onMentionItemClick={handleMentionItemClick}
1942
+ theme={theme}
1943
+ terminalHeight={terminalHeight}
1944
+ separatorWidth={separatorWidth}
1945
+ shouldCenterInputVertically={shouldCenterInputVertically}
1946
+ inputBoxTitle={inputBoxTitle}
1947
+ isCompactHeight={isCompactHeight}
1948
+ isNarrowWidth={isNarrowWidth}
1949
+ feedbackMode={feedbackMode}
1950
+ handleExitFeedback={handleExitFeedback}
1951
+ publishMode={publishMode}
1952
+ handleExitPublish={handleExitPublish}
1953
+ handlePublish={handlePublish}
1954
+ handleSubmit={handleSubmit}
1955
+ onPaste={createPasteHandler({
1956
+ text: inputValue,
1957
+ cursorPosition,
1958
+ onChange: setInputValue,
1959
+ onPasteImage: chatKeyboardHandlers.onPasteImage,
1960
+ onPasteImagePath: chatKeyboardHandlers.onPasteImagePath,
1961
+ onPasteFilePath: chatKeyboardHandlers.onPasteFilePath,
1962
+ onPasteLongText: (pastedText) => {
1963
+ const id = crypto.randomUUID()
1964
+ const preview = pastedText.slice(0, 100).replace(/\n/g, ' ')
1965
+ useChatStore.getState().addPendingTextAttachment({
1966
+ id,
1967
+ content: pastedText,
1968
+ preview,
1969
+ charCount: pastedText.length,
1970
+ })
1971
+ // Show temporary status message
1972
+ showClipboardMessage(
1973
+ `📋 Pasted text (${pastedText.length.toLocaleString()} chars)`,
1974
+ { durationMs: 5000 },
1975
+ )
1976
+ },
1977
+ cwd: getProjectRoot() ?? process.cwd(),
1978
+ })}
1979
+ onInterruptStream={chatKeyboardHandlers.onInterruptStream}
1980
+ />
1981
+ </>
1982
+ )}
1983
+ </box>
1984
+ </box>
1985
+
1986
+ {/* Right sidebar — session info, tools, history.
1987
+ Hidden on narrow terminals so the chat column remains usable. */}
1988
+ {showSidebar && (
1989
+ <RightSidebar
1990
+ tokensUsed={contextTokensUsed}
1991
+ tokensMax={contextTokensMax}
1992
+ cost={sessionCost}
1993
+ model={sidebarModel || 'unknown'}
1994
+ mode={agentMode}
1995
+ agent={agentId ?? 'Savant'}
1996
+ toolsUsed={toolsUsed}
1997
+ toolsAvailable={SIDEBAR_TOOLS_AVAILABLE}
1998
+ filesChanged={filesChanged}
1999
+ agentStack={agentStack}
2000
+ toolHistory={toolHistory}
2001
+ isStreaming={isStreaming}
2002
+ isWaitingForResponse={isWaitingForResponse}
2003
+ fsmPhase={fsmPhase}
2004
+ />
2005
+ )}
2006
+ </box>
2007
+ )
2008
+ }