@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,115 @@
1
+ /**
2
+ * 交互模式里的选择器(设计 §12.6):居中覆盖层 + `SelectList`。[B7]
3
+ *
4
+ * - 模型:按供应商分组并标 key 状态(`modelItems`,与启动期共用),当前模型预选;
5
+ * - 会话:名字或首条提示、相对时间、消息数(`sessionItems`);
6
+ * - 树:会话文件里全部用户消息,按分叉缩进,`●` 标出当前分支;选中项回填编辑器(由 commands.ts 处理);
7
+ * - 权限模式(从严到宽)与思考级别。
8
+ */
9
+ import { THINKING_LEVELS } from "../../ai/thinking.js";
10
+ import { PERMISSION_MODES_STRICT_FIRST } from "../../permissions/types.js";
11
+ import { Box, SelectList, } from "../../tui.js";
12
+ import { contentText } from "./message-view.js";
13
+ import { relativeTime } from "./startup-ui.js";
14
+ export { modelItems, sessionItems } from "./startup-ui.js";
15
+ /** 打开一个居中选择器;Enter 返回选中项,Esc / Ctrl+C 返回 undefined。 */
16
+ export function openPicker(host, spec) {
17
+ return new Promise((resolve) => {
18
+ let handle;
19
+ const close = (item) => {
20
+ handle?.hide();
21
+ resolve(item);
22
+ };
23
+ const list = new SelectList(spec.items, {
24
+ theme: host.theme,
25
+ maxVisible: spec.maxVisible ?? 12,
26
+ filterable: spec.filterable ?? spec.items.length > 8,
27
+ ...(host.keybindings !== undefined ? { keybindings: host.keybindings } : {}),
28
+ ...(spec.emptyText !== undefined ? { emptyText: spec.emptyText } : {}),
29
+ onSelect: (item) => close(item),
30
+ onCancel: () => close(undefined),
31
+ });
32
+ if (spec.selected !== undefined)
33
+ list.selectValue(spec.selected);
34
+ const width = Math.max(20, Math.min(host.columns() - 2, 72));
35
+ handle = host.showOverlay(new Box(list, { title: spec.title, theme: host.theme }), {
36
+ anchor: "center",
37
+ width,
38
+ });
39
+ });
40
+ }
41
+ const PERMISSION_TEXT = {
42
+ plan: "只读,不改文件不跑命令",
43
+ default: "写入与命令逐次确认",
44
+ "auto-edit": "文件编辑自动放行,命令确认",
45
+ "full-auto": "全部放行(危险命令仍确认)",
46
+ };
47
+ export function permissionItems() {
48
+ return PERMISSION_MODES_STRICT_FIRST.map((mode) => ({
49
+ value: mode,
50
+ label: mode,
51
+ description: PERMISSION_TEXT[mode],
52
+ }));
53
+ }
54
+ export function thinkingItems(reasoning) {
55
+ return THINKING_LEVELS.map((level) => {
56
+ const item = { value: level, label: level };
57
+ if (!reasoning && level !== "off")
58
+ item.description = "当前模型不支持思考";
59
+ return item;
60
+ });
61
+ }
62
+ function userText(entry) {
63
+ if (entry.type !== "message" || entry.message.role !== "user")
64
+ return undefined;
65
+ return contentText(entry.message.content).replace(/\s+/g, " ").trim();
66
+ }
67
+ /** 只保留用户消息的树:每条用户消息挂到最近的用户消息祖先下。 */
68
+ export function userMessageTree(entries) {
69
+ const byId = new Map(entries.map((e) => [e.id, e]));
70
+ const nodes = new Map();
71
+ const roots = [];
72
+ for (const entry of entries) {
73
+ const text = userText(entry);
74
+ if (text === undefined)
75
+ continue;
76
+ const node = { entry, text, children: [] };
77
+ nodes.set(entry.id, node);
78
+ let parentId = entry.parentId;
79
+ let parent;
80
+ const seen = new Set();
81
+ while (parentId !== null && !seen.has(parentId)) {
82
+ seen.add(parentId);
83
+ parent = nodes.get(parentId);
84
+ if (parent !== undefined)
85
+ break;
86
+ parentId = byId.get(parentId)?.parentId ?? null;
87
+ }
88
+ (parent?.children ?? roots).push(node);
89
+ }
90
+ return roots;
91
+ }
92
+ /**
93
+ * 树 → 列表项:分叉处子分支缩进一级,单链保持同级;`●` 在当前分支上、`○` 不在。
94
+ * value 是用户消息条目的 id。
95
+ */
96
+ export function treeItems(entries, activeIds, now) {
97
+ const items = [];
98
+ const visit = (list, depth) => {
99
+ const fork = list.length > 1;
100
+ for (const node of list) {
101
+ const level = fork ? depth + 1 : depth;
102
+ const mark = activeIds.has(node.entry.id) ? "● " : "○ ";
103
+ const text = node.text.length > 60 ? `${node.text.slice(0, 59)}…` : node.text;
104
+ items.push({
105
+ value: node.entry.id,
106
+ label: `${" ".repeat(level)}${mark}${text === "" ? "(空)" : text}`,
107
+ description: relativeTime(node.entry.timestamp, now),
108
+ });
109
+ visit(node.children, level);
110
+ }
111
+ };
112
+ const roots = userMessageTree(entries);
113
+ visit(roots, roots.length > 1 ? -1 : 0);
114
+ return items;
115
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 启动期的迷你界面:bootstrap 第 7 / 8 / 11 步的 `InteractiveUi` 回调(实施计划 §3.3)。[B7]
3
+ *
4
+ * 这些回调先于交互模式运行(会话、信任、模型还没定),所以每次问答新建一个只含问题与
5
+ * 选择列表 / 输入框的 `TUI`;答完把问答收成一行再 `stop()`——不清屏,问答留在回滚里。
6
+ * 取消(Esc / Ctrl+C)按各回调的约定返回 undefined(bootstrap 据此给退出码)。
7
+ *
8
+ * `modelItems` / `sessionItems` 同时供交互模式里的选择器(pickers.ts)使用。
9
+ */
10
+ import type { Model, ProviderRegistryApi } from "../../ai/types.js";
11
+ import type { InteractiveUi } from "../../cli/deps.js";
12
+ import type { SessionListItem } from "../../session/types.js";
13
+ import { type Keybindings, type SelectItem, type Terminal, type Theme } from "../../tui.js";
14
+ export interface StartupUiOptions {
15
+ /** 每次问答新建的终端;缺省 `new ProcessTerminal()`。 */
16
+ terminal?: () => Terminal;
17
+ theme?: Theme;
18
+ keybindings?: Keybindings;
19
+ /** 会话列表的相对时间基准(测试注入)。 */
20
+ now?: () => number;
21
+ }
22
+ /** 选择器里模型的说明:名称(与 id 不同时)、上下文、`img`(收图片)。 */
23
+ export declare function modelDescription(model: Model): string | undefined;
24
+ /**
25
+ * 模型按「供应商 · 渠道」分组;有 key(或本地)的供应商排前,组标题标 key 状态。多渠道供应商的模型在
26
+ * 每个挂载的渠道下各出现一次,非首选渠道的值带 `@渠道`。
27
+ */
28
+ export declare function modelItems(providers: ProviderRegistryApi): Promise<SelectItem[]>;
29
+ /** `刚刚 / 5 分钟前 / 3 小时前 / 2 天前 / 2026-09-01`。 */
30
+ export declare function relativeTime(iso: string, now: number): string;
31
+ /** 会话:标签 = 名字或首条提示,描述 = 相对时间 · 消息数。 */
32
+ export declare function sessionItems(items: readonly SessionListItem[], now: number): SelectItem[];
33
+ export declare function createStartupUi(options?: StartupUiOptions): Required<InteractiveUi>;
@@ -0,0 +1,268 @@
1
+ /**
2
+ * 启动期的迷你界面:bootstrap 第 7 / 8 / 11 步的 `InteractiveUi` 回调(实施计划 §3.3)。[B7]
3
+ *
4
+ * 这些回调先于交互模式运行(会话、信任、模型还没定),所以每次问答新建一个只含问题与
5
+ * 选择列表 / 输入框的 `TUI`;答完把问答收成一行再 `stop()`——不清屏,问答留在回滚里。
6
+ * 取消(Esc / Ctrl+C)按各回调的约定返回 undefined(bootstrap 据此给退出码)。
7
+ *
8
+ * `modelItems` / `sessionItems` 同时供交互模式里的选择器(pickers.ts)使用。
9
+ */
10
+ import { existsSync, statSync } from "node:fs";
11
+ import { resolve } from "node:path";
12
+ import { Container, Editor, ProcessTerminal, SelectList, TUI, Text, createTheme, defaultKeybindings, detectCapabilities, } from "../../tui.js";
13
+ function resolveOptions(options) {
14
+ return {
15
+ terminal: options.terminal ?? (() => new ProcessTerminal()),
16
+ theme: options.theme ?? createTheme("dark", { caps: detectCapabilities() }),
17
+ keys: options.keybindings ?? defaultKeybindings,
18
+ now: options.now ?? Date.now,
19
+ };
20
+ }
21
+ /** 一个问题 + 正文的迷你 TUI;`finish(answer)` 把界面收成一行「标题 → 答案」后交还终端。 */
22
+ function miniTui(r, prompt) {
23
+ const tui = new TUI(r.terminal(), { anchorToTop: false });
24
+ const body = new Container();
25
+ body.addChild(new Text(r.theme.fg("accent", "? ") + r.theme.bold(prompt.title)));
26
+ for (const line of prompt.lines ?? [])
27
+ body.addChild(new Text(r.theme.fg("dim", line)));
28
+ tui.addChild(body);
29
+ const finish = (answer) => {
30
+ tui.clear();
31
+ tui.addChild(new Text(r.theme.fg("accent", "? ") + r.theme.bold(prompt.title) + " " + r.theme.fg("dim", answer)));
32
+ tui.renderNow();
33
+ tui.stop();
34
+ };
35
+ return { tui, body, finish };
36
+ }
37
+ /** 选择题:返回选中项,Esc / Ctrl+C 返回 undefined。 */
38
+ function askSelect(r, prompt) {
39
+ return new Promise((done) => {
40
+ const { tui, body, finish } = miniTui(r, prompt);
41
+ const list = new SelectList(prompt.items, {
42
+ theme: r.theme,
43
+ keybindings: r.keys,
44
+ maxVisible: 10,
45
+ filterable: prompt.filterable === true,
46
+ onSelect: (item) => {
47
+ finish(item.label);
48
+ done(item);
49
+ },
50
+ onCancel: () => {
51
+ finish("(已取消)");
52
+ done(undefined);
53
+ },
54
+ });
55
+ if (prompt.selected !== undefined)
56
+ list.selectValue(prompt.selected);
57
+ body.addChild(list);
58
+ body.addChild(new Text(r.theme.fg("dim", prompt.filterable === true
59
+ ? "输入过滤 · ↑↓ 选择 · Enter 确认 · Esc 取消"
60
+ : "↑↓ 选择 · Enter 确认 · Esc 取消")));
61
+ tui.start();
62
+ tui.setFocus(list);
63
+ });
64
+ }
65
+ /** 填空题:`validate` 返回错误文本时留在原地重输;Esc / Ctrl+C 返回 undefined。 */
66
+ function askText(r, prompt) {
67
+ return new Promise((done) => {
68
+ const { tui, body, finish } = miniTui(r, prompt);
69
+ const error = new Text("");
70
+ const editor = new Editor({
71
+ theme: r.theme,
72
+ keybindings: r.keys,
73
+ maxVisibleLines: 3,
74
+ requestRender: () => tui.requestRender(),
75
+ ...(prompt.placeholder !== undefined ? { placeholder: prompt.placeholder } : {}),
76
+ onSubmit: (text) => {
77
+ const value = text.trim();
78
+ const problem = prompt.validate?.(value);
79
+ if (problem !== undefined) {
80
+ error.setText(r.theme.fg("error", problem));
81
+ editor.setText(value);
82
+ return;
83
+ }
84
+ finish(value);
85
+ done(value);
86
+ },
87
+ });
88
+ body.addChild(editor);
89
+ body.addChild(error);
90
+ tui.addInputListener((data) => {
91
+ if (r.keys.matches(data, "tui.select.cancel") && !editor.isCompletionOpen) {
92
+ finish("(已取消)");
93
+ done(undefined);
94
+ return true;
95
+ }
96
+ return false;
97
+ });
98
+ tui.start();
99
+ tui.setFocus(editor);
100
+ });
101
+ }
102
+ // ---------------------------------------------------------------------------
103
+ // 列表项(选择器共用)
104
+ // ---------------------------------------------------------------------------
105
+ /** 选择器里模型的说明:名称(与 id 不同时)、上下文、`img`(收图片)。 */
106
+ export function modelDescription(model) {
107
+ const ctx = model.contextWindow;
108
+ const parts = [
109
+ model.name !== "" && model.name !== model.id ? model.name : undefined,
110
+ ctx === undefined
111
+ ? undefined
112
+ : ctx >= 1_000_000
113
+ ? `${Math.round(ctx / 100_000) / 10}M`
114
+ : `${Math.round(ctx / 1000)}k`,
115
+ model.input.includes("image") ? "img" : undefined,
116
+ ].filter((x) => x !== undefined);
117
+ return parts.length > 0 ? parts.join(" · ") : undefined;
118
+ }
119
+ /**
120
+ * 模型按「供应商 · 渠道」分组;有 key(或本地)的供应商排前,组标题标 key 状态。多渠道供应商的模型在
121
+ * 每个挂载的渠道下各出现一次,非首选渠道的值带 `@渠道`。
122
+ */
123
+ export async function modelItems(providers) {
124
+ const groups = [];
125
+ for (const provider of providers.list()) {
126
+ if (provider.models.length === 0)
127
+ continue;
128
+ let status;
129
+ let ready = true;
130
+ if (!provider.requiresApiKey)
131
+ status = "本地";
132
+ else {
133
+ const key = await providers.resolveApiKey(provider.id).catch(() => ({ apiKey: undefined }));
134
+ ready = key.apiKey !== undefined;
135
+ status = ready ? "key ✓" : "无 key";
136
+ }
137
+ const item = (model, group, channel) => {
138
+ const suffix = channel !== undefined && channel !== model.channel ? `@${channel}` : "";
139
+ const out = {
140
+ value: `${provider.id}/${model.id}${suffix}`,
141
+ label: `${model.id}${suffix}`,
142
+ group,
143
+ };
144
+ const description = modelDescription(model);
145
+ if (description !== undefined)
146
+ out.description = description;
147
+ return out;
148
+ };
149
+ if (provider.channels === undefined) {
150
+ const group = `${provider.id} · ${status}`;
151
+ groups.push({ ready, items: provider.models.map((model) => item(model, group)) });
152
+ continue;
153
+ }
154
+ for (const channel of provider.channels) {
155
+ const group = `${provider.id} · ${channel.name} · ${status}`;
156
+ const models = provider.models.filter((m) => m.channels?.includes(channel.name));
157
+ if (models.length > 0)
158
+ groups.push({ ready, items: models.map((model) => item(model, group, channel.name)) });
159
+ }
160
+ }
161
+ return [...groups.filter((g) => g.ready), ...groups.filter((g) => !g.ready)].flatMap((g) => g.items);
162
+ }
163
+ /** `刚刚 / 5 分钟前 / 3 小时前 / 2 天前 / 2026-09-01`。 */
164
+ export function relativeTime(iso, now) {
165
+ const at = Date.parse(iso);
166
+ if (!Number.isFinite(at))
167
+ return "";
168
+ const minutes = Math.floor((now - at) / 60_000);
169
+ if (minutes < 1)
170
+ return "刚刚";
171
+ if (minutes < 60)
172
+ return `${minutes} 分钟前`;
173
+ const hours = Math.floor(minutes / 60);
174
+ if (hours < 24)
175
+ return `${hours} 小时前`;
176
+ const days = Math.floor(hours / 24);
177
+ if (days < 30)
178
+ return `${days} 天前`;
179
+ return iso.slice(0, 10);
180
+ }
181
+ function oneLine(text, max = 60) {
182
+ const flat = text.replace(/\s+/g, " ").trim();
183
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
184
+ }
185
+ /** 会话:标签 = 名字或首条提示,描述 = 相对时间 · 消息数。 */
186
+ export function sessionItems(items, now) {
187
+ return [...items]
188
+ .sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt))
189
+ .map((item) => ({
190
+ value: item.id,
191
+ label: oneLine(item.name ?? item.firstPrompt ?? item.id.slice(0, 8)),
192
+ description: `${relativeTime(item.modifiedAt, now)} · ${item.messageCount} 条`,
193
+ }));
194
+ }
195
+ // ---------------------------------------------------------------------------
196
+ // InteractiveUi
197
+ // ---------------------------------------------------------------------------
198
+ const TRUST_CHOICES = [
199
+ { value: "trust", label: "信任并记住", answer: { trusted: true, remember: true } },
200
+ { value: "once", label: "仅本次信任", answer: { trusted: true, remember: false } },
201
+ { value: "skip", label: "本次不信任", answer: { trusted: false, remember: false } },
202
+ { value: "never", label: "不信任并记住", answer: { trusted: false, remember: true } },
203
+ ];
204
+ function isDirectory(path) {
205
+ try {
206
+ return statSync(path).isDirectory();
207
+ }
208
+ catch {
209
+ return false;
210
+ }
211
+ }
212
+ export function createStartupUi(options = {}) {
213
+ const r = resolveOptions(options);
214
+ return {
215
+ async promptTrust(cwd, resources) {
216
+ const shown = resources.slice(0, 6).map((p) => ` ${p}`);
217
+ if (resources.length > shown.length)
218
+ shown.push(` …另有 ${resources.length - shown.length} 项`);
219
+ const picked = await askSelect(r, {
220
+ title: "信任这个目录的项目资源?",
221
+ lines: [cwd, ...shown],
222
+ items: TRUST_CHOICES.map(({ value, label }) => ({ value, label })),
223
+ selected: "once",
224
+ });
225
+ const choice = TRUST_CHOICES.find((c) => c.value === picked?.value);
226
+ return choice?.answer ?? { trusted: false, remember: false };
227
+ },
228
+ async pickSession(items) {
229
+ if (items.length === 0)
230
+ return undefined;
231
+ const picked = await askSelect(r, {
232
+ title: "恢复哪个会话?",
233
+ items: sessionItems(items, r.now()),
234
+ filterable: true,
235
+ });
236
+ return picked?.value;
237
+ },
238
+ async pickModel(providers, reason) {
239
+ const items = await modelItems(providers);
240
+ if (items.length === 0)
241
+ return undefined;
242
+ const picked = await askSelect(r, {
243
+ title: "选择模型",
244
+ lines: [reason],
245
+ items,
246
+ filterable: true,
247
+ });
248
+ return picked?.value;
249
+ },
250
+ askCwd(missing) {
251
+ return askText(r, {
252
+ title: "会话的工作目录不存在",
253
+ lines: [missing, "输入替代目录(Enter 确认 · Esc 取消)"],
254
+ placeholder: "目录路径",
255
+ validate: (text) => {
256
+ if (text === "")
257
+ return "请输入目录";
258
+ const abs = resolve(text);
259
+ if (!existsSync(abs))
260
+ return `不存在:${abs}`;
261
+ if (!isDirectory(abs))
262
+ return `不是目录:${abs}`;
263
+ return undefined;
264
+ },
265
+ });
266
+ },
267
+ };
268
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * 状态栏(设计 §12.6):编辑器下方一行。[B7]
3
+ *
4
+ * `provider/model · think:medium · ↑12.3k ↓1.2k · cache 80% ♨ · $0.12 · rebill $0.11 · ctx 34%
5
+ * · queue 1 · mode:default · codemode:only net! · preset:default · [宿主状态]`
6
+ *
7
+ * - 用量来自 `session.getStats()`(`↑` = 输入含缓存读写,`↓` = 输出),只在 `refresh()` 时读取
8
+ * (事件驱动),渲染只拼字符串;
9
+ * - [W3-C2] `cache` 是**最近一次**请求的命中率(会话累计在 `/session`);端点三态:`unknown` →
10
+ * `cache —`,`silent` → `cache 未报告`;保温计时中追加 `♨`;会话有重计费时显示 `rebill $x`
11
+ * (无价模型显示重计费 token);会话层缓存未接线时回落到会话命中率 `cacheHitRate`;
12
+ * - `ctx ?` 表示模型没有上下文窗口;≥ 70% 黄、≥ 90% 红;没有用量时不显示 token / cache / 费用;
13
+ * - [W3-C2 A4] codemode 生效时显示 `codemode:on|only`,运行时 Node 的权限模型不隔离网络
14
+ * (`detectSandboxCapability().strict` 为 false)时追加红色 `net!`;
15
+ * - 宿主状态读 `HostAdapterHandle.status()`(宿主 `ui.setStatus`),每次渲染现取;
16
+ * - 宽度不够时按优先级丢弃(宿主 → 预设 → rebill → 费用 → cache → token → think → 队列 →
17
+ * codemode),模型、ctx、权限模式最后丢。
18
+ */
19
+ import type { AgentSession, SessionCacheStats } from "../../agent/types.js";
20
+ import { type Component, type Theme } from "../../tui.js";
21
+ export interface StatusBarSource {
22
+ session(): AgentSession;
23
+ /** 工具预设名(`config.tools.preset`)。 */
24
+ preset(): string;
25
+ /** 宿主 `ui.setStatus` 的当前值;无宿主为 undefined。 */
26
+ hostStatus?(): ReadonlyMap<string, string> | undefined;
27
+ /** 生效的 codemode 方式(codemode 工具活动时);off / 未注册为 undefined。 */
28
+ codemode?(): "on" | "only" | undefined;
29
+ /** 沙箱是否隔离网络(缺省 true,不标 `net!`)。 */
30
+ sandboxStrict?(): boolean;
31
+ }
32
+ export declare function formatTokens(count: number): string;
33
+ export declare function formatCost(cost: number): string;
34
+ /** `cache 83%` / `cache —` / `cache 未报告`,保温计时中追加 ` ♨`。 */
35
+ export declare function cacheText(cache: SessionCacheStats): string;
36
+ export declare class StatusBar implements Component {
37
+ private readonly source;
38
+ private readonly theme;
39
+ private stats;
40
+ private queue;
41
+ constructor(source: StatusBarSource, theme: Theme);
42
+ /** 用量、模型等变化后调用(事件驱动;流式期间不必每帧算)。 */
43
+ refresh(): void;
44
+ setQueue(steering: number, followUp: number): void;
45
+ private parts;
46
+ render(width: number): string[];
47
+ invalidate(): void;
48
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * 状态栏(设计 §12.6):编辑器下方一行。[B7]
3
+ *
4
+ * `provider/model · think:medium · ↑12.3k ↓1.2k · cache 80% ♨ · $0.12 · rebill $0.11 · ctx 34%
5
+ * · queue 1 · mode:default · codemode:only net! · preset:default · [宿主状态]`
6
+ *
7
+ * - 用量来自 `session.getStats()`(`↑` = 输入含缓存读写,`↓` = 输出),只在 `refresh()` 时读取
8
+ * (事件驱动),渲染只拼字符串;
9
+ * - [W3-C2] `cache` 是**最近一次**请求的命中率(会话累计在 `/session`);端点三态:`unknown` →
10
+ * `cache —`,`silent` → `cache 未报告`;保温计时中追加 `♨`;会话有重计费时显示 `rebill $x`
11
+ * (无价模型显示重计费 token);会话层缓存未接线时回落到会话命中率 `cacheHitRate`;
12
+ * - `ctx ?` 表示模型没有上下文窗口;≥ 70% 黄、≥ 90% 红;没有用量时不显示 token / cache / 费用;
13
+ * - [W3-C2 A4] codemode 生效时显示 `codemode:on|only`,运行时 Node 的权限模型不隔离网络
14
+ * (`detectSandboxCapability().strict` 为 false)时追加红色 `net!`;
15
+ * - 宿主状态读 `HostAdapterHandle.status()`(宿主 `ui.setStatus`),每次渲染现取;
16
+ * - 宽度不够时按优先级丢弃(宿主 → 预设 → rebill → 费用 → cache → token → think → 队列 →
17
+ * codemode),模型、ctx、权限模式最后丢。
18
+ */
19
+ import { truncateToWidth, visibleWidth } from "../../tui.js";
20
+ export function formatTokens(count) {
21
+ if (count < 1000)
22
+ return String(count);
23
+ if (count < 10_000)
24
+ return `${(count / 1000).toFixed(1)}k`;
25
+ if (count < 1_000_000)
26
+ return `${Math.round(count / 1000)}k`;
27
+ return `${(count / 1_000_000).toFixed(1)}M`;
28
+ }
29
+ export function formatCost(cost) {
30
+ if (cost >= 0.01)
31
+ return `$${cost.toFixed(2)}`;
32
+ return `$${cost.toFixed(3)}`;
33
+ }
34
+ /** `cache 83%` / `cache —` / `cache 未报告`,保温计时中追加 ` ♨`。 */
35
+ export function cacheText(cache) {
36
+ const rate = cache.lastHitRate ?? cache.hitRate;
37
+ const value = cache.reporting === "silent"
38
+ ? "未报告"
39
+ : cache.reporting === "reported" && rate !== undefined
40
+ ? `${Math.round(rate * 100)}%`
41
+ : "—";
42
+ return `cache ${value}${cache.warming.state === "scheduled" ? " ♨" : ""}`;
43
+ }
44
+ export class StatusBar {
45
+ source;
46
+ theme;
47
+ stats;
48
+ queue = 0;
49
+ constructor(source, theme) {
50
+ this.source = source;
51
+ this.theme = theme;
52
+ }
53
+ /** 用量、模型等变化后调用(事件驱动;流式期间不必每帧算)。 */
54
+ refresh() {
55
+ this.stats = this.source.session().getStats();
56
+ }
57
+ setQueue(steering, followUp) {
58
+ this.queue = steering + followUp;
59
+ }
60
+ parts() {
61
+ const theme = this.theme;
62
+ const session = this.source.session();
63
+ const state = session.state;
64
+ const stats = this.stats ?? session.getStats();
65
+ this.stats = stats;
66
+ const parts = [];
67
+ const model = state.model === undefined ? "?" : `${state.model.provider}/${state.model.id}`;
68
+ parts.push({ text: theme.fg("accent", model), priority: 0 });
69
+ parts.push({ text: `think:${state.thinkingLevel}`, priority: 5 });
70
+ const t = stats.tokens;
71
+ const prompt = t.input + t.cacheRead + t.cacheWrite;
72
+ if (prompt + t.output > 0) {
73
+ parts.push({ text: `↑${formatTokens(prompt)} ↓${formatTokens(t.output)}`, priority: 6 });
74
+ const cache = stats.cache;
75
+ if (cache !== undefined)
76
+ parts.push({ text: cacheText(cache), priority: 7 });
77
+ else if (stats.cacheHitRate !== undefined) {
78
+ parts.push({ text: `cache ${Math.round(stats.cacheHitRate * 100)}%`, priority: 7 });
79
+ }
80
+ if (stats.cost === undefined)
81
+ parts.push({ text: "$?", priority: 8 });
82
+ else if (stats.cost > 0)
83
+ parts.push({ text: formatCost(stats.cost), priority: 8 });
84
+ if (cache !== undefined && cache.reBilledTokens > 0) {
85
+ const amount = cache.reBilledUsd === undefined
86
+ ? `${formatTokens(cache.reBilledTokens)} tok`
87
+ : formatCost(cache.reBilledUsd);
88
+ parts.push({ text: theme.fg("warning", `rebill ${amount}`), priority: 9 });
89
+ }
90
+ }
91
+ const ctx = stats.contextPercent;
92
+ const ctxText = ctx === undefined ? "ctx ?" : `ctx ${Math.round(ctx)}%`;
93
+ parts.push({
94
+ text: ctx !== undefined && ctx >= 90
95
+ ? theme.fg("error", ctxText)
96
+ : ctx !== undefined && ctx >= 70
97
+ ? theme.fg("warning", ctxText)
98
+ : ctxText,
99
+ priority: 1,
100
+ });
101
+ if (this.queue > 0)
102
+ parts.push({ text: theme.fg("warning", `queue ${this.queue}`), priority: 4 });
103
+ const mode = `mode:${state.permissionMode}`;
104
+ parts.push({
105
+ text: state.permissionMode === "full-auto" ? theme.fg("warning", mode) : mode,
106
+ priority: 2,
107
+ });
108
+ const codemode = this.source.codemode?.();
109
+ if (codemode !== undefined) {
110
+ const strict = this.source.sandboxStrict?.() ?? true;
111
+ parts.push({
112
+ text: `codemode:${codemode}${strict ? "" : ` ${theme.fg("error", "net!")}`}`,
113
+ priority: 3,
114
+ });
115
+ }
116
+ parts.push({ text: `preset:${this.source.preset()}`, priority: 10 });
117
+ const host = this.source.hostStatus?.();
118
+ if (host !== undefined && host.size > 0) {
119
+ const values = [...host.values()].filter((v) => v.trim() !== "");
120
+ if (values.length > 0)
121
+ parts.push({ text: `[${values.join(" · ")}]`, priority: 11 });
122
+ }
123
+ return parts;
124
+ }
125
+ render(width) {
126
+ const parts = this.parts();
127
+ const sep = this.theme.fg("dim", " · ");
128
+ const join = (list) => list.map((p) => p.text).join(sep);
129
+ let kept = parts;
130
+ while (kept.length > 1 && visibleWidth(join(kept)) > width) {
131
+ const drop = kept.reduce((worst, p) => (p.priority > worst.priority ? p : worst));
132
+ kept = kept.filter((p) => p !== drop);
133
+ }
134
+ return [truncateToWidth(join(kept), width)];
135
+ }
136
+ invalidate() {
137
+ this.stats = undefined;
138
+ }
139
+ }