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,1875 @@
1
+ import { describe, test, expect } from 'bun:test'
2
+
3
+ import {
4
+ appendTextToRootStream,
5
+ appendTextToAgentBlock,
6
+ appendToolToAgentBlock,
7
+ isNativeReasoningBlock,
8
+ closeNativeReasoningBlock,
9
+ closeNativeReasoningInAgent,
10
+ markAgentComplete,
11
+ markRunningAgentsAsCancelled,
12
+ } from '../block-operations'
13
+ import {
14
+ updateBlocksRecursively,
15
+ scrubPlanTags,
16
+ scrubPlanTagsInBlocks,
17
+ extractPlanFromBuffer,
18
+ createAgentBlock,
19
+ getAgentBaseName,
20
+ autoCollapseBlocks,
21
+ updateToolBlockWithOutput,
22
+ transformAskUserBlocks,
23
+ appendInterruptionNotice,
24
+ extractSpawnAgentResultContent,
25
+ } from '../message-block-helpers'
26
+ import {
27
+ createModeDividerMessage,
28
+ createAiMessageShell,
29
+ createErrorMessage,
30
+ generateAiMessageId,
31
+ autoCollapsePreviousMessages,
32
+ createSpawnAgentBlocks,
33
+ isSpawnAgentsResult,
34
+ markMessageComplete,
35
+ sanitizeRestoredMessages,
36
+ setMessageError,
37
+ } from '../send-message-helpers'
38
+
39
+ import type {
40
+ ContentBlock,
41
+ AgentContentBlock,
42
+ AskUserContentBlock,
43
+ ChatMessage,
44
+ ModeDividerContentBlock,
45
+ TextContentBlock,
46
+ ToolContentBlock,
47
+ } from '../../types/chat'
48
+
49
+ // ============================================================================
50
+ // Block Manipulation Helpers Tests (from message-block-helpers)
51
+ // ============================================================================
52
+
53
+ describe('updateBlocksRecursively', () => {
54
+ test('updates a top-level agent block', () => {
55
+ const blocks: ContentBlock[] = [
56
+ {
57
+ type: 'agent',
58
+ agentId: 'agent-1',
59
+ agentName: 'Test',
60
+ agentType: 'test',
61
+ content: '',
62
+ status: 'running',
63
+ },
64
+ ]
65
+
66
+ const result = updateBlocksRecursively(blocks, 'agent-1', (block) => ({
67
+ ...block,
68
+ status: 'complete' as const,
69
+ }))
70
+
71
+ expect(result[0].type).toBe('agent')
72
+ expect((result[0] as AgentContentBlock).status).toBe('complete')
73
+ })
74
+
75
+ test('updates a nested agent block', () => {
76
+ const blocks: ContentBlock[] = [
77
+ {
78
+ type: 'agent',
79
+ agentId: 'parent',
80
+ agentName: 'Parent',
81
+ agentType: 'parent',
82
+ content: '',
83
+ status: 'running',
84
+ blocks: [
85
+ {
86
+ type: 'agent',
87
+ agentId: 'child',
88
+ agentName: 'Child',
89
+ agentType: 'child',
90
+ content: '',
91
+ status: 'running',
92
+ },
93
+ ],
94
+ },
95
+ ]
96
+
97
+ const result = updateBlocksRecursively(blocks, 'child', (block) => ({
98
+ ...block,
99
+ status: 'complete' as const,
100
+ }))
101
+
102
+ const parent = result[0] as AgentContentBlock
103
+ const child = parent.blocks![0] as AgentContentBlock
104
+ expect(child.status).toBe('complete')
105
+ })
106
+
107
+ test('returns original array if no match found', () => {
108
+ const blocks: ContentBlock[] = [{ type: 'text', content: 'Hello' }]
109
+
110
+ const result = updateBlocksRecursively(blocks, 'nonexistent', (block) => ({
111
+ ...block,
112
+ }))
113
+
114
+ expect(result).toBe(blocks) // Same reference
115
+ })
116
+
117
+ test('does not create new blocks for unchanged nested structures', () => {
118
+ const blocks: ContentBlock[] = [
119
+ {
120
+ type: 'agent',
121
+ agentId: 'agent-1',
122
+ agentName: 'Test',
123
+ agentType: 'test',
124
+ content: '',
125
+ status: 'running',
126
+ blocks: [{ type: 'text', content: 'Nested text' }],
127
+ },
128
+ ]
129
+
130
+ const result = updateBlocksRecursively(blocks, 'nonexistent', (block) => ({
131
+ ...block,
132
+ }))
133
+
134
+ expect(result).toBe(blocks)
135
+ })
136
+ })
137
+
138
+ describe('scrubPlanTags', () => {
139
+ test('removes complete PLAN tags', () => {
140
+ const input = 'Before <PLAN>plan content</cb_plan> After'
141
+ expect(scrubPlanTags(input)).toBe('Before After')
142
+ })
143
+
144
+ test('removes incomplete trailing PLAN tags', () => {
145
+ const input = 'Content <PLAN>incomplete plan'
146
+ expect(scrubPlanTags(input)).toBe('Content ')
147
+ })
148
+
149
+ test('handles string with no PLAN tags', () => {
150
+ const input = 'Just regular content'
151
+ expect(scrubPlanTags(input)).toBe('Just regular content')
152
+ })
153
+
154
+ test('handles empty string', () => {
155
+ expect(scrubPlanTags('')).toBe('')
156
+ })
157
+ })
158
+
159
+ describe('scrubPlanTagsInBlocks', () => {
160
+ test('removes plan tags from text blocks', () => {
161
+ const blocks: ContentBlock[] = [
162
+ { type: 'text', content: 'Hello <PLAN>plan</cb_plan> World' },
163
+ ]
164
+
165
+ const result = scrubPlanTagsInBlocks(blocks)
166
+ expect((result[0] as TextContentBlock).content).toBe('Hello World')
167
+ })
168
+
169
+ test('filters out empty text blocks after scrubbing', () => {
170
+ const blocks: ContentBlock[] = [
171
+ { type: 'text', content: '<PLAN>only plan</cb_plan>' },
172
+ { type: 'text', content: 'Keep this' },
173
+ ]
174
+
175
+ const result = scrubPlanTagsInBlocks(blocks)
176
+ expect(result).toHaveLength(1)
177
+ expect((result[0] as TextContentBlock).content).toBe('Keep this')
178
+ })
179
+
180
+ test('preserves non-text blocks', () => {
181
+ const blocks: ContentBlock[] = [
182
+ {
183
+ type: 'agent',
184
+ agentId: '1',
185
+ agentName: 'Test',
186
+ agentType: 'test',
187
+ content: '',
188
+ status: 'running',
189
+ },
190
+ ]
191
+
192
+ const result = scrubPlanTagsInBlocks(blocks)
193
+ expect(result).toEqual(blocks)
194
+ })
195
+ })
196
+
197
+ // ============================================================================
198
+ // Message Creation Helpers Tests (from send-message-helpers)
199
+ // ============================================================================
200
+
201
+ describe('createModeDividerMessage', () => {
202
+ test('creates a mode divider message', () => {
203
+ const message = createModeDividerMessage('SCAFFOLD')
204
+
205
+ expect(message.variant).toBe('ai')
206
+ expect(message.content).toBe('')
207
+ expect(message.blocks).toHaveLength(1)
208
+ expect(message.blocks![0].type).toBe('mode-divider')
209
+ expect((message.blocks![0] as ModeDividerContentBlock).mode).toBe(
210
+ 'SCAFFOLD',
211
+ )
212
+ expect(message.id).toMatch(/^divider-/)
213
+ })
214
+ })
215
+
216
+ describe('createAiMessageShell', () => {
217
+ test('creates an empty AI message shell', () => {
218
+ const message = createAiMessageShell('ai-123')
219
+
220
+ expect(message.id).toBe('ai-123')
221
+ expect(message.variant).toBe('ai')
222
+ expect(message.content).toBe('')
223
+ expect(message.blocks).toEqual([])
224
+ expect(message.metadata?.allowInlineAds).toBe(true)
225
+ })
226
+ })
227
+
228
+ describe('createErrorMessage', () => {
229
+ test('creates an error message', () => {
230
+ const message = createErrorMessage('Something went wrong')
231
+
232
+ expect(message.variant).toBe('error')
233
+ expect(message.content).toBe('Something went wrong')
234
+ expect(message.id).toMatch(/^error-/)
235
+ })
236
+ })
237
+
238
+ describe('generateAiMessageId', () => {
239
+ test('generates unique IDs', () => {
240
+ const id1 = generateAiMessageId()
241
+ const id2 = generateAiMessageId()
242
+
243
+ expect(id1).toMatch(/^ai-\d+-[a-f0-9]+$/)
244
+ expect(id1).not.toBe(id2)
245
+ })
246
+ })
247
+
248
+ // ============================================================================
249
+ // Auto-Collapse Logic Tests
250
+ // ============================================================================
251
+
252
+ describe('autoCollapseBlocks', () => {
253
+ test('collapses text blocks with thinkingId', () => {
254
+ const blocks: ContentBlock[] = [
255
+ { type: 'text', content: 'thinking', thinkingId: 'think-1' },
256
+ ]
257
+
258
+ const result = autoCollapseBlocks(blocks)
259
+ expect((result[0] as TextContentBlock).thinkingCollapseState).toBe('hidden')
260
+ })
261
+
262
+ test('does not collapse user-opened blocks', () => {
263
+ const blocks: ContentBlock[] = [
264
+ {
265
+ type: 'text',
266
+ content: 'thinking',
267
+ thinkingId: 'think-1',
268
+ userOpened: true,
269
+ },
270
+ ]
271
+
272
+ const result = autoCollapseBlocks(blocks)
273
+ expect((result[0] as TextContentBlock).isCollapsed).toBeUndefined()
274
+ })
275
+
276
+ test('collapses agent blocks', () => {
277
+ const blocks: ContentBlock[] = [
278
+ {
279
+ type: 'agent',
280
+ agentId: '1',
281
+ agentName: 'Test',
282
+ agentType: 'test',
283
+ content: '',
284
+ status: 'running',
285
+ },
286
+ ]
287
+
288
+ const result = autoCollapseBlocks(blocks)
289
+ expect((result[0] as AgentContentBlock).isCollapsed).toBe(true)
290
+ })
291
+
292
+ test('collapses tool blocks', () => {
293
+ const blocks: ContentBlock[] = [
294
+ {
295
+ type: 'tool',
296
+ toolCallId: 'tool-1',
297
+ toolName: 'read_files',
298
+ input: {},
299
+ },
300
+ ]
301
+
302
+ const result = autoCollapseBlocks(blocks)
303
+ expect((result[0] as ToolContentBlock).isCollapsed).toBe(true)
304
+ })
305
+
306
+ test('recursively collapses nested agent blocks', () => {
307
+ const blocks: ContentBlock[] = [
308
+ {
309
+ type: 'agent',
310
+ agentId: 'parent',
311
+ agentName: 'Parent',
312
+ agentType: 'parent',
313
+ content: '',
314
+ status: 'running',
315
+ blocks: [
316
+ {
317
+ type: 'agent',
318
+ agentId: 'child',
319
+ agentName: 'Child',
320
+ agentType: 'child',
321
+ content: '',
322
+ status: 'running',
323
+ },
324
+ ],
325
+ },
326
+ ]
327
+
328
+ const result = autoCollapseBlocks(blocks)
329
+ const parent = result[0] as AgentContentBlock
330
+ const child = parent.blocks![0] as AgentContentBlock
331
+
332
+ expect(parent.isCollapsed).toBe(true)
333
+ expect(child.isCollapsed).toBe(true)
334
+ })
335
+ })
336
+
337
+ describe('autoCollapsePreviousMessages', () => {
338
+ test('does not collapse the current AI message', () => {
339
+ const messages: ChatMessage[] = [
340
+ {
341
+ id: 'ai-123',
342
+ variant: 'ai',
343
+ content: '',
344
+ blocks: [
345
+ {
346
+ type: 'agent',
347
+ agentId: '1',
348
+ agentName: 'Test',
349
+ agentType: 'test',
350
+ content: '',
351
+ status: 'running',
352
+ },
353
+ ],
354
+ timestamp: '',
355
+ },
356
+ ]
357
+
358
+ const result = autoCollapsePreviousMessages(messages, 'ai-123')
359
+ expect(
360
+ (result[0].blocks![0] as AgentContentBlock).isCollapsed,
361
+ ).toBeUndefined()
362
+ })
363
+
364
+ test('collapses previous messages', () => {
365
+ const messages: ChatMessage[] = [
366
+ {
367
+ id: 'ai-old',
368
+ variant: 'ai',
369
+ content: '',
370
+ blocks: [
371
+ {
372
+ type: 'agent',
373
+ agentId: '1',
374
+ agentName: 'Test',
375
+ agentType: 'test',
376
+ content: '',
377
+ status: 'running',
378
+ },
379
+ ],
380
+ timestamp: '',
381
+ },
382
+ {
383
+ id: 'ai-new',
384
+ variant: 'ai',
385
+ content: '',
386
+ blocks: [],
387
+ timestamp: '',
388
+ },
389
+ ]
390
+
391
+ const result = autoCollapsePreviousMessages(messages, 'ai-new')
392
+ expect((result[0].blocks![0] as AgentContentBlock).isCollapsed).toBe(true)
393
+ })
394
+
395
+ test('respects user-opened agent messages', () => {
396
+ const messages: ChatMessage[] = [
397
+ {
398
+ id: 'agent-msg',
399
+ variant: 'agent',
400
+ content: '',
401
+ timestamp: '',
402
+ metadata: { userOpened: true },
403
+ },
404
+ ]
405
+
406
+ const result = autoCollapsePreviousMessages(messages, 'ai-new')
407
+ expect(result[0].metadata?.isCollapsed).toBeUndefined()
408
+ })
409
+ })
410
+
411
+ // ============================================================================
412
+ // Stream Chunk Processing Tests (from block-operations)
413
+ // ============================================================================
414
+
415
+ describe('appendTextToRootStream', () => {
416
+ test('creates new text block for empty blocks array', () => {
417
+ const result = appendTextToRootStream([], { type: 'text', text: 'Hello' })
418
+
419
+ expect(result).toHaveLength(1)
420
+ expect(result[0].type).toBe('text')
421
+ expect((result[0] as TextContentBlock).content).toBe('Hello')
422
+ })
423
+
424
+ test('appends to existing text block of same type', () => {
425
+ const blocks: ContentBlock[] = [
426
+ { type: 'text', content: 'Hello', textType: 'text' },
427
+ ]
428
+
429
+ const result = appendTextToRootStream(blocks, {
430
+ type: 'text',
431
+ text: ' World',
432
+ })
433
+
434
+ expect(result).toHaveLength(1)
435
+ expect((result[0] as TextContentBlock).content).toBe('Hello World')
436
+ })
437
+
438
+ test('creates new block for different text type', () => {
439
+ const blocks: ContentBlock[] = [
440
+ { type: 'text', content: 'Hello', textType: 'text' },
441
+ ]
442
+
443
+ const result = appendTextToRootStream(blocks, {
444
+ type: 'reasoning',
445
+ text: 'Thinking...',
446
+ })
447
+
448
+ expect(result).toHaveLength(2)
449
+ expect((result[1] as TextContentBlock).textType).toBe('reasoning')
450
+ expect((result[1] as TextContentBlock).thinkingCollapseState).toBe(
451
+ 'preview',
452
+ )
453
+ })
454
+
455
+ test('returns original blocks for empty text', () => {
456
+ const blocks: ContentBlock[] = [{ type: 'text', content: 'Hello' }]
457
+
458
+ const result = appendTextToRootStream(blocks, { type: 'text', text: '' })
459
+
460
+ expect(result).toBe(blocks)
461
+ })
462
+
463
+ // Think tag parsing tests
464
+ test('handles unclosed think tag', () => {
465
+ const result = appendTextToRootStream([], {
466
+ type: 'text',
467
+ text: 'Before <think>unclosed thoughts',
468
+ })
469
+
470
+ expect(result).toHaveLength(2)
471
+ expect((result[0] as TextContentBlock).content).toBe('Before ')
472
+ expect((result[1] as TextContentBlock).content).toBe('unclosed thoughts')
473
+ expect((result[1] as TextContentBlock).textType).toBe('reasoning')
474
+ expect((result[1] as TextContentBlock).thinkingOpen).toBe(true)
475
+ })
476
+
477
+ test('continues appending to open thinking block', () => {
478
+ const blocks: ContentBlock[] = [
479
+ {
480
+ type: 'text',
481
+ content: 'initial thoughts',
482
+ textType: 'reasoning',
483
+ isCollapsed: true,
484
+ thinkingOpen: true,
485
+ },
486
+ ]
487
+
488
+ const result = appendTextToRootStream(blocks, {
489
+ type: 'text',
490
+ text: ' more thoughts',
491
+ })
492
+
493
+ expect(result).toHaveLength(1)
494
+ expect((result[0] as TextContentBlock).content).toBe(
495
+ 'initial thoughts more thoughts',
496
+ )
497
+ expect((result[0] as TextContentBlock).textType).toBe('reasoning')
498
+ })
499
+
500
+ test('closes thinking block when close tag received', () => {
501
+ const blocks: ContentBlock[] = [
502
+ {
503
+ type: 'text',
504
+ content: 'initial thoughts',
505
+ textType: 'reasoning',
506
+ isCollapsed: true,
507
+ thinkingOpen: true,
508
+ },
509
+ ]
510
+
511
+ const result = appendTextToRootStream(blocks, {
512
+ type: 'text',
513
+ text: ' final</think> regular text',
514
+ })
515
+
516
+ expect(result).toHaveLength(2)
517
+ expect((result[0] as TextContentBlock).content).toBe(
518
+ 'initial thoughts final',
519
+ )
520
+ expect((result[0] as TextContentBlock).textType).toBe('reasoning')
521
+ expect((result[0] as TextContentBlock).thinkingOpen).toBe(false)
522
+ expect((result[1] as TextContentBlock).content).toBe(' regular text')
523
+ expect((result[1] as TextContentBlock).textType).toBe('text')
524
+ })
525
+
526
+ test('text without think tags works normally', () => {
527
+ const result = appendTextToRootStream([], {
528
+ type: 'text',
529
+ text: 'Just regular text without tags',
530
+ })
531
+
532
+ expect(result).toHaveLength(1)
533
+ expect((result[0] as TextContentBlock).content).toBe(
534
+ 'Just regular text without tags',
535
+ )
536
+ expect((result[0] as TextContentBlock).textType).toBe('text')
537
+ })
538
+
539
+ test('closes thinking block when receiving just </think> tag', () => {
540
+ const blocks: ContentBlock[] = [
541
+ {
542
+ type: 'text',
543
+ content: 'thoughts',
544
+ textType: 'reasoning',
545
+ isCollapsed: true,
546
+ thinkingOpen: true,
547
+ },
548
+ ]
549
+
550
+ const result = appendTextToRootStream(blocks, {
551
+ type: 'text',
552
+ text: '</think>',
553
+ })
554
+
555
+ expect(result).toHaveLength(1)
556
+ expect((result[0] as TextContentBlock).content).toBe('thoughts')
557
+ expect((result[0] as TextContentBlock).textType).toBe('reasoning')
558
+ expect((result[0] as TextContentBlock).thinkingOpen).toBe(false)
559
+ })
560
+
561
+ test('closes thinking block and adds text after </think>', () => {
562
+ const blocks: ContentBlock[] = [
563
+ {
564
+ type: 'text',
565
+ content: 'thoughts',
566
+ textType: 'reasoning',
567
+ isCollapsed: true,
568
+ thinkingOpen: true,
569
+ },
570
+ ]
571
+
572
+ const result = appendTextToRootStream(blocks, {
573
+ type: 'text',
574
+ text: '</think>after',
575
+ })
576
+
577
+ expect(result).toHaveLength(2)
578
+ expect((result[0] as TextContentBlock).content).toBe('thoughts')
579
+ expect((result[0] as TextContentBlock).textType).toBe('reasoning')
580
+ expect((result[0] as TextContentBlock).thinkingOpen).toBe(false)
581
+ expect((result[1] as TextContentBlock).content).toBe('after')
582
+ expect((result[1] as TextContentBlock).textType).toBe('text')
583
+ })
584
+
585
+ // Streaming simulation tests
586
+ test('streaming: does not create duplicate block when closing existing thinking block', () => {
587
+ // Simulate streaming: first chunk opens thinking, second chunk closes it
588
+ // First chunk: '<think>My thoughts' creates open thinking block
589
+ const afterFirstChunk = appendTextToRootStream([], {
590
+ type: 'text',
591
+ text: '<think>My thoughts',
592
+ })
593
+
594
+ expect(afterFirstChunk).toHaveLength(1)
595
+ expect((afterFirstChunk[0] as TextContentBlock).textType).toBe('reasoning')
596
+ expect((afterFirstChunk[0] as TextContentBlock).content).toBe('My thoughts')
597
+ expect((afterFirstChunk[0] as TextContentBlock).thinkingOpen).toBe(true)
598
+
599
+ // Second chunk: '</think> after' should close the block, not create a duplicate
600
+ const afterSecondChunk = appendTextToRootStream(afterFirstChunk, {
601
+ type: 'text',
602
+ text: '</think> after',
603
+ })
604
+
605
+ expect(afterSecondChunk).toHaveLength(2)
606
+ expect((afterSecondChunk[0] as TextContentBlock).textType).toBe('reasoning')
607
+ expect((afterSecondChunk[0] as TextContentBlock).content).toBe(
608
+ 'My thoughts',
609
+ )
610
+ expect((afterSecondChunk[0] as TextContentBlock).thinkingOpen).toBe(false)
611
+ expect((afterSecondChunk[1] as TextContentBlock).textType).toBe('text')
612
+ expect((afterSecondChunk[1] as TextContentBlock).content).toBe(' after')
613
+ })
614
+
615
+ // Native reasoning tests
616
+ test('closes native reasoning block when text arrives', () => {
617
+ // Native reasoning block (thinkingOpen === undefined)
618
+ const blocks: ContentBlock[] = [
619
+ {
620
+ type: 'text',
621
+ content: 'Thinking...',
622
+ textType: 'reasoning',
623
+ isCollapsed: true,
624
+ thinkingId: 'think-1',
625
+ // Note: thinkingOpen is undefined for native reasoning
626
+ },
627
+ ]
628
+
629
+ const result = appendTextToRootStream(blocks, {
630
+ type: 'text',
631
+ text: 'Regular text',
632
+ })
633
+
634
+ expect(result).toHaveLength(2)
635
+ // Native reasoning block should be closed
636
+ expect((result[0] as TextContentBlock).thinkingOpen).toBe(false)
637
+ // New text block added
638
+ expect((result[1] as TextContentBlock).content).toBe('Regular text')
639
+ expect((result[1] as TextContentBlock).textType).toBe('text')
640
+ })
641
+
642
+ test('appends to existing native reasoning block', () => {
643
+ const blocks: ContentBlock[] = [
644
+ {
645
+ type: 'text',
646
+ content: 'First thought',
647
+ textType: 'reasoning',
648
+ isCollapsed: true,
649
+ thinkingId: 'think-1',
650
+ // thinkingOpen is undefined for native reasoning
651
+ },
652
+ ]
653
+
654
+ const result = appendTextToRootStream(blocks, {
655
+ type: 'reasoning',
656
+ text: ' second thought',
657
+ })
658
+
659
+ expect(result).toHaveLength(1)
660
+ expect((result[0] as TextContentBlock).content).toBe(
661
+ 'First thought second thought',
662
+ )
663
+ expect((result[0] as TextContentBlock).textType).toBe('reasoning')
664
+ })
665
+ })
666
+
667
+ // ============================================================================
668
+ // Native Reasoning Block Tests (from block-operations)
669
+ // ============================================================================
670
+
671
+ describe('isNativeReasoningBlock', () => {
672
+ test('returns true for native reasoning block (thinkingOpen undefined)', () => {
673
+ const block: ContentBlock = {
674
+ type: 'text',
675
+ content: 'Thinking...',
676
+ textType: 'reasoning',
677
+ isCollapsed: true,
678
+ thinkingId: 'think-1',
679
+ }
680
+
681
+ expect(isNativeReasoningBlock(block)).toBe(true)
682
+ })
683
+
684
+ test('returns false for closed native reasoning block (thinkingOpen false)', () => {
685
+ const block: ContentBlock = {
686
+ type: 'text',
687
+ content: 'Thinking...',
688
+ textType: 'reasoning',
689
+ isCollapsed: true,
690
+ thinkingOpen: false,
691
+ thinkingId: 'think-1',
692
+ }
693
+
694
+ expect(isNativeReasoningBlock(block)).toBe(false)
695
+ })
696
+
697
+ test('returns false for <think> tag block (thinkingOpen true)', () => {
698
+ const block: ContentBlock = {
699
+ type: 'text',
700
+ content: 'Thinking...',
701
+ textType: 'reasoning',
702
+ isCollapsed: true,
703
+ thinkingOpen: true,
704
+ thinkingId: 'think-1',
705
+ }
706
+
707
+ expect(isNativeReasoningBlock(block)).toBe(false)
708
+ })
709
+
710
+ test('returns false for regular text block', () => {
711
+ const block: ContentBlock = {
712
+ type: 'text',
713
+ content: 'Hello',
714
+ textType: 'text',
715
+ }
716
+
717
+ expect(isNativeReasoningBlock(block)).toBe(false)
718
+ })
719
+
720
+ test('returns false for non-text blocks', () => {
721
+ const agentBlock: ContentBlock = {
722
+ type: 'agent',
723
+ agentId: 'agent-1',
724
+ agentName: 'Test',
725
+ agentType: 'test',
726
+ content: '',
727
+ status: 'running',
728
+ }
729
+
730
+ expect(isNativeReasoningBlock(agentBlock)).toBe(false)
731
+ })
732
+
733
+ test('returns false for undefined', () => {
734
+ expect(isNativeReasoningBlock(undefined)).toBe(false)
735
+ })
736
+ })
737
+
738
+ describe('closeNativeReasoningBlock', () => {
739
+ test('closes native reasoning block by setting thinkingOpen to false', () => {
740
+ const blocks: ContentBlock[] = [
741
+ {
742
+ type: 'text',
743
+ content: 'Thinking...',
744
+ textType: 'reasoning',
745
+ isCollapsed: true,
746
+ thinkingId: 'think-1',
747
+ },
748
+ ]
749
+
750
+ const result = closeNativeReasoningBlock(blocks)
751
+
752
+ expect(result).toHaveLength(1)
753
+ expect((result[0] as TextContentBlock).thinkingOpen).toBe(false)
754
+ expect((result[0] as TextContentBlock).content).toBe('Thinking...')
755
+ })
756
+
757
+ test('returns original blocks if no native reasoning block exists', () => {
758
+ const blocks: ContentBlock[] = [
759
+ { type: 'text', content: 'Hello', textType: 'text' },
760
+ ]
761
+
762
+ const result = closeNativeReasoningBlock(blocks)
763
+
764
+ expect(result).toBe(blocks) // Same reference
765
+ })
766
+
767
+ test('does not close already-closed reasoning blocks', () => {
768
+ const blocks: ContentBlock[] = [
769
+ {
770
+ type: 'text',
771
+ content: 'Already closed',
772
+ textType: 'reasoning',
773
+ isCollapsed: true,
774
+ thinkingOpen: false,
775
+ thinkingId: 'think-1',
776
+ },
777
+ ]
778
+
779
+ const result = closeNativeReasoningBlock(blocks)
780
+
781
+ expect(result).toBe(blocks) // Same reference, no change
782
+ })
783
+
784
+ test('does not close <think> tag blocks (thinkingOpen true)', () => {
785
+ const blocks: ContentBlock[] = [
786
+ {
787
+ type: 'text',
788
+ content: 'Think tag block',
789
+ textType: 'reasoning',
790
+ isCollapsed: true,
791
+ thinkingOpen: true,
792
+ thinkingId: 'think-1',
793
+ },
794
+ ]
795
+
796
+ const result = closeNativeReasoningBlock(blocks)
797
+
798
+ expect(result).toBe(blocks) // Same reference, no change
799
+ })
800
+
801
+ test('finds native reasoning block even when not at end', () => {
802
+ const blocks: ContentBlock[] = [
803
+ {
804
+ type: 'text',
805
+ content: 'Native reasoning',
806
+ textType: 'reasoning',
807
+ isCollapsed: true,
808
+ thinkingId: 'think-1',
809
+ },
810
+ {
811
+ type: 'agent',
812
+ agentId: 'agent-1',
813
+ agentName: 'Test',
814
+ agentType: 'test',
815
+ content: '',
816
+ status: 'running',
817
+ },
818
+ ]
819
+
820
+ const result = closeNativeReasoningBlock(blocks)
821
+
822
+ expect((result[0] as TextContentBlock).thinkingOpen).toBe(false)
823
+ expect(result[1]).toEqual(blocks[1]) // Agent block unchanged
824
+ })
825
+ })
826
+
827
+ describe('closeNativeReasoningInAgent', () => {
828
+ test('closes native reasoning in specific agent', () => {
829
+ const blocks: ContentBlock[] = [
830
+ {
831
+ type: 'agent',
832
+ agentId: 'agent-1',
833
+ agentName: 'Test',
834
+ agentType: 'test',
835
+ content: '',
836
+ status: 'running',
837
+ blocks: [
838
+ {
839
+ type: 'text',
840
+ content: 'Agent thinking...',
841
+ textType: 'reasoning',
842
+ isCollapsed: true,
843
+ thinkingId: 'think-1',
844
+ },
845
+ ],
846
+ },
847
+ ]
848
+
849
+ const result = closeNativeReasoningInAgent(blocks, 'agent-1')
850
+
851
+ const agentBlock = result[0] as AgentContentBlock
852
+ expect((agentBlock.blocks![0] as TextContentBlock).thinkingOpen).toBe(false)
853
+ })
854
+
855
+ test('does not modify other agents', () => {
856
+ const blocks: ContentBlock[] = [
857
+ {
858
+ type: 'agent',
859
+ agentId: 'agent-1',
860
+ agentName: 'Test 1',
861
+ agentType: 'test',
862
+ content: '',
863
+ status: 'running',
864
+ blocks: [
865
+ {
866
+ type: 'text',
867
+ content: 'Agent 1 thinking...',
868
+ textType: 'reasoning',
869
+ isCollapsed: true,
870
+ thinkingId: 'think-1',
871
+ },
872
+ ],
873
+ },
874
+ {
875
+ type: 'agent',
876
+ agentId: 'agent-2',
877
+ agentName: 'Test 2',
878
+ agentType: 'test',
879
+ content: '',
880
+ status: 'running',
881
+ blocks: [
882
+ {
883
+ type: 'text',
884
+ content: 'Agent 2 thinking...',
885
+ textType: 'reasoning',
886
+ isCollapsed: true,
887
+ thinkingId: 'think-2',
888
+ },
889
+ ],
890
+ },
891
+ ]
892
+
893
+ const result = closeNativeReasoningInAgent(blocks, 'agent-1')
894
+
895
+ const agent1 = result[0] as AgentContentBlock
896
+ const agent2 = result[1] as AgentContentBlock
897
+ expect((agent1.blocks![0] as TextContentBlock).thinkingOpen).toBe(false)
898
+ // Agent 2 should still have undefined thinkingOpen
899
+ expect((agent2.blocks![0] as TextContentBlock).thinkingOpen).toBeUndefined()
900
+ })
901
+
902
+ test('returns original blocks if agent not found', () => {
903
+ const blocks: ContentBlock[] = [{ type: 'text', content: 'Hello' }]
904
+
905
+ const result = closeNativeReasoningInAgent(blocks, 'nonexistent')
906
+
907
+ expect(result).toBe(blocks)
908
+ })
909
+ })
910
+
911
+ describe('appendTextToAgentBlock with native reasoning', () => {
912
+ test('creates native reasoning block when textType is reasoning', () => {
913
+ const blocks: ContentBlock[] = [
914
+ {
915
+ type: 'agent',
916
+ agentId: 'agent-1',
917
+ agentName: 'Test',
918
+ agentType: 'test',
919
+ content: '',
920
+ status: 'running',
921
+ blocks: [],
922
+ },
923
+ ]
924
+
925
+ const result = appendTextToAgentBlock(
926
+ blocks,
927
+ 'agent-1',
928
+ 'Thinking...',
929
+ 'reasoning',
930
+ )
931
+
932
+ const agentBlock = result[0] as AgentContentBlock
933
+ expect(agentBlock.blocks).toHaveLength(1)
934
+ expect((agentBlock.blocks![0] as TextContentBlock).textType).toBe(
935
+ 'reasoning',
936
+ )
937
+ expect((agentBlock.blocks![0] as TextContentBlock).content).toBe(
938
+ 'Thinking...',
939
+ )
940
+ expect(
941
+ (agentBlock.blocks![0] as TextContentBlock).thinkingCollapseState,
942
+ ).toBe('preview')
943
+ // Native reasoning has thinkingOpen undefined
944
+ expect(
945
+ (agentBlock.blocks![0] as TextContentBlock).thinkingOpen,
946
+ ).toBeUndefined()
947
+ })
948
+
949
+ test('appends to existing open native reasoning block', () => {
950
+ const blocks: ContentBlock[] = [
951
+ {
952
+ type: 'agent',
953
+ agentId: 'agent-1',
954
+ agentName: 'Test',
955
+ agentType: 'test',
956
+ content: 'First',
957
+ status: 'running',
958
+ blocks: [
959
+ {
960
+ type: 'text',
961
+ content: 'First',
962
+ textType: 'reasoning',
963
+ isCollapsed: true,
964
+ thinkingId: 'think-1',
965
+ },
966
+ ],
967
+ },
968
+ ]
969
+
970
+ const result = appendTextToAgentBlock(
971
+ blocks,
972
+ 'agent-1',
973
+ ' second',
974
+ 'reasoning',
975
+ )
976
+
977
+ const agentBlock = result[0] as AgentContentBlock
978
+ expect(agentBlock.blocks).toHaveLength(1)
979
+ expect((agentBlock.blocks![0] as TextContentBlock).content).toBe(
980
+ 'First second',
981
+ )
982
+ })
983
+
984
+ test('does NOT append to closed native reasoning block', () => {
985
+ const blocks: ContentBlock[] = [
986
+ {
987
+ type: 'agent',
988
+ agentId: 'agent-1',
989
+ agentName: 'Test',
990
+ agentType: 'test',
991
+ content: 'Closed',
992
+ status: 'running',
993
+ blocks: [
994
+ {
995
+ type: 'text',
996
+ content: 'Closed',
997
+ textType: 'reasoning',
998
+ isCollapsed: true,
999
+ thinkingOpen: false, // Already closed
1000
+ thinkingId: 'think-1',
1001
+ },
1002
+ ],
1003
+ },
1004
+ ]
1005
+
1006
+ const result = appendTextToAgentBlock(
1007
+ blocks,
1008
+ 'agent-1',
1009
+ 'New thought',
1010
+ 'reasoning',
1011
+ )
1012
+
1013
+ const agentBlock = result[0] as AgentContentBlock
1014
+ // Should create a NEW reasoning block, not append to closed one
1015
+ expect(agentBlock.blocks).toHaveLength(2)
1016
+ expect((agentBlock.blocks![0] as TextContentBlock).content).toBe('Closed')
1017
+ expect((agentBlock.blocks![1] as TextContentBlock).content).toBe(
1018
+ 'New thought',
1019
+ )
1020
+ })
1021
+
1022
+ test('does NOT append to <think> tag block', () => {
1023
+ const blocks: ContentBlock[] = [
1024
+ {
1025
+ type: 'agent',
1026
+ agentId: 'agent-1',
1027
+ agentName: 'Test',
1028
+ agentType: 'test',
1029
+ content: 'Think tag',
1030
+ status: 'running',
1031
+ blocks: [
1032
+ {
1033
+ type: 'text',
1034
+ content: 'Think tag',
1035
+ textType: 'reasoning',
1036
+ isCollapsed: true,
1037
+ thinkingOpen: true, // <think> tag block
1038
+ thinkingId: 'think-1',
1039
+ },
1040
+ ],
1041
+ },
1042
+ ]
1043
+
1044
+ const result = appendTextToAgentBlock(
1045
+ blocks,
1046
+ 'agent-1',
1047
+ 'Native thought',
1048
+ 'reasoning',
1049
+ )
1050
+
1051
+ const agentBlock = result[0] as AgentContentBlock
1052
+ // Should create a NEW native reasoning block, not append to <think> block
1053
+ expect(agentBlock.blocks).toHaveLength(2)
1054
+ expect((agentBlock.blocks![0] as TextContentBlock).thinkingOpen).toBe(true)
1055
+ expect(
1056
+ (agentBlock.blocks![1] as TextContentBlock).thinkingOpen,
1057
+ ).toBeUndefined()
1058
+ })
1059
+
1060
+ test('closes native reasoning when regular text arrives', () => {
1061
+ const blocks: ContentBlock[] = [
1062
+ {
1063
+ type: 'agent',
1064
+ agentId: 'agent-1',
1065
+ agentName: 'Test',
1066
+ agentType: 'test',
1067
+ content: 'Thinking',
1068
+ status: 'running',
1069
+ blocks: [
1070
+ {
1071
+ type: 'text',
1072
+ content: 'Thinking',
1073
+ textType: 'reasoning',
1074
+ isCollapsed: true,
1075
+ thinkingId: 'think-1',
1076
+ },
1077
+ ],
1078
+ },
1079
+ ]
1080
+
1081
+ const result = appendTextToAgentBlock(
1082
+ blocks,
1083
+ 'agent-1',
1084
+ 'Regular text',
1085
+ 'text',
1086
+ )
1087
+
1088
+ const agentBlock = result[0] as AgentContentBlock
1089
+ expect(agentBlock.blocks).toHaveLength(2)
1090
+ // Native reasoning should be closed
1091
+ expect((agentBlock.blocks![0] as TextContentBlock).thinkingOpen).toBe(false)
1092
+ // New text block added
1093
+ expect((agentBlock.blocks![1] as TextContentBlock).content).toBe(
1094
+ 'Regular text',
1095
+ )
1096
+ expect((agentBlock.blocks![1] as TextContentBlock).textType).toBe('text')
1097
+ })
1098
+ })
1099
+
1100
+ describe('appendToolToAgentBlock closes native reasoning', () => {
1101
+ test('closes native reasoning when tool is appended', () => {
1102
+ const blocks: ContentBlock[] = [
1103
+ {
1104
+ type: 'agent',
1105
+ agentId: 'agent-1',
1106
+ agentName: 'Test',
1107
+ agentType: 'test',
1108
+ content: 'Thinking',
1109
+ status: 'running',
1110
+ blocks: [
1111
+ {
1112
+ type: 'text',
1113
+ content: 'Thinking',
1114
+ textType: 'reasoning',
1115
+ isCollapsed: true,
1116
+ thinkingId: 'think-1',
1117
+ },
1118
+ ],
1119
+ },
1120
+ ]
1121
+
1122
+ const toolBlock: ToolContentBlock = {
1123
+ type: 'tool',
1124
+ toolCallId: 'tool-1',
1125
+ toolName: 'read_files',
1126
+ input: { paths: ['test.ts'] },
1127
+ }
1128
+
1129
+ const result = appendToolToAgentBlock(blocks, 'agent-1', toolBlock)
1130
+
1131
+ const agentBlock = result[0] as AgentContentBlock
1132
+ expect(agentBlock.blocks).toHaveLength(2)
1133
+ // Native reasoning should be closed
1134
+ expect((agentBlock.blocks![0] as TextContentBlock).thinkingOpen).toBe(false)
1135
+ // Tool block added
1136
+ expect(agentBlock.blocks![1].type).toBe('tool')
1137
+ })
1138
+ })
1139
+
1140
+ describe('markAgentComplete closes native reasoning', () => {
1141
+ test('closes native reasoning when agent completes', () => {
1142
+ const blocks: ContentBlock[] = [
1143
+ {
1144
+ type: 'agent',
1145
+ agentId: 'agent-1',
1146
+ agentName: 'Test',
1147
+ agentType: 'test',
1148
+ content: 'Thinking',
1149
+ status: 'running',
1150
+ blocks: [
1151
+ {
1152
+ type: 'text',
1153
+ content: 'Thinking',
1154
+ textType: 'reasoning',
1155
+ isCollapsed: true,
1156
+ thinkingId: 'think-1',
1157
+ },
1158
+ ],
1159
+ },
1160
+ ]
1161
+
1162
+ const result = markAgentComplete(blocks, 'agent-1')
1163
+
1164
+ const agentBlock = result[0] as AgentContentBlock
1165
+ expect(agentBlock.status).toBe('complete')
1166
+ expect((agentBlock.blocks![0] as TextContentBlock).thinkingOpen).toBe(false)
1167
+ })
1168
+ })
1169
+
1170
+ describe('markRunningAgentsAsCancelled closes native reasoning', () => {
1171
+ test('closes native reasoning in cancelled agents', () => {
1172
+ const blocks: ContentBlock[] = [
1173
+ {
1174
+ type: 'agent',
1175
+ agentId: 'agent-1',
1176
+ agentName: 'Test',
1177
+ agentType: 'test',
1178
+ content: 'Thinking',
1179
+ status: 'running',
1180
+ blocks: [
1181
+ {
1182
+ type: 'text',
1183
+ content: 'Thinking',
1184
+ textType: 'reasoning',
1185
+ isCollapsed: true,
1186
+ thinkingId: 'think-1',
1187
+ },
1188
+ ],
1189
+ },
1190
+ ]
1191
+
1192
+ const result = markRunningAgentsAsCancelled(blocks)
1193
+
1194
+ const agentBlock = result[0] as AgentContentBlock
1195
+ expect(agentBlock.status).toBe('cancelled')
1196
+ expect((agentBlock.blocks![0] as TextContentBlock).thinkingOpen).toBe(false)
1197
+ })
1198
+
1199
+ test('closes native reasoning in nested cancelled agents', () => {
1200
+ const blocks: ContentBlock[] = [
1201
+ {
1202
+ type: 'agent',
1203
+ agentId: 'parent',
1204
+ agentName: 'Parent',
1205
+ agentType: 'parent',
1206
+ content: '',
1207
+ status: 'running',
1208
+ blocks: [
1209
+ {
1210
+ type: 'agent',
1211
+ agentId: 'child',
1212
+ agentName: 'Child',
1213
+ agentType: 'child',
1214
+ content: 'Child thinking',
1215
+ status: 'running',
1216
+ blocks: [
1217
+ {
1218
+ type: 'text',
1219
+ content: 'Child thinking',
1220
+ textType: 'reasoning',
1221
+ isCollapsed: true,
1222
+ thinkingId: 'think-child',
1223
+ },
1224
+ ],
1225
+ },
1226
+ ],
1227
+ },
1228
+ ]
1229
+
1230
+ const result = markRunningAgentsAsCancelled(blocks)
1231
+
1232
+ const parentBlock = result[0] as AgentContentBlock
1233
+ const childBlock = parentBlock.blocks![0] as AgentContentBlock
1234
+
1235
+ expect(parentBlock.status).toBe('cancelled')
1236
+ expect(childBlock.status).toBe('cancelled')
1237
+ expect((childBlock.blocks![0] as TextContentBlock).thinkingOpen).toBe(false)
1238
+ })
1239
+
1240
+ test('closes native reasoning even in non-running agents during cancellation', () => {
1241
+ const blocks: ContentBlock[] = [
1242
+ {
1243
+ type: 'agent',
1244
+ agentId: 'agent-1',
1245
+ agentName: 'Test',
1246
+ agentType: 'test',
1247
+ content: '',
1248
+ status: 'complete', // Already complete
1249
+ blocks: [
1250
+ {
1251
+ type: 'agent',
1252
+ agentId: 'child',
1253
+ agentName: 'Child',
1254
+ agentType: 'child',
1255
+ content: 'Thinking',
1256
+ status: 'running',
1257
+ blocks: [
1258
+ {
1259
+ type: 'text',
1260
+ content: 'Thinking',
1261
+ textType: 'reasoning',
1262
+ isCollapsed: true,
1263
+ thinkingId: 'think-1',
1264
+ },
1265
+ ],
1266
+ },
1267
+ ],
1268
+ },
1269
+ ]
1270
+
1271
+ const result = markRunningAgentsAsCancelled(blocks)
1272
+
1273
+ const parentBlock = result[0] as AgentContentBlock
1274
+ const childBlock = parentBlock.blocks![0] as AgentContentBlock
1275
+
1276
+ // Parent stays complete
1277
+ expect(parentBlock.status).toBe('complete')
1278
+ // Child is cancelled
1279
+ expect(childBlock.status).toBe('cancelled')
1280
+ // Child's reasoning is closed
1281
+ expect((childBlock.blocks![0] as TextContentBlock).thinkingOpen).toBe(false)
1282
+ })
1283
+
1284
+ test('does not modify agents without native reasoning blocks', () => {
1285
+ const blocks: ContentBlock[] = [
1286
+ {
1287
+ type: 'agent',
1288
+ agentId: 'agent-1',
1289
+ agentName: 'Test',
1290
+ agentType: 'test',
1291
+ content: 'Hello',
1292
+ status: 'running',
1293
+ blocks: [{ type: 'text', content: 'Hello', textType: 'text' }],
1294
+ },
1295
+ ]
1296
+
1297
+ const result = markRunningAgentsAsCancelled(blocks)
1298
+
1299
+ const agentBlock = result[0] as AgentContentBlock
1300
+ expect(agentBlock.status).toBe('cancelled')
1301
+ // Text block should be unchanged
1302
+ expect(
1303
+ (agentBlock.blocks![0] as TextContentBlock).thinkingOpen,
1304
+ ).toBeUndefined()
1305
+ })
1306
+ })
1307
+
1308
+ describe('extractPlanFromBuffer', () => {
1309
+ test('extracts plan content from complete tags', () => {
1310
+ const buffer = 'Some text <PLAN>This is the plan</PLAN> more text'
1311
+
1312
+ const result = extractPlanFromBuffer(buffer)
1313
+
1314
+ expect(result).toBe('This is the plan')
1315
+ })
1316
+
1317
+ test('returns null for incomplete plan', () => {
1318
+ const buffer = 'Some text <PLAN>Incomplete plan'
1319
+
1320
+ expect(extractPlanFromBuffer(buffer)).toBeNull()
1321
+ })
1322
+
1323
+ test('returns null when no plan tags exist', () => {
1324
+ expect(extractPlanFromBuffer('No plan here')).toBeNull()
1325
+ })
1326
+
1327
+ test('trims whitespace from extracted plan', () => {
1328
+ const buffer = '<PLAN> Trimmed plan </PLAN>'
1329
+
1330
+ expect(extractPlanFromBuffer(buffer)).toBe('Trimmed plan')
1331
+ })
1332
+ })
1333
+
1334
+ // ============================================================================
1335
+ // Agent Block Helpers Tests (from message-block-helpers)
1336
+ // ============================================================================
1337
+
1338
+ describe('createAgentBlock', () => {
1339
+ test('creates an agent block with required fields', () => {
1340
+ const block = createAgentBlock({
1341
+ agentId: 'agent-1',
1342
+ agentType: 'scout',
1343
+ })
1344
+
1345
+ expect(block.type).toBe('agent')
1346
+ expect(block.agentId).toBe('agent-1')
1347
+ expect(block.agentType).toBe('scout')
1348
+ expect(block.status).toBe('running')
1349
+ expect(block.content).toBe('')
1350
+ })
1351
+
1352
+ test('includes optional prompt', () => {
1353
+ const block = createAgentBlock({
1354
+ agentId: 'agent-1',
1355
+ agentType: 'scout',
1356
+ prompt: 'Find files',
1357
+ })
1358
+
1359
+ expect(block.initialPrompt).toBe('Find files')
1360
+ })
1361
+
1362
+ test('includes optional params', () => {
1363
+ const block = createAgentBlock({
1364
+ agentId: 'agent-1',
1365
+ agentType: 'scout',
1366
+ params: { path: '/src' },
1367
+ })
1368
+
1369
+ expect(block.params).toEqual({ path: '/src' })
1370
+ })
1371
+ })
1372
+
1373
+ describe('getAgentBaseName', () => {
1374
+ test('extracts base name from scoped versioned name', () => {
1375
+ expect(getAgentBaseName('savant-code/scout@0.0.2')).toBe('scout')
1376
+ })
1377
+
1378
+ test('extracts base name from simple versioned name', () => {
1379
+ expect(getAgentBaseName('scout@1.0.0')).toBe('scout')
1380
+ })
1381
+
1382
+ test('returns simple name unchanged', () => {
1383
+ expect(getAgentBaseName('scout')).toBe('scout')
1384
+ })
1385
+
1386
+ test('normalizes direct tool aliases to canonical agent names', () => {
1387
+ expect(getAgentBaseName('code_reviewer_lite')).toBe('code-reviewer-lite')
1388
+ })
1389
+ })
1390
+
1391
+ describe('agentTypesMatch', () => {
1392
+ test('matches same base names with different versions', () => {
1393
+ expect(
1394
+ getAgentBaseName('savant-code/scout@0.0.2') ===
1395
+ getAgentBaseName('scout@1.0.0'),
1396
+ ).toBe(true)
1397
+ })
1398
+
1399
+ test('matches same simple names', () => {
1400
+ expect(getAgentBaseName('scout') === getAgentBaseName('scout')).toBe(true)
1401
+ })
1402
+
1403
+ test('does not match different base names', () => {
1404
+ expect(
1405
+ getAgentBaseName('scout') === getAgentBaseName('code-searcher'),
1406
+ ).toBe(false)
1407
+ })
1408
+ })
1409
+
1410
+ // ============================================================================
1411
+ // Tool Block Helpers Tests (from message-block-helpers)
1412
+ // ============================================================================
1413
+
1414
+ describe('updateToolBlockWithOutput', () => {
1415
+ test('updates tool block with output', () => {
1416
+ const blocks: ContentBlock[] = [
1417
+ {
1418
+ type: 'tool',
1419
+ toolCallId: 'tool-1',
1420
+ toolName: 'read_files',
1421
+ input: {},
1422
+ },
1423
+ ]
1424
+
1425
+ const result = updateToolBlockWithOutput(blocks, {
1426
+ toolCallId: 'tool-1',
1427
+ toolOutput: ['File contents'],
1428
+ })
1429
+
1430
+ expect((result[0] as ToolContentBlock).output).toBe('File contents')
1431
+ })
1432
+
1433
+ test('updates nested tool block', () => {
1434
+ const blocks: ContentBlock[] = [
1435
+ {
1436
+ type: 'agent',
1437
+ agentId: 'agent-1',
1438
+ agentName: 'Test',
1439
+ agentType: 'test',
1440
+ content: '',
1441
+ status: 'running',
1442
+ blocks: [
1443
+ {
1444
+ type: 'tool',
1445
+ toolCallId: 'tool-1',
1446
+ toolName: 'read_files',
1447
+ input: {},
1448
+ },
1449
+ ],
1450
+ },
1451
+ ]
1452
+
1453
+ const result = updateToolBlockWithOutput(blocks, {
1454
+ toolCallId: 'tool-1',
1455
+ toolOutput: ['File contents'],
1456
+ })
1457
+ const agent = result[0] as AgentContentBlock
1458
+ expect((agent.blocks![0] as ToolContentBlock).output).toBe('File contents')
1459
+ })
1460
+
1461
+ test('returns same reference if no match', () => {
1462
+ const blocks: ContentBlock[] = [{ type: 'text', content: 'Hello' }]
1463
+
1464
+ const result = updateToolBlockWithOutput(blocks, {
1465
+ toolCallId: 'tool-1',
1466
+ toolOutput: ['Output'],
1467
+ })
1468
+
1469
+ expect(result).toEqual(blocks)
1470
+ })
1471
+ })
1472
+
1473
+ // ============================================================================
1474
+ // Ask User Transformation Tests (from message-block-helpers)
1475
+ // ============================================================================
1476
+
1477
+ describe('transformAskUserBlocks', () => {
1478
+ test('transforms ask_user tool block to ask-user block', () => {
1479
+ const blocks: ContentBlock[] = [
1480
+ {
1481
+ type: 'tool',
1482
+ toolCallId: 'tool-1',
1483
+ toolName: 'ask_user',
1484
+ input: { questions: [{ question: 'Choose?', options: ['A', 'B'] }] },
1485
+ },
1486
+ ]
1487
+
1488
+ const result = transformAskUserBlocks(blocks, {
1489
+ toolCallId: 'tool-1',
1490
+ resultValue: { answers: [{ questionIndex: 0, selectedOption: 'A' }] },
1491
+ })
1492
+
1493
+ expect(result[0].type).toBe('ask-user')
1494
+ expect((result[0] as AskUserContentBlock).answers).toEqual([
1495
+ { questionIndex: 0, selectedOption: 'A' },
1496
+ ])
1497
+ })
1498
+
1499
+ test('keeps tool block if no answers or skipped', () => {
1500
+ const blocks: ContentBlock[] = [
1501
+ {
1502
+ type: 'tool',
1503
+ toolCallId: 'tool-1',
1504
+ toolName: 'ask_user',
1505
+ input: { questions: [] },
1506
+ },
1507
+ ]
1508
+
1509
+ const result = transformAskUserBlocks(blocks, {
1510
+ toolCallId: 'tool-1',
1511
+ resultValue: {},
1512
+ })
1513
+
1514
+ expect(result[0].type).toBe('tool')
1515
+ })
1516
+
1517
+ test('handles skipped state', () => {
1518
+ const blocks: ContentBlock[] = [
1519
+ {
1520
+ type: 'tool',
1521
+ toolCallId: 'tool-1',
1522
+ toolName: 'ask_user',
1523
+ input: { questions: [] },
1524
+ },
1525
+ ]
1526
+
1527
+ const result = transformAskUserBlocks(blocks, {
1528
+ toolCallId: 'tool-1',
1529
+ resultValue: { skipped: true },
1530
+ })
1531
+
1532
+ expect(result[0].type).toBe('ask-user')
1533
+ expect((result[0] as AskUserContentBlock).skipped).toBe(true)
1534
+ })
1535
+ })
1536
+
1537
+ // ============================================================================
1538
+ // Interruption Handling Tests (from message-block-helpers)
1539
+ // ============================================================================
1540
+
1541
+ describe('appendInterruptionNotice', () => {
1542
+ test('appends to existing text block', () => {
1543
+ const blocks: ContentBlock[] = [
1544
+ { type: 'text', content: 'Partial response' },
1545
+ ]
1546
+
1547
+ const result = appendInterruptionNotice(blocks)
1548
+
1549
+ expect((result[0] as TextContentBlock).content).toBe(
1550
+ 'Partial response\n\n[response interrupted]',
1551
+ )
1552
+ })
1553
+
1554
+ test('creates new text block if no existing text', () => {
1555
+ const blocks: ContentBlock[] = []
1556
+
1557
+ const result = appendInterruptionNotice(blocks)
1558
+
1559
+ expect(result).toHaveLength(1)
1560
+ expect((result[0] as TextContentBlock).content).toBe(
1561
+ '[response interrupted]',
1562
+ )
1563
+ })
1564
+
1565
+ test('creates new block if last block is not text', () => {
1566
+ const blocks: ContentBlock[] = [
1567
+ {
1568
+ type: 'tool',
1569
+ toolCallId: 'tool-1',
1570
+ toolName: 'read_files',
1571
+ input: {},
1572
+ },
1573
+ ]
1574
+
1575
+ const result = appendInterruptionNotice(blocks)
1576
+
1577
+ expect(result).toHaveLength(2)
1578
+ expect(result[1].type).toBe('text')
1579
+ })
1580
+ })
1581
+
1582
+ // ============================================================================
1583
+ // Spawn Agents Helpers Tests (from send-message-helpers)
1584
+ // ============================================================================
1585
+
1586
+ describe('createSpawnAgentBlocks', () => {
1587
+ test('creates agent blocks from spawn_agents input', () => {
1588
+ const agents = [
1589
+ { agent_type: 'scout', prompt: 'Find files' },
1590
+ { agent_type: 'code-searcher', prompt: 'Search code' },
1591
+ ]
1592
+
1593
+ const result = createSpawnAgentBlocks('tool-1', agents)
1594
+
1595
+ expect(result).toHaveLength(2)
1596
+ expect(result[0].type).toBe('agent')
1597
+ expect((result[0] as AgentContentBlock).agentId).toBe('tool-1-0')
1598
+ expect((result[1] as AgentContentBlock).agentId).toBe('tool-1-1')
1599
+ })
1600
+
1601
+ test('filters out hidden agents', () => {
1602
+ const agents = [
1603
+ { agent_type: 'scout' },
1604
+ { agent_type: 'savant-code/context-pruner' }, // This should be hidden
1605
+ ]
1606
+
1607
+ const result = createSpawnAgentBlocks('tool-1', agents)
1608
+
1609
+ // context-pruner is in the hidden agents list
1610
+ expect(result.length).toBeLessThanOrEqual(2)
1611
+ })
1612
+ })
1613
+
1614
+ describe('isSpawnAgentsResult', () => {
1615
+ test('returns true for spawn_agents result structure', () => {
1616
+ const output = [{ agentName: 'scout', value: 'result' }]
1617
+
1618
+ expect(isSpawnAgentsResult(output)).toBe(true)
1619
+ })
1620
+
1621
+ test('returns false for non-array', () => {
1622
+ expect(isSpawnAgentsResult('string')).toBe(false)
1623
+ expect(isSpawnAgentsResult(null)).toBe(false)
1624
+ })
1625
+
1626
+ test('returns false for array without agent properties', () => {
1627
+ expect(isSpawnAgentsResult([{ foo: 'bar' }])).toBe(false)
1628
+ })
1629
+ })
1630
+
1631
+ describe('extractSpawnAgentResultContent', () => {
1632
+ test('extracts string value directly', () => {
1633
+ const result = extractSpawnAgentResultContent('Simple result')
1634
+
1635
+ expect(result.content).toBe('Simple result')
1636
+ expect(result.hasError).toBe(false)
1637
+ })
1638
+
1639
+ test('extracts string from value property', () => {
1640
+ const result = extractSpawnAgentResultContent({ value: 'Nested string' })
1641
+
1642
+ expect(result.content).toBe('Nested string')
1643
+ expect(result.hasError).toBe(false)
1644
+ })
1645
+
1646
+ test('extracts error message', () => {
1647
+ const result = extractSpawnAgentResultContent({ errorMessage: 'Failed!' })
1648
+
1649
+ expect(result.content).toBe('Failed!')
1650
+ expect(result.hasError).toBe(true)
1651
+ })
1652
+
1653
+ test('extracts nested error message', () => {
1654
+ const result = extractSpawnAgentResultContent({
1655
+ value: { errorMessage: 'Nested error!' },
1656
+ })
1657
+
1658
+ expect(result.content).toBe('Nested error!')
1659
+ expect(result.hasError).toBe(true)
1660
+ })
1661
+
1662
+ test('extracts message property', () => {
1663
+ const result = extractSpawnAgentResultContent({
1664
+ message: 'Message content',
1665
+ })
1666
+
1667
+ expect(result.content).toBe('Message content')
1668
+ expect(result.hasError).toBe(false)
1669
+ })
1670
+
1671
+ test('extracts nested message property', () => {
1672
+ const result = extractSpawnAgentResultContent({
1673
+ value: { message: 'Nested message' },
1674
+ })
1675
+
1676
+ expect(result.content).toBe('Nested message')
1677
+ expect(result.hasError).toBe(false)
1678
+ })
1679
+
1680
+ test('returns empty for null/undefined', () => {
1681
+ const result = extractSpawnAgentResultContent(null)
1682
+
1683
+ expect(result.content).toBe('')
1684
+ expect(result.hasError).toBe(false)
1685
+ })
1686
+
1687
+ test('returns empty for empty object', () => {
1688
+ const result = extractSpawnAgentResultContent({})
1689
+
1690
+ expect(result.content).toBe('')
1691
+ expect(result.hasError).toBe(false)
1692
+ })
1693
+ })
1694
+
1695
+ // ============================================================================
1696
+ // Message Completion Helpers Tests (from send-message-helpers)
1697
+ // ============================================================================
1698
+
1699
+ describe('markMessageComplete', () => {
1700
+ const baseMessage: ChatMessage = {
1701
+ id: 'msg-1',
1702
+ variant: 'ai',
1703
+ content: 'Hello',
1704
+ timestamp: '',
1705
+ }
1706
+
1707
+ test('marks message as complete', () => {
1708
+ const result = markMessageComplete(baseMessage)
1709
+
1710
+ expect(result.isComplete).toBe(true)
1711
+ })
1712
+
1713
+ test('adds completion time', () => {
1714
+ const result = markMessageComplete(baseMessage, { completionTime: '5s' })
1715
+
1716
+ expect(result.completionTime).toBe('5s')
1717
+ })
1718
+
1719
+ test('adds credits', () => {
1720
+ const result = markMessageComplete(baseMessage, { credits: 100 })
1721
+
1722
+ expect(result.credits).toBe(100)
1723
+ })
1724
+
1725
+ test('adds runState to metadata', () => {
1726
+ const runState = { output: { type: 'text', text: 'Done' } }
1727
+ const result = markMessageComplete(baseMessage, { runState })
1728
+
1729
+ expect(result.metadata?.runState).toEqual(runState)
1730
+ })
1731
+
1732
+ test('preserves existing metadata', () => {
1733
+ const message: ChatMessage = {
1734
+ ...baseMessage,
1735
+ metadata: { userOpened: true },
1736
+ }
1737
+
1738
+ const result = markMessageComplete(message, { credits: 50 })
1739
+
1740
+ expect(result.metadata?.userOpened).toBe(true)
1741
+ })
1742
+ })
1743
+
1744
+ describe('setMessageError', () => {
1745
+ test('sets error content and clears blocks', () => {
1746
+ const message: ChatMessage = {
1747
+ id: 'msg-1',
1748
+ variant: 'ai',
1749
+ content: '',
1750
+ blocks: [{ type: 'text', content: 'Old content' }],
1751
+ timestamp: '',
1752
+ }
1753
+
1754
+ const result = setMessageError(message, 'Error occurred')
1755
+
1756
+ expect(result.content).toBe('Error occurred')
1757
+ expect(result.blocks).toBeUndefined()
1758
+ expect(result.isComplete).toBe(true)
1759
+ })
1760
+ })
1761
+
1762
+ // ============================================================================
1763
+ // sanitizeRestoredMessages Tests
1764
+ // ============================================================================
1765
+
1766
+ describe('sanitizeRestoredMessages', () => {
1767
+ const streamedShell = (
1768
+ overrides: Partial<ChatMessage> = {},
1769
+ ): ChatMessage => ({
1770
+ id: 'ai-1750000000000-abc123',
1771
+ variant: 'ai',
1772
+ content: '',
1773
+ timestamp: new Date().toISOString(),
1774
+ blocks: [{ type: 'text', content: 'partial answer' }],
1775
+ ...overrides,
1776
+ })
1777
+
1778
+ test('marks an interrupted AI response complete with an interruption notice', () => {
1779
+ const [result] = sanitizeRestoredMessages([streamedShell()])
1780
+
1781
+ expect(result.isComplete).toBe(true)
1782
+ const lastBlock = result.blocks![result.blocks!.length - 1]
1783
+ expect(lastBlock.type).toBe('text')
1784
+ expect((lastBlock as TextContentBlock).content).toContain(
1785
+ '[response interrupted]',
1786
+ )
1787
+ })
1788
+
1789
+ test('cancels running agent blocks in an interrupted AI response', () => {
1790
+ const [result] = sanitizeRestoredMessages([
1791
+ streamedShell({
1792
+ blocks: [
1793
+ {
1794
+ type: 'agent',
1795
+ agentId: 'agent-1',
1796
+ agentName: 'TestAgent',
1797
+ agentType: 'inline',
1798
+ content: '',
1799
+ status: 'running',
1800
+ blocks: [],
1801
+ },
1802
+ ],
1803
+ }),
1804
+ ])
1805
+
1806
+ const agentBlock = result.blocks![0] as AgentContentBlock
1807
+ expect(agentBlock.status).toBe('cancelled')
1808
+ })
1809
+
1810
+ test('adds a notice to an interrupted AI response with no blocks yet', () => {
1811
+ const [result] = sanitizeRestoredMessages([streamedShell({ blocks: [] })])
1812
+
1813
+ expect(result.isComplete).toBe(true)
1814
+ expect(result.blocks).toHaveLength(1)
1815
+ expect((result.blocks![0] as TextContentBlock).content).toBe(
1816
+ '[response interrupted]',
1817
+ )
1818
+ })
1819
+
1820
+ test('leaves completed AI responses untouched', () => {
1821
+ const message = streamedShell({ isComplete: true })
1822
+ const [result] = sanitizeRestoredMessages([message])
1823
+
1824
+ expect(result).toBe(message)
1825
+ })
1826
+
1827
+ test('strips the live inline-ad marker from restored responses', () => {
1828
+ const [result] = sanitizeRestoredMessages([
1829
+ streamedShell({
1830
+ isComplete: true,
1831
+ metadata: { allowInlineAds: true, userOpened: true },
1832
+ }),
1833
+ ])
1834
+
1835
+ expect(result.metadata).toEqual({ userOpened: true })
1836
+ })
1837
+
1838
+ test('leaves mode dividers, system messages, and user messages untouched', () => {
1839
+ const divider = createModeDividerMessage('EDIT')
1840
+ const system: ChatMessage = {
1841
+ id: 'sys-123',
1842
+ variant: 'ai',
1843
+ content: 'system notice',
1844
+ timestamp: new Date().toISOString(),
1845
+ }
1846
+ const user: ChatMessage = {
1847
+ id: 'user-123',
1848
+ variant: 'user',
1849
+ content: 'hello',
1850
+ timestamp: new Date().toISOString(),
1851
+ }
1852
+
1853
+ const results = sanitizeRestoredMessages([divider, system, user])
1854
+
1855
+ expect(results[0]).toBe(divider)
1856
+ expect(results[1]).toBe(system)
1857
+ expect(results[2]).toBe(user)
1858
+ })
1859
+ })
1860
+
1861
+ describe('sanitizeRestoredMessages corruption tolerance', () => {
1862
+ test('does not throw on null entries in a restored blocks array', () => {
1863
+ const corrupted: ChatMessage = {
1864
+ id: 'ai-1750000000000-abc123',
1865
+ variant: 'ai',
1866
+ content: '',
1867
+ timestamp: new Date().toISOString(),
1868
+ blocks: [null as unknown as ContentBlock, { type: 'text', content: 'x' }],
1869
+ }
1870
+
1871
+ const [result] = sanitizeRestoredMessages([corrupted])
1872
+
1873
+ expect(result.isComplete).toBe(true)
1874
+ })
1875
+ })