@armadra/agent 0.0.0-stage → 0.2.1

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 (407) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +426 -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 +511 -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 +118 -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 +104 -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 +484 -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 +303 -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 +102 -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/registry.d.ts +83 -0
  101. package/dist/ai/providers/registry.js +305 -0
  102. package/dist/ai/sse.d.ts +32 -0
  103. package/dist/ai/sse.js +140 -0
  104. package/dist/ai/thinking.d.ts +42 -0
  105. package/dist/ai/thinking.js +101 -0
  106. package/dist/ai/types.d.ts +428 -0
  107. package/dist/ai/types.js +19 -0
  108. package/dist/bundle/ama-sandbox.cjs +324 -0
  109. package/dist/bundle/ama.cjs +25770 -0
  110. package/dist/bundle.d.ts +9 -0
  111. package/dist/bundle.js +15 -0
  112. package/dist/cli/args.d.ts +82 -0
  113. package/dist/cli/args.js +381 -0
  114. package/dist/cli/bootstrap.d.ts +18 -0
  115. package/dist/cli/bootstrap.js +398 -0
  116. package/dist/cli/compose-providers.d.ts +37 -0
  117. package/dist/cli/compose-providers.js +104 -0
  118. package/dist/cli/compose-session.d.ts +69 -0
  119. package/dist/cli/compose-session.js +370 -0
  120. package/dist/cli/compose-store.d.ts +24 -0
  121. package/dist/cli/compose-store.js +125 -0
  122. package/dist/cli/compose.d.ts +106 -0
  123. package/dist/cli/compose.js +206 -0
  124. package/dist/cli/default-model.d.ts +16 -0
  125. package/dist/cli/default-model.js +24 -0
  126. package/dist/cli/deps.d.ts +205 -0
  127. package/dist/cli/deps.js +12 -0
  128. package/dist/cli/exit-codes.d.ts +29 -0
  129. package/dist/cli/exit-codes.js +40 -0
  130. package/dist/cli/main.d.ts +26 -0
  131. package/dist/cli/main.js +165 -0
  132. package/dist/cli/runtime.d.ts +90 -0
  133. package/dist/cli/runtime.js +8 -0
  134. package/dist/cli/startup-screen.d.ts +15 -0
  135. package/dist/cli/startup-screen.js +56 -0
  136. package/dist/cli/startup-steps.d.ts +35 -0
  137. package/dist/cli/startup-steps.js +197 -0
  138. package/dist/cli/subcommands/auth.d.ts +12 -0
  139. package/dist/cli/subcommands/auth.js +91 -0
  140. package/dist/cli/subcommands/config.d.ts +44 -0
  141. package/dist/cli/subcommands/config.js +201 -0
  142. package/dist/cli/subcommands/context.d.ts +21 -0
  143. package/dist/cli/subcommands/context.js +41 -0
  144. package/dist/cli/subcommands/doctor.d.ts +11 -0
  145. package/dist/cli/subcommands/doctor.js +246 -0
  146. package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
  147. package/dist/cli/subcommands/models-cache-probe.js +190 -0
  148. package/dist/cli/subcommands/models-discover.d.ts +40 -0
  149. package/dist/cli/subcommands/models-discover.js +204 -0
  150. package/dist/cli/subcommands/models.d.ts +40 -0
  151. package/dist/cli/subcommands/models.js +144 -0
  152. package/dist/cli/subcommands/sessions.d.ts +11 -0
  153. package/dist/cli/subcommands/sessions.js +100 -0
  154. package/dist/codemode/capability.d.ts +37 -0
  155. package/dist/codemode/capability.js +55 -0
  156. package/dist/codemode/declarations.d.ts +44 -0
  157. package/dist/codemode/declarations.js +111 -0
  158. package/dist/codemode/host-side.d.ts +59 -0
  159. package/dist/codemode/host-side.js +253 -0
  160. package/dist/codemode/modes.d.ts +23 -0
  161. package/dist/codemode/modes.js +40 -0
  162. package/dist/codemode/protocol.d.ts +91 -0
  163. package/dist/codemode/protocol.js +114 -0
  164. package/dist/codemode/sandbox-entry.d.ts +25 -0
  165. package/dist/codemode/sandbox-entry.js +323 -0
  166. package/dist/codemode/store.d.ts +26 -0
  167. package/dist/codemode/store.js +53 -0
  168. package/dist/codemode/tool.d.ts +105 -0
  169. package/dist/codemode/tool.js +283 -0
  170. package/dist/compaction/branch-summary.d.ts +30 -0
  171. package/dist/compaction/branch-summary.js +79 -0
  172. package/dist/compaction/breaker.d.ts +32 -0
  173. package/dist/compaction/breaker.js +65 -0
  174. package/dist/compaction/cut-point.d.ts +23 -0
  175. package/dist/compaction/cut-point.js +70 -0
  176. package/dist/compaction/estimate.d.ts +30 -0
  177. package/dist/compaction/estimate.js +114 -0
  178. package/dist/compaction/prune-tier.d.ts +31 -0
  179. package/dist/compaction/prune-tier.js +83 -0
  180. package/dist/compaction/serialize.d.ts +20 -0
  181. package/dist/compaction/serialize.js +115 -0
  182. package/dist/compaction/summarize-tier.d.ts +111 -0
  183. package/dist/compaction/summarize-tier.js +305 -0
  184. package/dist/config/auth-file.d.ts +39 -0
  185. package/dist/config/auth-file.js +92 -0
  186. package/dist/config/context-files.d.ts +30 -0
  187. package/dist/config/context-files.js +77 -0
  188. package/dist/config/load.d.ts +45 -0
  189. package/dist/config/load.js +97 -0
  190. package/dist/config/merge.d.ts +73 -0
  191. package/dist/config/merge.js +248 -0
  192. package/dist/config/paths.d.ts +38 -0
  193. package/dist/config/paths.js +108 -0
  194. package/dist/config/profile.d.ts +27 -0
  195. package/dist/config/profile.js +65 -0
  196. package/dist/config/schema.d.ts +34 -0
  197. package/dist/config/schema.js +413 -0
  198. package/dist/config/trust.d.ts +42 -0
  199. package/dist/config/trust.js +122 -0
  200. package/dist/config/types.d.ts +172 -0
  201. package/dist/config/types.js +27 -0
  202. package/dist/config/write.d.ts +10 -0
  203. package/dist/config/write.js +25 -0
  204. package/dist/errors.d.ts +28 -0
  205. package/dist/errors.js +30 -0
  206. package/dist/hooks/config.d.ts +49 -0
  207. package/dist/hooks/config.js +102 -0
  208. package/dist/hooks/dispatcher.d.ts +41 -0
  209. package/dist/hooks/dispatcher.js +104 -0
  210. package/dist/hooks/matcher.d.ts +22 -0
  211. package/dist/hooks/matcher.js +108 -0
  212. package/dist/hooks/protocol.d.ts +33 -0
  213. package/dist/hooks/protocol.js +240 -0
  214. package/dist/hooks/runner.d.ts +30 -0
  215. package/dist/hooks/runner.js +143 -0
  216. package/dist/hooks/types.d.ts +147 -0
  217. package/dist/hooks/types.js +20 -0
  218. package/dist/host/api-impl.d.ts +75 -0
  219. package/dist/host/api-impl.js +173 -0
  220. package/dist/host/loader.d.ts +31 -0
  221. package/dist/host/loader.js +147 -0
  222. package/dist/host/types.d.ts +169 -0
  223. package/dist/host/types.js +14 -0
  224. package/dist/host.d.ts +5 -0
  225. package/dist/host.js +4 -0
  226. package/dist/index.d.ts +41 -0
  227. package/dist/index.js +28 -0
  228. package/dist/modes/commands-core.d.ts +44 -0
  229. package/dist/modes/commands-core.js +171 -0
  230. package/dist/modes/interactive/approval-dialog.d.ts +48 -0
  231. package/dist/modes/interactive/approval-dialog.js +202 -0
  232. package/dist/modes/interactive/commands.d.ts +41 -0
  233. package/dist/modes/interactive/commands.js +202 -0
  234. package/dist/modes/interactive/completion.d.ts +53 -0
  235. package/dist/modes/interactive/completion.js +157 -0
  236. package/dist/modes/interactive/interactive-mode.d.ts +46 -0
  237. package/dist/modes/interactive/interactive-mode.js +487 -0
  238. package/dist/modes/interactive/key-dispatch.d.ts +29 -0
  239. package/dist/modes/interactive/key-dispatch.js +107 -0
  240. package/dist/modes/interactive/line/line-editor.d.ts +50 -0
  241. package/dist/modes/interactive/line/line-editor.js +232 -0
  242. package/dist/modes/interactive/line/line-mode.d.ts +20 -0
  243. package/dist/modes/interactive/line/line-mode.js +216 -0
  244. package/dist/modes/interactive/line/line-render.d.ts +35 -0
  245. package/dist/modes/interactive/line/line-render.js +171 -0
  246. package/dist/modes/interactive/line/paste-state.d.ts +26 -0
  247. package/dist/modes/interactive/line/paste-state.js +72 -0
  248. package/dist/modes/interactive/message-view.d.ts +77 -0
  249. package/dist/modes/interactive/message-view.js +269 -0
  250. package/dist/modes/interactive/pickers.d.ts +42 -0
  251. package/dist/modes/interactive/pickers.js +115 -0
  252. package/dist/modes/interactive/startup-ui.d.ts +28 -0
  253. package/dist/modes/interactive/startup-ui.js +238 -0
  254. package/dist/modes/interactive/status-bar.d.ts +48 -0
  255. package/dist/modes/interactive/status-bar.js +139 -0
  256. package/dist/modes/interactive/tool-view.d.ts +95 -0
  257. package/dist/modes/interactive/tool-view.js +344 -0
  258. package/dist/modes/print/json-event.d.ts +15 -0
  259. package/dist/modes/print/json-event.js +39 -0
  260. package/dist/modes/print/print-mode.d.ts +15 -0
  261. package/dist/modes/print/print-mode.js +96 -0
  262. package/dist/modes/rpc/commands.d.ts +40 -0
  263. package/dist/modes/rpc/commands.js +258 -0
  264. package/dist/modes/rpc/jsonl.d.ts +16 -0
  265. package/dist/modes/rpc/jsonl.js +55 -0
  266. package/dist/modes/rpc/rpc-mode.d.ts +22 -0
  267. package/dist/modes/rpc/rpc-mode.js +153 -0
  268. package/dist/modes/session-report.d.ts +55 -0
  269. package/dist/modes/session-report.js +278 -0
  270. package/dist/modes/shared.d.ts +13 -0
  271. package/dist/modes/shared.js +52 -0
  272. package/dist/modes/startup-ui-text.d.ts +16 -0
  273. package/dist/modes/startup-ui-text.js +103 -0
  274. package/dist/permissions/broker.d.ts +47 -0
  275. package/dist/permissions/broker.js +107 -0
  276. package/dist/permissions/dangerous.d.ts +47 -0
  277. package/dist/permissions/dangerous.js +370 -0
  278. package/dist/permissions/pipeline.d.ts +51 -0
  279. package/dist/permissions/pipeline.js +181 -0
  280. package/dist/permissions/preview.d.ts +43 -0
  281. package/dist/permissions/preview.js +463 -0
  282. package/dist/permissions/rules.d.ts +58 -0
  283. package/dist/permissions/rules.js +249 -0
  284. package/dist/permissions/types.d.ts +100 -0
  285. package/dist/permissions/types.js +18 -0
  286. package/dist/rpc.d.ts +171 -0
  287. package/dist/rpc.js +13 -0
  288. package/dist/sdk.d.ts +98 -0
  289. package/dist/sdk.js +195 -0
  290. package/dist/session/manager.d.ts +70 -0
  291. package/dist/session/manager.js +278 -0
  292. package/dist/session/migrate.d.ts +19 -0
  293. package/dist/session/migrate.js +53 -0
  294. package/dist/session/projection.d.ts +50 -0
  295. package/dist/session/projection.js +180 -0
  296. package/dist/session/store.d.ts +47 -0
  297. package/dist/session/store.js +210 -0
  298. package/dist/session/tree.d.ts +21 -0
  299. package/dist/session/tree.js +88 -0
  300. package/dist/session/types.d.ts +199 -0
  301. package/dist/session/types.js +18 -0
  302. package/dist/skills/discover.d.ts +60 -0
  303. package/dist/skills/discover.js +160 -0
  304. package/dist/skills/expand.d.ts +35 -0
  305. package/dist/skills/expand.js +46 -0
  306. package/dist/skills/frontmatter.d.ts +27 -0
  307. package/dist/skills/frontmatter.js +157 -0
  308. package/dist/skills/index-prompt.d.ts +13 -0
  309. package/dist/skills/index-prompt.js +42 -0
  310. package/dist/skills/templates.d.ts +52 -0
  311. package/dist/skills/templates.js +177 -0
  312. package/dist/tools/bash.d.ts +49 -0
  313. package/dist/tools/bash.js +222 -0
  314. package/dist/tools/edit-fuzzy.d.ts +36 -0
  315. package/dist/tools/edit-fuzzy.js +129 -0
  316. package/dist/tools/edit.d.ts +38 -0
  317. package/dist/tools/edit.js +241 -0
  318. package/dist/tools/file-mutex.d.ts +8 -0
  319. package/dist/tools/file-mutex.js +29 -0
  320. package/dist/tools/glob.d.ts +44 -0
  321. package/dist/tools/glob.js +217 -0
  322. package/dist/tools/grep.d.ts +39 -0
  323. package/dist/tools/grep.js +179 -0
  324. package/dist/tools/ignore.d.ts +40 -0
  325. package/dist/tools/ignore.js +174 -0
  326. package/dist/tools/ls.d.ts +21 -0
  327. package/dist/tools/ls.js +96 -0
  328. package/dist/tools/output-accumulator.d.ts +48 -0
  329. package/dist/tools/output-accumulator.js +106 -0
  330. package/dist/tools/paths.d.ts +20 -0
  331. package/dist/tools/paths.js +58 -0
  332. package/dist/tools/presets.d.ts +71 -0
  333. package/dist/tools/presets.js +128 -0
  334. package/dist/tools/process-tree.d.ts +35 -0
  335. package/dist/tools/process-tree.js +125 -0
  336. package/dist/tools/read.d.ts +28 -0
  337. package/dist/tools/read.js +181 -0
  338. package/dist/tools/registry.d.ts +43 -0
  339. package/dist/tools/registry.js +106 -0
  340. package/dist/tools/shell.d.ts +38 -0
  341. package/dist/tools/shell.js +91 -0
  342. package/dist/tools/task.d.ts +29 -0
  343. package/dist/tools/task.js +107 -0
  344. package/dist/tools/todo.d.ts +28 -0
  345. package/dist/tools/todo.js +107 -0
  346. package/dist/tools/truncate.d.ts +44 -0
  347. package/dist/tools/truncate.js +175 -0
  348. package/dist/tools/types.d.ts +135 -0
  349. package/dist/tools/types.js +19 -0
  350. package/dist/tools/write.d.ts +20 -0
  351. package/dist/tools/write.js +84 -0
  352. package/dist/tui/ansi.d.ts +29 -0
  353. package/dist/tui/ansi.js +420 -0
  354. package/dist/tui/component.d.ts +38 -0
  355. package/dist/tui/component.js +13 -0
  356. package/dist/tui/components/box.d.ts +26 -0
  357. package/dist/tui/components/box.js +65 -0
  358. package/dist/tui/components/container.d.ts +14 -0
  359. package/dist/tui/components/container.js +39 -0
  360. package/dist/tui/components/editor-buffer.d.ts +67 -0
  361. package/dist/tui/components/editor-buffer.js +300 -0
  362. package/dist/tui/components/editor-paste.d.ts +29 -0
  363. package/dist/tui/components/editor-paste.js +64 -0
  364. package/dist/tui/components/editor.d.ts +114 -0
  365. package/dist/tui/components/editor.js +530 -0
  366. package/dist/tui/components/key-value.d.ts +29 -0
  367. package/dist/tui/components/key-value.js +48 -0
  368. package/dist/tui/components/loader.d.ts +35 -0
  369. package/dist/tui/components/loader.js +71 -0
  370. package/dist/tui/components/markdown.d.ts +90 -0
  371. package/dist/tui/components/markdown.js +462 -0
  372. package/dist/tui/components/meter.d.ts +29 -0
  373. package/dist/tui/components/meter.js +47 -0
  374. package/dist/tui/components/overlay.d.ts +24 -0
  375. package/dist/tui/components/overlay.js +52 -0
  376. package/dist/tui/components/select-list.d.ts +57 -0
  377. package/dist/tui/components/select-list.js +179 -0
  378. package/dist/tui/components/spacer.d.ts +11 -0
  379. package/dist/tui/components/spacer.js +16 -0
  380. package/dist/tui/components/text.d.ts +30 -0
  381. package/dist/tui/components/text.js +65 -0
  382. package/dist/tui/keybindings.d.ts +66 -0
  383. package/dist/tui/keybindings.js +120 -0
  384. package/dist/tui/keys.d.ts +38 -0
  385. package/dist/tui/keys.js +275 -0
  386. package/dist/tui/stdin-buffer.d.ts +41 -0
  387. package/dist/tui/stdin-buffer.js +206 -0
  388. package/dist/tui/terminal.d.ts +79 -0
  389. package/dist/tui/terminal.js +153 -0
  390. package/dist/tui/theme.d.ts +31 -0
  391. package/dist/tui/theme.js +199 -0
  392. package/dist/tui/tui.d.ts +103 -0
  393. package/dist/tui/tui.js +363 -0
  394. package/dist/tui/vt-screen.d.ts +46 -0
  395. package/dist/tui/vt-screen.js +251 -0
  396. package/dist/tui.d.ts +30 -0
  397. package/dist/tui.js +31 -0
  398. package/dist/version.d.ts +8 -0
  399. package/dist/version.js +24 -0
  400. package/docs/codemode.md +102 -0
  401. package/docs/hooks.md +196 -0
  402. package/docs/host-api.md +161 -0
  403. package/docs/providers.md +316 -0
  404. package/docs/rpc.md +253 -0
  405. package/docs/session-format.md +119 -0
  406. package/docs/tui.md +168 -0
  407. package/package.json +101 -4
@@ -0,0 +1,171 @@
1
+ /**
2
+ * 行式界面的输出:会话事件 → 纯文本(助手文本流式写 stdout,工具 / 重试 / 压缩一行提示)。[B6]
3
+ * 选择器在行式界面里退化为列出候选、提示带参数重发。
4
+ */
5
+ import { THINKING_LEVELS } from "../../../ai/thinking.js";
6
+ import { listSessions } from "../../../cli/compose-store.js";
7
+ import { PERMISSION_MODES_STRICT_FIRST } from "../../../permissions/types.js";
8
+ import { cacheEventNotice, warmSentNotice } from "../../session-report.js";
9
+ const SUMMARY_KEYS = ["command", "path", "pattern", "file_path", "url", "description", "name"];
10
+ /** 工具参数的一行摘要(第一个常见字段,折叠换行、截到 80 字符)。 */
11
+ export function argsSummary(args) {
12
+ if (typeof args !== "object" || args === null)
13
+ return "";
14
+ const record = args;
15
+ for (const key of SUMMARY_KEYS) {
16
+ const value = record[key];
17
+ if (typeof value === "string" && value !== "") {
18
+ const flat = value.replace(/\s*\n\s*/g, " ⏎ ");
19
+ return flat.length > 80 ? `${flat.slice(0, 79)}…` : flat;
20
+ }
21
+ }
22
+ return "";
23
+ }
24
+ export function approvalQuestion(request) {
25
+ const task = (request.context?.depth ?? 0) > 0 ? "[task] " : "";
26
+ const summary = argsSummary(request.input);
27
+ const why = request.reason === "dangerous"
28
+ ? "(危险命令)"
29
+ : request.hookReason !== undefined
30
+ ? `(${request.hookReason})`
31
+ : "";
32
+ return `${task}允许 ${request.toolName}${summary !== "" ? ` ${summary}` : ""}${why}?[y 允许 / a 本会话都允许 / N 拒绝] `;
33
+ }
34
+ /** `AMA_LOG=info|debug`。 */
35
+ export function logsInfo(env) {
36
+ return env["AMA_LOG"] === "info" || env["AMA_LOG"] === "debug";
37
+ }
38
+ export class EventPrinter {
39
+ out;
40
+ err;
41
+ options;
42
+ midLine = false;
43
+ constructor(out, err, options = {}) {
44
+ this.out = out;
45
+ this.err = err;
46
+ this.options = options;
47
+ }
48
+ line(text, toErr = false) {
49
+ if (this.midLine) {
50
+ this.out("\n");
51
+ this.midLine = false;
52
+ }
53
+ (toErr ? this.err : this.out)(`${text}\n`);
54
+ }
55
+ /** 收尾:保证光标在行首。 */
56
+ endLine() {
57
+ if (this.midLine)
58
+ this.out("\n");
59
+ this.midLine = false;
60
+ }
61
+ handle(event) {
62
+ switch (event.type) {
63
+ case "message_update": {
64
+ const e = event.assistantMessageEvent;
65
+ if (e.type === "text_delta" && e.delta !== "") {
66
+ this.out(e.delta);
67
+ this.midLine = !e.delta.endsWith("\n");
68
+ }
69
+ return;
70
+ }
71
+ case "message_end":
72
+ if (event.message.role === "assistant") {
73
+ this.endLine();
74
+ if (event.message.stopReason === "error")
75
+ this.line(`ama: 错误:${event.message.errorMessage ?? "模型调用失败"}`, true);
76
+ }
77
+ return;
78
+ case "tool_execution_start": {
79
+ const summary = argsSummary(event.args);
80
+ this.line(`● ${event.toolName}${summary !== "" ? ` ${summary}` : ""}`);
81
+ return;
82
+ }
83
+ case "tool_execution_end":
84
+ if (event.isError) {
85
+ const content = event.result.content;
86
+ const text = typeof content === "string"
87
+ ? content
88
+ : content.map((b) => (b.type === "text" ? b.text : "")).join("");
89
+ this.line(` ✗ ${text.split("\n")[0]?.slice(0, 200) ?? ""}`);
90
+ }
91
+ return;
92
+ case "auto_retry_start":
93
+ this.line(`↻ 重试 ${event.attempt}/${event.maxAttempts}(${Math.round(event.delayMs / 1000)}s):${event.errorMessage}`, true);
94
+ return;
95
+ case "compaction_start":
96
+ this.line("… 压缩上下文", true);
97
+ return;
98
+ case "compaction_end":
99
+ if (event.error !== undefined)
100
+ this.line(`压缩失败:${event.error}`, true);
101
+ return;
102
+ case "cache_miss":
103
+ case "context_pressure": {
104
+ const shown = cacheEventNotice(event, this.options.missNotices?.() ?? true);
105
+ if (shown !== undefined)
106
+ this.line(`ama: ${shown.text}`, true);
107
+ return;
108
+ }
109
+ case "cache_warm": {
110
+ const text = this.options.info === true ? warmSentNotice(event) : undefined;
111
+ if (text !== undefined)
112
+ this.line(`ama: ${text}`, true);
113
+ return;
114
+ }
115
+ case "agent_settled":
116
+ this.endLine();
117
+ if (event.warning !== undefined)
118
+ this.line(`ama: ${event.warning}`, true);
119
+ return;
120
+ default:
121
+ return;
122
+ }
123
+ }
124
+ }
125
+ /** `pick` 在行式界面:列出候选,提示带参数重发。 */
126
+ export async function pickHint(what, runtime, session) {
127
+ switch (what) {
128
+ case "model": {
129
+ const refs = [];
130
+ for (const provider of runtime.providers.list()) {
131
+ const key = await runtime.providers.resolveApiKey(provider.id);
132
+ if (provider.requiresApiKey && key.apiKey === undefined)
133
+ continue;
134
+ for (const model of provider.models)
135
+ refs.push(` ${provider.id}/${model.id}`);
136
+ }
137
+ return ["可用模型(/model <provider/id>):", ...refs.slice(0, 40)].join("\n");
138
+ }
139
+ case "session": {
140
+ const items = listSessions({ sessionDir: runtime.paths.sessionDir, cwd: session.state.cwd });
141
+ if (items.length === 0)
142
+ return "本目录没有会话";
143
+ return [
144
+ "最近的会话(/resume <id>):",
145
+ ...items
146
+ .slice(0, 15)
147
+ .map((i) => ` ${i.id.slice(0, 8)} ${i.modifiedAt.slice(0, 16).replace("T", " ")} ${(i.name ?? i.firstPrompt ?? "").replace(/\s+/g, " ").slice(0, 50)}`),
148
+ ].join("\n");
149
+ }
150
+ case "tree": {
151
+ const users = session.entries.filter((e) => e.type === "message" && e.message.role === "user");
152
+ if (users.length === 0)
153
+ return "还没有可分叉的消息";
154
+ return [
155
+ "可分叉的位置(/fork <条目 id>):",
156
+ ...users.slice(-15).map((e) => {
157
+ const message = e.type === "message" ? e.message : undefined;
158
+ const content = message?.role === "user" ? message.content : "";
159
+ const text = typeof content === "string"
160
+ ? content
161
+ : content.map((b) => (b.type === "text" ? b.text : "")).join("");
162
+ return ` ${e.id} ${text.replace(/\s+/g, " ").slice(0, 60)}`;
163
+ }),
164
+ ].join("\n");
165
+ }
166
+ case "permission":
167
+ return `权限模式(/permission <模式>):${PERMISSION_MODES_STRICT_FIRST.join(" | ")};当前 ${session.state.permissionMode}`;
168
+ case "thinking":
169
+ return `思考级别(/thinking <级别>):${THINKING_LEVELS.join(" | ")};当前 ${session.state.thinkingLevel}`;
170
+ }
171
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 括号粘贴状态机(`ESC[200~ … ESC[201~`,设计 §11.2 / §12.3):按字符流喂入,跨 chunk 识别起止标记,
3
+ * 产出普通输入与完整粘贴两类片段。与 tui/stdin-buffer.ts 独立实现(行式界面不依赖 TUI 组件库)。[B6]
4
+ *
5
+ * - 标记可能被拆在两个 chunk 之间:末尾若是标记的前缀,先留着等下一块;
6
+ * - 粘贴里的 `\r\n` / `\r` 统一成 `\n`;
7
+ * - `flush()`:输入暂停时把留着的前缀当普通输入交出(例如单独按下的 ESC)。
8
+ */
9
+ export declare const PASTE_START = "\u001B[200~";
10
+ export declare const PASTE_END = "\u001B[201~";
11
+ export type InputPiece = {
12
+ kind: "text";
13
+ text: string;
14
+ } | {
15
+ kind: "paste";
16
+ text: string;
17
+ };
18
+ export declare class PasteState {
19
+ private inPaste;
20
+ private pending;
21
+ private pasted;
22
+ get pasting(): boolean;
23
+ feed(chunk: string): InputPiece[];
24
+ /** 交出留着的标记前缀(粘贴中途则不动:等结束标记)。 */
25
+ flush(): InputPiece[];
26
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * 括号粘贴状态机(`ESC[200~ … ESC[201~`,设计 §11.2 / §12.3):按字符流喂入,跨 chunk 识别起止标记,
3
+ * 产出普通输入与完整粘贴两类片段。与 tui/stdin-buffer.ts 独立实现(行式界面不依赖 TUI 组件库)。[B6]
4
+ *
5
+ * - 标记可能被拆在两个 chunk 之间:末尾若是标记的前缀,先留着等下一块;
6
+ * - 粘贴里的 `\r\n` / `\r` 统一成 `\n`;
7
+ * - `flush()`:输入暂停时把留着的前缀当普通输入交出(例如单独按下的 ESC)。
8
+ */
9
+ export const PASTE_START = "\x1b[200~";
10
+ export const PASTE_END = "\x1b[201~";
11
+ /** `text` 末尾与 `marker` 开头重合的最长长度(不含完整标记)。 */
12
+ function partialSuffix(text, marker) {
13
+ for (let n = Math.min(marker.length - 1, text.length); n > 0; n--) {
14
+ if (text.endsWith(marker.slice(0, n)))
15
+ return n;
16
+ }
17
+ return 0;
18
+ }
19
+ export class PasteState {
20
+ inPaste = false;
21
+ pending = "";
22
+ pasted = "";
23
+ get pasting() {
24
+ return this.inPaste;
25
+ }
26
+ feed(chunk) {
27
+ const out = [];
28
+ let data = this.pending + chunk;
29
+ this.pending = "";
30
+ while (data.length > 0) {
31
+ if (!this.inPaste) {
32
+ const at = data.indexOf(PASTE_START);
33
+ if (at >= 0) {
34
+ if (at > 0)
35
+ out.push({ kind: "text", text: data.slice(0, at) });
36
+ this.inPaste = true;
37
+ this.pasted = "";
38
+ data = data.slice(at + PASTE_START.length);
39
+ continue;
40
+ }
41
+ const keep = partialSuffix(data, PASTE_START);
42
+ const text = data.slice(0, data.length - keep);
43
+ if (text !== "")
44
+ out.push({ kind: "text", text });
45
+ this.pending = data.slice(data.length - keep);
46
+ break;
47
+ }
48
+ const end = data.indexOf(PASTE_END);
49
+ if (end >= 0) {
50
+ this.pasted += data.slice(0, end);
51
+ out.push({ kind: "paste", text: this.pasted.replace(/\r\n?/g, "\n") });
52
+ this.inPaste = false;
53
+ this.pasted = "";
54
+ data = data.slice(end + PASTE_END.length);
55
+ continue;
56
+ }
57
+ const keep = partialSuffix(data, PASTE_END);
58
+ this.pasted += data.slice(0, data.length - keep);
59
+ this.pending = data.slice(data.length - keep);
60
+ break;
61
+ }
62
+ return out;
63
+ }
64
+ /** 交出留着的标记前缀(粘贴中途则不动:等结束标记)。 */
65
+ flush() {
66
+ if (this.inPaste || this.pending === "")
67
+ return [];
68
+ const text = this.pending;
69
+ this.pending = "";
70
+ return [{ kind: "text", text }];
71
+ }
72
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 消息区(设计 §12.5)。[B7]
3
+ *
4
+ * 一个纵向 `Container`,块与块之间空一行:
5
+ * - 用户消息 `› 文本`;steer / followUp / 宿主注入按 `UserMessage.origin` 标 `↳ steer` / `↳ host` …;
6
+ * - 助手消息按内容块渲染:文本走 `Markdown`(`ui.markdown: false` 时纯文本),流式时只改末块
7
+ * (Markdown 自己按块缓存);思考块按 `ui.showThinking`:`collapsed`(缺省)一行 `思考 · N token`、
8
+ * `full` 显示全文、`hidden` 不显示;
9
+ * - 压缩摘要卡、重试提示、Hook 阻止提示、宿主 / 系统通知;
10
+ * - `replay()` 把已有消息(恢复会话、切换会话、/tree 之后)重新画出来,工具调用交给调用方构造。
11
+ *
12
+ * 滚动交给终端回滚:消息区只追加,切换会话时整体清空重画。
13
+ */
14
+ import type { AssistantMessage, ContentBlock, ToolCallBlock, UserMessage } from "../../ai/types.js";
15
+ import type { CompactionResult, ToolResultMessage } from "../../agent/types.js";
16
+ import type { AgentMessage } from "../../session/types.js";
17
+ import { Container, type Component, type Theme } from "../../tui.js";
18
+ export type ThinkingDisplay = "full" | "collapsed" | "hidden";
19
+ export type NoticeLevel = "info" | "warn" | "error";
20
+ export interface MessageViewOptions {
21
+ theme: Theme;
22
+ showThinking?: ThinkingDisplay;
23
+ /** false:助手文本不做 Markdown 渲染。 */
24
+ markdown?: boolean;
25
+ }
26
+ /** 粗估 token(4 字符 ≈ 1 token),只用于思考块的流式计数。 */
27
+ export declare function estimateTokens(text: string): number;
28
+ export declare function contentText(content: string | readonly ContentBlock[]): string;
29
+ /** 一条助手消息:按内容块(思考 / 文本)同步子组件;工具调用块由 tool-view 单独显示。 */
30
+ export declare class AssistantView extends Container {
31
+ private readonly options;
32
+ private readonly parts;
33
+ private readonly status;
34
+ private streaming;
35
+ constructor(options: MessageViewOptions);
36
+ update(message: AssistantMessage): void;
37
+ /** 收尾:错误 / 中断 / 长度截断提示,思考计数换成用量里的值。 */
38
+ finish(message: AssistantMessage): void;
39
+ get isEmpty(): boolean;
40
+ private part;
41
+ }
42
+ export interface ReplayHooks {
43
+ /** 工具调用块 + 配对的结果(可能缺)→ 组件;返回 undefined 不显示。 */
44
+ tool(call: ToolCallBlock, result: ToolResultMessage | undefined): Component | undefined;
45
+ }
46
+ export declare class MessageView extends Container {
47
+ private readonly options;
48
+ private current;
49
+ private readonly blocks;
50
+ constructor(options: MessageViewOptions);
51
+ private get theme();
52
+ /** 追加一块(与上一块之间空一行)。 */
53
+ add(component: Component): void;
54
+ /** 去掉一块(空的助手消息等)。 */
55
+ remove(component: Component): void;
56
+ reset(): void;
57
+ get blockCount(): number;
58
+ /** 启动画面:首行加粗,其余变暗。 */
59
+ addHeader(lines: readonly string[]): void;
60
+ addUser(message: Pick<UserMessage, "content" | "origin">): void;
61
+ startAssistant(message: AssistantMessage): AssistantView;
62
+ updateAssistant(message: AssistantMessage): void;
63
+ endAssistant(message: AssistantMessage): void;
64
+ addNotice(level: NoticeLevel, message: string): void;
65
+ addHookBlocked(reason: string): void;
66
+ addRetry(attempt: number, maxAttempts: number, delayMs: number, error: string): void;
67
+ addRetryFailed(error: string | undefined): void;
68
+ /** 压缩 / 分支摘要卡:token 变化(有则显示)+ 摘要前 3 行。 */
69
+ addSummaryCard(summary: string, options?: {
70
+ title?: string;
71
+ tokensBefore?: number;
72
+ tokensAfter?: number;
73
+ }): void;
74
+ addCompaction(result: Pick<CompactionResult, "summary" | "tokensBefore" | "tokensAfter">): void;
75
+ /** 把已有消息重新画出来。 */
76
+ replay(messages: readonly AgentMessage[], hooks: ReplayHooks): void;
77
+ }
@@ -0,0 +1,269 @@
1
+ /**
2
+ * 消息区(设计 §12.5)。[B7]
3
+ *
4
+ * 一个纵向 `Container`,块与块之间空一行:
5
+ * - 用户消息 `› 文本`;steer / followUp / 宿主注入按 `UserMessage.origin` 标 `↳ steer` / `↳ host` …;
6
+ * - 助手消息按内容块渲染:文本走 `Markdown`(`ui.markdown: false` 时纯文本),流式时只改末块
7
+ * (Markdown 自己按块缓存);思考块按 `ui.showThinking`:`collapsed`(缺省)一行 `思考 · N token`、
8
+ * `full` 显示全文、`hidden` 不显示;
9
+ * - 压缩摘要卡、重试提示、Hook 阻止提示、宿主 / 系统通知;
10
+ * - `replay()` 把已有消息(恢复会话、切换会话、/tree 之后)重新画出来,工具调用交给调用方构造。
11
+ *
12
+ * 滚动交给终端回滚:消息区只追加,切换会话时整体清空重画。
13
+ */
14
+ import { Box, Container, Markdown, Spacer, Text } from "../../tui.js";
15
+ /** 粗估 token(4 字符 ≈ 1 token),只用于思考块的流式计数。 */
16
+ export function estimateTokens(text) {
17
+ return Math.ceil(text.length / 4);
18
+ }
19
+ export function contentText(content) {
20
+ if (typeof content === "string")
21
+ return content;
22
+ return content
23
+ .map((block) => (block.type === "text" ? block.text : block.type === "image" ? "[图片]" : ""))
24
+ .join("");
25
+ }
26
+ /** 一条助手消息:按内容块(思考 / 文本)同步子组件;工具调用块由 tool-view 单独显示。 */
27
+ export class AssistantView extends Container {
28
+ options;
29
+ parts = new Map();
30
+ status = new Text("");
31
+ streaming = true;
32
+ constructor(options) {
33
+ super();
34
+ this.options = options;
35
+ }
36
+ update(message) {
37
+ const { theme } = this.options;
38
+ const display = this.options.showThinking ?? "collapsed";
39
+ const order = [];
40
+ message.content.forEach((block, index) => {
41
+ if (block.type === "toolCall")
42
+ return;
43
+ if (block.type === "thinking") {
44
+ if (display === "hidden" || (block.thinking === "" && block.redacted !== true))
45
+ return;
46
+ const text = this.part(index, () => new Text(""));
47
+ if (display === "full" && block.redacted !== true) {
48
+ text.setText(theme.fg("dim", theme.italic(block.thinking.trim())));
49
+ }
50
+ else {
51
+ const single = message.content.filter((b) => b.type === "thinking").length === 1;
52
+ const tokens = !this.streaming && single && message.usage.reasoning !== undefined
53
+ ? message.usage.reasoning
54
+ : estimateTokens(block.thinking);
55
+ text.setText(theme.fg("dim", `${this.streaming ? "思考…" : "思考 ·"} ${tokens} token`));
56
+ }
57
+ order.push(text);
58
+ return;
59
+ }
60
+ if (block.text.trim() === "")
61
+ return;
62
+ const view = this.part(index, () => this.options.markdown === false ? new Text("") : new Markdown("", { theme }));
63
+ if (view.getText() !== block.text)
64
+ view.setText(block.text);
65
+ order.push(view);
66
+ });
67
+ this.clear();
68
+ order.forEach((child, i) => {
69
+ if (i > 0)
70
+ this.addChild(new Spacer());
71
+ this.addChild(child);
72
+ });
73
+ if (this.status.getText() !== "") {
74
+ if (order.length > 0)
75
+ this.addChild(new Spacer());
76
+ this.addChild(this.status);
77
+ }
78
+ }
79
+ /** 收尾:错误 / 中断 / 长度截断提示,思考计数换成用量里的值。 */
80
+ finish(message) {
81
+ this.streaming = false;
82
+ const { theme } = this.options;
83
+ if (message.stopReason === "error") {
84
+ this.status.setText(theme.fg("error", `✗ ${message.errorMessage ?? "模型调用失败"}`));
85
+ }
86
+ else if (message.stopReason === "aborted") {
87
+ this.status.setText(theme.fg("dim", "已中断"));
88
+ }
89
+ else if (message.stopReason === "length") {
90
+ this.status.setText(theme.fg("warning", "输出达到长度上限"));
91
+ }
92
+ this.update(message);
93
+ }
94
+ get isEmpty() {
95
+ return this.children.length === 0;
96
+ }
97
+ part(index, make) {
98
+ let part = this.parts.get(index);
99
+ if (part === undefined) {
100
+ part = make();
101
+ this.parts.set(index, part);
102
+ }
103
+ return part;
104
+ }
105
+ }
106
+ export class MessageView extends Container {
107
+ options;
108
+ current;
109
+ blocks = [];
110
+ constructor(options) {
111
+ super();
112
+ this.options = options;
113
+ }
114
+ get theme() {
115
+ return this.options.theme;
116
+ }
117
+ /** 追加一块(与上一块之间空一行)。 */
118
+ add(component) {
119
+ if (this.blocks.length > 0)
120
+ this.addChild(new Spacer());
121
+ this.blocks.push(component);
122
+ this.addChild(component);
123
+ }
124
+ /** 去掉一块(空的助手消息等)。 */
125
+ remove(component) {
126
+ const index = this.blocks.indexOf(component);
127
+ if (index === -1)
128
+ return;
129
+ this.blocks.splice(index, 1);
130
+ const at = this.children.indexOf(component);
131
+ if (at === -1)
132
+ return;
133
+ // 连同它前面(首块则是后面)的空行
134
+ if (at > 0 && this.children[at - 1] instanceof Spacer)
135
+ this.children.splice(at - 1, 2);
136
+ else if (this.children[at + 1] instanceof Spacer)
137
+ this.children.splice(at, 2);
138
+ else
139
+ this.children.splice(at, 1);
140
+ }
141
+ reset() {
142
+ this.clear();
143
+ this.blocks.length = 0;
144
+ this.current = undefined;
145
+ }
146
+ get blockCount() {
147
+ return this.blocks.length;
148
+ }
149
+ /** 启动画面:首行加粗,其余变暗。 */
150
+ addHeader(lines) {
151
+ if (lines.length === 0)
152
+ return;
153
+ const [first, ...rest] = lines;
154
+ const text = [this.theme.bold(first ?? ""), ...rest.map((l) => this.theme.fg("dim", l))];
155
+ this.add(new Text(text.join("\n")));
156
+ }
157
+ addUser(message) {
158
+ const text = contentText(message.content).replace(/\s+$/, "");
159
+ const origin = message.origin;
160
+ const label = origin === undefined || origin === "user"
161
+ ? this.theme.fg("user", "› ")
162
+ : this.theme.fg("dim", `↳ ${origin} `);
163
+ this.add(new Text(label + text));
164
+ }
165
+ startAssistant(message) {
166
+ const view = new AssistantView(this.options);
167
+ view.update(message);
168
+ this.current = view;
169
+ this.add(view);
170
+ return view;
171
+ }
172
+ updateAssistant(message) {
173
+ if (this.current === undefined)
174
+ this.startAssistant(message);
175
+ else
176
+ this.current.update(message);
177
+ }
178
+ endAssistant(message) {
179
+ const view = this.current ?? this.startAssistant(message);
180
+ view.finish(message);
181
+ this.current = undefined;
182
+ // 只有工具调用的助手消息不占一块
183
+ if (view.isEmpty)
184
+ this.remove(view);
185
+ }
186
+ addNotice(level, message) {
187
+ const color = level === "error" ? "error" : level === "warn" ? "warning" : "dim";
188
+ const mark = level === "error" ? "✗ " : level === "warn" ? "! " : "";
189
+ this.add(new Text(this.theme.fg(color, mark + message)));
190
+ }
191
+ addHookBlocked(reason) {
192
+ this.add(new Text(this.theme.fg("warning", `⛔ Hook 阻止:${reason}`)));
193
+ }
194
+ addRetry(attempt, maxAttempts, delayMs, error) {
195
+ const seconds = Math.max(1, Math.round(delayMs / 1000));
196
+ this.add(new Text(this.theme.fg("warning", `↻ 重试 ${attempt}/${maxAttempts}(${seconds}s 后):${error}`)));
197
+ }
198
+ addRetryFailed(error) {
199
+ this.addNotice("error", `重试失败${error !== undefined ? `:${error}` : ""}`);
200
+ }
201
+ /** 压缩 / 分支摘要卡:token 变化(有则显示)+ 摘要前 3 行。 */
202
+ addSummaryCard(summary, options = {}) {
203
+ const dim = (s) => this.theme.fg("dim", s);
204
+ const lines = summary
205
+ .trim()
206
+ .split("\n")
207
+ .filter((l) => l.trim() !== "");
208
+ const head = lines.slice(0, 3);
209
+ const body = [];
210
+ if (options.tokensBefore !== undefined) {
211
+ const after = options.tokensAfter !== undefined ? ` → ${options.tokensAfter}` : "";
212
+ body.push(dim(`${options.tokensBefore}${after} token`));
213
+ }
214
+ body.push(...head.map(dim));
215
+ if (lines.length > head.length)
216
+ body.push(dim(`…(另 ${lines.length - head.length} 行)`));
217
+ this.add(new Box(new Text(body.join("\n")), {
218
+ title: options.title ?? "上下文已压缩",
219
+ theme: this.theme,
220
+ }));
221
+ }
222
+ addCompaction(result) {
223
+ this.addSummaryCard(result.summary, {
224
+ tokensBefore: result.tokensBefore,
225
+ ...(result.tokensAfter !== undefined ? { tokensAfter: result.tokensAfter } : {}),
226
+ });
227
+ }
228
+ /** 把已有消息重新画出来。 */
229
+ replay(messages, hooks) {
230
+ const results = new Map();
231
+ for (const message of messages) {
232
+ if (message.role === "toolResult")
233
+ results.set(message.toolCallId, message);
234
+ }
235
+ for (const message of messages) {
236
+ switch (message.role) {
237
+ case "user":
238
+ this.addUser(message);
239
+ break;
240
+ case "assistant": {
241
+ const view = new AssistantView(this.options);
242
+ view.finish(message);
243
+ if (!view.isEmpty)
244
+ this.add(view);
245
+ for (const block of message.content) {
246
+ if (block.type !== "toolCall")
247
+ continue;
248
+ const tool = hooks.tool(block, results.get(block.id));
249
+ if (tool !== undefined)
250
+ this.add(tool);
251
+ }
252
+ break;
253
+ }
254
+ case "compactionSummary":
255
+ this.addSummaryCard(message.summary, { tokensBefore: message.tokensBefore });
256
+ break;
257
+ case "branchSummary":
258
+ this.addSummaryCard(message.summary, { title: "分支摘要" });
259
+ break;
260
+ case "custom":
261
+ if (message.display)
262
+ this.add(new Text(this.theme.fg("dim", contentText(message.content))));
263
+ break;
264
+ default:
265
+ break;
266
+ }
267
+ }
268
+ }
269
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 交互模式里的选择器(设计 §12.6):居中覆盖层 + `SelectList`。[B7]
3
+ *
4
+ * - 模型:按供应商分组并标 key 状态(`modelItems`,与启动期共用),当前模型预选;
5
+ * - 会话:名字或首条提示、相对时间、消息数(`sessionItems`);
6
+ * - 树:会话文件里全部用户消息,按分叉缩进,`●` 标出当前分支;选中项回填编辑器(由 commands.ts 处理);
7
+ * - 权限模式(从严到宽)与思考级别。
8
+ */
9
+ import type { SessionEntry } from "../../session/types.js";
10
+ import { type Keybindings, type OverlayHandle, type OverlayOptions, type SelectItem, type Theme, type Component } from "../../tui.js";
11
+ export { modelItems, sessionItems } from "./startup-ui.js";
12
+ export interface PickerHost {
13
+ theme: Theme;
14
+ keybindings?: Keybindings;
15
+ showOverlay(component: Component, options: OverlayOptions): OverlayHandle;
16
+ /** 终端列数(决定覆盖层宽度)。 */
17
+ columns(): number;
18
+ }
19
+ export interface PickerSpec {
20
+ title: string;
21
+ items: readonly SelectItem[];
22
+ selected?: string;
23
+ filterable?: boolean;
24
+ maxVisible?: number;
25
+ emptyText?: string;
26
+ }
27
+ /** 打开一个居中选择器;Enter 返回选中项,Esc / Ctrl+C 返回 undefined。 */
28
+ export declare function openPicker(host: PickerHost, spec: PickerSpec): Promise<SelectItem | undefined>;
29
+ export declare function permissionItems(): SelectItem[];
30
+ export declare function thinkingItems(reasoning: boolean): SelectItem[];
31
+ export interface TreeNode {
32
+ entry: SessionEntry;
33
+ text: string;
34
+ children: TreeNode[];
35
+ }
36
+ /** 只保留用户消息的树:每条用户消息挂到最近的用户消息祖先下。 */
37
+ export declare function userMessageTree(entries: readonly SessionEntry[]): TreeNode[];
38
+ /**
39
+ * 树 → 列表项:分叉处子分支缩进一级,单链保持同级;`●` 在当前分支上、`○` 不在。
40
+ * value 是用户消息条目的 id。
41
+ */
42
+ export declare function treeItems(entries: readonly SessionEntry[], activeIds: ReadonlySet<string>, now: number): SelectItem[];