talon-agent 1.12.0 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/README.md +111 -52
  2. package/package.json +39 -12
  3. package/prompts/README.md +112 -0
  4. package/prompts/base.md +12 -12
  5. package/prompts/discord.md +10 -47
  6. package/prompts/heartbeat.md +23 -8
  7. package/prompts/identity.md +12 -22
  8. package/prompts/native.md +23 -0
  9. package/prompts/system/contract-text-or-tools.md +8 -0
  10. package/prompts/system/contract-text-preferred.md +5 -0
  11. package/prompts/system/contract-tool-only.md +10 -0
  12. package/prompts/system/cron.md +3 -0
  13. package/prompts/system/daily-memory.md +3 -0
  14. package/prompts/system/goals.md +7 -0
  15. package/prompts/system/heartbeat-agent.md +44 -0
  16. package/prompts/system/persistent-memory.md +8 -0
  17. package/prompts/system/skills.md +9 -0
  18. package/prompts/system/triggers.md +5 -0
  19. package/prompts/system/workspace.md +9 -0
  20. package/prompts/teams.md +4 -14
  21. package/prompts/telegram.md +17 -89
  22. package/src/app.ts +278 -0
  23. package/src/backend/claude-sdk/constants.ts +22 -10
  24. package/src/backend/claude-sdk/factory.ts +79 -22
  25. package/src/backend/claude-sdk/handler.ts +431 -160
  26. package/src/backend/claude-sdk/index.ts +6 -2
  27. package/src/backend/claude-sdk/mcp-ready.ts +54 -0
  28. package/src/backend/claude-sdk/model-drift.ts +65 -0
  29. package/src/backend/claude-sdk/model-provider.ts +12 -6
  30. package/src/backend/claude-sdk/models/convert.ts +169 -0
  31. package/src/backend/claude-sdk/models/discovery.ts +203 -0
  32. package/src/backend/claude-sdk/models/index.ts +18 -0
  33. package/src/backend/claude-sdk/models/parsing.ts +264 -0
  34. package/src/backend/claude-sdk/models/static.ts +45 -0
  35. package/src/backend/claude-sdk/one-shot.ts +19 -13
  36. package/src/backend/claude-sdk/options.ts +228 -104
  37. package/src/backend/claude-sdk/state.ts +1 -1
  38. package/src/backend/claude-sdk/stream.ts +153 -45
  39. package/src/backend/claude-sdk/warm.ts +12 -5
  40. package/src/backend/codex/auth.ts +216 -40
  41. package/src/backend/codex/constants.ts +63 -22
  42. package/src/backend/codex/discovery.ts +427 -0
  43. package/src/backend/codex/factory.ts +63 -33
  44. package/src/backend/codex/handler/events.ts +239 -0
  45. package/src/backend/codex/handler/index.ts +15 -0
  46. package/src/backend/codex/handler/message.ts +669 -0
  47. package/src/backend/codex/handler/state.ts +14 -0
  48. package/src/backend/codex/handler/usage.ts +68 -0
  49. package/src/backend/codex/index.ts +2 -2
  50. package/src/backend/codex/init.ts +92 -11
  51. package/src/backend/codex/mcp-config.ts +89 -62
  52. package/src/backend/codex/models.ts +305 -54
  53. package/src/backend/codex/oauth-incompat.ts +302 -0
  54. package/src/backend/codex/one-shot.ts +91 -8
  55. package/src/backend/codex/state.ts +54 -3
  56. package/src/backend/codex/token-usage.ts +434 -0
  57. package/src/backend/kilo/factory.ts +60 -35
  58. package/src/backend/kilo/handler/index.ts +13 -0
  59. package/src/backend/kilo/handler/message.ts +317 -0
  60. package/src/backend/kilo/handler/state.ts +14 -0
  61. package/src/backend/kilo/handler/turn.ts +321 -0
  62. package/src/backend/kilo/index.ts +5 -5
  63. package/src/backend/kilo/model-provider.ts +26 -168
  64. package/src/backend/kilo/models/index.ts +52 -0
  65. package/src/backend/kilo/one-shot.ts +20 -142
  66. package/src/backend/kilo/server.ts +14 -21
  67. package/src/backend/openai-agents/builtins.ts +215 -114
  68. package/src/backend/openai-agents/constants.ts +41 -15
  69. package/src/backend/openai-agents/discovery.ts +332 -0
  70. package/src/backend/openai-agents/factory.ts +66 -28
  71. package/src/backend/openai-agents/handler/events.ts +170 -0
  72. package/src/backend/openai-agents/handler/index.ts +13 -0
  73. package/src/backend/openai-agents/handler/message.ts +527 -0
  74. package/src/backend/openai-agents/handler/state.ts +13 -0
  75. package/src/backend/openai-agents/index.ts +22 -4
  76. package/src/backend/openai-agents/init.ts +51 -88
  77. package/src/backend/openai-agents/mcp-pool.ts +252 -0
  78. package/src/backend/openai-agents/models.ts +118 -11
  79. package/src/backend/openai-agents/session.ts +277 -0
  80. package/src/backend/openai-agents/state.ts +82 -11
  81. package/src/backend/opencode/factory.ts +54 -25
  82. package/src/backend/opencode/handler/index.ts +13 -0
  83. package/src/backend/opencode/handler/message.ts +325 -0
  84. package/src/backend/opencode/handler/state.ts +14 -0
  85. package/src/backend/opencode/handler/turn.ts +304 -0
  86. package/src/backend/opencode/index.ts +2 -2
  87. package/src/backend/opencode/model-provider.ts +26 -168
  88. package/src/backend/opencode/models/index.ts +51 -0
  89. package/src/backend/opencode/one-shot.ts +20 -137
  90. package/src/backend/opencode/server.ts +12 -11
  91. package/src/backend/remote-server/client.ts +13 -6
  92. package/src/backend/remote-server/events.ts +59 -9
  93. package/src/backend/remote-server/lifecycle.ts +3 -1
  94. package/src/backend/remote-server/mcp.ts +52 -19
  95. package/src/backend/remote-server/model-catalog/catalog.ts +270 -0
  96. package/src/backend/remote-server/model-catalog/index.ts +100 -0
  97. package/src/backend/remote-server/model-catalog/presentation.ts +298 -0
  98. package/src/backend/remote-server/model-catalog/provider.ts +193 -0
  99. package/src/backend/remote-server/model-catalog/resolve.ts +225 -0
  100. package/src/backend/remote-server/model-catalog/types.ts +136 -0
  101. package/src/backend/remote-server/one-shot.ts +225 -0
  102. package/src/backend/remote-server/session-helpers.ts +3 -4
  103. package/src/backend/remote-server/state.ts +1 -1
  104. package/src/backend/shared/delivery-contract.ts +161 -0
  105. package/src/backend/shared/delivery.ts +49 -0
  106. package/src/backend/shared/flow-violation.ts +38 -7
  107. package/src/backend/shared/frontends.ts +66 -0
  108. package/src/backend/shared/handle-retry.ts +244 -0
  109. package/src/backend/shared/handler-to-events.ts +186 -0
  110. package/src/backend/shared/handler-types.ts +57 -0
  111. package/src/backend/shared/index.ts +51 -5
  112. package/src/backend/shared/metrics.ts +199 -0
  113. package/src/backend/shared/model-retry.ts +1 -1
  114. package/src/backend/shared/prompt-format.ts +69 -0
  115. package/src/backend/shared/session-name.ts +1 -1
  116. package/src/backend/shared/stream-state.ts +36 -2
  117. package/src/backend/shared/system-prompt.ts +200 -23
  118. package/src/bootstrap.ts +338 -38
  119. package/src/cli/chat.ts +49 -0
  120. package/src/cli/config-view.ts +100 -0
  121. package/src/cli/config.ts +105 -0
  122. package/src/cli/context.ts +23 -0
  123. package/src/cli/daemon.ts +95 -0
  124. package/src/cli/doctor.ts +53 -0
  125. package/src/cli/index.ts +139 -0
  126. package/src/cli/logs.ts +60 -0
  127. package/src/cli/menu.ts +104 -0
  128. package/src/cli/setup.ts +455 -0
  129. package/src/cli/status.ts +75 -0
  130. package/src/cli.ts +20 -1211
  131. package/src/core/agent-runtime/README.md +173 -0
  132. package/src/{backend/registry.ts → core/agent-runtime/backend-registry.ts} +15 -6
  133. package/src/core/agent-runtime/capabilities.ts +330 -0
  134. package/src/core/agent-runtime/contract-tests.ts +378 -0
  135. package/src/core/agent-runtime/events.ts +291 -0
  136. package/src/core/agent-runtime/index.ts +62 -0
  137. package/src/core/agent-runtime/model-ref.ts +130 -0
  138. package/src/core/background/cron.ts +469 -0
  139. package/src/core/{dream.ts → background/dream.ts} +118 -53
  140. package/src/core/background/heartbeat/agent.ts +333 -0
  141. package/src/core/background/heartbeat/index.ts +25 -0
  142. package/src/core/background/heartbeat/scheduler.ts +211 -0
  143. package/src/core/background/heartbeat/state.ts +153 -0
  144. package/src/core/background/index.ts +21 -0
  145. package/src/core/background/isolated-agent.ts +115 -0
  146. package/src/core/background/job-health.ts +140 -0
  147. package/src/core/background/job-oneshot.ts +165 -0
  148. package/src/core/background/job-prompt.ts +54 -0
  149. package/src/core/{pulse.ts → background/pulse.ts} +6 -5
  150. package/src/core/background/triggers/command.ts +60 -0
  151. package/src/core/background/triggers/exit.ts +202 -0
  152. package/src/core/background/triggers/index.ts +38 -0
  153. package/src/core/background/triggers/output.ts +142 -0
  154. package/src/core/background/triggers/resume.ts +127 -0
  155. package/src/core/background/triggers/spawn.ts +282 -0
  156. package/src/core/background/triggers/state.ts +52 -0
  157. package/src/core/constants.ts +57 -28
  158. package/src/core/daemon/control.ts +229 -0
  159. package/src/core/daemon/discovery.ts +169 -0
  160. package/src/core/daemon/pidfile.ts +98 -0
  161. package/src/core/doctor.ts +459 -0
  162. package/src/core/engine/backend-controller/holders.ts +17 -0
  163. package/src/core/engine/backend-controller/index.ts +71 -0
  164. package/src/core/engine/backend-controller/legacy.ts +111 -0
  165. package/src/core/engine/backend-controller/pool.ts +296 -0
  166. package/src/core/engine/backend-controller/rebind.ts +205 -0
  167. package/src/core/engine/backend-controller/state.ts +150 -0
  168. package/src/core/engine/backend-controller/types.ts +51 -0
  169. package/src/core/engine/dispatcher.ts +68 -0
  170. package/src/core/engine/gateway-actions/cron.ts +518 -0
  171. package/src/core/engine/gateway-actions/fetch-url.ts +120 -0
  172. package/src/core/engine/gateway-actions/goals.ts +178 -0
  173. package/src/core/engine/gateway-actions/history.ts +53 -0
  174. package/src/core/engine/gateway-actions/index.ts +60 -0
  175. package/src/core/engine/gateway-actions/models.ts +120 -0
  176. package/src/core/engine/gateway-actions/plugins.ts +83 -0
  177. package/src/core/engine/gateway-actions/scripts.ts +127 -0
  178. package/src/core/engine/gateway-actions/shared.ts +66 -0
  179. package/src/core/engine/gateway-actions/skills.ts +120 -0
  180. package/src/core/engine/gateway-actions/triggers.ts +237 -0
  181. package/src/core/engine/gateway-actions/types.ts +18 -0
  182. package/src/core/{gateway.ts → engine/gateway.ts} +223 -60
  183. package/src/core/engine/index.ts +20 -0
  184. package/src/core/engine/model-audit.ts +121 -0
  185. package/src/core/errors.ts +68 -6
  186. package/src/core/mcp-hub/children.ts +254 -0
  187. package/src/core/mcp-hub/index.ts +335 -0
  188. package/src/core/mcp-hub/proxy-server.ts +45 -0
  189. package/src/core/mcp-hub/talon-server.ts +71 -0
  190. package/src/core/memory/retrieval.ts +90 -0
  191. package/src/core/models/active-model.ts +441 -0
  192. package/src/core/{models.ts → models/catalog.ts} +6 -0
  193. package/src/core/models/index.ts +17 -0
  194. package/src/core/models/reasoning-levels.ts +57 -0
  195. package/src/core/plugin/actions.ts +32 -0
  196. package/src/core/plugin/builtins.ts +162 -0
  197. package/src/core/plugin/index.ts +37 -0
  198. package/src/core/plugin/loader.ts +289 -0
  199. package/src/core/plugin/mcp.ts +86 -0
  200. package/src/core/plugin/registry.ts +124 -0
  201. package/src/core/plugin/types.ts +126 -0
  202. package/src/core/prompt/assemble.ts +273 -0
  203. package/src/core/prompt/disk-prompts.ts +41 -0
  204. package/src/core/prompt/embed.ts +107 -0
  205. package/src/core/prompt/embedded-prompts.ts +85 -0
  206. package/src/core/prompt/index.ts +30 -0
  207. package/src/core/prompt/templates.ts +124 -0
  208. package/src/core/prompt/workspace-listing.ts +91 -0
  209. package/src/core/scripting/lua-runner.ts +210 -0
  210. package/src/core/scripts/runner.ts +125 -0
  211. package/src/core/soul/README.md +110 -0
  212. package/src/core/soul/RESEARCH.md +98 -0
  213. package/src/core/soul/associative.ts +98 -0
  214. package/src/core/soul/centrality.ts +98 -0
  215. package/src/core/soul/cluster.ts +83 -0
  216. package/src/core/soul/compiler.ts +207 -0
  217. package/src/core/soul/consolidate.ts +179 -0
  218. package/src/core/soul/critic.ts +159 -0
  219. package/src/core/soul/dag.ts +265 -0
  220. package/src/core/soul/delta.ts +123 -0
  221. package/src/core/soul/drift.ts +99 -0
  222. package/src/core/soul/embedder.ts +129 -0
  223. package/src/core/soul/emergent-critic.ts +96 -0
  224. package/src/core/soul/forgetting.ts +131 -0
  225. package/src/core/soul/governance.ts +93 -0
  226. package/src/core/soul/hash.ts +97 -0
  227. package/src/core/soul/hdc.ts +154 -0
  228. package/src/core/soul/index.ts +140 -0
  229. package/src/core/soul/kernel.ts +540 -0
  230. package/src/core/soul/lattice.ts +103 -0
  231. package/src/core/soul/lens.ts +110 -0
  232. package/src/core/soul/projector.ts +240 -0
  233. package/src/core/soul/reflect.ts +170 -0
  234. package/src/core/soul/reflex.ts +164 -0
  235. package/src/core/soul/retrieve.ts +146 -0
  236. package/src/core/soul/salience.ts +142 -0
  237. package/src/core/soul/service.ts +204 -0
  238. package/src/core/soul/settings.ts +47 -0
  239. package/src/core/soul/signals.ts +117 -0
  240. package/src/core/soul/talon-embedder.ts +80 -0
  241. package/src/core/soul/taps.ts +199 -0
  242. package/src/core/soul/types.ts +298 -0
  243. package/src/core/soul/valence.ts +83 -0
  244. package/src/core/tools/goals.ts +117 -0
  245. package/src/core/tools/index.ts +10 -0
  246. package/src/core/tools/mcp-env.ts +94 -0
  247. package/src/core/tools/mcp-server.ts +27 -4
  248. package/src/core/tools/messaging.ts +122 -29
  249. package/src/core/tools/models.ts +39 -0
  250. package/src/core/tools/scheduling.ts +133 -14
  251. package/src/core/tools/schemas.ts +16 -4
  252. package/src/core/tools/scripts.ts +78 -0
  253. package/src/core/tools/skills.ts +90 -0
  254. package/src/core/tools/stickers.ts +1 -1
  255. package/src/core/tools/triggers.ts +19 -3
  256. package/src/core/tools/types.ts +6 -6
  257. package/src/core/types.ts +62 -112
  258. package/src/core/update/self-update.ts +269 -0
  259. package/src/core/weaver/index.ts +10 -0
  260. package/src/core/weaver/loom.ts +125 -0
  261. package/src/core/weaver/thread-session.ts +51 -0
  262. package/src/core/weaver/thread.ts +196 -0
  263. package/src/core/weaver/weaver.ts +357 -0
  264. package/src/frontend/discord/actions/chat-info.ts +268 -0
  265. package/src/frontend/discord/actions/index.ts +77 -0
  266. package/src/frontend/discord/actions/media.ts +190 -0
  267. package/src/frontend/discord/actions/messaging.ts +277 -0
  268. package/src/frontend/discord/actions/shared.ts +100 -0
  269. package/src/frontend/discord/actions/types.ts +33 -0
  270. package/src/frontend/discord/admin.ts +13 -23
  271. package/src/frontend/discord/callbacks/autocomplete.ts +55 -0
  272. package/src/frontend/discord/{callbacks.ts → callbacks/components.ts} +150 -190
  273. package/src/frontend/discord/callbacks/index.ts +14 -0
  274. package/src/frontend/discord/callbacks/modals.ts +76 -0
  275. package/src/frontend/discord/callbacks/shared.ts +22 -0
  276. package/src/frontend/discord/commands/admin.ts +88 -0
  277. package/src/frontend/discord/commands/definitions.ts +200 -0
  278. package/src/frontend/discord/commands/index.ts +19 -0
  279. package/src/frontend/discord/commands/info.ts +106 -0
  280. package/src/frontend/discord/commands/router.ts +149 -0
  281. package/src/frontend/discord/commands/session.ts +89 -0
  282. package/src/frontend/discord/commands/settings.ts +405 -0
  283. package/src/frontend/discord/commands/shared.ts +50 -0
  284. package/src/frontend/discord/formatting.ts +8 -34
  285. package/src/frontend/discord/handlers/access.ts +248 -0
  286. package/src/frontend/discord/handlers/context.ts +161 -0
  287. package/src/frontend/discord/handlers/delivery.ts +94 -0
  288. package/src/frontend/discord/handlers/index.ts +34 -0
  289. package/src/frontend/discord/handlers/messages.ts +120 -0
  290. package/src/frontend/discord/handlers/queue.ts +135 -0
  291. package/src/frontend/discord/handlers/registry.ts +29 -0
  292. package/src/frontend/discord/handlers/state.ts +94 -0
  293. package/src/frontend/discord/helpers.ts +46 -57
  294. package/src/frontend/discord/index.ts +10 -7
  295. package/src/frontend/discord/middleware.ts +2 -2
  296. package/src/frontend/native/actions.ts +133 -0
  297. package/src/frontend/native/chats.ts +146 -0
  298. package/src/frontend/native/discovery.ts +50 -0
  299. package/src/frontend/native/index.ts +1227 -0
  300. package/src/frontend/native/logs.ts +95 -0
  301. package/src/frontend/native/protocol.ts +253 -0
  302. package/src/frontend/native/server.ts +612 -0
  303. package/src/frontend/native/settings.ts +205 -0
  304. package/src/frontend/native/turn-meta.ts +43 -0
  305. package/src/frontend/shared/format.ts +51 -0
  306. package/src/frontend/shared/reasoning-levels.ts +46 -0
  307. package/src/frontend/shared/session-status.ts +189 -0
  308. package/src/frontend/shared/status-context.ts +125 -0
  309. package/src/frontend/teams/actions.ts +7 -5
  310. package/src/frontend/teams/formatting.ts +7 -18
  311. package/src/frontend/teams/graph.ts +2 -1
  312. package/src/frontend/teams/index.ts +76 -54
  313. package/src/frontend/telegram/actions/chat-info.ts +323 -0
  314. package/src/frontend/telegram/actions/index.ts +68 -0
  315. package/src/frontend/telegram/actions/media.ts +224 -0
  316. package/src/frontend/telegram/actions/messaging.ts +295 -0
  317. package/src/frontend/telegram/actions/shared.ts +58 -0
  318. package/src/frontend/telegram/actions/types.ts +31 -0
  319. package/src/frontend/telegram/admin.ts +12 -22
  320. package/src/frontend/telegram/callbacks/effort.ts +68 -0
  321. package/src/frontend/telegram/callbacks/index.ts +68 -0
  322. package/src/frontend/telegram/callbacks/model.ts +311 -0
  323. package/src/frontend/telegram/callbacks/pulse.ts +50 -0
  324. package/src/frontend/telegram/callbacks/settings.ts +177 -0
  325. package/src/frontend/telegram/callbacks/shared.ts +71 -0
  326. package/src/frontend/telegram/commands/admin.ts +226 -0
  327. package/src/frontend/telegram/commands/definitions.ts +60 -0
  328. package/src/frontend/telegram/commands/index.ts +39 -0
  329. package/src/frontend/telegram/commands/info.ts +125 -0
  330. package/src/frontend/telegram/commands/session.ts +77 -0
  331. package/src/frontend/telegram/commands/settings.ts +345 -0
  332. package/src/frontend/telegram/commands/state.ts +34 -0
  333. package/src/frontend/telegram/formatting.ts +14 -25
  334. package/src/frontend/telegram/handlers/access.ts +287 -0
  335. package/src/frontend/telegram/handlers/context.ts +210 -0
  336. package/src/frontend/telegram/handlers/delivery.ts +236 -0
  337. package/src/frontend/telegram/handlers/index.ts +39 -0
  338. package/src/frontend/telegram/handlers/messages.ts +477 -0
  339. package/src/frontend/telegram/handlers/queue.ts +241 -0
  340. package/src/frontend/telegram/handlers/state.ts +75 -0
  341. package/src/frontend/telegram/helpers/diagnostics.ts +188 -0
  342. package/src/frontend/telegram/helpers/format.ts +41 -0
  343. package/src/frontend/telegram/helpers/index.ts +13 -0
  344. package/src/frontend/telegram/{helpers.ts → helpers/menu.ts} +209 -210
  345. package/src/frontend/telegram/index.ts +24 -27
  346. package/src/frontend/telegram/middleware.ts +16 -3
  347. package/src/frontend/telegram/model-callbacks.ts +14 -0
  348. package/src/frontend/telegram/model-menu.ts +317 -0
  349. package/src/frontend/telegram/sticker-library.ts +178 -0
  350. package/src/frontend/telegram/userbot.ts +2 -44
  351. package/src/frontend/terminal/commands.ts +47 -41
  352. package/src/frontend/terminal/index.ts +46 -31
  353. package/src/frontend/terminal/input.ts +5 -0
  354. package/src/frontend/terminal/renderer.ts +4 -2
  355. package/src/index.ts +13 -171
  356. package/src/login.ts +3 -2
  357. package/src/native/blake3-wasm-bytes.ts +7 -0
  358. package/src/native/blake3.ts +234 -0
  359. package/src/native/htmlents-wasm-bytes.ts +7 -0
  360. package/src/native/htmlents.ts +56 -0
  361. package/src/native/prelude.d.mts +168 -0
  362. package/src/native/prelude.mjs +1561 -0
  363. package/src/native/registry.ts +142 -0
  364. package/src/native/runtime.ts +164 -0
  365. package/src/native/scheduler-core/gleam.d.mts +2 -0
  366. package/src/native/scheduler-core/gleam.mjs +1 -0
  367. package/src/native/scheduler-core/scheduler_core.d.mts +76 -0
  368. package/src/native/scheduler-core/scheduler_core.mjs +252 -0
  369. package/src/native/scheduler-core.ts +124 -0
  370. package/src/native/sqlguard-wasm-bytes.ts +7 -0
  371. package/src/native/sqlguard.ts +107 -0
  372. package/src/native/strsim-wasm-bytes.ts +7 -0
  373. package/src/native/strsim.ts +98 -0
  374. package/src/native/textops-wasm-bytes.ts +7 -0
  375. package/src/native/textops.ts +83 -0
  376. package/src/native/warden.ts +287 -0
  377. package/src/plugins/github/index.ts +1 -1
  378. package/src/plugins/mempalace/index.ts +1 -1
  379. package/src/plugins/playwright/index.ts +1 -1
  380. package/src/storage/chat-settings.ts +335 -85
  381. package/src/storage/cron-store.ts +266 -109
  382. package/src/storage/daily-log.ts +15 -3
  383. package/src/storage/db.ts +206 -0
  384. package/src/storage/goal-store.ts +186 -0
  385. package/src/storage/history.ts +137 -188
  386. package/src/storage/kv.ts +52 -0
  387. package/src/storage/legacy-import.ts +47 -0
  388. package/src/storage/media-index.ts +118 -61
  389. package/src/storage/repositories/chat-settings-repo.ts +60 -0
  390. package/src/storage/repositories/cron-repo.ts +128 -0
  391. package/src/storage/repositories/goals-repo.ts +122 -0
  392. package/src/storage/repositories/history-repo.ts +207 -0
  393. package/src/storage/repositories/kv-repo.ts +25 -0
  394. package/src/storage/repositories/media-index-repo.ts +147 -0
  395. package/src/storage/repositories/scripts-repo.ts +81 -0
  396. package/src/storage/repositories/sessions-repo.ts +129 -0
  397. package/src/storage/repositories/triggers-repo.ts +158 -0
  398. package/src/storage/repositories/turn-meta-repo.ts +32 -0
  399. package/src/storage/scheduled-store.ts +84 -0
  400. package/src/storage/script-store.ts +198 -0
  401. package/src/storage/sessions.ts +227 -102
  402. package/src/storage/skill-store.ts +325 -0
  403. package/src/storage/sql/README.md +18 -0
  404. package/src/storage/sql/chat-settings.sql +11 -0
  405. package/src/storage/sql/cron.sql +40 -0
  406. package/src/storage/sql/db.sql +11 -0
  407. package/src/storage/sql/embed.ts +118 -0
  408. package/src/storage/sql/goals.sql +41 -0
  409. package/src/storage/sql/history.sql +83 -0
  410. package/src/storage/sql/kv.sql +11 -0
  411. package/src/storage/sql/media-index.sql +45 -0
  412. package/src/storage/sql/schema.sql +233 -0
  413. package/src/storage/sql/scripts.sql +28 -0
  414. package/src/storage/sql/sessions.sql +22 -0
  415. package/src/storage/sql/statements.generated.ts +500 -0
  416. package/src/storage/sql/triggers.sql +69 -0
  417. package/src/storage/sql/turn-meta.sql +24 -0
  418. package/src/storage/sticker-store.ts +141 -0
  419. package/src/storage/trigger-store.ts +104 -126
  420. package/src/storage/turn-meta.ts +118 -0
  421. package/src/util/chat-id.ts +30 -0
  422. package/src/util/config.ts +313 -195
  423. package/src/util/fs-path.ts +32 -0
  424. package/src/util/log.ts +53 -31
  425. package/src/util/mcp-launcher.ts +351 -33
  426. package/src/util/paths.ts +45 -17
  427. package/src/util/tail-file.ts +24 -0
  428. package/src/util/workspace.ts +161 -10
  429. package/tsconfig.json +1 -0
  430. package/src/backend/claude-sdk/models.ts +0 -501
  431. package/src/backend/codex/handler.ts +0 -595
  432. package/src/backend/kilo/handler.ts +0 -707
  433. package/src/backend/kilo/models.ts +0 -762
  434. package/src/backend/openai-agents/handler.ts +0 -539
  435. package/src/backend/openai-agents/mcp.ts +0 -139
  436. package/src/backend/opencode/handler.ts +0 -663
  437. package/src/backend/opencode/models.ts +0 -741
  438. package/src/core/cron.ts +0 -184
  439. package/src/core/dispatcher.ts +0 -144
  440. package/src/core/gateway-actions.ts +0 -603
  441. package/src/core/heartbeat.ts +0 -575
  442. package/src/core/plugin.ts +0 -776
  443. package/src/core/triggers.ts +0 -640
  444. package/src/frontend/discord/actions.ts +0 -729
  445. package/src/frontend/discord/commands.ts +0 -1036
  446. package/src/frontend/discord/handlers.ts +0 -798
  447. package/src/frontend/telegram/actions.ts +0 -731
  448. package/src/frontend/telegram/callbacks.ts +0 -419
  449. package/src/frontend/telegram/commands.ts +0 -642
  450. package/src/frontend/telegram/handlers.ts +0 -1352
  451. package/src/util/mcp-launcher.mjs +0 -140
@@ -7,8 +7,12 @@
7
7
 
8
8
  export { initAgent, updateSystemPrompt } from "./state.js";
9
9
  export { warmSession } from "./warm.js";
10
- export { handleMessage, getActiveQuery } from "./handler.js";
11
- export { buildMcpServers, getActiveFrontends } from "./options.js";
10
+ export { getActiveQuery } from "./handler.js";
11
+ export {
12
+ buildMcpServers,
13
+ buildPluginMcpServers,
14
+ getActiveFrontends,
15
+ } from "./options.js";
12
16
  export { getBridgePort } from "./state.js";
13
17
  export {
14
18
  runOneShotAgent,
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Helper to bridge the gap between MCP server *registration* and *connection*.
3
+ *
4
+ * `Query.setMcpServers` resolves as soon as servers are registered — MCP
5
+ * startup is non-blocking by design. A stdio server that dials a slow remote
6
+ * endpoint (e.g. the playwright plugin connecting to the Camoufox websocket)
7
+ * finishes its `initialize` handshake seconds later. During that window the
8
+ * server is reported as `pending` and its tools are absent from the live
9
+ * registry, so a turn that proceeds immediately after `refreshTools` sees
10
+ * `mcp__playwright-tools__*` stuck "connecting" until the next refresh.
11
+ *
12
+ * `waitForMcpServersReady` polls `mcpServerStatus()` until the named servers
13
+ * leave the transient `pending` state (or a bounded timeout elapses), so the
14
+ * caller can return only once the freshly-added tools are actually usable.
15
+ */
16
+
17
+ import type { Query } from "@anthropic-ai/claude-agent-sdk";
18
+
19
+ /**
20
+ * Resolve once every server in `names` has left the `pending` state, or after
21
+ * `timeoutMs`. Best-effort and non-throwing: any error from `mcpServerStatus()`
22
+ * ends the wait so the turn is never blocked. Servers that settle into a
23
+ * terminal state (`connected`/`failed`/`needs-auth`/`disabled`) also resolve
24
+ * the wait — only the transient `pending` window is blocked on.
25
+ */
26
+ export async function waitForMcpServersReady(
27
+ qi: Pick<Query, "mcpServerStatus">,
28
+ names: string[],
29
+ timeoutMs = 60_000,
30
+ pollMs = 250,
31
+ ): Promise<void> {
32
+ if (names.length === 0) return;
33
+ const pending = new Set(names);
34
+ const deadline = Date.now() + timeoutMs;
35
+ while (Date.now() < deadline) {
36
+ let statuses;
37
+ try {
38
+ statuses = await qi.mcpServerStatus();
39
+ } catch {
40
+ return; // status query unsupported/failed — don't block the turn
41
+ }
42
+ // Best-effort: a backend (or stub) may report status in an unexpected
43
+ // shape (undefined / non-array). Treat anything non-iterable as
44
+ // "unsupported" and return rather than throwing into the turn.
45
+ if (!Array.isArray(statuses)) return;
46
+ for (const s of statuses) {
47
+ if (pending.has(s.name) && s.status !== "pending") {
48
+ pending.delete(s.name);
49
+ }
50
+ }
51
+ if (pending.size === 0) return;
52
+ await new Promise((resolve) => setTimeout(resolve, pollMs));
53
+ }
54
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Turn-time model-drift detection.
3
+ *
4
+ * The SDK result's `modelUsage` is keyed by the model that ACTUALLY
5
+ * served the turn. Nothing compared it to the model we asked for — so
6
+ * when the API substitutes (requested model withdrawn from the
7
+ * catalog, rate-limit downshift, silent server-side fallback), the
8
+ * only evidence was an INFO accounting line nobody reads. This module
9
+ * turns that into a WARN with the fix in it, once per
10
+ * (requested → actual) pair per process.
11
+ */
12
+
13
+ import { logWarn } from "../../util/log.js";
14
+
15
+ /**
16
+ * Alias-tolerant model identity: "opus" is the same model as
17
+ * "claude-opus-4-8", "sonnet[1m]" the same as "sonnet". Substring
18
+ * containment after normalisation is deliberately loose — a false
19
+ * "same" (missed warning) is better than crying wolf on every alias
20
+ * expansion.
21
+ */
22
+ export function isSameModel(requested: string, actual: string): boolean {
23
+ const norm = (s: string) =>
24
+ s
25
+ .toLowerCase()
26
+ .replace(/\[1m\]/g, "")
27
+ .trim();
28
+ const r = norm(requested);
29
+ const a = norm(actual);
30
+ if (!r || !a) return true; // nothing to compare — don't warn
31
+ return a === r || a.includes(r) || r.includes(a);
32
+ }
33
+
34
+ /** (requested→actual) pairs already warned about this process. */
35
+ const warned = new Set<string>();
36
+
37
+ /** Test seam. */
38
+ export function resetModelDriftWarnings(): void {
39
+ warned.clear();
40
+ }
41
+
42
+ /**
43
+ * Compare the requested model against the models that actually served
44
+ * the turn (the `modelUsage` keys). Warns once per distinct pair.
45
+ * "default" requests are skipped — no pin, nothing to drift from.
46
+ */
47
+ export function checkModelDrift(
48
+ requested: string,
49
+ actualModels: readonly string[],
50
+ ): void {
51
+ if (!requested || requested === "default") return;
52
+ if (actualModels.length === 0) return;
53
+ if (actualModels.some((actual) => isSameModel(requested, actual))) return;
54
+
55
+ const actual = actualModels.join(", ");
56
+ const key = `${requested}→${actual}`;
57
+ if (warned.has(key)) return;
58
+ warned.add(key);
59
+ logWarn(
60
+ "agent",
61
+ `[MODEL DRIFT] requested "${requested}" but the API served "${actual}" — ` +
62
+ `the pinned model may no longer exist or was substituted upstream. ` +
63
+ `Check "model" in config.json (or this chat's /model override).`,
64
+ );
65
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Model provider methods for the Claude SDK backend.
3
3
  *
4
- * Implements the optional model methods from QueryBackend by delegating
4
+ * Implements the optional model methods from Backend by delegating
5
5
  * to the core model registry. The Claude SDK exposes a single provider
6
6
  * ("anthropic") with models discovered from the SDK at startup.
7
7
  */
@@ -11,8 +11,8 @@ import {
11
11
  getModels,
12
12
  resolveModel as coreResolveModel,
13
13
  resolveModelId,
14
- } from "../../core/models.js";
15
- import type { ModelInfo } from "../../core/models.js";
14
+ } from "../../core/models/catalog.js";
15
+ import type { ModelInfo } from "../../core/models/catalog.js";
16
16
  import type {
17
17
  UnifiedModelInfo,
18
18
  UnifiedModelResolution,
@@ -34,10 +34,16 @@ function toUnified(model: ModelInfo): UnifiedModelInfo {
34
34
  provider: PROVIDER_ID,
35
35
  providerName: PROVIDER_NAME,
36
36
  selectable: true,
37
+ supportedReasoningLevels: model.supportedReasoningLevels,
38
+ defaultReasoningLevel: model.defaultReasoningLevel,
37
39
  };
38
40
  }
39
41
 
40
- /** De-duplicate models by displayName (1M variants share a label with their base). */
42
+ /**
43
+ * De-duplicate models by displayName. Base and 1M variants carry distinct
44
+ * labels ("Sonnet 4.6" vs "Sonnet 4.6 (1M context)"), so both survive here;
45
+ * this only guards against accidental collisions.
46
+ */
41
47
  function getUniqueModels(): ModelInfo[] {
42
48
  const options: ModelInfo[] = [];
43
49
  const seenKeys = new Set<string>();
@@ -156,7 +162,7 @@ export async function getProviders(): Promise<UnifiedProviderInfo[]> {
156
162
 
157
163
  export async function getProviderModels(
158
164
  providerId: string,
159
- page = 0,
165
+ page = 1,
160
166
  pageSize = 20,
161
167
  ): Promise<{ models: UnifiedModelInfo[]; total: number }> {
162
168
  if (providerId !== PROVIDER_ID) {
@@ -164,7 +170,7 @@ export async function getProviderModels(
164
170
  }
165
171
 
166
172
  const all = getModels(PROVIDER_ID).map(toUnified);
167
- const start = page * pageSize;
173
+ const start = (page - 1) * pageSize;
168
174
  return {
169
175
  models: all.slice(start, start + pageSize),
170
176
  total: all.length,
@@ -0,0 +1,169 @@
1
+ /**
2
+ * SDK → registry conversion. Groups SDK models into variant buckets, picks a
3
+ * canonical per bucket, derives display names + aliases + the fallback chain,
4
+ * and emits the registry `ModelInfo[]`.
5
+ */
6
+
7
+ import type { ModelInfo } from "../../../core/models/catalog.js";
8
+ import { normalizeReasoningLevels } from "../../../core/models/reasoning-levels.js";
9
+ import {
10
+ buildSdkModelRecords,
11
+ buildGeneratedAliases,
12
+ deriveDisplayName,
13
+ getPreferredModelPriority,
14
+ mergeAliases,
15
+ type AliasFormOptions,
16
+ type SdkModelInfo,
17
+ type SdkModelRecord,
18
+ } from "./parsing.js";
19
+
20
+ function extractSdkReasoningLevels(model: SdkModelInfo) {
21
+ if (model.supportsEffort === false) return [];
22
+
23
+ const sdkLevels = normalizeReasoningLevels(model.supportedEffortLevels);
24
+ if (sdkLevels.length > 0) return sdkLevels;
25
+
26
+ const effort = model.capabilities?.effort;
27
+ if (!effort?.supported) return [];
28
+
29
+ return normalizeReasoningLevels(
30
+ (["low", "medium", "high", "max", "xhigh"] as const).filter(
31
+ (level) => effort[level]?.supported === true,
32
+ ),
33
+ );
34
+ }
35
+
36
+ /**
37
+ * Group records into variant buckets and pick the canonical record for each.
38
+ * Records without a parseable identity become their own singleton bucket so
39
+ * they still surface (keyed by value to stay unique).
40
+ */
41
+ function groupVariants(
42
+ records: readonly SdkModelRecord[],
43
+ ): Map<string, SdkModelRecord[]> {
44
+ const groups = new Map<string, SdkModelRecord[]>();
45
+ for (const record of records) {
46
+ const key = record.variantKey ?? `raw:${record.value}`;
47
+ const bucket = groups.get(key) ?? [];
48
+ bucket.push(record);
49
+ groups.set(key, bucket);
50
+ }
51
+ return groups;
52
+ }
53
+
54
+ function pickCanonical(bucket: readonly SdkModelRecord[]): SdkModelRecord {
55
+ return [...bucket].sort((left, right) => {
56
+ const priorityDelta =
57
+ getPreferredModelPriority(left) - getPreferredModelPriority(right);
58
+ if (priorityDelta !== 0) return priorityDelta;
59
+ return left.index - right.index;
60
+ })[0]!;
61
+ }
62
+
63
+ /**
64
+ * Assign a best-effort fallback (used on overload/timeout) to every model
65
+ * except the last:
66
+ * - A 1M variant prefers its base sibling of the same family+version.
67
+ * - Otherwise a model falls back to the next model in SDK order.
68
+ */
69
+ function assignFallbacks(
70
+ models: ModelInfo[],
71
+ recordByValue: ReadonlyMap<string, SdkModelRecord>,
72
+ ): void {
73
+ const baseByFamily = new Map<string, string>();
74
+ for (const model of models) {
75
+ const rec = recordByValue.get(model.id);
76
+ if (rec && !rec.identity.isOneMillion && rec.familyKey) {
77
+ baseByFamily.set(rec.familyKey, model.id);
78
+ }
79
+ }
80
+
81
+ models.forEach((model, index) => {
82
+ const rec = recordByValue.get(model.id);
83
+ if (rec?.identity.isOneMillion && rec.familyKey) {
84
+ const baseSibling = baseByFamily.get(rec.familyKey);
85
+ if (baseSibling && baseSibling !== model.id) {
86
+ model.fallback = baseSibling;
87
+ return;
88
+ }
89
+ }
90
+ if (index < models.length - 1) {
91
+ model.fallback = models[index + 1]!.id;
92
+ }
93
+ });
94
+ }
95
+
96
+ /**
97
+ * Convert SDK ModelInfo to our registry format.
98
+ *
99
+ * Base and 1M variants of the same family+version each surface as their own
100
+ * selectable entry (so the picker shows both), while true duplicates — the
101
+ * same model exposed under multiple ids — collapse into one canonical entry
102
+ * that absorbs the others' aliases. Display names, aliases, and the fallback
103
+ * chain are all derived from SDK metadata, never hardcoded versions.
104
+ */
105
+ export function convertSdkModels(sdkModels: SdkModelInfo[]): ModelInfo[] {
106
+ const records = buildSdkModelRecords(sdkModels);
107
+ const groups = groupVariants(records);
108
+
109
+ // One canonical per variant bucket, ordered by SDK position so the registry
110
+ // preserves the SDK's ordering.
111
+ const canonicals = [...groups.values()]
112
+ .map(pickCanonical)
113
+ .sort((a, b) => a.index - b.index);
114
+
115
+ // Which family+version pairs have a base (non-1M) canonical? A 1M entry only
116
+ // claims the bare family aliases when there is no base sibling to own them.
117
+ const baseFamilyVersions = new Set<string>();
118
+ for (const canonical of canonicals) {
119
+ if (!canonical.identity.isOneMillion && canonical.familyKey) {
120
+ baseFamilyVersions.add(canonical.familyKey);
121
+ }
122
+ }
123
+
124
+ const usedKeys = new Set<string>();
125
+ const recordByValue = new Map<string, SdkModelRecord>();
126
+ const models: ModelInfo[] = [];
127
+
128
+ for (const canonical of canonicals) {
129
+ const groupKey = canonical.variantKey ?? `raw:${canonical.value}`;
130
+ const bucket = groups.get(groupKey)!;
131
+ const { identity } = canonical;
132
+
133
+ const hasBaseSibling = identity.isOneMillion
134
+ ? !!canonical.familyKey && baseFamilyVersions.has(canonical.familyKey)
135
+ : true;
136
+ const aliasForms: AliasFormOptions = {
137
+ includeBare: !identity.isOneMillion || !hasBaseSibling,
138
+ include1m: identity.isOneMillion,
139
+ };
140
+
141
+ // Aliases come from every record the canonical absorbs (its own value plus
142
+ // each duplicate's value and generated forms), so legacy ids keep resolving.
143
+ const canonicalKey = canonical.value.toLowerCase();
144
+ const aliases = mergeAliases(
145
+ ...bucket.map((record) => [
146
+ record.value,
147
+ ...buildGeneratedAliases(record.identity, aliasForms),
148
+ ]),
149
+ )
150
+ .filter((alias) => alias.toLowerCase() !== canonicalKey)
151
+ .filter((alias) => !usedKeys.has(alias.toLowerCase()));
152
+
153
+ usedKeys.add(canonicalKey);
154
+ for (const alias of aliases) usedKeys.add(alias.toLowerCase());
155
+
156
+ recordByValue.set(canonical.value, canonical);
157
+ models.push({
158
+ id: canonical.value,
159
+ displayName: deriveDisplayName(identity, canonical.displayName),
160
+ description: canonical.description,
161
+ aliases,
162
+ provider: "anthropic",
163
+ supportedReasoningLevels: extractSdkReasoningLevels(canonical),
164
+ });
165
+ }
166
+
167
+ assignFallbacks(models, recordByValue);
168
+ return models;
169
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Claude model discovery — spawns throwaway SDK subprocesses, calls
3
+ * `supportedModels()`, unions the results, and registers them in the global
4
+ * model registry. Also the static-registration path for tests / CLI setup.
5
+ */
6
+
7
+ import { query } from "@anthropic-ai/claude-agent-sdk";
8
+ import {
9
+ registerModels,
10
+ clearModelsByProvider,
11
+ registerProviderPrefix,
12
+ } from "../../../core/models/catalog.js";
13
+ import type { ModelInfo } from "../../../core/models/catalog.js";
14
+ import { log, logError } from "../../../util/log.js";
15
+ import { describeSdkModel, type SdkModelInfo } from "./parsing.js";
16
+ import { convertSdkModels } from "./convert.js";
17
+
18
+ type ProbeOptions = {
19
+ cwd?: string;
20
+ permissionMode?: string;
21
+ allowDangerouslySkipPermissions?: boolean;
22
+ pathToClaudeCodeExecutable?: string;
23
+ };
24
+
25
+ const DISCOVERY_TIMEOUT_MS = 15_000;
26
+ const MAX_DISCOVERY_SEEDS = 8;
27
+
28
+ /**
29
+ * The `claude` binary returns "Custom model" for a `model` it doesn't
30
+ * recognise — it echoes the requested id straight back as a passthrough entry
31
+ * with no real metadata. We drop those (except the user's own configured
32
+ * model) so seed probing doesn't pollute the registry with junk families.
33
+ */
34
+ const CUSTOM_MODEL_DESCRIPTION = "custom model";
35
+
36
+ /**
37
+ * Spawn a throwaway SDK subprocess seeded with `seedModel` and return its
38
+ * `supportedModels()` list. The binary always echoes the requested model into
39
+ * the list, so the seed controls which models surface beyond the base set.
40
+ */
41
+ async function probeSupportedModels(
42
+ seedModel: string,
43
+ probeOptions: ProbeOptions,
44
+ ): Promise<SdkModelInfo[]> {
45
+ const abort = new AbortController();
46
+ let drainPromise: Promise<void> | undefined;
47
+
48
+ try {
49
+ const neverYield = async function* (): AsyncGenerator<never> {
50
+ await new Promise<never>((_, reject) => {
51
+ abort.signal.addEventListener("abort", () =>
52
+ reject(new Error("aborted")),
53
+ );
54
+ });
55
+ };
56
+
57
+ const q = query({
58
+ prompt: neverYield(),
59
+ options: {
60
+ ...probeOptions,
61
+ model: seedModel,
62
+ abortController: abort,
63
+ } as Parameters<typeof query>[0]["options"],
64
+ });
65
+
66
+ drainPromise = (async () => {
67
+ try {
68
+ for await (const _ of q) {
69
+ /* discard */
70
+ }
71
+ } catch {
72
+ /* expected on abort */
73
+ }
74
+ })();
75
+
76
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
77
+ const timeout = new Promise<never>((_, reject) => {
78
+ timeoutId = setTimeout(
79
+ () =>
80
+ reject(
81
+ new Error(
82
+ `model discovery timed out after 15s (seed "${seedModel}")`,
83
+ ),
84
+ ),
85
+ DISCOVERY_TIMEOUT_MS,
86
+ );
87
+ });
88
+
89
+ try {
90
+ return await Promise.race([q.supportedModels(), timeout]);
91
+ } finally {
92
+ if (timeoutId !== undefined) clearTimeout(timeoutId);
93
+ abort.abort();
94
+ await drainPromise.catch(() => {});
95
+ }
96
+ } catch (err) {
97
+ abort.abort();
98
+ if (drainPromise) await drainPromise.catch(() => {});
99
+ throw err;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Merge probe results into one list, first-occurrence-wins by value, dropping
105
+ * unrecognised "Custom model" passthrough echoes (but always keeping the user's
106
+ * configured model even if the binary treats it as custom).
107
+ */
108
+ function unionSdkModels(
109
+ lists: readonly SdkModelInfo[][],
110
+ configuredModel: string,
111
+ ): SdkModelInfo[] {
112
+ const byValue = new Map<string, SdkModelInfo>();
113
+ for (const list of lists) {
114
+ for (const model of list) {
115
+ if (byValue.has(model.value)) continue;
116
+ const isPassthroughJunk =
117
+ (model.description ?? "").trim().toLowerCase() ===
118
+ CUSTOM_MODEL_DESCRIPTION && model.value !== configuredModel;
119
+ if (isPassthroughJunk) continue;
120
+ byValue.set(model.value, model);
121
+ }
122
+ }
123
+ return [...byValue.values()];
124
+ }
125
+
126
+ /**
127
+ * Discover available models from the Claude Agent SDK and register them.
128
+ *
129
+ * Discovery is a union of probes. The binary only echoes the *requested* model
130
+ * (and a small base set) from `supportedModels()`, so a single probe misses
131
+ * the base-vs-1M counterpart of most families. We therefore probe the
132
+ * configured model first (mandatory), then best-effort re-probe each real
133
+ * family alias it revealed, and union the results. Throws only if the
134
+ * mandatory first probe fails — if the SDK can't provide models, Talon cannot
135
+ * function.
136
+ */
137
+ export async function registerClaudeModels(sdkOptions: {
138
+ model: string;
139
+ cwd?: string;
140
+ permissionMode?: string;
141
+ allowDangerouslySkipPermissions?: boolean;
142
+ pathToClaudeCodeExecutable?: string;
143
+ }): Promise<void> {
144
+ const { model: configuredModel, ...probeOptions } = sdkOptions;
145
+
146
+ try {
147
+ // Pass 1 (mandatory): the configured model is always echoed back.
148
+ const primary = await probeSupportedModels(configuredModel, probeOptions);
149
+ if (primary.length === 0) {
150
+ throw new Error("SDK returned empty model list");
151
+ }
152
+
153
+ // Pass 2 (best-effort): re-probe each real family alias from pass 1 to coax
154
+ // out the context variants the first probe didn't echo. Bare family aliases
155
+ // (e.g. "opus") return real metadata; unrecognised ones come back as
156
+ // "Custom model" and are filtered by unionSdkModels.
157
+ const seeds = new Set<string>();
158
+ for (const model of primary) {
159
+ const { family } = describeSdkModel(model);
160
+ if (family && family !== "default") seeds.add(family);
161
+ }
162
+ seeds.delete(configuredModel);
163
+
164
+ const seedList = [...seeds].slice(0, MAX_DISCOVERY_SEEDS);
165
+ const secondary = await Promise.allSettled(
166
+ seedList.map((seed) => probeSupportedModels(seed, probeOptions)),
167
+ );
168
+ const extraLists = secondary
169
+ .filter(
170
+ (result): result is PromiseFulfilledResult<SdkModelInfo[]> =>
171
+ result.status === "fulfilled",
172
+ )
173
+ .map((result) => result.value);
174
+
175
+ const union = unionSdkModels([primary, ...extraLists], configuredModel);
176
+ const models = convertSdkModels(union);
177
+ clearModelsByProvider("anthropic");
178
+ registerProviderPrefix("claude-");
179
+ registerModels(models);
180
+ log(
181
+ "agent",
182
+ `Discovered ${models.length} models from SDK ` +
183
+ `(${1 + extraLists.length}/${1 + seedList.length} probes): ` +
184
+ models.map((m) => m.id).join(", "),
185
+ );
186
+ } catch (err) {
187
+ const msg = err instanceof Error ? err.message : String(err);
188
+ logError("agent", `Fatal: model discovery failed — ${msg}`);
189
+ throw new Error(
190
+ `Claude SDK model discovery failed: ${msg}. ` +
191
+ `Check that Claude Code is installed and your API key is valid.`,
192
+ );
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Register models from a static list. For use in tests and the CLI setup
198
+ * wizard where the SDK subprocess is not available.
199
+ */
200
+ export function registerClaudeModelsStatic(models: ModelInfo[]): void {
201
+ registerProviderPrefix("claude-");
202
+ registerModels(models);
203
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Claude model discovery — queries the SDK for available models.
3
+ *
4
+ * Split by responsibility:
5
+ * - `parsing` — model-identity parsing, display names, alias generation
6
+ * - `convert` — SDK ModelInfo → registry ModelInfo[] (variants, fallbacks)
7
+ * - `discovery` — SDK subprocess probing + registration (registerClaudeModels,
8
+ * registerClaudeModelsStatic)
9
+ * - `static` — CLAUDE_MODELS_STATIC fallback list
10
+ *
11
+ * Re-exports the same public surface the old single-file module exposed.
12
+ */
13
+
14
+ export {
15
+ registerClaudeModels,
16
+ registerClaudeModelsStatic,
17
+ } from "./discovery.js";
18
+ export { CLAUDE_MODELS_STATIC } from "./static.js";