savant-code 0.0.16 → 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 -216
  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,1512 @@
1
+ import * as analytics from '@savant-code/common/analytics'
2
+ import { TEST_USER_ID } from '@savant-code/common/old-constants'
3
+ import {
4
+ createTestAgentRuntimeParams,
5
+ emptyMcpServers,
6
+ } from '@savant-code/common/testing/fixtures/agent-runtime'
7
+ import { clearMockedModules } from '@savant-code/common/testing/mock-modules'
8
+ import {
9
+ createMockDbOperations,
10
+ setupDbSpies,
11
+ } from '@savant-code/common/testing/mocks/database'
12
+ import { getInitialSessionState } from '@savant-code/common/types/session-state'
13
+ import { AbortError, promptSuccess } from '@savant-code/common/util/error'
14
+ import {
15
+ assistantMessage,
16
+ userMessage,
17
+ } from '@savant-code/common/util/messages'
18
+ import { APICallError, RetryError } from 'ai'
19
+ import {
20
+ afterAll,
21
+ afterEach,
22
+ beforeAll,
23
+ beforeEach,
24
+ describe,
25
+ expect,
26
+ it,
27
+ mock,
28
+ spyOn,
29
+ } from 'bun:test'
30
+ import { z } from 'zod/v4'
31
+
32
+ import { createToolCallChunk, mockFileContext } from './test-utils'
33
+ import { loopAgentSteps } from '../run-agent-step'
34
+ import { clearAgentGeneratorCache } from '../run-programmatic-step'
35
+ import { clearThinkerConvergenceStateForTests } from '../tools/thinker-convergence-gate'
36
+ import { clearAllThoughtSessionsForTests } from '../tools/thought-session-store'
37
+
38
+ import type { AgentTemplate } from '../templates/types'
39
+ import type { DbSpies } from '@savant-code/common/testing/mocks/database'
40
+ import type { StepGenerator } from '@savant-code/common/types/agent-template'
41
+ import type { AgentState } from '@savant-code/common/types/session-state'
42
+
43
+ describe('loopAgentSteps - runAgentStep vs runProgrammaticStep behavior', () => {
44
+ let mockTemplate: AgentTemplate
45
+ let mockAgentState: AgentState
46
+ let llmCallCount: number
47
+ let agentRuntimeImpl: Omit<
48
+ ReturnType<typeof createTestAgentRuntimeParams>,
49
+ 'agentTemplate' | 'localAgentTemplates'
50
+ > & {
51
+ promptAiSdkStream?: ReturnType<typeof mock>
52
+ }
53
+ let loopAgentStepsBaseParams: Parameters<typeof loopAgentSteps>[0]
54
+ let dbSpies: DbSpies
55
+
56
+ beforeAll(async () => {
57
+ // Set up mocks.
58
+ })
59
+
60
+ beforeEach(() => {
61
+ const {
62
+ agentTemplate: _,
63
+ localAgentTemplates: __,
64
+ ...baseRuntimeParams
65
+ } = createTestAgentRuntimeParams()
66
+
67
+ agentRuntimeImpl = {
68
+ ...baseRuntimeParams,
69
+ }
70
+
71
+ llmCallCount = 0
72
+
73
+ // Setup spies for database operations using typed helper
74
+ dbSpies = setupDbSpies(createMockDbOperations())
75
+
76
+ agentRuntimeImpl.promptAiSdkStream = mock(async function* ({}) {
77
+ llmCallCount++
78
+ yield { type: 'text' as const, text: 'LLM response\n\n' }
79
+ yield createToolCallChunk('end_turn', {})
80
+ return promptSuccess('mock-message-id')
81
+ })
82
+
83
+ // Mock analytics
84
+ spyOn(analytics, 'trackEvent').mockImplementation(() => {})
85
+
86
+ // Mock crypto.randomUUID
87
+ spyOn(crypto, 'randomUUID').mockImplementation(
88
+ () => 'mock-uuid-0000-0000-0000-000000000000' as const,
89
+ )
90
+
91
+ // Create mock template with programmatic agent
92
+ mockTemplate = {
93
+ id: 'test-agent',
94
+ displayName: 'Test Agent',
95
+ spawnerPrompt: 'Testing',
96
+ model: 'claude-3-5-sonnet-20241022',
97
+ inputSchema: {},
98
+ outputMode: 'structured_output',
99
+ includeMessageHistory: true,
100
+ inheritParentSystemPrompt: false,
101
+ mcpServers: emptyMcpServers,
102
+ toolNames: ['read_files', 'write_file', 'end_turn'],
103
+ spawnableAgents: [],
104
+ systemPrompt: 'Test system prompt',
105
+ instructionsPrompt: 'Test user prompt',
106
+ stepPrompt: 'Test agent step prompt',
107
+ handleSteps: undefined, // Will be set in individual tests
108
+ } satisfies AgentTemplate as AgentTemplate
109
+
110
+ // Create mock agent state
111
+ const sessionState = getInitialSessionState(mockFileContext)
112
+ mockAgentState = {
113
+ ...sessionState.mainAgentState,
114
+ agentId: 'test-agent-id',
115
+ messageHistory: [
116
+ userMessage('Initial message'),
117
+ assistantMessage('Initial response'),
118
+ ],
119
+ output: undefined,
120
+ stepsRemaining: 10, // Ensure we don't hit the limit
121
+ }
122
+
123
+ loopAgentStepsBaseParams = {
124
+ ...agentRuntimeImpl,
125
+ agentType: 'test-agent',
126
+ localAgentTemplates: { 'test-agent': mockTemplate },
127
+ repoId: undefined,
128
+ repoUrl: undefined,
129
+ userInputId: 'test-user-input',
130
+ agentState: mockAgentState,
131
+ prompt: 'Test prompt',
132
+ spawnParams: undefined,
133
+ fingerprintId: 'test-fingerprint',
134
+ fileContext: mockFileContext,
135
+ userId: TEST_USER_ID,
136
+ clientSessionId: 'test-session',
137
+ ancestorRunIds: [],
138
+ onResponseChunk: () => {},
139
+ signal: new AbortController().signal,
140
+ }
141
+ })
142
+
143
+ afterEach(() => {
144
+ clearAgentGeneratorCache(agentRuntimeImpl)
145
+ dbSpies.restore()
146
+ mock.restore()
147
+ const {
148
+ agentTemplate: _,
149
+ localAgentTemplates: __,
150
+ ...baseRuntimeParams
151
+ } = createTestAgentRuntimeParams()
152
+ agentRuntimeImpl = {
153
+ ...baseRuntimeParams,
154
+ }
155
+ })
156
+
157
+ afterAll(() => {
158
+ clearMockedModules()
159
+ })
160
+
161
+ it('should verify correct STEP behavior - LLM called once after STEP', async () => {
162
+ // This test verifies that when a programmatic agent yields STEP,
163
+ // the LLM should be called once in the next iteration
164
+
165
+ let stepCount = 0
166
+ const mockGeneratorFunction = function* () {
167
+ stepCount++
168
+ // Execute a tool, then STEP
169
+ yield { toolName: 'read_files', input: { paths: ['file1.txt'] } }
170
+ yield 'STEP' // Should pause here and let LLM run
171
+ // Continue after LLM runs (this won't be reached in this test since LLM ends turn)
172
+ yield {
173
+ toolName: 'write_file',
174
+ input: { path: 'output.txt', content: 'test' },
175
+ }
176
+ yield { toolName: 'end_turn', input: {} }
177
+ } as () => StepGenerator
178
+
179
+ mockTemplate.handleSteps = mockGeneratorFunction
180
+
181
+ const localAgentTemplates = {
182
+ 'test-agent': mockTemplate,
183
+ }
184
+
185
+ const _result = await loopAgentSteps({
186
+ ...loopAgentStepsBaseParams,
187
+ agentType: 'test-agent',
188
+ localAgentTemplates,
189
+ })
190
+
191
+ console.log(`LLM calls made: ${llmCallCount}`)
192
+ console.log(`Step count: ${stepCount}`)
193
+
194
+ // CORRECT BEHAVIOR: After STEP, LLM should be called once
195
+ // The programmatic agent yields STEP, then LLM runs once and ends turn
196
+ expect(llmCallCount).toBe(1) // LLM called once after STEP
197
+
198
+ // The programmatic agent should have been called once (yielded STEP)
199
+ expect(stepCount).toBe(1)
200
+ })
201
+
202
+ it('should demonstrate correct behavior when programmatic agent completes without STEP', async () => {
203
+ // This test shows that when a programmatic agent doesn't yield STEP,
204
+ // it should complete without calling the LLM at all (since it ends with end_turn)
205
+
206
+ const mockGeneratorFunction = function* () {
207
+ yield { toolName: 'read_files', input: { paths: ['file1.txt'] } }
208
+ yield {
209
+ toolName: 'write_file',
210
+ input: { path: 'output.txt', content: 'test' },
211
+ }
212
+ yield { toolName: 'end_turn', input: {} }
213
+ } as () => StepGenerator
214
+
215
+ mockTemplate.handleSteps = mockGeneratorFunction
216
+
217
+ const localAgentTemplates = {
218
+ 'test-agent': mockTemplate,
219
+ }
220
+
221
+ const result = await loopAgentSteps({
222
+ ...loopAgentStepsBaseParams,
223
+ agentType: 'test-agent',
224
+ localAgentTemplates,
225
+ })
226
+
227
+ // Should NOT call LLM since the programmatic agent ended with end_turn
228
+ expect(llmCallCount).toBe(0)
229
+ // The result should have agentState
230
+ expect(result.agentState).toBeDefined()
231
+ })
232
+
233
+ it('should run programmatic step first, then LLM step, then continue', async () => {
234
+ // This test verifies the correct execution order in loopAgentSteps:
235
+ // 1. Programmatic step runs first and yields STEP
236
+ // 2. LLM step runs once
237
+ // 3. Loop continues but generator is complete after first STEP
238
+
239
+ let stepCount = 0
240
+ const mockGeneratorFunction = function* () {
241
+ stepCount++
242
+ // First execution: do some work, then STEP
243
+ yield { toolName: 'read_files', input: { paths: ['file1.txt'] } }
244
+ yield 'STEP' // Hand control to LLM
245
+ // After LLM runs, continue (this happens in the same generator instance)
246
+ yield {
247
+ toolName: 'write_file',
248
+ input: { path: 'output.txt', content: 'updated by LLM' },
249
+ }
250
+ yield { toolName: 'end_turn', input: {} }
251
+ } as () => StepGenerator
252
+
253
+ mockTemplate.handleSteps = mockGeneratorFunction
254
+
255
+ const localAgentTemplates = {
256
+ 'test-agent': mockTemplate,
257
+ }
258
+
259
+ const result = await loopAgentSteps({
260
+ ...loopAgentStepsBaseParams,
261
+ agentType: 'test-agent',
262
+ localAgentTemplates,
263
+ })
264
+
265
+ // Verify execution order:
266
+ // 1. Programmatic step function was called once (creates generator)
267
+ // 2. LLM was called once after STEP
268
+ // 3. Generator continued after LLM step
269
+ expect(stepCount).toBe(1) // Generator function called once
270
+ expect(llmCallCount).toBe(1) // LLM called once after first STEP
271
+ expect(result.agentState).toBeDefined()
272
+ })
273
+
274
+ it('should handle programmatic agent that yields STEP_ALL', async () => {
275
+ // Test STEP_ALL behavior - should run LLM then continue with programmatic step
276
+
277
+ let stepCount = 0
278
+ const mockGeneratorFunction = function* () {
279
+ stepCount++
280
+ yield { toolName: 'read_files', input: { paths: ['file1.txt'] } }
281
+ yield 'STEP_ALL' // Hand all remaining control to LLM
282
+ // Should continue after LLM completes all its steps
283
+ yield {
284
+ toolName: 'write_file',
285
+ input: { path: 'final.txt', content: 'done' },
286
+ }
287
+ yield { toolName: 'end_turn', input: {} }
288
+ } as () => StepGenerator
289
+
290
+ mockTemplate.handleSteps = mockGeneratorFunction
291
+
292
+ const localAgentTemplates = {
293
+ 'test-agent': mockTemplate,
294
+ }
295
+
296
+ const result = await loopAgentSteps({
297
+ ...loopAgentStepsBaseParams,
298
+ agentType: 'test-agent',
299
+ localAgentTemplates,
300
+ })
301
+
302
+ expect(stepCount).toBe(1) // Generator function called once
303
+ expect(llmCallCount).toBe(1) // LLM should be called once
304
+ expect(result.agentState).toBeDefined()
305
+ })
306
+
307
+ it('should not call LLM when programmatic agent returns without STEP', async () => {
308
+ // Test that programmatic agents that don't yield STEP don't trigger LLM
309
+
310
+ const mockGeneratorFunction = function* () {
311
+ yield { toolName: 'read_files', input: { paths: ['test.txt'] } }
312
+ yield {
313
+ toolName: 'write_file',
314
+ input: { path: 'result.txt', content: 'processed' },
315
+ }
316
+ // No STEP - agent completes without LLM involvement
317
+ yield { toolName: 'end_turn', input: {} }
318
+ } as () => StepGenerator
319
+
320
+ mockTemplate.handleSteps = mockGeneratorFunction
321
+
322
+ const localAgentTemplates = {
323
+ 'test-agent': mockTemplate,
324
+ }
325
+
326
+ const result = await loopAgentSteps({
327
+ ...loopAgentStepsBaseParams,
328
+ agentType: 'test-agent',
329
+ localAgentTemplates,
330
+ })
331
+
332
+ expect(llmCallCount).toBe(0) // No LLM calls should be made
333
+ expect(result.agentState).toBeDefined()
334
+ })
335
+
336
+ it('should handle LLM-only agent (no handleSteps)', async () => {
337
+ // Test traditional LLM-based agents that don't have handleSteps
338
+
339
+ const llmOnlyTemplate = {
340
+ ...mockTemplate,
341
+ handleSteps: undefined, // No programmatic step function
342
+ }
343
+
344
+ const localAgentTemplates = {
345
+ 'test-agent': llmOnlyTemplate,
346
+ }
347
+
348
+ const result = await loopAgentSteps({
349
+ ...loopAgentStepsBaseParams,
350
+ agentType: 'test-agent',
351
+ localAgentTemplates,
352
+ })
353
+
354
+ expect(llmCallCount).toBe(1) // LLM should be called once
355
+ expect(result.agentState).toBeDefined()
356
+ })
357
+
358
+ it('should pass the full message history to the traceWriter when provided', async () => {
359
+ const recordedSteps: Array<{ agentId: string; messages: unknown[] }> = []
360
+ const traceWriter = {
361
+ recordStep: (params: { agentId: string; messages: unknown[] }) => {
362
+ recordedSteps.push(params)
363
+ },
364
+ }
365
+
366
+ const result = await loopAgentSteps({
367
+ ...loopAgentStepsBaseParams,
368
+ traceWriter,
369
+ agentType: 'test-agent',
370
+ localAgentTemplates: {
371
+ 'test-agent': { ...mockTemplate, handleSteps: undefined },
372
+ },
373
+ })
374
+
375
+ expect(result.agentState).toBeDefined()
376
+ // Called at least at the start and end of the step
377
+ expect(recordedSteps.length).toBeGreaterThanOrEqual(2)
378
+ expect(recordedSteps[0]!.agentId).toBe('test-agent-id')
379
+ // End-of-step call sees the assistant response appended to the history
380
+ const lastMessages = recordedSteps[recordedSteps.length - 1]!.messages
381
+ expect(lastMessages.length).toBeGreaterThan(
382
+ recordedSteps[0]!.messages.length,
383
+ )
384
+ })
385
+
386
+ it('should handle programmatic agent error and still call LLM', async () => {
387
+ // Test error handling in programmatic step - should still allow LLM to run
388
+
389
+ const mockGeneratorFunction = function* () {
390
+ yield { toolName: 'read_files', input: { paths: ['file1.txt'] } }
391
+ throw new Error('Programmatic step failed')
392
+ } as () => StepGenerator
393
+
394
+ mockTemplate.handleSteps = mockGeneratorFunction
395
+
396
+ const localAgentTemplates = {
397
+ 'test-agent': mockTemplate,
398
+ }
399
+
400
+ const result = await loopAgentSteps({
401
+ ...loopAgentStepsBaseParams,
402
+ agentType: 'test-agent',
403
+ localAgentTemplates,
404
+ })
405
+
406
+ // After programmatic step error, should end turn and not call LLM
407
+ expect(llmCallCount).toBe(0)
408
+ expect(result.agentState).toBeDefined()
409
+ expect(result.agentState.output?.error).toContain(
410
+ 'Error executing handleSteps for agent test-agent',
411
+ )
412
+ })
413
+
414
+ it('should handle mixed execution with multiple STEP yields', async () => {
415
+ // Test complex scenario with multiple STEP yields and LLM interactions
416
+ // Note: In current implementation, LLM typically ends turn after running,
417
+ // so this tests the first STEP interaction
418
+
419
+ let stepCount = 0
420
+ const mockGeneratorFunction = function* () {
421
+ stepCount++
422
+ yield { toolName: 'read_files', input: { paths: ['input.txt'] } }
423
+ yield 'STEP' // First LLM interaction
424
+ yield {
425
+ toolName: 'write_file',
426
+ input: { path: 'temp.txt', content: 'intermediate' },
427
+ }
428
+ yield {
429
+ toolName: 'write_file',
430
+ input: { path: 'final.txt', content: 'complete' },
431
+ }
432
+ yield { toolName: 'end_turn', input: {} }
433
+ } as () => StepGenerator
434
+
435
+ mockTemplate.handleSteps = mockGeneratorFunction
436
+
437
+ const localAgentTemplates = {
438
+ 'test-agent': mockTemplate,
439
+ }
440
+
441
+ const result = await loopAgentSteps({
442
+ ...loopAgentStepsBaseParams,
443
+ agentType: 'test-agent',
444
+ localAgentTemplates,
445
+ })
446
+
447
+ expect(stepCount).toBe(1) // Generator function called once
448
+ expect(llmCallCount).toBe(1) // LLM called once after STEP
449
+ expect(result.agentState).toBeDefined()
450
+ })
451
+
452
+ it('should pass shouldEndTurn: true as stepsComplete when end_turn tool is called', async () => {
453
+ // Test that when LLM calls end_turn, shouldEndTurn (stepsComplete) is correctly passed
454
+ // to the handleSteps generator via the step result.
455
+ //
456
+ // Flow:
457
+ // 1. Generator yields 'STEP', runProgrammaticStep returns
458
+ // 2. loopAgentSteps calls runAgentStep (LLM), which calls end_turn -> shouldEndTurn = true
459
+ // 3. loopAgentSteps calls runProgrammaticStep again with stepsComplete: true
460
+ // 4. Generator resumes from yield 'STEP' and receives { stepsComplete: true }
461
+
462
+ let stepsCompleteValues: boolean[] = []
463
+
464
+ const mockGeneratorFunction = function* () {
465
+ // First STEP - after LLM runs and calls end_turn, we receive stepsComplete: true
466
+ const result1 = yield 'STEP'
467
+ stepsCompleteValues.push(result1.stepsComplete)
468
+
469
+ // Since stepsComplete was true, we should end gracefully
470
+ yield { toolName: 'end_turn', input: {} }
471
+ } as () => StepGenerator
472
+
473
+ mockTemplate.handleSteps = mockGeneratorFunction
474
+
475
+ const localAgentTemplates = {
476
+ 'test-agent': mockTemplate,
477
+ }
478
+
479
+ await loopAgentSteps({
480
+ ...loopAgentStepsBaseParams,
481
+ agentType: 'test-agent',
482
+ localAgentTemplates,
483
+ })
484
+
485
+ // Verify that stepsComplete was passed correctly:
486
+ // After yielding STEP and LLM running (which calls end_turn),
487
+ // the generator receives stepsComplete: true
488
+ expect(stepsCompleteValues).toHaveLength(1)
489
+ expect(stepsCompleteValues[0]).toBe(true)
490
+ })
491
+
492
+ it('should continue loop when handleSteps returns endTurn: false even if LLM calls end_turn', async () => {
493
+ // Test that handleSteps endTurn: false takes precedence over LLM end_turn tool call
494
+
495
+ let programmaticStepCount = 0
496
+ let llmStepCount = 0
497
+
498
+ const mockGeneratorFunction = function* () {
499
+ // First iteration: return endTurn: false
500
+ programmaticStepCount++
501
+ yield 'STEP'
502
+
503
+ // Second iteration: also return endTurn: false
504
+ programmaticStepCount++
505
+ yield 'STEP'
506
+
507
+ // Third iteration: finally return endTurn: true to end the loop
508
+ programmaticStepCount++
509
+ yield { toolName: 'end_turn', input: {} }
510
+ } as () => StepGenerator
511
+
512
+ mockTemplate.handleSteps = mockGeneratorFunction
513
+
514
+ const localAgentTemplates = {
515
+ 'test-agent': mockTemplate,
516
+ }
517
+
518
+ // Mock LLM to always call end_turn, but handleSteps should override it
519
+ let promptCallCount = 0
520
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
521
+ promptCallCount++
522
+ llmStepCount++
523
+
524
+ // LLM always tries to end turn
525
+ yield { type: 'text' as const, text: 'LLM response\n\n' }
526
+ yield createToolCallChunk('end_turn', {})
527
+ return promptSuccess(`mock-message-id-${promptCallCount}`)
528
+ }
529
+
530
+ await loopAgentSteps({
531
+ ...loopAgentStepsBaseParams,
532
+ agentType: 'test-agent',
533
+ localAgentTemplates,
534
+ })
535
+
536
+ // Verify handleSteps ran 3 times (yielded STEP twice, then end_turn)
537
+ expect(programmaticStepCount).toBe(3)
538
+
539
+ // Verify LLM was called 2 times (once per STEP yield)
540
+ expect(llmStepCount).toBe(2)
541
+
542
+ // This confirms that even though LLM called end_turn every time,
543
+ // the loop continued because handleSteps kept yielding STEP before finally ending
544
+ })
545
+
546
+ it('should restart loop when agent finishes without setting required output', async () => {
547
+ // Test that when an agent has outputSchema but finishes without calling set_output,
548
+ // the loop restarts with a system message
549
+
550
+ const outputSchema = z.object({
551
+ result: z.string(),
552
+ status: z.string(),
553
+ })
554
+
555
+ const templateWithOutputSchema = {
556
+ ...mockTemplate,
557
+ outputSchema,
558
+ toolNames: ['set_output', 'end_turn'], // Add set_output to available tools
559
+ handleSteps: undefined, // LLM-only agent
560
+ }
561
+
562
+ const localAgentTemplates = {
563
+ 'test-agent': templateWithOutputSchema,
564
+ }
565
+
566
+ let llmCallNumber = 0
567
+ let capturedAgentState: AgentState | null = null
568
+
569
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* ({}) {
570
+ llmCallNumber++
571
+ if (llmCallNumber === 1) {
572
+ // First call: agent tries to end turn without setting output
573
+ yield {
574
+ type: 'text' as const,
575
+ text: 'First response without output\n\n',
576
+ }
577
+ yield createToolCallChunk('end_turn', {})
578
+ } else if (llmCallNumber === 2) {
579
+ // Second call: agent sets output after being reminded
580
+ // Manually set the output to simulate the set_output tool execution
581
+ if (capturedAgentState) {
582
+ capturedAgentState.output = {
583
+ result: 'test result',
584
+ status: 'success',
585
+ }
586
+ }
587
+ yield { type: 'text' as const, text: 'Setting output now\n\n' }
588
+ yield createToolCallChunk('set_output', {
589
+ result: 'test result',
590
+ status: 'success',
591
+ })
592
+ yield { type: 'text' as const, text: '\n\n' }
593
+ yield createToolCallChunk('end_turn', {})
594
+ } else {
595
+ // Safety: if called more than twice, just end
596
+ yield { type: 'text' as const, text: 'Ending\n\n' }
597
+ yield createToolCallChunk('end_turn', {})
598
+ }
599
+ return promptSuccess('mock-message-id')
600
+ }
601
+
602
+ mockAgentState.output = undefined
603
+ capturedAgentState = mockAgentState
604
+
605
+ const result = await loopAgentSteps({
606
+ ...loopAgentStepsBaseParams,
607
+ agentType: 'test-agent',
608
+ localAgentTemplates,
609
+ })
610
+
611
+ // Should call LLM twice: once to try ending without output, once after reminder
612
+ expect(llmCallNumber).toBe(2)
613
+
614
+ // Should have output set after the second attempt
615
+ expect(result.agentState.output).toEqual({
616
+ result: 'test result',
617
+ status: 'success',
618
+ })
619
+
620
+ // Check that a system message was added to message history
621
+ const systemMessages = result.agentState.messageHistory.filter(
622
+ (msg) =>
623
+ msg.role === 'user' &&
624
+ msg.content[0].type === 'text' &&
625
+ msg.content[0].text.includes('set_output'),
626
+ )
627
+ expect(systemMessages.length).toBeGreaterThan(0)
628
+ })
629
+
630
+ it('should not restart loop if output is set correctly', async () => {
631
+ // Test that when an agent has outputSchema and sets output correctly,
632
+ // the loop ends normally without restarting
633
+
634
+ const outputSchema = z.object({
635
+ result: z.string(),
636
+ })
637
+
638
+ const templateWithOutputSchema = {
639
+ ...mockTemplate,
640
+ outputSchema,
641
+ toolNames: ['set_output', 'end_turn'],
642
+ handleSteps: undefined,
643
+ }
644
+
645
+ const localAgentTemplates = {
646
+ 'test-agent': templateWithOutputSchema,
647
+ }
648
+
649
+ let llmCallNumber = 0
650
+ let capturedAgentState: AgentState | null = null
651
+
652
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* ({}) {
653
+ llmCallNumber++
654
+ // Agent sets output correctly on first call
655
+ if (capturedAgentState) {
656
+ capturedAgentState.output = { result: 'success' }
657
+ }
658
+ yield { type: 'text' as const, text: 'Setting output\n\n' }
659
+ yield createToolCallChunk('set_output', { result: 'success' })
660
+ yield { type: 'text' as const, text: '\n\n' }
661
+ yield createToolCallChunk('end_turn', {})
662
+ return promptSuccess('mock-message-id')
663
+ }
664
+
665
+ mockAgentState.output = undefined
666
+ capturedAgentState = mockAgentState
667
+
668
+ const result = await loopAgentSteps({
669
+ ...loopAgentStepsBaseParams,
670
+ agentType: 'test-agent',
671
+ localAgentTemplates,
672
+ })
673
+
674
+ // Should only call LLM once since output was set correctly
675
+ expect(llmCallNumber).toBe(1)
676
+
677
+ // Should have output set
678
+ expect(result.agentState.output).toEqual({ result: 'success' })
679
+ })
680
+
681
+ it('should pass generateN from programmatic step to runAgentStep as n parameter', async () => {
682
+ // Test that when programmatic step returns generateN, it's passed to runAgentStep
683
+
684
+ let agentStepN: number | undefined
685
+
686
+ const mockGeneratorFunction = function* () {
687
+ // Yield GENERATE_N to trigger n parameter
688
+ yield { type: 'GENERATE_N', n: 5 }
689
+ } as () => StepGenerator
690
+
691
+ mockTemplate.handleSteps = mockGeneratorFunction
692
+
693
+ const localAgentTemplates = {
694
+ 'test-agent': mockTemplate,
695
+ }
696
+
697
+ // Mock promptAiSdk to capture the n parameter
698
+ loopAgentStepsBaseParams.promptAiSdk = async (params: any) => {
699
+ agentStepN = params.n
700
+ return promptSuccess(
701
+ JSON.stringify([
702
+ 'Response 1',
703
+ 'Response 2',
704
+ 'Response 3',
705
+ 'Response 4',
706
+ 'Response 5',
707
+ ]),
708
+ )
709
+ }
710
+
711
+ await loopAgentSteps({
712
+ ...loopAgentStepsBaseParams,
713
+ agentType: 'test-agent',
714
+ localAgentTemplates,
715
+ })
716
+
717
+ // Verify generateN was passed to runAgentStep as n
718
+ expect(agentStepN).toBe(5)
719
+ })
720
+
721
+ it('should pass nResponses from runAgentStep back to programmatic step', async () => {
722
+ // Test that nResponses returned by runAgentStep are passed to next programmatic step
723
+
724
+ let receivedNResponses: string[] | undefined
725
+
726
+ const mockGeneratorFunction = function* () {
727
+ const { nResponses } = yield { type: 'GENERATE_N', n: 3 }
728
+ receivedNResponses = nResponses
729
+ const _step = yield {
730
+ toolName: 'read_files',
731
+ input: { paths: ['test.txt'] },
732
+ }
733
+ yield { toolName: 'end_turn', input: {} }
734
+ } as () => StepGenerator
735
+
736
+ mockTemplate.handleSteps = mockGeneratorFunction
737
+
738
+ const localAgentTemplates = {
739
+ 'test-agent': mockTemplate,
740
+ }
741
+
742
+ const expectedResponses = [
743
+ 'Implementation A',
744
+ 'Implementation B',
745
+ 'Implementation C',
746
+ ]
747
+ loopAgentStepsBaseParams.promptAiSdk = async () => {
748
+ return promptSuccess(JSON.stringify(expectedResponses))
749
+ }
750
+
751
+ await loopAgentSteps({
752
+ ...loopAgentStepsBaseParams,
753
+ agentType: 'test-agent',
754
+ localAgentTemplates,
755
+ })
756
+
757
+ expect(receivedNResponses).toEqual(expectedResponses)
758
+ })
759
+
760
+ it('should allow agents without outputSchema to end normally', async () => {
761
+ // Test that agents without outputSchema can end without setting output
762
+
763
+ const templateWithoutOutputSchema = {
764
+ ...mockTemplate,
765
+ outputSchema: undefined,
766
+ handleSteps: undefined,
767
+ }
768
+
769
+ const localAgentTemplates = {
770
+ 'test-agent': templateWithoutOutputSchema,
771
+ }
772
+
773
+ let llmCallNumber = 0
774
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* ({}) {
775
+ llmCallNumber++
776
+ yield { type: 'text' as const, text: 'Response without output\n\n' }
777
+ yield createToolCallChunk('end_turn', {})
778
+ return promptSuccess('mock-message-id')
779
+ }
780
+
781
+ const result = await loopAgentSteps({
782
+ ...loopAgentStepsBaseParams,
783
+ agentType: 'test-agent',
784
+ localAgentTemplates,
785
+ })
786
+
787
+ // Should only call LLM once and end normally
788
+ expect(llmCallNumber).toBe(1)
789
+
790
+ // Output should be undefined since no outputSchema required
791
+ expect(result.agentState.output).toBeUndefined()
792
+ })
793
+
794
+ it('should continue loop if agent does not end turn (has more work)', async () => {
795
+ // Test that validation only triggers when shouldEndTurn is true
796
+
797
+ const outputSchema = z.object({
798
+ result: z.string(),
799
+ })
800
+
801
+ const templateWithOutputSchema = {
802
+ ...mockTemplate,
803
+ outputSchema,
804
+ toolNames: ['read_files', 'set_output', 'end_turn'],
805
+ handleSteps: undefined,
806
+ }
807
+
808
+ const localAgentTemplates = {
809
+ 'test-agent': templateWithOutputSchema,
810
+ }
811
+
812
+ let llmCallNumber = 0
813
+ let capturedAgentState: AgentState | null = null
814
+
815
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* ({}) {
816
+ llmCallNumber++
817
+ if (llmCallNumber === 1) {
818
+ // First call: agent does some work but doesn't end turn
819
+ yield { type: 'text' as const, text: 'Doing work\n\n' }
820
+ yield createToolCallChunk('read_files', { paths: ['test.txt'] })
821
+ } else {
822
+ // Second call: agent sets output and ends
823
+ if (capturedAgentState) {
824
+ capturedAgentState.output = { result: 'done' }
825
+ }
826
+ yield { type: 'text' as const, text: 'Finishing\n\n' }
827
+ yield createToolCallChunk('set_output', { result: 'done' })
828
+ yield { type: 'text' as const, text: '\n\n' }
829
+ yield createToolCallChunk('end_turn', {})
830
+ }
831
+ return promptSuccess('mock-message-id')
832
+ }
833
+
834
+ mockAgentState.output = undefined
835
+ capturedAgentState = mockAgentState
836
+
837
+ const result = await loopAgentSteps({
838
+ ...loopAgentStepsBaseParams,
839
+ agentType: 'test-agent',
840
+ localAgentTemplates,
841
+ })
842
+
843
+ // Should call LLM twice: once for work, once to set output and end
844
+ expect(llmCallNumber).toBe(2)
845
+
846
+ // Should have output set
847
+ expect(result.agentState.output).toEqual({ result: 'done' })
848
+ })
849
+
850
+ describe('abort handling', () => {
851
+ it('should handle AbortError and finish with cancelled status', async () => {
852
+ // Test that when an AbortError is thrown (e.g., from a tool handler),
853
+ // loopAgentSteps catches it, finishes with 'cancelled' status, and returns
854
+ // an error output indicating the run was cancelled.
855
+
856
+ const llmOnlyTemplate = {
857
+ ...mockTemplate,
858
+ handleSteps: undefined,
859
+ }
860
+
861
+ const localAgentTemplates = {
862
+ 'test-agent': llmOnlyTemplate,
863
+ }
864
+
865
+ // Track finishAgentRun calls
866
+ let finishAgentRunStatus: string | undefined
867
+ const mockFinishAgentRun = mock(async (params: { status: string }) => {
868
+ finishAgentRunStatus = params.status
869
+ })
870
+
871
+ // Mock promptAiSdkStream to throw an AbortError (simulating user cancellation mid-stream)
872
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
873
+ // Yield some content first
874
+ yield { type: 'text' as const, text: 'Starting work...\n' }
875
+ // Then throw AbortError to simulate user cancellation
876
+ throw new AbortError('User pressed Ctrl+C')
877
+ }
878
+
879
+ const result = await loopAgentSteps({
880
+ ...loopAgentStepsBaseParams,
881
+ agentType: 'test-agent',
882
+ localAgentTemplates,
883
+ finishAgentRun: mockFinishAgentRun,
884
+ })
885
+
886
+ // Verify the output indicates cancellation
887
+ expect(result.output.type).toBe('error')
888
+ if (result.output.type === 'error') {
889
+ expect(result.output.message).toBe('Run cancelled by user')
890
+ }
891
+
892
+ // Verify finishAgentRun was called with 'cancelled' status
893
+ expect(mockFinishAgentRun).toHaveBeenCalled()
894
+ expect(finishAgentRunStatus).toBe('cancelled')
895
+ })
896
+
897
+ it('should distinguish AbortError from other errors', async () => {
898
+ // Test that non-abort errors are NOT treated as cancellations
899
+
900
+ const llmOnlyTemplate = {
901
+ ...mockTemplate,
902
+ handleSteps: undefined,
903
+ }
904
+
905
+ const localAgentTemplates = {
906
+ 'test-agent': llmOnlyTemplate,
907
+ }
908
+
909
+ // Track finishAgentRun calls
910
+ let finishAgentRunStatus: string | undefined
911
+ const mockFinishAgentRun = mock(async (params: { status: string }) => {
912
+ finishAgentRunStatus = params.status
913
+ })
914
+
915
+ // Mock promptAiSdkStream to throw a regular error (not AbortError)
916
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
917
+ yield { type: 'text' as const, text: 'Starting...\n' }
918
+ throw new Error('Network connection failed')
919
+ }
920
+
921
+ const result = await loopAgentSteps({
922
+ ...loopAgentStepsBaseParams,
923
+ agentType: 'test-agent',
924
+ localAgentTemplates,
925
+ finishAgentRun: mockFinishAgentRun,
926
+ })
927
+
928
+ // Verify the output indicates an error (not cancellation)
929
+ expect(result.output.type).toBe('error')
930
+ if (result.output.type === 'error') {
931
+ expect(result.output.message).toContain('Network connection failed')
932
+ expect(result.output.message).not.toBe('Run cancelled by user')
933
+ }
934
+
935
+ // Verify finishAgentRun was called with 'failed' status (not 'cancelled')
936
+ expect(mockFinishAgentRun).toHaveBeenCalled()
937
+ expect(finishAgentRunStatus).toBe('failed')
938
+ })
939
+
940
+ it('should handle signal.aborted before loop starts', async () => {
941
+ // Test that if signal is already aborted when loopAgentSteps is called,
942
+ // it returns immediately with a cancelled message
943
+
944
+ const abortController = new AbortController()
945
+ abortController.abort() // Abort immediately
946
+
947
+ const llmOnlyTemplate = {
948
+ ...mockTemplate,
949
+ handleSteps: undefined,
950
+ }
951
+
952
+ const localAgentTemplates = {
953
+ 'test-agent': llmOnlyTemplate,
954
+ }
955
+
956
+ const result = await loopAgentSteps({
957
+ ...loopAgentStepsBaseParams,
958
+ agentType: 'test-agent',
959
+ localAgentTemplates,
960
+ signal: abortController.signal,
961
+ })
962
+
963
+ // Verify the output indicates cancellation
964
+ expect(result.output.type).toBe('error')
965
+ if (result.output.type === 'error') {
966
+ expect(result.output.message).toBe('Run cancelled by user')
967
+ }
968
+
969
+ // LLM should not have been called since we aborted before starting
970
+ expect(llmCallCount).toBe(0)
971
+ })
972
+ })
973
+
974
+ describe('API error handling', () => {
975
+ it('should propagate error code and server message from 403 APICallError responseBody', async () => {
976
+ const llmOnlyTemplate = {
977
+ ...mockTemplate,
978
+ handleSteps: undefined,
979
+ }
980
+
981
+ const localAgentTemplates = {
982
+ 'test-agent': llmOnlyTemplate,
983
+ }
984
+
985
+ // Mock promptAiSdkStream to throw an APICallError with a 403 status
986
+ // and a responseBody containing the server's structured error
987
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
988
+ throw new APICallError({
989
+ statusCode: 403,
990
+ message: 'Forbidden',
991
+ url: 'https://api.savant-code.com/v1/chat/completions',
992
+ requestBodyValues: {},
993
+ responseBody: JSON.stringify({
994
+ error: 'free_mode_unavailable',
995
+ message: 'Free mode is not available in your country.',
996
+ countryCode: 'US',
997
+ countryBlockReason: 'anonymous_network',
998
+ ipPrivacySignals: ['vpn', 'hosting'],
999
+ }),
1000
+ isRetryable: false,
1001
+ })
1002
+ }
1003
+
1004
+ const result = await loopAgentSteps({
1005
+ ...loopAgentStepsBaseParams,
1006
+ agentType: 'test-agent',
1007
+ localAgentTemplates,
1008
+ })
1009
+
1010
+ expect(result.output.type).toBe('error')
1011
+ if (result.output.type === 'error') {
1012
+ // Should use the server's message, NOT the generic "Forbidden"
1013
+ expect(result.output.message).toBe(
1014
+ 'Free mode is not available in your country.',
1015
+ )
1016
+ // Should NOT have the 'Agent run error: ' prefix since message came from responseBody
1017
+ expect(result.output.message).not.toContain('Agent run error:')
1018
+ // Should propagate the error code so the CLI can match on it
1019
+ expect(result.output.error).toBe('free_mode_unavailable')
1020
+ // Should propagate the status code
1021
+ expect(result.output.statusCode).toBe(403)
1022
+ expect(result.output.countryCode).toBe('US')
1023
+ expect(result.output.countryBlockReason).toBe('anonymous_network')
1024
+ expect(result.output.ipPrivacySignals).toEqual(['vpn', 'hosting'])
1025
+ }
1026
+ })
1027
+
1028
+ it('should prefix with "Agent run error:" when responseBody has no parseable message', async () => {
1029
+ const llmOnlyTemplate = {
1030
+ ...mockTemplate,
1031
+ handleSteps: undefined,
1032
+ }
1033
+
1034
+ const localAgentTemplates = {
1035
+ 'test-agent': llmOnlyTemplate,
1036
+ }
1037
+
1038
+ // APICallError with no responseBody
1039
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1040
+ throw new APICallError({
1041
+ statusCode: 500,
1042
+ message: 'Internal Server Error',
1043
+ url: 'https://api.savant-code.com/v1/chat/completions',
1044
+ requestBodyValues: {},
1045
+ responseBody: undefined,
1046
+ isRetryable: true,
1047
+ })
1048
+ }
1049
+
1050
+ const result = await loopAgentSteps({
1051
+ ...loopAgentStepsBaseParams,
1052
+ agentType: 'test-agent',
1053
+ localAgentTemplates,
1054
+ })
1055
+
1056
+ expect(result.output.type).toBe('error')
1057
+ if (result.output.type === 'error') {
1058
+ // Should have the prefix since there's no server message
1059
+ expect(result.output.message).toContain('Agent run error:')
1060
+ expect(result.output.message).toContain('Internal Server Error')
1061
+ // No error code since responseBody wasn't parseable
1062
+ expect(result.output.error).toBeUndefined()
1063
+ }
1064
+ })
1065
+
1066
+ it('should unwrap retry errors to propagate underlying 409 gate errors', async () => {
1067
+ const llmOnlyTemplate = {
1068
+ ...mockTemplate,
1069
+ handleSteps: undefined,
1070
+ }
1071
+
1072
+ const localAgentTemplates = {
1073
+ 'test-agent': llmOnlyTemplate,
1074
+ }
1075
+
1076
+ const apiError = new APICallError({
1077
+ statusCode: 409,
1078
+ message: 'Conflict',
1079
+ url: 'https://api.savant-code.com/v1/chat/completions',
1080
+ requestBodyValues: {},
1081
+ responseBody: JSON.stringify({
1082
+ error: 'session_superseded',
1083
+ message:
1084
+ 'Another instance of savant-free has taken over this session. Only one instance per account is allowed.',
1085
+ }),
1086
+ isRetryable: true,
1087
+ })
1088
+
1089
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1090
+ throw new RetryError({
1091
+ message: 'Failed after 4 attempts. Last error: Conflict',
1092
+ reason: 'maxRetriesExceeded',
1093
+ errors: [apiError],
1094
+ })
1095
+ }
1096
+
1097
+ const result = await loopAgentSteps({
1098
+ ...loopAgentStepsBaseParams,
1099
+ agentType: 'test-agent',
1100
+ localAgentTemplates,
1101
+ })
1102
+
1103
+ expect(result.output.type).toBe('error')
1104
+ if (result.output.type === 'error') {
1105
+ expect(result.output.message).toBe(
1106
+ 'Another instance of savant-free has taken over this session. Only one instance per account is allowed.',
1107
+ )
1108
+ expect(result.output.message).not.toContain('Agent run error:')
1109
+ expect(result.output.error).toBe('session_superseded')
1110
+ expect(result.output.statusCode).toBe(409)
1111
+ }
1112
+ })
1113
+
1114
+ it('should explain fetch idle timeouts instead of showing the raw runtime message', async () => {
1115
+ const llmOnlyTemplate = {
1116
+ ...mockTemplate,
1117
+ handleSteps: undefined,
1118
+ }
1119
+
1120
+ const localAgentTemplates = {
1121
+ 'test-agent': llmOnlyTemplate,
1122
+ }
1123
+
1124
+ // Bun aborts a fetch after 5 minutes without receiving bytes, throwing a
1125
+ // DOMException named TimeoutError with this exact message.
1126
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1127
+ const timeoutError = new Error('The operation timed out.')
1128
+ timeoutError.name = 'TimeoutError'
1129
+ throw timeoutError
1130
+ }
1131
+
1132
+ const result = await loopAgentSteps({
1133
+ ...loopAgentStepsBaseParams,
1134
+ agentType: 'test-agent',
1135
+ localAgentTemplates,
1136
+ })
1137
+
1138
+ expect(result.output.type).toBe('error')
1139
+ if (result.output.type === 'error') {
1140
+ expect(result.output.message).toContain(
1141
+ 'no data was received from the server for 5 minutes',
1142
+ )
1143
+ expect(result.output.message).not.toContain('Agent run error:')
1144
+ expect(result.output.message).not.toBe('The operation timed out.')
1145
+ }
1146
+ })
1147
+
1148
+ it('should explain dropped socket connections instead of showing the raw runtime message', async () => {
1149
+ const llmOnlyTemplate = {
1150
+ ...mockTemplate,
1151
+ handleSteps: undefined,
1152
+ }
1153
+
1154
+ const localAgentTemplates = {
1155
+ 'test-agent': llmOnlyTemplate,
1156
+ }
1157
+
1158
+ // Bun's fetch throws a plain Error with this message (and code
1159
+ // ECONNRESET/ConnectionClosed) when the TCP connection is dropped.
1160
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1161
+ const socketError = new Error(
1162
+ 'The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()',
1163
+ ) as Error & { code: string }
1164
+ socketError.code = 'ECONNRESET'
1165
+ throw socketError
1166
+ }
1167
+
1168
+ const result = await loopAgentSteps({
1169
+ ...loopAgentStepsBaseParams,
1170
+ agentType: 'test-agent',
1171
+ localAgentTemplates,
1172
+ })
1173
+
1174
+ expect(result.output.type).toBe('error')
1175
+ if (result.output.type === 'error') {
1176
+ expect(result.output.message).toContain('Connection interrupted')
1177
+ expect(result.output.message).not.toContain('Agent run error:')
1178
+ expect(result.output.message).not.toContain(
1179
+ 'pass `verbose: true` in the second argument to fetch()',
1180
+ )
1181
+ }
1182
+ })
1183
+ })
1184
+
1185
+ describe('native tool-call recovery (FID-2026-0801-010)', () => {
1186
+ it('retries once, then fails visibly without a third model call', async () => {
1187
+ const llmOnlyTemplate = {
1188
+ ...mockTemplate,
1189
+ handleSteps: undefined,
1190
+ }
1191
+ let finishStatus: string | undefined
1192
+ let finishError: string | undefined
1193
+
1194
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1195
+ llmCallCount++
1196
+ yield {
1197
+ type: 'error' as const,
1198
+ message: 'Incomplete arguments for tool sequentialthinking',
1199
+ errorClass: 'native-incomplete' as const,
1200
+ toolName: 'sequentialthinking',
1201
+ }
1202
+ return promptSuccess(`native-incomplete-${llmCallCount}`)
1203
+ }
1204
+
1205
+ const result = await loopAgentSteps({
1206
+ ...loopAgentStepsBaseParams,
1207
+ agentTemplate: llmOnlyTemplate,
1208
+ localAgentTemplates: { 'test-agent': llmOnlyTemplate },
1209
+ finishAgentRun: mock(
1210
+ async (params: { status: string; errorMessage?: string }) => {
1211
+ finishStatus = params.status
1212
+ finishError = params.errorMessage
1213
+ },
1214
+ ),
1215
+ })
1216
+
1217
+ expect(llmCallCount).toBe(2)
1218
+ expect(result.output.type).toBe('error')
1219
+ if (result.output.type === 'error') {
1220
+ expect(result.output.message).toContain(
1221
+ 'Native tool-call recovery failed twice consecutively',
1222
+ )
1223
+ }
1224
+ expect(finishStatus).toBe('failed')
1225
+ expect(finishError).toContain(
1226
+ 'Native tool-call recovery failed twice consecutively',
1227
+ )
1228
+
1229
+ const history = result.agentState.messageHistory
1230
+ expect(
1231
+ history.some(
1232
+ (message) =>
1233
+ message.role === 'assistant' &&
1234
+ message.content.some((part) => part.type === 'tool-call'),
1235
+ ),
1236
+ ).toBe(false)
1237
+ expect(history.some((message) => message.role === 'tool')).toBe(false)
1238
+ expect(
1239
+ history.some(
1240
+ (message) =>
1241
+ message.role === 'user' &&
1242
+ message.tags?.includes('TOOL_CALL_ERROR'),
1243
+ ),
1244
+ ).toBe(true)
1245
+ })
1246
+
1247
+ it('recovers on the next step with one valid sequentialthinking result', async () => {
1248
+ const llmOnlyTemplate = {
1249
+ ...mockTemplate,
1250
+ id: 'thinker-test-agent',
1251
+ handleSteps: undefined,
1252
+ toolNames: ['sequentialthinking', 'end_turn'],
1253
+ } satisfies AgentTemplate
1254
+
1255
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1256
+ llmCallCount++
1257
+ if (llmCallCount === 1) {
1258
+ yield {
1259
+ type: 'error' as const,
1260
+ message: 'Incomplete arguments for tool sequentialthinking',
1261
+ errorClass: 'native-incomplete' as const,
1262
+ toolName: 'sequentialthinking',
1263
+ }
1264
+ } else {
1265
+ yield createToolCallChunk('sequentialthinking', {
1266
+ thought: 'The continuation is executing the complete native call.',
1267
+ thoughtNumber: 1,
1268
+ totalThoughts: 1,
1269
+ nextThoughtNeeded: false,
1270
+ })
1271
+ yield createToolCallChunk('end_turn', {})
1272
+ }
1273
+ return promptSuccess(`recovery-${llmCallCount}`)
1274
+ }
1275
+
1276
+ const result = await loopAgentSteps({
1277
+ ...loopAgentStepsBaseParams,
1278
+ agentTemplate: llmOnlyTemplate,
1279
+ localAgentTemplates: { 'test-agent': llmOnlyTemplate },
1280
+ })
1281
+
1282
+ expect(llmCallCount).toBe(2)
1283
+ expect(result.output.type).not.toBe('error')
1284
+
1285
+ const assistantToolCalls = result.agentState.messageHistory.filter(
1286
+ (message) =>
1287
+ message.role === 'assistant' &&
1288
+ message.content.some(
1289
+ (part) =>
1290
+ part.type === 'tool-call' &&
1291
+ part.toolName === 'sequentialthinking',
1292
+ ),
1293
+ )
1294
+ const sequentialThinkingResults = result.agentState.messageHistory.filter(
1295
+ (message) =>
1296
+ message.role === 'tool' && message.toolName === 'sequentialthinking',
1297
+ )
1298
+
1299
+ expect(assistantToolCalls).toHaveLength(1)
1300
+ expect(sequentialThinkingResults).toHaveLength(1)
1301
+ expect(
1302
+ result.agentState.messageHistory.some(
1303
+ (message) =>
1304
+ message.role === 'user' &&
1305
+ message.tags?.includes('TOOL_CALL_ERROR'),
1306
+ ),
1307
+ ).toBe(true)
1308
+ })
1309
+
1310
+ it('resets the native-incomplete streak after an unrelated tool error', async () => {
1311
+ const llmOnlyTemplate = {
1312
+ ...mockTemplate,
1313
+ handleSteps: undefined,
1314
+ }
1315
+ let finishStatus: string | undefined
1316
+
1317
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1318
+ llmCallCount++
1319
+ if (llmCallCount === 2) {
1320
+ yield {
1321
+ type: 'error' as const,
1322
+ message: 'An unrelated tool validation error',
1323
+ }
1324
+ } else {
1325
+ yield {
1326
+ type: 'error' as const,
1327
+ message: 'Incomplete arguments for tool sequentialthinking',
1328
+ errorClass: 'native-incomplete' as const,
1329
+ toolName: 'sequentialthinking',
1330
+ }
1331
+ }
1332
+ return promptSuccess(`recovery-${llmCallCount}`)
1333
+ }
1334
+
1335
+ const result = await loopAgentSteps({
1336
+ ...loopAgentStepsBaseParams,
1337
+ agentTemplate: llmOnlyTemplate,
1338
+ localAgentTemplates: { 'test-agent': llmOnlyTemplate },
1339
+ finishAgentRun: mock(async (params: { status: string }) => {
1340
+ finishStatus = params.status
1341
+ }),
1342
+ })
1343
+
1344
+ expect(llmCallCount).toBe(4)
1345
+ expect(result.output.type).toBe('error')
1346
+ expect(finishStatus).toBe('failed')
1347
+ })
1348
+ })
1349
+
1350
+ describe('Thinker convergence gate integration (FID-2026-0801-012)', () => {
1351
+ const thinkerTemplate = (): AgentTemplate => ({
1352
+ ...mockTemplate,
1353
+ id: 'thinker-test-agent',
1354
+ outputMode: 'structured_output',
1355
+ outputSchema: z.object({
1356
+ status: z.string(),
1357
+ payload: z.object({ message: z.string() }).nullable(),
1358
+ }),
1359
+ toolNames: ['sequentialthinking', 'end_turn'],
1360
+ handleSteps: undefined,
1361
+ })
1362
+
1363
+ afterEach(() => {
1364
+ clearAllThoughtSessionsForTests()
1365
+ clearThinkerConvergenceStateForTests()
1366
+ })
1367
+
1368
+ it('sets output from the session snapshot and breaks without the set_output restart', async () => {
1369
+ // The Thinker converges with a single nextThoughtNeeded=false thought and
1370
+ // ends its turn. The gate must build the FinalArtifact from the session
1371
+ // snapshot and set agentState.output BEFORE the loop-top
1372
+ // `output === undefined && shouldEndTurn` restart check can inject the
1373
+ // "You must use set_output" message (which would reintroduce the null).
1374
+ const template = thinkerTemplate()
1375
+ let llmCallNumber = 0
1376
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1377
+ llmCallNumber++
1378
+ yield createToolCallChunk('sequentialthinking', {
1379
+ thought: 'Conclusion: use the hybrid approach.',
1380
+ thoughtNumber: 1,
1381
+ totalThoughts: 1,
1382
+ nextThoughtNeeded: false,
1383
+ })
1384
+ yield { type: 'text' as const, text: '\n\n' }
1385
+ yield createToolCallChunk('end_turn', {})
1386
+ return promptSuccess('mock-message-id')
1387
+ }
1388
+
1389
+ const result = await loopAgentSteps({
1390
+ ...loopAgentStepsBaseParams,
1391
+ agentType: 'thinker-test-agent',
1392
+ localAgentTemplates: { 'thinker-test-agent': template },
1393
+ })
1394
+
1395
+ // Exactly one LLM call: the loop must NOT have restarted.
1396
+ expect(llmCallNumber).toBe(1)
1397
+ expect(result.agentState.output).toBeDefined()
1398
+ expect((result.agentState.output as { status?: string }).status).toBe(
1399
+ 'success',
1400
+ )
1401
+ const restartMessages = result.agentState.messageHistory.filter(
1402
+ (m) =>
1403
+ m.role === 'user' &&
1404
+ m.content[0].type === 'text' &&
1405
+ m.content[0].text.includes('set_output'),
1406
+ )
1407
+ expect(restartMessages.length).toBe(0)
1408
+ })
1409
+
1410
+ it('retries on non-convergence instead of restarting, then converges', async () => {
1411
+ // First turn: the model thinks (nextThoughtNeeded=true) and ends the turn
1412
+ // unconverged. The gate appends a typed retry message and keeps the loop
1413
+ // going — it must NOT hit the set_output restart path. Second turn
1414
+ // converges and the gate sets the artifact.
1415
+ const template = thinkerTemplate()
1416
+ let llmCallNumber = 0
1417
+ loopAgentStepsBaseParams.promptAiSdkStream = async function* () {
1418
+ llmCallNumber++
1419
+ if (llmCallNumber === 1) {
1420
+ yield createToolCallChunk('sequentialthinking', {
1421
+ thought: 'Partial analysis, not done yet.',
1422
+ thoughtNumber: 1,
1423
+ totalThoughts: 2,
1424
+ nextThoughtNeeded: true,
1425
+ })
1426
+ } else {
1427
+ yield createToolCallChunk('sequentialthinking', {
1428
+ thought: 'Final conclusion: hybrid wins.',
1429
+ thoughtNumber: 2,
1430
+ totalThoughts: 2,
1431
+ nextThoughtNeeded: false,
1432
+ })
1433
+ }
1434
+ yield { type: 'text' as const, text: '\n\n' }
1435
+ yield createToolCallChunk('end_turn', {})
1436
+ return promptSuccess('mock-message-id')
1437
+ }
1438
+
1439
+ const result = await loopAgentSteps({
1440
+ ...loopAgentStepsBaseParams,
1441
+ agentType: 'thinker-test-agent',
1442
+ localAgentTemplates: { 'thinker-test-agent': template },
1443
+ })
1444
+
1445
+ // Two LLM calls: first unconverged turn + retry, second converged turn.
1446
+ expect(llmCallNumber).toBe(2)
1447
+ expect(result.agentState.output).toBeDefined()
1448
+ expect((result.agentState.output as { status?: string }).status).toBe(
1449
+ 'success',
1450
+ )
1451
+ // The typed retry message was appended after the first turn...
1452
+ const retryMessages = result.agentState.messageHistory.filter(
1453
+ (m) =>
1454
+ m.role === 'user' &&
1455
+ m.content[0].type === 'text' &&
1456
+ m.content[0].text.includes('nextThoughtNeeded=false'),
1457
+ )
1458
+ expect(retryMessages.length).toBeGreaterThan(0)
1459
+ // ...and the set_output restart message was never injected.
1460
+ const restartMessages = result.agentState.messageHistory.filter(
1461
+ (m) =>
1462
+ m.role === 'user' &&
1463
+ m.content[0].type === 'text' &&
1464
+ m.content[0].text.includes('set_output'),
1465
+ )
1466
+ expect(restartMessages.length).toBe(0)
1467
+ })
1468
+ })
1469
+
1470
+ describe('steering (drainSteeringMessages)', () => {
1471
+ it('appends a steering message at the step boundary and continues the turn', async () => {
1472
+ // The mock LLM ends the turn after one step. A steering message that arrives
1473
+ // during that step should be appended to history and keep the turn going, so
1474
+ // the agent runs a second step that can see (and act on) the new message.
1475
+ const steerText = 'Also rename the variable to fooBar'
1476
+ let drainCalls = 0
1477
+ const drainSteeringMessages = () => {
1478
+ drainCalls++
1479
+ return drainCalls === 1 ? [steerText] : []
1480
+ }
1481
+
1482
+ const result = await loopAgentSteps({
1483
+ ...loopAgentStepsBaseParams,
1484
+ agentType: 'test-agent',
1485
+ drainSteeringMessages,
1486
+ })
1487
+
1488
+ // Step 1 wanted to end the turn, but the steer kept it going → a 2nd step ran.
1489
+ expect(llmCallCount).toBe(2)
1490
+
1491
+ // The steered text landed in history as a user message.
1492
+ const steered = result.agentState.messageHistory.find(
1493
+ (m) =>
1494
+ m.role === 'user' && JSON.stringify(m.content).includes(steerText),
1495
+ )
1496
+ expect(steered).toBeDefined()
1497
+ expect((steered as { tags?: string[] }).tags).toContain('USER_PROMPT')
1498
+ })
1499
+
1500
+ it('does not extend the turn when no steering messages arrive', async () => {
1501
+ const result = await loopAgentSteps({
1502
+ ...loopAgentStepsBaseParams,
1503
+ agentType: 'test-agent',
1504
+ drainSteeringMessages: () => [],
1505
+ })
1506
+
1507
+ // No steer → the agent ends the turn after its single step, as usual.
1508
+ expect(llmCallCount).toBe(1)
1509
+ expect(result.agentState).toBeDefined()
1510
+ })
1511
+ })
1512
+ })