@armadra/agent 0.0.0-stage → 0.3.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 (433) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +470 -2
  4. package/dist/agent/agent.d.ts +56 -0
  5. package/dist/agent/agent.js +129 -0
  6. package/dist/agent/loop.d.ts +51 -0
  7. package/dist/agent/loop.js +158 -0
  8. package/dist/agent/queue.d.ts +25 -0
  9. package/dist/agent/queue.js +55 -0
  10. package/dist/agent/retry.d.ts +22 -0
  11. package/dist/agent/retry.js +92 -0
  12. package/dist/agent/schema.d.ts +27 -0
  13. package/dist/agent/schema.js +186 -0
  14. package/dist/agent/session-cache.d.ts +100 -0
  15. package/dist/agent/session-cache.js +514 -0
  16. package/dist/agent/session-compaction.d.ts +49 -0
  17. package/dist/agent/session-compaction.js +251 -0
  18. package/dist/agent/session-core.d.ts +103 -0
  19. package/dist/agent/session-core.js +7 -0
  20. package/dist/agent/session-run.d.ts +55 -0
  21. package/dist/agent/session-run.js +246 -0
  22. package/dist/agent/session-state.d.ts +39 -0
  23. package/dist/agent/session-state.js +119 -0
  24. package/dist/agent/session-subagent.d.ts +54 -0
  25. package/dist/agent/session-subagent.js +157 -0
  26. package/dist/agent/session-sync.d.ts +20 -0
  27. package/dist/agent/session-sync.js +111 -0
  28. package/dist/agent/session-tools.d.ts +24 -0
  29. package/dist/agent/session-tools.js +277 -0
  30. package/dist/agent/session.d.ts +117 -0
  31. package/dist/agent/session.js +485 -0
  32. package/dist/agent/system-prompt.d.ts +49 -0
  33. package/dist/agent/system-prompt.js +121 -0
  34. package/dist/agent/tool-runner.d.ts +64 -0
  35. package/dist/agent/tool-runner.js +309 -0
  36. package/dist/agent/transform.d.ts +26 -0
  37. package/dist/agent/transform.js +171 -0
  38. package/dist/agent/types.d.ts +378 -0
  39. package/dist/agent/types.js +21 -0
  40. package/dist/ai/apis/anthropic-messages.d.ts +26 -0
  41. package/dist/ai/apis/anthropic-messages.js +214 -0
  42. package/dist/ai/apis/anthropic-request.d.ts +42 -0
  43. package/dist/ai/apis/anthropic-request.js +282 -0
  44. package/dist/ai/apis/api.d.ts +28 -0
  45. package/dist/ai/apis/api.js +102 -0
  46. package/dist/ai/apis/cache-params.d.ts +48 -0
  47. package/dist/ai/apis/cache-params.js +163 -0
  48. package/dist/ai/apis/google-generative-ai.d.ts +32 -0
  49. package/dist/ai/apis/google-generative-ai.js +249 -0
  50. package/dist/ai/apis/google-request.d.ts +40 -0
  51. package/dist/ai/apis/google-request.js +255 -0
  52. package/dist/ai/apis/openai-compat.d.ts +35 -0
  53. package/dist/ai/apis/openai-compat.js +142 -0
  54. package/dist/ai/apis/openai-completions.d.ts +28 -0
  55. package/dist/ai/apis/openai-completions.js +246 -0
  56. package/dist/ai/apis/openai-request.d.ts +35 -0
  57. package/dist/ai/apis/openai-request.js +303 -0
  58. package/dist/ai/apis/openai-responses-request.d.ts +52 -0
  59. package/dist/ai/apis/openai-responses-request.js +266 -0
  60. package/dist/ai/apis/openai-responses.d.ts +28 -0
  61. package/dist/ai/apis/openai-responses.js +304 -0
  62. package/dist/ai/apis/shared.d.ts +56 -0
  63. package/dist/ai/apis/shared.js +179 -0
  64. package/dist/ai/cache/economics.d.ts +24 -0
  65. package/dist/ai/cache/economics.js +69 -0
  66. package/dist/ai/cache/fingerprint.d.ts +17 -0
  67. package/dist/ai/cache/fingerprint.js +39 -0
  68. package/dist/ai/cache/miss.d.ts +49 -0
  69. package/dist/ai/cache/miss.js +96 -0
  70. package/dist/ai/cache/reporting.d.ts +39 -0
  71. package/dist/ai/cache/reporting.js +103 -0
  72. package/dist/ai/cache/types.d.ts +94 -0
  73. package/dist/ai/cache/types.js +19 -0
  74. package/dist/ai/cache/warmer.d.ts +94 -0
  75. package/dist/ai/cache/warmer.js +224 -0
  76. package/dist/ai/context.d.ts +35 -0
  77. package/dist/ai/context.js +114 -0
  78. package/dist/ai/cost.d.ts +21 -0
  79. package/dist/ai/cost.js +60 -0
  80. package/dist/ai/event-stream.d.ts +33 -0
  81. package/dist/ai/event-stream.js +101 -0
  82. package/dist/ai/fake/fake-provider.d.ts +68 -0
  83. package/dist/ai/fake/fake-provider.js +278 -0
  84. package/dist/ai/fake/fake-script.d.ts +69 -0
  85. package/dist/ai/fake/fake-script.js +142 -0
  86. package/dist/ai/http.d.ts +54 -0
  87. package/dist/ai/http.js +212 -0
  88. package/dist/ai/json-partial.d.ts +17 -0
  89. package/dist/ai/json-partial.js +243 -0
  90. package/dist/ai/overflow.d.ts +22 -0
  91. package/dist/ai/overflow.js +61 -0
  92. package/dist/ai/providers/auth.d.ts +71 -0
  93. package/dist/ai/providers/auth.js +202 -0
  94. package/dist/ai/providers/builtin.d.ts +23 -0
  95. package/dist/ai/providers/builtin.js +148 -0
  96. package/dist/ai/providers/catalog-data.d.ts +5 -0
  97. package/dist/ai/providers/catalog-data.js +19 -0
  98. package/dist/ai/providers/catalog.d.ts +28 -0
  99. package/dist/ai/providers/catalog.js +166 -0
  100. package/dist/ai/providers/channels.d.ts +51 -0
  101. package/dist/ai/providers/channels.js +96 -0
  102. package/dist/ai/providers/enrich.d.ts +34 -0
  103. package/dist/ai/providers/enrich.js +86 -0
  104. package/dist/ai/providers/models-dev-cache.d.ts +53 -0
  105. package/dist/ai/providers/models-dev-cache.js +147 -0
  106. package/dist/ai/providers/models-dev.d.ts +99 -0
  107. package/dist/ai/providers/models-dev.js +315 -0
  108. package/dist/ai/providers/registry.d.ts +114 -0
  109. package/dist/ai/providers/registry.js +458 -0
  110. package/dist/ai/sse.d.ts +32 -0
  111. package/dist/ai/sse.js +140 -0
  112. package/dist/ai/thinking.d.ts +42 -0
  113. package/dist/ai/thinking.js +101 -0
  114. package/dist/ai/types.d.ts +448 -0
  115. package/dist/ai/types.js +19 -0
  116. package/dist/bundle/ama-sandbox.cjs +324 -0
  117. package/dist/bundle/ama.cjs +27837 -0
  118. package/dist/bundle.d.ts +9 -0
  119. package/dist/bundle.js +15 -0
  120. package/dist/cli/args.d.ts +85 -0
  121. package/dist/cli/args.js +408 -0
  122. package/dist/cli/bootstrap.d.ts +18 -0
  123. package/dist/cli/bootstrap.js +399 -0
  124. package/dist/cli/compose-providers.d.ts +37 -0
  125. package/dist/cli/compose-providers.js +107 -0
  126. package/dist/cli/compose-session.d.ts +72 -0
  127. package/dist/cli/compose-session.js +370 -0
  128. package/dist/cli/compose-store.d.ts +24 -0
  129. package/dist/cli/compose-store.js +125 -0
  130. package/dist/cli/compose.d.ts +108 -0
  131. package/dist/cli/compose.js +221 -0
  132. package/dist/cli/default-model.d.ts +16 -0
  133. package/dist/cli/default-model.js +24 -0
  134. package/dist/cli/deps.d.ts +207 -0
  135. package/dist/cli/deps.js +12 -0
  136. package/dist/cli/exit-codes.d.ts +29 -0
  137. package/dist/cli/exit-codes.js +40 -0
  138. package/dist/cli/main.d.ts +27 -0
  139. package/dist/cli/main.js +177 -0
  140. package/dist/cli/runtime.d.ts +90 -0
  141. package/dist/cli/runtime.js +8 -0
  142. package/dist/cli/startup-screen.d.ts +15 -0
  143. package/dist/cli/startup-screen.js +56 -0
  144. package/dist/cli/startup-steps.d.ts +35 -0
  145. package/dist/cli/startup-steps.js +202 -0
  146. package/dist/cli/subcommands/auth.d.ts +12 -0
  147. package/dist/cli/subcommands/auth.js +91 -0
  148. package/dist/cli/subcommands/config.d.ts +53 -0
  149. package/dist/cli/subcommands/config.js +277 -0
  150. package/dist/cli/subcommands/context.d.ts +21 -0
  151. package/dist/cli/subcommands/context.js +42 -0
  152. package/dist/cli/subcommands/doctor.d.ts +11 -0
  153. package/dist/cli/subcommands/doctor.js +246 -0
  154. package/dist/cli/subcommands/init.d.ts +6 -0
  155. package/dist/cli/subcommands/init.js +22 -0
  156. package/dist/cli/subcommands/model-meta.d.ts +16 -0
  157. package/dist/cli/subcommands/model-meta.js +54 -0
  158. package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
  159. package/dist/cli/subcommands/models-cache-probe.js +190 -0
  160. package/dist/cli/subcommands/models-discover.d.ts +46 -0
  161. package/dist/cli/subcommands/models-discover.js +232 -0
  162. package/dist/cli/subcommands/models.d.ts +40 -0
  163. package/dist/cli/subcommands/models.js +152 -0
  164. package/dist/cli/subcommands/providers-list.d.ts +8 -0
  165. package/dist/cli/subcommands/providers-list.js +97 -0
  166. package/dist/cli/subcommands/providers-plan.d.ts +79 -0
  167. package/dist/cli/subcommands/providers-plan.js +215 -0
  168. package/dist/cli/subcommands/providers.d.ts +27 -0
  169. package/dist/cli/subcommands/providers.js +433 -0
  170. package/dist/cli/subcommands/sessions.d.ts +11 -0
  171. package/dist/cli/subcommands/sessions.js +100 -0
  172. package/dist/codemode/capability.d.ts +37 -0
  173. package/dist/codemode/capability.js +55 -0
  174. package/dist/codemode/declarations.d.ts +44 -0
  175. package/dist/codemode/declarations.js +111 -0
  176. package/dist/codemode/host-side.d.ts +59 -0
  177. package/dist/codemode/host-side.js +253 -0
  178. package/dist/codemode/modes.d.ts +23 -0
  179. package/dist/codemode/modes.js +40 -0
  180. package/dist/codemode/protocol.d.ts +91 -0
  181. package/dist/codemode/protocol.js +114 -0
  182. package/dist/codemode/sandbox-entry.d.ts +25 -0
  183. package/dist/codemode/sandbox-entry.js +323 -0
  184. package/dist/codemode/store.d.ts +26 -0
  185. package/dist/codemode/store.js +53 -0
  186. package/dist/codemode/tool.d.ts +105 -0
  187. package/dist/codemode/tool.js +283 -0
  188. package/dist/compaction/branch-summary.d.ts +30 -0
  189. package/dist/compaction/branch-summary.js +79 -0
  190. package/dist/compaction/breaker.d.ts +32 -0
  191. package/dist/compaction/breaker.js +65 -0
  192. package/dist/compaction/cut-point.d.ts +23 -0
  193. package/dist/compaction/cut-point.js +70 -0
  194. package/dist/compaction/estimate.d.ts +30 -0
  195. package/dist/compaction/estimate.js +114 -0
  196. package/dist/compaction/prune-tier.d.ts +31 -0
  197. package/dist/compaction/prune-tier.js +83 -0
  198. package/dist/compaction/serialize.d.ts +20 -0
  199. package/dist/compaction/serialize.js +115 -0
  200. package/dist/compaction/summarize-tier.d.ts +111 -0
  201. package/dist/compaction/summarize-tier.js +305 -0
  202. package/dist/config/auth-file.d.ts +39 -0
  203. package/dist/config/auth-file.js +92 -0
  204. package/dist/config/context-files.d.ts +30 -0
  205. package/dist/config/context-files.js +77 -0
  206. package/dist/config/init.d.ts +30 -0
  207. package/dist/config/init.js +92 -0
  208. package/dist/config/json-schema.d.ts +14 -0
  209. package/dist/config/json-schema.js +182 -0
  210. package/dist/config/load.d.ts +45 -0
  211. package/dist/config/load.js +97 -0
  212. package/dist/config/merge.d.ts +73 -0
  213. package/dist/config/merge.js +248 -0
  214. package/dist/config/paths.d.ts +38 -0
  215. package/dist/config/paths.js +108 -0
  216. package/dist/config/profile.d.ts +27 -0
  217. package/dist/config/profile.js +65 -0
  218. package/dist/config/schema.d.ts +34 -0
  219. package/dist/config/schema.js +476 -0
  220. package/dist/config/trust.d.ts +42 -0
  221. package/dist/config/trust.js +122 -0
  222. package/dist/config/types.d.ts +190 -0
  223. package/dist/config/types.js +29 -0
  224. package/dist/config/write.d.ts +10 -0
  225. package/dist/config/write.js +25 -0
  226. package/dist/errors.d.ts +28 -0
  227. package/dist/errors.js +30 -0
  228. package/dist/hooks/config.d.ts +49 -0
  229. package/dist/hooks/config.js +102 -0
  230. package/dist/hooks/dispatcher.d.ts +41 -0
  231. package/dist/hooks/dispatcher.js +104 -0
  232. package/dist/hooks/matcher.d.ts +22 -0
  233. package/dist/hooks/matcher.js +108 -0
  234. package/dist/hooks/protocol.d.ts +33 -0
  235. package/dist/hooks/protocol.js +240 -0
  236. package/dist/hooks/runner.d.ts +30 -0
  237. package/dist/hooks/runner.js +143 -0
  238. package/dist/hooks/types.d.ts +147 -0
  239. package/dist/hooks/types.js +20 -0
  240. package/dist/host/api-impl.d.ts +75 -0
  241. package/dist/host/api-impl.js +173 -0
  242. package/dist/host/loader.d.ts +31 -0
  243. package/dist/host/loader.js +147 -0
  244. package/dist/host/types.d.ts +169 -0
  245. package/dist/host/types.js +14 -0
  246. package/dist/host.d.ts +5 -0
  247. package/dist/host.js +4 -0
  248. package/dist/index.d.ts +41 -0
  249. package/dist/index.js +28 -0
  250. package/dist/modes/commands-core.d.ts +44 -0
  251. package/dist/modes/commands-core.js +171 -0
  252. package/dist/modes/image-input.d.ts +27 -0
  253. package/dist/modes/image-input.js +78 -0
  254. package/dist/modes/interactive/approval-dialog.d.ts +48 -0
  255. package/dist/modes/interactive/approval-dialog.js +202 -0
  256. package/dist/modes/interactive/commands.d.ts +41 -0
  257. package/dist/modes/interactive/commands.js +201 -0
  258. package/dist/modes/interactive/completion.d.ts +53 -0
  259. package/dist/modes/interactive/completion.js +157 -0
  260. package/dist/modes/interactive/interactive-mode.d.ts +46 -0
  261. package/dist/modes/interactive/interactive-mode.js +491 -0
  262. package/dist/modes/interactive/key-dispatch.d.ts +29 -0
  263. package/dist/modes/interactive/key-dispatch.js +107 -0
  264. package/dist/modes/interactive/line/line-editor.d.ts +50 -0
  265. package/dist/modes/interactive/line/line-editor.js +232 -0
  266. package/dist/modes/interactive/line/line-mode.d.ts +20 -0
  267. package/dist/modes/interactive/line/line-mode.js +218 -0
  268. package/dist/modes/interactive/line/line-render.d.ts +35 -0
  269. package/dist/modes/interactive/line/line-render.js +171 -0
  270. package/dist/modes/interactive/line/paste-state.d.ts +26 -0
  271. package/dist/modes/interactive/line/paste-state.js +72 -0
  272. package/dist/modes/interactive/message-view.d.ts +77 -0
  273. package/dist/modes/interactive/message-view.js +269 -0
  274. package/dist/modes/interactive/pickers.d.ts +42 -0
  275. package/dist/modes/interactive/pickers.js +115 -0
  276. package/dist/modes/interactive/startup-ui.d.ts +33 -0
  277. package/dist/modes/interactive/startup-ui.js +268 -0
  278. package/dist/modes/interactive/status-bar.d.ts +48 -0
  279. package/dist/modes/interactive/status-bar.js +139 -0
  280. package/dist/modes/interactive/tool-view.d.ts +95 -0
  281. package/dist/modes/interactive/tool-view.js +344 -0
  282. package/dist/modes/print/json-event.d.ts +15 -0
  283. package/dist/modes/print/json-event.js +39 -0
  284. package/dist/modes/print/print-mode.d.ts +17 -0
  285. package/dist/modes/print/print-mode.js +107 -0
  286. package/dist/modes/rpc/commands.d.ts +40 -0
  287. package/dist/modes/rpc/commands.js +263 -0
  288. package/dist/modes/rpc/jsonl.d.ts +16 -0
  289. package/dist/modes/rpc/jsonl.js +55 -0
  290. package/dist/modes/rpc/rpc-mode.d.ts +22 -0
  291. package/dist/modes/rpc/rpc-mode.js +153 -0
  292. package/dist/modes/session-report.d.ts +55 -0
  293. package/dist/modes/session-report.js +278 -0
  294. package/dist/modes/shared.d.ts +13 -0
  295. package/dist/modes/shared.js +52 -0
  296. package/dist/modes/startup-ui-text.d.ts +16 -0
  297. package/dist/modes/startup-ui-text.js +103 -0
  298. package/dist/permissions/broker.d.ts +47 -0
  299. package/dist/permissions/broker.js +107 -0
  300. package/dist/permissions/dangerous.d.ts +47 -0
  301. package/dist/permissions/dangerous.js +370 -0
  302. package/dist/permissions/pipeline.d.ts +51 -0
  303. package/dist/permissions/pipeline.js +181 -0
  304. package/dist/permissions/preview.d.ts +43 -0
  305. package/dist/permissions/preview.js +463 -0
  306. package/dist/permissions/rules.d.ts +58 -0
  307. package/dist/permissions/rules.js +249 -0
  308. package/dist/permissions/types.d.ts +100 -0
  309. package/dist/permissions/types.js +18 -0
  310. package/dist/rpc.d.ts +173 -0
  311. package/dist/rpc.js +13 -0
  312. package/dist/sdk.d.ts +98 -0
  313. package/dist/sdk.js +195 -0
  314. package/dist/session/manager.d.ts +70 -0
  315. package/dist/session/manager.js +278 -0
  316. package/dist/session/migrate.d.ts +19 -0
  317. package/dist/session/migrate.js +53 -0
  318. package/dist/session/projection.d.ts +50 -0
  319. package/dist/session/projection.js +184 -0
  320. package/dist/session/store.d.ts +47 -0
  321. package/dist/session/store.js +210 -0
  322. package/dist/session/tree.d.ts +21 -0
  323. package/dist/session/tree.js +88 -0
  324. package/dist/session/types.d.ts +201 -0
  325. package/dist/session/types.js +18 -0
  326. package/dist/skills/discover.d.ts +60 -0
  327. package/dist/skills/discover.js +160 -0
  328. package/dist/skills/expand.d.ts +35 -0
  329. package/dist/skills/expand.js +46 -0
  330. package/dist/skills/frontmatter.d.ts +27 -0
  331. package/dist/skills/frontmatter.js +157 -0
  332. package/dist/skills/index-prompt.d.ts +13 -0
  333. package/dist/skills/index-prompt.js +42 -0
  334. package/dist/skills/templates.d.ts +52 -0
  335. package/dist/skills/templates.js +177 -0
  336. package/dist/tools/bash.d.ts +49 -0
  337. package/dist/tools/bash.js +222 -0
  338. package/dist/tools/edit-fuzzy.d.ts +36 -0
  339. package/dist/tools/edit-fuzzy.js +129 -0
  340. package/dist/tools/edit.d.ts +38 -0
  341. package/dist/tools/edit.js +241 -0
  342. package/dist/tools/file-mutex.d.ts +8 -0
  343. package/dist/tools/file-mutex.js +29 -0
  344. package/dist/tools/glob.d.ts +44 -0
  345. package/dist/tools/glob.js +217 -0
  346. package/dist/tools/grep.d.ts +39 -0
  347. package/dist/tools/grep.js +179 -0
  348. package/dist/tools/ignore.d.ts +40 -0
  349. package/dist/tools/ignore.js +174 -0
  350. package/dist/tools/image-file.d.ts +31 -0
  351. package/dist/tools/image-file.js +114 -0
  352. package/dist/tools/ls.d.ts +21 -0
  353. package/dist/tools/ls.js +96 -0
  354. package/dist/tools/output-accumulator.d.ts +48 -0
  355. package/dist/tools/output-accumulator.js +106 -0
  356. package/dist/tools/paths.d.ts +20 -0
  357. package/dist/tools/paths.js +58 -0
  358. package/dist/tools/presets.d.ts +71 -0
  359. package/dist/tools/presets.js +128 -0
  360. package/dist/tools/process-tree.d.ts +35 -0
  361. package/dist/tools/process-tree.js +125 -0
  362. package/dist/tools/read.d.ts +24 -0
  363. package/dist/tools/read.js +132 -0
  364. package/dist/tools/registry.d.ts +43 -0
  365. package/dist/tools/registry.js +106 -0
  366. package/dist/tools/shell.d.ts +38 -0
  367. package/dist/tools/shell.js +91 -0
  368. package/dist/tools/task.d.ts +29 -0
  369. package/dist/tools/task.js +107 -0
  370. package/dist/tools/todo.d.ts +28 -0
  371. package/dist/tools/todo.js +107 -0
  372. package/dist/tools/truncate.d.ts +44 -0
  373. package/dist/tools/truncate.js +175 -0
  374. package/dist/tools/types.d.ts +135 -0
  375. package/dist/tools/types.js +19 -0
  376. package/dist/tools/write.d.ts +20 -0
  377. package/dist/tools/write.js +84 -0
  378. package/dist/tui/ansi.d.ts +29 -0
  379. package/dist/tui/ansi.js +420 -0
  380. package/dist/tui/component.d.ts +38 -0
  381. package/dist/tui/component.js +13 -0
  382. package/dist/tui/components/box.d.ts +26 -0
  383. package/dist/tui/components/box.js +65 -0
  384. package/dist/tui/components/container.d.ts +14 -0
  385. package/dist/tui/components/container.js +39 -0
  386. package/dist/tui/components/editor-buffer.d.ts +67 -0
  387. package/dist/tui/components/editor-buffer.js +300 -0
  388. package/dist/tui/components/editor-paste.d.ts +29 -0
  389. package/dist/tui/components/editor-paste.js +64 -0
  390. package/dist/tui/components/editor.d.ts +114 -0
  391. package/dist/tui/components/editor.js +530 -0
  392. package/dist/tui/components/key-value.d.ts +29 -0
  393. package/dist/tui/components/key-value.js +48 -0
  394. package/dist/tui/components/loader.d.ts +35 -0
  395. package/dist/tui/components/loader.js +71 -0
  396. package/dist/tui/components/markdown.d.ts +90 -0
  397. package/dist/tui/components/markdown.js +462 -0
  398. package/dist/tui/components/meter.d.ts +29 -0
  399. package/dist/tui/components/meter.js +47 -0
  400. package/dist/tui/components/overlay.d.ts +24 -0
  401. package/dist/tui/components/overlay.js +52 -0
  402. package/dist/tui/components/select-list.d.ts +57 -0
  403. package/dist/tui/components/select-list.js +179 -0
  404. package/dist/tui/components/spacer.d.ts +11 -0
  405. package/dist/tui/components/spacer.js +16 -0
  406. package/dist/tui/components/text.d.ts +30 -0
  407. package/dist/tui/components/text.js +65 -0
  408. package/dist/tui/keybindings.d.ts +66 -0
  409. package/dist/tui/keybindings.js +120 -0
  410. package/dist/tui/keys.d.ts +38 -0
  411. package/dist/tui/keys.js +275 -0
  412. package/dist/tui/stdin-buffer.d.ts +41 -0
  413. package/dist/tui/stdin-buffer.js +206 -0
  414. package/dist/tui/terminal.d.ts +79 -0
  415. package/dist/tui/terminal.js +153 -0
  416. package/dist/tui/theme.d.ts +31 -0
  417. package/dist/tui/theme.js +199 -0
  418. package/dist/tui/tui.d.ts +103 -0
  419. package/dist/tui/tui.js +363 -0
  420. package/dist/tui/vt-screen.d.ts +46 -0
  421. package/dist/tui/vt-screen.js +251 -0
  422. package/dist/tui.d.ts +30 -0
  423. package/dist/tui.js +31 -0
  424. package/dist/version.d.ts +8 -0
  425. package/dist/version.js +24 -0
  426. package/docs/codemode.md +102 -0
  427. package/docs/hooks.md +196 -0
  428. package/docs/host-api.md +161 -0
  429. package/docs/providers.md +531 -0
  430. package/docs/rpc.md +253 -0
  431. package/docs/session-format.md +119 -0
  432. package/docs/tui.md +170 -0
  433. package/package.json +101 -4
@@ -0,0 +1,458 @@
1
+ /**
2
+ * ProviderRegistry(设计 §3.2–§3.5):内置供应商 + 目录 + config.json 合并、模型查找、
3
+ * `provider/model` 解析、key 解析、协议实现查找。实现 B0 契约 `ProviderRegistryApi`。
4
+ *
5
+ * 合并顺序:内置(builtin.ts + catalog)← config.json `providers.<id>`(字段覆盖;`headers` /
6
+ * `compat` 合并;`models[]` 同 id 整条替换、新 id 追加;`modelOverrides[]` 只改元数据;两者都可带
7
+ * 模型级 `api`,缺省沿用供应商的协议)←
8
+ * auth.json 条目的 `baseUrl`。内置 openai / anthropic 的 baseUrl 还可来自 `OPENAI_BASE_URL` /
9
+ * `ANTHROPIC_BASE_URL`(第三波 §2.3,优先级最低,`baseUrlEnv(id)` 报告是否生效)。之后「物化」每个模型:补 baseUrl / headers / compat /
10
+ * authHeader / requiresApiKey,让协议实现只看 Model 就能发请求。
11
+ *
12
+ * 模型引用:`provider/model-id`(model id 自身可含斜杠,如 `openrouter/anthropic/claude-…`);
13
+ * 前缀不是供应商 id 时整串当 model id。无供应商前缀时全局唯一匹配;多家同名时只留已配置 key
14
+ * 的;仍不唯一 → ambiguous 并列出候选。模型表为空的供应商(ollama、lmstudio、未列模型的
15
+ * 自定义供应商)与 baseUrl 被改到非官方主机的内置供应商(中转站)接受任意 model id,按自定义
16
+ * 缺省合成;后者 compat 未显式配置 `sendPromptCacheKey` 时置 false。
17
+ *
18
+ * 渠道(channels.ts):有 `channels` 的供应商,模型按首选渠道物化;`provider/model@channel` 换渠道
19
+ * 重新物化(协议、地址、headers、compat 取渠道的,模型级 api / baseUrl 仍覆盖);渠道 key 经
20
+ * `resolveApiKey(provider, channel)`。没有 `channels` 的供应商行为不变。
21
+ *
22
+ * models.dev(enrich.ts):config 里缺元数据的自定义模型与合成模型,用传入的索引(只读缓存)补
23
+ * contextWindow / maxTokens / input / reasoning / cost,来源记在 `modelMetadata()`;内置目录不补。
24
+ */
25
+ import { ApiRegistry, createDefaultApiRegistry } from "../apis/api.js";
26
+ import { fakeProviderData } from "../fake/fake-provider.js";
27
+ import { mergeHeaders } from "../http.js";
28
+ import { ApiKeyResolver } from "./auth.js";
29
+ import { BUILTIN_PROVIDERS, fallbackEnvKey, isRelayedBaseUrl } from "./builtin.js";
30
+ import { applyModelOverride, loadBuiltinCatalog, toModel, withCustomDefaults } from "./catalog.js";
31
+ import { channelKeyId, modelChannels, parseChannels, splitChannelRef } from "./channels.js";
32
+ import { catalogMetadata, enrichEntry, lazyIndex, } from "./enrich.js";
33
+ /**
34
+ * 物化:模型补齐供应商级字段(不可变:返回新对象)。给了 `channel` 时协议与地址取渠道的(模型级
35
+ * `explicit` 覆盖优先),headers / compat 按 供应商 ← 渠道 ← 模型 合并,并记下渠道名。
36
+ */
37
+ export function materializeModel(model, provider, channel, explicit) {
38
+ const out = {
39
+ ...model,
40
+ provider: provider.id,
41
+ baseUrl: model.baseUrl ?? provider.baseUrl,
42
+ };
43
+ if (channel !== undefined) {
44
+ out.api = explicit?.api ?? channel.api;
45
+ out.baseUrl = explicit?.baseUrl ?? channel.baseUrl;
46
+ out.channel = channel.name;
47
+ }
48
+ if (provider.headers || channel?.headers || model.headers)
49
+ out.headers = mergeHeaders(provider.headers, channel?.headers, model.headers);
50
+ if (provider.compat || channel?.compat || model.compat)
51
+ out.compat = { ...provider.compat, ...channel?.compat, ...model.compat };
52
+ const authHeader = channel?.authHeader ?? provider.authHeader;
53
+ if (authHeader !== undefined)
54
+ out.authHeader = model.authHeader ?? authHeader;
55
+ out.requiresApiKey = provider.requiresApiKey;
56
+ return out;
57
+ }
58
+ function configKeysOf(config) {
59
+ const keys = {};
60
+ for (const [id, provider] of Object.entries(config?.providers ?? {})) {
61
+ keys[id] = provider.apiKey;
62
+ for (const [name, channel] of Object.entries(provider.channels ?? {})) {
63
+ if (channel?.apiKey !== undefined)
64
+ keys[channelKeyId(id, name)] = channel.apiKey;
65
+ }
66
+ }
67
+ return keys;
68
+ }
69
+ export class ProviderRegistry {
70
+ warnings = [];
71
+ providers = new Map();
72
+ sources = new Map();
73
+ /** baseUrl 来自环境变量的供应商 → 变量名(被 config / auth.json 覆盖后删除)。 */
74
+ envBaseUrls = new Map();
75
+ /** baseUrl 指向非官方主机的内置供应商:接受目录外的 model id。 */
76
+ relayed = new Set();
77
+ /** `provider/model` → 物化前的模型(多渠道供应商切换渠道时重新物化)。 */
78
+ raw = new Map();
79
+ /** `provider/model` → 元数据来源与 models.dev 匹配。 */
80
+ metadata = new Map();
81
+ modelsDev;
82
+ apis;
83
+ keys;
84
+ onWarning;
85
+ constructor(options = {}) {
86
+ this.onWarning = options.onWarning;
87
+ this.modelsDev = lazyIndex(options.modelsDev);
88
+ this.apis = options.apis ?? createDefaultApiRegistry();
89
+ this.keys = new ApiKeyResolver({
90
+ ...options.keys,
91
+ configKeys: options.keys?.configKeys ?? configKeysOf(options.config),
92
+ onWarning: (message) => this.warn(message),
93
+ });
94
+ const catalog = loadBuiltinCatalog();
95
+ const env = options.keys?.useEnv === false ? {} : (options.keys?.env ?? process.env);
96
+ for (const { baseUrlEnv, ...base } of BUILTIN_PROVIDERS) {
97
+ const models = (catalog.get(base.id) ?? []).map((entry) => {
98
+ const api = entry.api ?? base.api;
99
+ this.sources.set(`${base.id}/${entry.id}`, "builtin");
100
+ const model = toModel(entry, base.id, api);
101
+ this.metadata.set(`${base.id}/${entry.id}`, catalogMetadata(model));
102
+ if (entry.api !== undefined)
103
+ this.raw.set(`${base.id}/${entry.id}`, { model, explicit: { api } });
104
+ return model;
105
+ });
106
+ const fromEnv = baseUrlEnv !== undefined ? env[baseUrlEnv]?.trim() : undefined;
107
+ if (baseUrlEnv !== undefined && fromEnv)
108
+ this.envBaseUrls.set(base.id, baseUrlEnv);
109
+ this.providers.set(base.id, {
110
+ ...base,
111
+ ...(fromEnv ? { baseUrl: fromEnv } : {}),
112
+ models,
113
+ builtin: true,
114
+ });
115
+ }
116
+ for (const [id, config] of Object.entries(options.config?.providers ?? {})) {
117
+ this.applyConfig(id, config);
118
+ }
119
+ for (const provider of this.providers.values()) {
120
+ const baseUrl = this.keys.authEntry(provider.id)?.entry.baseUrl;
121
+ if (baseUrl) {
122
+ provider.baseUrl = baseUrl;
123
+ this.envBaseUrls.delete(provider.id);
124
+ }
125
+ if (provider.builtin && isRelayedBaseUrl(provider.id, provider.baseUrl)) {
126
+ this.relayed.add(provider.id);
127
+ if (provider.compat?.sendPromptCacheKey === undefined)
128
+ provider.compat = { ...provider.compat, sendPromptCacheKey: false };
129
+ }
130
+ }
131
+ if (options.includeFake !== false && !this.providers.has("fake")) {
132
+ const fake = fakeProviderData();
133
+ for (const model of fake.models)
134
+ this.sources.set(`fake/${model.id}`, "builtin");
135
+ this.providers.set(fake.id, fake);
136
+ }
137
+ for (const provider of this.providers.values()) {
138
+ provider.models = provider.models.map((model) => this.materialize(provider, model));
139
+ }
140
+ }
141
+ warn(message) {
142
+ this.warnings.push(message);
143
+ this.onWarning?.(message);
144
+ }
145
+ /** 物化并记下原始模型;多渠道供应商用模型的首选渠道。 */
146
+ materialize(provider, model, channelName) {
147
+ const key = `${provider.id}/${model.id}`;
148
+ const raw = this.raw.get(key) ?? { model, explicit: {} };
149
+ if (!this.raw.has(key)) {
150
+ if (model.baseUrl !== undefined)
151
+ raw.explicit.baseUrl = model.baseUrl;
152
+ this.raw.set(key, raw);
153
+ }
154
+ if (provider.channels === undefined || provider.channels.length === 0)
155
+ return materializeModel(raw.model, provider);
156
+ const name = channelName ?? raw.model.channels?.[0] ?? provider.defaultChannel;
157
+ const channel = provider.channels.find((c) => c.name === name) ?? provider.channels[0];
158
+ return materializeModel(raw.model, provider, channel, raw.explicit);
159
+ }
160
+ applyConfig(id, config) {
161
+ const existing = this.providers.get(id);
162
+ const parsed = config.channels !== undefined ? parseChannels(id, config) : undefined;
163
+ for (const warning of parsed?.warnings ?? [])
164
+ this.warn(warning);
165
+ const first = parsed?.channels.find((c) => c.name === parsed.defaultChannel);
166
+ if (!existing && !config.baseUrl && first === undefined) {
167
+ this.warn(`provider "${id}" in config.json has no baseUrl; ignored`);
168
+ return;
169
+ }
170
+ const provider = existing ?? {
171
+ id,
172
+ name: config.name ?? id,
173
+ api: config.api ?? first?.api ?? "openai-completions",
174
+ baseUrl: config.baseUrl ?? first?.baseUrl ?? "",
175
+ envKeys: [fallbackEnvKey(id)],
176
+ models: [],
177
+ requiresApiKey: true,
178
+ builtin: false,
179
+ };
180
+ if (config.name !== undefined)
181
+ provider.name = config.name;
182
+ if (config.api !== undefined)
183
+ provider.api = config.api;
184
+ if (config.baseUrl !== undefined) {
185
+ provider.baseUrl = config.baseUrl;
186
+ this.envBaseUrls.delete(id);
187
+ }
188
+ if (parsed !== undefined && first !== undefined) {
189
+ // 有渠道时供应商级 api / baseUrl 取首选渠道的(discover 等单地址的用法看它)。
190
+ provider.channels = parsed.channels;
191
+ provider.defaultChannel = first.name;
192
+ provider.api = first.api;
193
+ provider.baseUrl = first.baseUrl;
194
+ this.envBaseUrls.delete(id);
195
+ }
196
+ if (config.envKeys !== undefined) {
197
+ provider.envKeys = [
198
+ ...config.envKeys,
199
+ ...provider.envKeys.filter((k) => !config.envKeys?.includes(k)),
200
+ ];
201
+ }
202
+ if (config.authHeader !== undefined)
203
+ provider.authHeader = config.authHeader;
204
+ if (config.headers)
205
+ provider.headers = mergeHeaders(provider.headers, config.headers);
206
+ if (config.compat)
207
+ provider.compat = { ...provider.compat, ...config.compat };
208
+ if (config.requiresApiKey !== undefined)
209
+ provider.requiresApiKey = config.requiresApiKey;
210
+ const channelsOf = (wanted, modelId) => modelChannels(wanted, provider.channels ?? [], provider.defaultChannel ?? "", (name) => this.warn(`model "${id}/${modelId}" channel "${name}" not found; ignored`));
211
+ for (const entry of config.models ?? []) {
212
+ // 模型级 api(第三波 §2.3):同一中转下不同模型走不同协议;缺省沿用供应商(或渠道)的。
213
+ const { entry: filled, metadata } = enrichEntry(entry, this.modelsDev);
214
+ if (typeof entry.modelsDev === "string" && metadata.looked && metadata.match === undefined)
215
+ this.warn(`modelsDev "${entry.modelsDev}" for "${id}/${entry.id}" not found`);
216
+ const model = withCustomDefaults(filled, id, entry.api ?? provider.api);
217
+ if (provider.channels !== undefined)
218
+ model.channels = channelsOf(entry.channels, entry.id);
219
+ const index = provider.models.findIndex((m) => m.id === entry.id);
220
+ if (index >= 0)
221
+ provider.models[index] = model;
222
+ else
223
+ provider.models.push(model);
224
+ const key = `${id}/${entry.id}`;
225
+ this.raw.set(key, {
226
+ model,
227
+ explicit: {
228
+ ...(entry.api !== undefined ? { api: entry.api } : {}),
229
+ ...(entry.baseUrl !== undefined ? { baseUrl: entry.baseUrl } : {}),
230
+ },
231
+ });
232
+ this.sources.set(key, "config");
233
+ this.metadata.set(key, metadata);
234
+ }
235
+ for (const override of config.modelOverrides ?? []) {
236
+ const index = provider.models.findIndex((m) => m.id === override.id);
237
+ const current = provider.models[index];
238
+ if (!current) {
239
+ this.warn(`modelOverrides: "${id}/${override.id}" not found; ignored`);
240
+ continue;
241
+ }
242
+ const { channels, modelsDev: _md, ...fields } = override;
243
+ let next = applyModelOverride(current, fields);
244
+ if (override.api !== undefined)
245
+ next = { ...next, api: override.api };
246
+ if (channels !== undefined && provider.channels !== undefined)
247
+ next.channels = channelsOf(channels, override.id);
248
+ provider.models[index] = next;
249
+ const key = `${id}/${override.id}`;
250
+ const raw = this.raw.get(key);
251
+ this.raw.set(key, {
252
+ model: next,
253
+ explicit: {
254
+ ...raw?.explicit,
255
+ ...(override.api !== undefined ? { api: override.api } : {}),
256
+ ...(override.baseUrl !== undefined ? { baseUrl: override.baseUrl } : {}),
257
+ },
258
+ });
259
+ const meta = this.metadata.get(key);
260
+ if (meta !== undefined)
261
+ for (const field of Object.keys(meta.sources))
262
+ if (fields[field] !== undefined)
263
+ meta.sources[field] = "config";
264
+ }
265
+ if (provider.channels !== undefined) {
266
+ // 目录模型(给内置供应商加了渠道时)挂到首选渠道。
267
+ for (const model of provider.models)
268
+ model.channels ??= [provider.defaultChannel ?? ""];
269
+ }
270
+ this.providers.set(id, provider);
271
+ }
272
+ /** baseUrl 来自哪个环境变量(未生效返回 undefined)。 */
273
+ baseUrlEnv(providerId) {
274
+ return this.envBaseUrls.get(providerId);
275
+ }
276
+ /** 内置供应商的 baseUrl 指向非官方主机(中转站)。 */
277
+ isRelayed(providerId) {
278
+ return this.relayed.has(providerId);
279
+ }
280
+ /** 模型元数据的来源与 models.dev 匹配(`ama models list` / `config show` / providers 表格)。 */
281
+ modelMetadata(providerId, modelId) {
282
+ return this.metadata.get(`${providerId}/${modelId}`);
283
+ }
284
+ list() {
285
+ return [...this.providers.values()];
286
+ }
287
+ get(providerId) {
288
+ return this.providers.get(providerId);
289
+ }
290
+ listModels() {
291
+ const out = [];
292
+ for (const provider of this.providers.values()) {
293
+ for (const model of provider.models) {
294
+ out.push({ model, provider, source: this.modelSource(provider.id, model.id) });
295
+ }
296
+ }
297
+ return out;
298
+ }
299
+ modelSource(providerId, modelId) {
300
+ return this.sources.get(`${providerId}/${modelId}`) ?? "config";
301
+ }
302
+ /** 追加 / 替换模型(例如本地枚举得到的 ollama 模型)。 */
303
+ addModels(providerId, models, source = "discovered") {
304
+ const provider = this.providers.get(providerId);
305
+ if (!provider)
306
+ return;
307
+ for (const raw of models) {
308
+ this.raw.delete(`${providerId}/${raw.id}`);
309
+ const model = this.materialize(provider, { ...raw, provider: providerId });
310
+ const index = provider.models.findIndex((m) => m.id === model.id);
311
+ if (index >= 0)
312
+ provider.models[index] = model;
313
+ else
314
+ provider.models.push(model);
315
+ this.sources.set(`${providerId}/${model.id}`, source);
316
+ }
317
+ }
318
+ synthesize(provider, modelId, channel) {
319
+ const key = `${provider.id}/${modelId}`;
320
+ const { entry, metadata } = enrichEntry({ id: modelId }, this.modelsDev);
321
+ if (!this.metadata.has(key))
322
+ this.metadata.set(key, metadata);
323
+ const model = withCustomDefaults(entry, provider.id, provider.api);
324
+ if (provider.channels !== undefined)
325
+ model.channels = [provider.defaultChannel ?? ""];
326
+ const channelData = provider.channels?.find((c) => c.name === (channel ?? model.channels?.[0]));
327
+ return materializeModel(model, provider, channelData);
328
+ }
329
+ /** 按渠道取模型:渠道不在模型的 channels 里 → 列出可用渠道。 */
330
+ withChannel(provider, model, channel) {
331
+ const available = model.channels ?? [];
332
+ if (!available.includes(channel)) {
333
+ return {
334
+ ok: false,
335
+ reason: "not_found",
336
+ candidates: available.map((c) => `${provider.id}/${model.id}@${c}`),
337
+ };
338
+ }
339
+ if (channel === model.channel)
340
+ return { ok: true, model, provider };
341
+ return { ok: true, model: this.materialize(provider, model, channel), provider };
342
+ }
343
+ findModel(ref) {
344
+ const trimmed = ref.trim();
345
+ const direct = this.findPlain(trimmed);
346
+ if (direct.ok)
347
+ return direct;
348
+ const split = splitChannelRef(trimmed);
349
+ if (split === undefined)
350
+ return direct;
351
+ const base = this.findPlain(split.base, split.channel);
352
+ if (!base.ok)
353
+ return base.reason === "ambiguous" ? base : direct;
354
+ if (!base.provider.channels?.some((c) => c.name === split.channel))
355
+ return direct;
356
+ return this.withChannel(base.provider, base.model, split.channel);
357
+ }
358
+ findPlain(trimmed, channel) {
359
+ const slash = trimmed.indexOf("/");
360
+ if (slash > 0) {
361
+ const provider = this.providers.get(trimmed.slice(0, slash));
362
+ if (provider) {
363
+ const id = trimmed.slice(slash + 1);
364
+ const model = provider.models.find((m) => m.id === id);
365
+ if (model)
366
+ return { ok: true, model, provider };
367
+ if ((provider.models.length === 0 || this.relayed.has(provider.id)) && id.length > 0) {
368
+ const synthesized = this.synthesize(provider, id);
369
+ if (channel !== undefined && provider.channels?.some((c) => c.name === channel))
370
+ synthesized.channels = [...new Set([...(synthesized.channels ?? []), channel])];
371
+ return { ok: true, model: synthesized, provider };
372
+ }
373
+ return { ok: false, reason: "not_found", candidates: this.similar(id, provider.id) };
374
+ }
375
+ }
376
+ const matches = this.listModels().filter((entry) => entry.model.id === trimmed);
377
+ if (matches.length === 1 && matches[0]) {
378
+ return { ok: true, model: matches[0].model, provider: matches[0].provider };
379
+ }
380
+ if (matches.length === 0) {
381
+ return { ok: false, reason: "not_found", candidates: this.similar(trimmed) };
382
+ }
383
+ const configured = matches.filter((entry) => this.keys.hasConfiguredKey(entry.provider));
384
+ if (configured.length === 1 && configured[0]) {
385
+ return { ok: true, model: configured[0].model, provider: configured[0].provider };
386
+ }
387
+ return {
388
+ ok: false,
389
+ reason: "ambiguous",
390
+ candidates: (configured.length > 0 ? configured : matches).map((entry) => `${entry.provider.id}/${entry.model.id}`),
391
+ };
392
+ }
393
+ similar(id, providerId) {
394
+ const needle = id.toLowerCase();
395
+ return this.listModels()
396
+ .filter((entry) => providerId === undefined || entry.provider.id === providerId)
397
+ .filter((entry) => {
398
+ const candidate = entry.model.id.toLowerCase();
399
+ return needle.length > 0 && (candidate.includes(needle) || needle.includes(candidate));
400
+ })
401
+ .slice(0, 10)
402
+ .map((entry) => `${entry.provider.id}/${entry.model.id}`);
403
+ }
404
+ hasConfiguredKey(providerId) {
405
+ const provider = this.providers.get(providerId);
406
+ return provider ? this.keys.hasConfiguredKey(provider) : false;
407
+ }
408
+ async resolveApiKey(providerId, channel) {
409
+ const provider = this.providers.get(providerId);
410
+ if (!provider)
411
+ return { apiKey: undefined, source: "none" };
412
+ if (channel !== undefined && provider.channels?.some((c) => c.name === channel)) {
413
+ const own = await this.keys.resolve({
414
+ id: channelKeyId(provider.id, channel),
415
+ envKeys: [],
416
+ requiresApiKey: provider.requiresApiKey,
417
+ });
418
+ if (own.apiKey !== undefined)
419
+ return own;
420
+ }
421
+ return this.keys.resolve(provider);
422
+ }
423
+ getApi(api) {
424
+ return this.apis.get(api);
425
+ }
426
+ }
427
+ /** 注册表若是 ProviderRegistry(或同形状),报告 baseUrl 来自的环境变量;否则 undefined。 */
428
+ export function baseUrlEnvOf(registry, providerId) {
429
+ const r = registry;
430
+ return typeof r.baseUrlEnv === "function" ? r.baseUrlEnv(providerId) : undefined;
431
+ }
432
+ /**
433
+ * 模型枚举:本地服务(`ama models list --provider ollama|lmstudio`)与 `ama models discover`
434
+ * (第三波 §2.3)共用。ollama 走 `/api/tags`,其它走 OpenAI 兼容的 `GET {baseUrl}/models`;
435
+ * `url` / `headers` 由调用方给出时原样使用(远端中转要带鉴权头)。失败抛错由调用方展示。
436
+ */
437
+ export async function discoverLocalModels(provider, options = {}) {
438
+ const signal = AbortSignal.any([
439
+ options.signal ?? new AbortController().signal,
440
+ AbortSignal.timeout(options.timeoutMs ?? 5000),
441
+ ]);
442
+ const base = provider.baseUrl.replace(/\/+$/, "");
443
+ const isOllama = options.url === undefined && (provider.id === "ollama" || base.includes(":11434"));
444
+ const url = options.url ?? (isOllama ? `${base.replace(/\/v1$/, "")}/api/tags` : `${base}/models`);
445
+ const response = await fetch(url, {
446
+ signal,
447
+ ...(options.headers !== undefined ? { headers: options.headers } : {}),
448
+ });
449
+ if (!response.ok)
450
+ throw new Error(`${response.status} listing models from ${url}`);
451
+ const body = (await response.json());
452
+ const ids = isOllama
453
+ ? (body.models ?? []).map((m) => m.name)
454
+ : (body.data ?? []).map((m) => m.id);
455
+ return [
456
+ ...new Set(ids.filter((id) => typeof id === "string" && id.length > 0)),
457
+ ].map((id) => withCustomDefaults({ id }, provider.id, provider.api));
458
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Server-Sent Events 解析(设计 §1.2 ai/sse.ts):event / data / id / 多行 data / 注释行 /
3
+ * CRLF、CR、LF 三种行尾 / UTF-8 跨块 / 首字节 BOM。
4
+ *
5
+ * 规则取自 WHATWG「Interpreting an event stream」:空行分派事件;`:` 开头是注释;字段名后的
6
+ * 第一个空格去掉;多条 data 以 LF 连接。为兼容不规范服务端,流结束时未以空行收尾的最后一个
7
+ * 事件也会分派。
8
+ */
9
+ export interface SseEvent {
10
+ /** `event:` 字段;缺省时为 undefined(规范上视为 "message")。 */
11
+ event: string | undefined;
12
+ data: string;
13
+ id: string | undefined;
14
+ }
15
+ export declare class SseParser {
16
+ private buffer;
17
+ private event;
18
+ private data;
19
+ private id;
20
+ private hasData;
21
+ private bomChecked;
22
+ /** 上一块以 CR 结尾:下一块开头的 LF 属于同一个行尾。 */
23
+ private pendingCr;
24
+ /** 喂入一段文本,返回其中完整分派的事件。 */
25
+ feed(chunk: string): SseEvent[];
26
+ /** 流结束:处理残留行,并分派未以空行收尾的事件。 */
27
+ flush(): SseEvent[];
28
+ private processLine;
29
+ private dispatch;
30
+ }
31
+ /** 从字节流读 SSE 事件;`signal` 中止时停止读取(调用方据此产出 aborted)。 */
32
+ export declare function readSseEvents(body: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<SseEvent>;
package/dist/ai/sse.js ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Server-Sent Events 解析(设计 §1.2 ai/sse.ts):event / data / id / 多行 data / 注释行 /
3
+ * CRLF、CR、LF 三种行尾 / UTF-8 跨块 / 首字节 BOM。
4
+ *
5
+ * 规则取自 WHATWG「Interpreting an event stream」:空行分派事件;`:` 开头是注释;字段名后的
6
+ * 第一个空格去掉;多条 data 以 LF 连接。为兼容不规范服务端,流结束时未以空行收尾的最后一个
7
+ * 事件也会分派。
8
+ */
9
+ export class SseParser {
10
+ buffer = "";
11
+ event;
12
+ data = [];
13
+ id;
14
+ hasData = false;
15
+ bomChecked = false;
16
+ /** 上一块以 CR 结尾:下一块开头的 LF 属于同一个行尾。 */
17
+ pendingCr = false;
18
+ /** 喂入一段文本,返回其中完整分派的事件。 */
19
+ feed(chunk) {
20
+ let text = chunk;
21
+ if (!this.bomChecked && text.length > 0) {
22
+ this.bomChecked = true;
23
+ if (text.charCodeAt(0) === 0xfeff)
24
+ text = text.slice(1);
25
+ }
26
+ if (this.pendingCr && text.startsWith("\n"))
27
+ text = text.slice(1);
28
+ this.pendingCr = false;
29
+ this.buffer += text;
30
+ const events = [];
31
+ let start = 0;
32
+ for (let i = 0; i < this.buffer.length; i++) {
33
+ const ch = this.buffer.charCodeAt(i);
34
+ if (ch !== 10 && ch !== 13)
35
+ continue;
36
+ const line = this.buffer.slice(start, i);
37
+ if (ch === 13) {
38
+ if (i + 1 < this.buffer.length) {
39
+ if (this.buffer.charCodeAt(i + 1) === 10)
40
+ i++;
41
+ }
42
+ else {
43
+ this.pendingCr = true;
44
+ }
45
+ }
46
+ start = i + 1;
47
+ const event = this.processLine(line);
48
+ if (event)
49
+ events.push(event);
50
+ }
51
+ this.buffer = this.buffer.slice(start);
52
+ return events;
53
+ }
54
+ /** 流结束:处理残留行,并分派未以空行收尾的事件。 */
55
+ flush() {
56
+ const events = [];
57
+ if (this.buffer.length > 0) {
58
+ const event = this.processLine(this.buffer);
59
+ if (event)
60
+ events.push(event);
61
+ this.buffer = "";
62
+ }
63
+ const last = this.dispatch();
64
+ if (last)
65
+ events.push(last);
66
+ return events;
67
+ }
68
+ processLine(line) {
69
+ if (line === "")
70
+ return this.dispatch();
71
+ if (line.startsWith(":"))
72
+ return undefined;
73
+ const colon = line.indexOf(":");
74
+ const field = colon === -1 ? line : line.slice(0, colon);
75
+ let value = colon === -1 ? "" : line.slice(colon + 1);
76
+ if (value.startsWith(" "))
77
+ value = value.slice(1);
78
+ switch (field) {
79
+ case "event":
80
+ this.event = value;
81
+ break;
82
+ case "data":
83
+ this.data.push(value);
84
+ this.hasData = true;
85
+ break;
86
+ case "id":
87
+ if (!value.includes("\0"))
88
+ this.id = value;
89
+ break;
90
+ default:
91
+ // retry 与未知字段忽略
92
+ break;
93
+ }
94
+ return undefined;
95
+ }
96
+ dispatch() {
97
+ const hadContent = this.hasData || this.event !== undefined;
98
+ const event = { event: this.event, data: this.data.join("\n"), id: this.id };
99
+ this.event = undefined;
100
+ this.data = [];
101
+ this.hasData = false;
102
+ return hadContent ? event : undefined;
103
+ }
104
+ }
105
+ /** 从字节流读 SSE 事件;`signal` 中止时停止读取(调用方据此产出 aborted)。 */
106
+ export async function* readSseEvents(body, signal) {
107
+ const reader = body.getReader();
108
+ const decoder = new TextDecoder("utf-8");
109
+ const parser = new SseParser();
110
+ const onAbort = () => {
111
+ reader.cancel().catch(() => undefined);
112
+ };
113
+ signal?.addEventListener("abort", onAbort, { once: true });
114
+ let drained = false;
115
+ try {
116
+ while (true) {
117
+ if (signal?.aborted)
118
+ return;
119
+ const { value, done } = await reader.read();
120
+ if (done)
121
+ break;
122
+ if (signal?.aborted)
123
+ return;
124
+ for (const event of parser.feed(decoder.decode(value, { stream: true })))
125
+ yield event;
126
+ }
127
+ drained = true;
128
+ for (const event of parser.feed(decoder.decode()))
129
+ yield event;
130
+ for (const event of parser.flush())
131
+ yield event;
132
+ }
133
+ finally {
134
+ signal?.removeEventListener("abort", onAbort);
135
+ // 消费者提前退出(读到终止事件即 break)或中止:取消底层流,释放连接
136
+ if (!drained)
137
+ await reader.cancel().catch(() => undefined);
138
+ reader.releaseLock();
139
+ }
140
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 思考级别(设计 §3.6「思考」):用户面 off / minimal / low / medium / high / xhigh。
3
+ *
4
+ * - `getSupportedLevels(model)`:非推理模型只有 off;`thinkingLevelMap[level] === null` 的级别
5
+ * 不支持;`xhigh` 只有在映射表里显式给出时才支持。
6
+ * - `clampThinkingLevel`:不支持的级别先向上找、再向下找最近的可用级别。
7
+ * - 预算型缺省:minimal 1024 / low 2048 / medium 8192 / high 16384(xhigh 按 high);
8
+ * 与回答共享 max_tokens 时至少给回答留 1024。
9
+ */
10
+ import type { Model, ModelThinkingLevel, ThinkingLevel } from "./types.js";
11
+ export declare const THINKING_LEVELS: readonly ModelThinkingLevel[];
12
+ export declare const DEFAULT_THINKING_BUDGETS: Readonly<Record<ThinkingLevel, number>>;
13
+ /** 与回答共享 max_tokens 时留给回答的最少 token。 */
14
+ export declare const MIN_ANSWER_TOKENS = 1024;
15
+ export declare function isThinkingLevel(value: unknown): value is ModelThinkingLevel;
16
+ type ThinkingModel = Pick<Model, "reasoning" | "thinkingLevelMap">;
17
+ export declare function getSupportedLevels(model: ThinkingModel): ModelThinkingLevel[];
18
+ export declare function clampThinkingLevel(model: ThinkingModel, level: ModelThinkingLevel): ModelThinkingLevel;
19
+ /** 级别在映射表里的供应商原生值;未映射返回 undefined(调用方按协议缺省处理)。 */
20
+ export declare function mappedThinkingValue(model: ThinkingModel, level: ModelThinkingLevel): string | number | undefined;
21
+ /** 预算:映射表给数字就用数字,否则按缺省表。 */
22
+ export declare function thinkingBudget(model: ThinkingModel, level: ThinkingLevel): number;
23
+ /** 预算不超过 `ceiling - MIN_ANSWER_TOKENS`(不足时为 0)。 */
24
+ export declare function clampBudgetToAnswerRoom(budget: number, ceiling: number): number;
25
+ /**
26
+ * 预算型思考(Anthropic 老模型):budget 计入 max_tokens。`requested` 是调用方要的回答上限
27
+ * (缺省 = 模型上限);返回值 maxTokens ≤ model.maxTokens,budget < maxTokens;budget < 1024 时调用方应关闭思考(Anthropic 下限)。
28
+ */
29
+ export declare function budgetedMaxTokens(modelMaxTokens: number, requested: number | undefined, budget: number): {
30
+ maxTokens: number;
31
+ budget: number;
32
+ };
33
+ /** 离散思考级别(Google Gemini 3 的 `thinkingLevel`)。 */
34
+ export type DiscreteThinkingLevel = "minimal" | "low" | "medium" | "high";
35
+ /**
36
+ * 用户面级别 → 离散级别:映射表给字串就用字串(大小写不敏感),否则用级别本身;`xhigh` 落到
37
+ * `high`。映射值不是四个离散级别之一(例如写成了数字预算)返回 undefined,调用方改走预算。
38
+ */
39
+ export declare function discreteThinkingLevel(model: ThinkingModel, level: ThinkingLevel): DiscreteThinkingLevel | undefined;
40
+ /** 解析请求级别:缺省 off;钳位到模型支持的级别。 */
41
+ export declare function resolveThinkingLevel(model: ThinkingModel, level: ModelThinkingLevel | undefined): ModelThinkingLevel;
42
+ export {};