@armadra/agent 0.4.0 → 0.5.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 (432) hide show
  1. package/CHANGELOG.md +184 -1
  2. package/README.md +202 -83
  3. package/THIRD_PARTY_NOTICES.md +34 -0
  4. package/dist/acp.d.ts +18 -0
  5. package/dist/acp.js +19 -0
  6. package/dist/agent/limits.d.ts +38 -0
  7. package/dist/agent/limits.js +127 -0
  8. package/dist/agent/loop-guard.d.ts +38 -0
  9. package/dist/agent/loop-guard.js +95 -0
  10. package/dist/agent/loop.d.ts +4 -0
  11. package/dist/agent/loop.js +7 -0
  12. package/dist/agent/prompt-rules.d.ts +12 -0
  13. package/dist/agent/prompt-rules.js +18 -0
  14. package/dist/agent/reminders.d.ts +59 -0
  15. package/dist/agent/reminders.js +327 -0
  16. package/dist/agent/session-cache.d.ts +5 -0
  17. package/dist/agent/session-cache.js +17 -1
  18. package/dist/agent/session-classifier.d.ts +1 -1
  19. package/dist/agent/session-classifier.js +3 -1
  20. package/dist/agent/session-compaction.d.ts +35 -6
  21. package/dist/agent/session-compaction.js +157 -31
  22. package/dist/agent/session-core.d.ts +27 -1
  23. package/dist/agent/session-extensions.d.ts +61 -0
  24. package/dist/agent/session-extensions.js +129 -0
  25. package/dist/agent/session-images.d.ts +23 -0
  26. package/dist/agent/session-images.js +97 -0
  27. package/dist/agent/session-plan.d.ts +21 -0
  28. package/dist/agent/session-plan.js +524 -0
  29. package/dist/agent/session-rewind.d.ts +81 -0
  30. package/dist/agent/session-rewind.js +354 -0
  31. package/dist/agent/session-run.d.ts +28 -1
  32. package/dist/agent/session-run.js +157 -6
  33. package/dist/agent/session-settings.d.ts +49 -0
  34. package/dist/agent/session-settings.js +95 -0
  35. package/dist/agent/session-subagent.d.ts +36 -24
  36. package/dist/agent/session-subagent.js +259 -129
  37. package/dist/agent/session-sync.d.ts +10 -3
  38. package/dist/agent/session-sync.js +37 -8
  39. package/dist/agent/session-telemetry.d.ts +39 -0
  40. package/dist/agent/session-telemetry.js +231 -0
  41. package/dist/agent/session-tools.js +40 -1
  42. package/dist/agent/session.d.ts +30 -29
  43. package/dist/agent/session.js +79 -123
  44. package/dist/agent/subagent-registry.d.ts +91 -0
  45. package/dist/agent/subagent-registry.js +494 -0
  46. package/dist/agent/system-prompt.d.ts +6 -3
  47. package/dist/agent/system-prompt.js +7 -2
  48. package/dist/agent/tool-runner.d.ts +8 -1
  49. package/dist/agent/tool-runner.js +57 -4
  50. package/dist/agent/types-w5.d.ts +192 -0
  51. package/dist/agent/types-w5.js +10 -0
  52. package/dist/agent/types.d.ts +47 -4
  53. package/dist/agent/types.js +2 -0
  54. package/dist/agent/worktree.d.ts +36 -0
  55. package/dist/agent/worktree.js +86 -0
  56. package/dist/agents/builtin.d.ts +14 -0
  57. package/dist/agents/builtin.js +39 -0
  58. package/dist/agents/catalog.d.ts +43 -0
  59. package/dist/agents/catalog.js +82 -0
  60. package/dist/agents/discover.d.ts +35 -0
  61. package/dist/agents/discover.js +83 -0
  62. package/dist/agents/external.d.ts +82 -0
  63. package/dist/agents/external.js +326 -0
  64. package/dist/agents/parse.d.ts +18 -0
  65. package/dist/agents/parse.js +116 -0
  66. package/dist/agents/result.d.ts +32 -0
  67. package/dist/agents/result.js +67 -0
  68. package/dist/agents/task-control.d.ts +17 -0
  69. package/dist/agents/task-control.js +17 -0
  70. package/dist/agents/task-record.d.ts +78 -0
  71. package/dist/agents/task-record.js +141 -0
  72. package/dist/agents/types.d.ts +46 -0
  73. package/dist/agents/types.js +8 -0
  74. package/dist/ai/apis/anthropic-compat.d.ts +36 -0
  75. package/dist/ai/apis/anthropic-compat.js +92 -0
  76. package/dist/ai/apis/anthropic-request.d.ts +7 -6
  77. package/dist/ai/apis/anthropic-request.js +10 -28
  78. package/dist/ai/apis/cache-params.d.ts +11 -2
  79. package/dist/ai/apis/cache-params.js +25 -6
  80. package/dist/ai/apis/openai-compat.d.ts +4 -1
  81. package/dist/ai/apis/openai-compat.js +8 -1
  82. package/dist/ai/image-limits.d.ts +45 -0
  83. package/dist/ai/image-limits.js +68 -0
  84. package/dist/ai/providers/builtin.d.ts +18 -6
  85. package/dist/ai/providers/builtin.js +135 -6
  86. package/dist/ai/providers/catalog-data.js +17 -13
  87. package/dist/ai/providers/catalog.d.ts +60 -7
  88. package/dist/ai/providers/catalog.js +197 -38
  89. package/dist/ai/providers/channels.d.ts +33 -3
  90. package/dist/ai/providers/channels.js +108 -7
  91. package/dist/ai/providers/enrich.d.ts +6 -3
  92. package/dist/ai/providers/enrich.js +19 -4
  93. package/dist/ai/providers/models-dev-cache.d.ts +28 -24
  94. package/dist/ai/providers/models-dev-cache.js +92 -78
  95. package/dist/ai/providers/models-dev-data.d.ts +7 -0
  96. package/dist/ai/providers/models-dev-data.js +32 -0
  97. package/dist/ai/providers/models-dev-snapshot.d.ts +50 -0
  98. package/dist/ai/providers/models-dev-snapshot.js +137 -0
  99. package/dist/ai/providers/models-dev.d.ts +51 -10
  100. package/dist/ai/providers/models-dev.js +132 -39
  101. package/dist/ai/providers/registry.d.ts +20 -10
  102. package/dist/ai/providers/registry.js +82 -43
  103. package/dist/ai/types.d.ts +28 -2
  104. package/dist/ai/types.js +5 -0
  105. package/dist/bundle/ama.cjs +37605 -21619
  106. package/dist/checkpoints/backend.d.ts +61 -0
  107. package/dist/checkpoints/backend.js +135 -0
  108. package/dist/checkpoints/blobs.d.ts +60 -0
  109. package/dist/checkpoints/blobs.js +203 -0
  110. package/dist/checkpoints/gc.d.ts +47 -0
  111. package/dist/checkpoints/gc.js +144 -0
  112. package/dist/checkpoints/git-head.d.ts +15 -0
  113. package/dist/checkpoints/git-head.js +89 -0
  114. package/dist/checkpoints/index.d.ts +17 -0
  115. package/dist/checkpoints/index.js +17 -0
  116. package/dist/checkpoints/replay.d.ts +48 -0
  117. package/dist/checkpoints/replay.js +154 -0
  118. package/dist/checkpoints/restore.d.ts +86 -0
  119. package/dist/checkpoints/restore.js +263 -0
  120. package/dist/checkpoints/settings.d.ts +14 -0
  121. package/dist/checkpoints/settings.js +23 -0
  122. package/dist/checkpoints/shadow-git.d.ts +108 -0
  123. package/dist/checkpoints/shadow-git.js +491 -0
  124. package/dist/checkpoints/shadow-restore.d.ts +25 -0
  125. package/dist/checkpoints/shadow-restore.js +128 -0
  126. package/dist/checkpoints/tracker.d.ts +64 -0
  127. package/dist/checkpoints/tracker.js +192 -0
  128. package/dist/checkpoints/types.d.ts +99 -0
  129. package/dist/checkpoints/types.js +12 -0
  130. package/dist/cli/args.d.ts +8 -4
  131. package/dist/cli/args.js +17 -97
  132. package/dist/cli/bootstrap.js +13 -1
  133. package/dist/cli/choice-prompt.d.ts +58 -0
  134. package/dist/cli/choice-prompt.js +140 -0
  135. package/dist/cli/codemode-notice.d.ts +3 -2
  136. package/dist/cli/codemode-notice.js +3 -2
  137. package/dist/cli/compose-agents.d.ts +27 -0
  138. package/dist/cli/compose-agents.js +111 -0
  139. package/dist/cli/compose-extensions.d.ts +29 -0
  140. package/dist/cli/compose-extensions.js +44 -0
  141. package/dist/cli/compose-session.d.ts +3 -0
  142. package/dist/cli/compose-session.js +47 -4
  143. package/dist/cli/compose.d.ts +3 -0
  144. package/dist/cli/compose.js +33 -6
  145. package/dist/cli/deps.d.ts +6 -0
  146. package/dist/cli/exit-codes.d.ts +10 -0
  147. package/dist/cli/exit-codes.js +12 -0
  148. package/dist/cli/help-text.d.ts +4 -0
  149. package/dist/cli/help-text.js +102 -0
  150. package/dist/cli/startup-screen.js +5 -3
  151. package/dist/cli/startup-steps.js +7 -1
  152. package/dist/cli/subcommands/config.d.ts +4 -1
  153. package/dist/cli/subcommands/config.js +17 -6
  154. package/dist/cli/subcommands/doctor.js +21 -0
  155. package/dist/cli/subcommands/models-cache-probe.d.ts +1 -1
  156. package/dist/cli/subcommands/models-cache-probe.js +3 -12
  157. package/dist/cli/subcommands/models-discover.js +5 -6
  158. package/dist/cli/subcommands/models.d.ts +2 -0
  159. package/dist/cli/subcommands/models.js +33 -14
  160. package/dist/cli/subcommands/providers.d.ts +1 -1
  161. package/dist/cli/subcommands/providers.js +8 -15
  162. package/dist/cli/subcommands/sessions.d.ts +3 -1
  163. package/dist/cli/subcommands/sessions.js +36 -2
  164. package/dist/codemode/capability.d.ts +24 -7
  165. package/dist/codemode/capability.js +38 -10
  166. package/dist/codemode/host-side.d.ts +14 -0
  167. package/dist/codemode/host-side.js +24 -1
  168. package/dist/codemode/tool.d.ts +1 -1
  169. package/dist/codemode/tool.js +6 -4
  170. package/dist/compaction/breaker.d.ts +25 -9
  171. package/dist/compaction/breaker.js +47 -17
  172. package/dist/compaction/estimate.d.ts +11 -3
  173. package/dist/compaction/estimate.js +64 -22
  174. package/dist/compaction/image-budget.d.ts +45 -0
  175. package/dist/compaction/image-budget.js +115 -0
  176. package/dist/compaction/post-compact.d.ts +38 -0
  177. package/dist/compaction/post-compact.js +152 -0
  178. package/dist/compaction/protect.d.ts +27 -0
  179. package/dist/compaction/protect.js +56 -0
  180. package/dist/compaction/prune-tier.d.ts +58 -12
  181. package/dist/compaction/prune-tier.js +120 -54
  182. package/dist/compaction/summarize-tier.d.ts +23 -4
  183. package/dist/compaction/summarize-tier.js +100 -29
  184. package/dist/config/checker.d.ts +28 -0
  185. package/dist/config/checker.js +97 -0
  186. package/dist/config/json-schema.js +63 -2
  187. package/dist/config/key-docs.js +72 -3
  188. package/dist/config/merge.d.ts +5 -3
  189. package/dist/config/merge.js +81 -7
  190. package/dist/config/profile.d.ts +2 -0
  191. package/dist/config/profile.js +2 -0
  192. package/dist/config/schema-w5.d.ts +18 -0
  193. package/dist/config/schema-w5.js +109 -0
  194. package/dist/config/schema.d.ts +5 -9
  195. package/dist/config/schema.js +68 -114
  196. package/dist/config/types-w5.d.ts +118 -0
  197. package/dist/config/types-w5.js +20 -0
  198. package/dist/config/types.d.ts +50 -1
  199. package/dist/config/types.js +12 -0
  200. package/dist/drivers/acp/client.d.ts +57 -0
  201. package/dist/drivers/acp/client.js +160 -0
  202. package/dist/drivers/acp/driver.d.ts +31 -0
  203. package/dist/drivers/acp/driver.js +274 -0
  204. package/dist/drivers/acp/testing/fake-agent-main.d.ts +5 -0
  205. package/dist/drivers/acp/testing/fake-agent-main.js +7 -0
  206. package/dist/drivers/acp/testing/fake-agent.d.ts +27 -0
  207. package/dist/drivers/acp/testing/fake-agent.js +211 -0
  208. package/dist/drivers/acp/types.d.ts +285 -0
  209. package/dist/drivers/acp/types.js +36 -0
  210. package/dist/drivers/agents.d.ts +58 -0
  211. package/dist/drivers/agents.js +142 -0
  212. package/dist/drivers/base.d.ts +35 -0
  213. package/dist/drivers/base.js +59 -0
  214. package/dist/drivers/catalog.d.ts +46 -0
  215. package/dist/drivers/catalog.js +167 -0
  216. package/dist/drivers/env.d.ts +20 -0
  217. package/dist/drivers/env.js +46 -0
  218. package/dist/drivers/host-runners.d.ts +18 -0
  219. package/dist/drivers/host-runners.js +45 -0
  220. package/dist/drivers/jsonrpc.d.ts +58 -0
  221. package/dist/drivers/jsonrpc.js +180 -0
  222. package/dist/drivers/native/claude-normalize.d.ts +46 -0
  223. package/dist/drivers/native/claude-normalize.js +106 -0
  224. package/dist/drivers/native/claude-stream.d.ts +29 -0
  225. package/dist/drivers/native/claude-stream.js +456 -0
  226. package/dist/drivers/native/codex-app-server.d.ts +26 -0
  227. package/dist/drivers/native/codex-app-server.js +407 -0
  228. package/dist/drivers/native/codex-normalize.d.ts +38 -0
  229. package/dist/drivers/native/codex-normalize.js +124 -0
  230. package/dist/drivers/native/oneshot.d.ts +28 -0
  231. package/dist/drivers/native/oneshot.js +312 -0
  232. package/dist/drivers/permissions.d.ts +40 -0
  233. package/dist/drivers/permissions.js +92 -0
  234. package/dist/drivers/pids.d.ts +32 -0
  235. package/dist/drivers/pids.js +93 -0
  236. package/dist/drivers/pool.d.ts +28 -0
  237. package/dist/drivers/pool.js +96 -0
  238. package/dist/drivers/probe.d.ts +47 -0
  239. package/dist/drivers/probe.js +172 -0
  240. package/dist/drivers/process.d.ts +48 -0
  241. package/dist/drivers/process.js +100 -0
  242. package/dist/drivers/runner.d.ts +61 -0
  243. package/dist/drivers/runner.js +388 -0
  244. package/dist/drivers/store.d.ts +49 -0
  245. package/dist/drivers/store.js +91 -0
  246. package/dist/drivers/turn.d.ts +41 -0
  247. package/dist/drivers/turn.js +99 -0
  248. package/dist/drivers/types.d.ts +122 -0
  249. package/dist/drivers/types.js +11 -0
  250. package/dist/git/info.d.ts +75 -0
  251. package/dist/git/info.js +191 -0
  252. package/dist/hooks/protocol.js +6 -1
  253. package/dist/hooks/types.d.ts +10 -1
  254. package/dist/hooks/types.js +2 -0
  255. package/dist/host/api-impl.d.ts +7 -0
  256. package/dist/host/api-impl.js +15 -0
  257. package/dist/host/types.d.ts +24 -2
  258. package/dist/host/types.js +4 -0
  259. package/dist/index.d.ts +3 -0
  260. package/dist/index.js +1 -0
  261. package/dist/modes/acp/acp-events.d.ts +46 -0
  262. package/dist/modes/acp/acp-events.js +233 -0
  263. package/dist/modes/acp/acp-mode.d.ts +17 -0
  264. package/dist/modes/acp/acp-mode.js +47 -0
  265. package/dist/modes/acp/acp-server.d.ts +79 -0
  266. package/dist/modes/acp/acp-server.js +315 -0
  267. package/dist/modes/commands-core.d.ts +13 -1
  268. package/dist/modes/commands-core.js +64 -0
  269. package/dist/modes/image-input.d.ts +12 -4
  270. package/dist/modes/image-input.js +22 -5
  271. package/dist/modes/interactive/agent-panels.d.ts +14 -0
  272. package/dist/modes/interactive/agent-panels.js +103 -0
  273. package/dist/modes/interactive/agent-ui.d.ts +73 -0
  274. package/dist/modes/interactive/agent-ui.js +234 -0
  275. package/dist/modes/interactive/approval-dialog.d.ts +21 -4
  276. package/dist/modes/interactive/approval-dialog.js +98 -20
  277. package/dist/modes/interactive/approval-merge.d.ts +41 -0
  278. package/dist/modes/interactive/approval-merge.js +89 -0
  279. package/dist/modes/interactive/clipboard-paste.d.ts +18 -0
  280. package/dist/modes/interactive/clipboard-paste.js +28 -0
  281. package/dist/modes/interactive/commands.d.ts +12 -1
  282. package/dist/modes/interactive/commands.js +31 -1
  283. package/dist/modes/interactive/confirm-dialog.d.ts +49 -0
  284. package/dist/modes/interactive/confirm-dialog.js +121 -0
  285. package/dist/modes/interactive/double-esc.d.ts +20 -0
  286. package/dist/modes/interactive/double-esc.js +34 -0
  287. package/dist/modes/interactive/event-notices.d.ts +32 -0
  288. package/dist/modes/interactive/event-notices.js +79 -0
  289. package/dist/modes/interactive/external-editor.d.ts +16 -0
  290. package/dist/modes/interactive/external-editor.js +39 -0
  291. package/dist/modes/interactive/interactive-mode.d.ts +14 -1
  292. package/dist/modes/interactive/interactive-mode.js +140 -136
  293. package/dist/modes/interactive/key-dispatch.d.ts +22 -2
  294. package/dist/modes/interactive/key-dispatch.js +66 -7
  295. package/dist/modes/interactive/line/line-mode.d.ts +4 -1
  296. package/dist/modes/interactive/line/line-mode.js +31 -5
  297. package/dist/modes/interactive/line/line-render.d.ts +2 -1
  298. package/dist/modes/interactive/line/line-render.js +40 -4
  299. package/dist/modes/interactive/message-view.d.ts +1 -1
  300. package/dist/modes/interactive/message-view.js +24 -2
  301. package/dist/modes/interactive/panels.d.ts +18 -1
  302. package/dist/modes/interactive/panels.js +19 -5
  303. package/dist/modes/interactive/plan-command.d.ts +40 -0
  304. package/dist/modes/interactive/plan-command.js +129 -0
  305. package/dist/modes/interactive/plan-dialog.d.ts +98 -0
  306. package/dist/modes/interactive/plan-dialog.js +290 -0
  307. package/dist/modes/interactive/plan-flow.d.ts +34 -0
  308. package/dist/modes/interactive/plan-flow.js +98 -0
  309. package/dist/modes/interactive/rewind-command.d.ts +31 -0
  310. package/dist/modes/interactive/rewind-command.js +106 -0
  311. package/dist/modes/interactive/rewind-flow.d.ts +37 -0
  312. package/dist/modes/interactive/rewind-flow.js +151 -0
  313. package/dist/modes/interactive/rewind-list.d.ts +36 -0
  314. package/dist/modes/interactive/rewind-list.js +123 -0
  315. package/dist/modes/interactive/rewind-panel.d.ts +77 -0
  316. package/dist/modes/interactive/rewind-panel.js +309 -0
  317. package/dist/modes/interactive/rewind-text.d.ts +40 -0
  318. package/dist/modes/interactive/rewind-text.js +139 -0
  319. package/dist/modes/interactive/session-events.d.ts +24 -0
  320. package/dist/modes/interactive/session-events.js +96 -0
  321. package/dist/modes/interactive/startup-ui.js +5 -1
  322. package/dist/modes/interactive/status-area.d.ts +73 -0
  323. package/dist/modes/interactive/status-area.js +190 -0
  324. package/dist/modes/interactive/status-bar.d.ts +99 -11
  325. package/dist/modes/interactive/status-bar.js +273 -90
  326. package/dist/modes/interactive/status-line.d.ts +33 -0
  327. package/dist/modes/interactive/status-line.js +113 -0
  328. package/dist/modes/interactive/subagent-view.d.ts +56 -0
  329. package/dist/modes/interactive/subagent-view.js +154 -0
  330. package/dist/modes/interactive/tasks-report.d.ts +27 -0
  331. package/dist/modes/interactive/tasks-report.js +114 -0
  332. package/dist/modes/interactive/tool-view.d.ts +12 -2
  333. package/dist/modes/interactive/tool-view.js +34 -4
  334. package/dist/modes/print/print-mode.d.ts +13 -4
  335. package/dist/modes/print/print-mode.js +56 -12
  336. package/dist/modes/rpc/commands.d.ts +9 -0
  337. package/dist/modes/rpc/commands.js +44 -0
  338. package/dist/modes/rpc/rpc-mode.js +7 -1
  339. package/dist/modes/session-report.d.ts +11 -0
  340. package/dist/modes/session-report.js +48 -1
  341. package/dist/permissions/auto-safe.d.ts +5 -0
  342. package/dist/permissions/auto-safe.js +18 -0
  343. package/dist/permissions/bypass.d.ts +32 -0
  344. package/dist/permissions/bypass.js +36 -0
  345. package/dist/permissions/classifier.d.ts +4 -0
  346. package/dist/permissions/classifier.js +2 -0
  347. package/dist/permissions/modes.js +1 -1
  348. package/dist/permissions/pipeline.d.ts +43 -4
  349. package/dist/permissions/pipeline.js +114 -9
  350. package/dist/permissions/readonly-bash.d.ts +30 -0
  351. package/dist/permissions/readonly-bash.js +118 -0
  352. package/dist/permissions/types.d.ts +42 -0
  353. package/dist/plan/compose.d.ts +9 -0
  354. package/dist/plan/compose.js +19 -0
  355. package/dist/plan/controller.d.ts +71 -0
  356. package/dist/plan/controller.js +27 -0
  357. package/dist/plan/done-markers.d.ts +17 -0
  358. package/dist/plan/done-markers.js +51 -0
  359. package/dist/plan/extract.d.ts +29 -0
  360. package/dist/plan/extract.js +160 -0
  361. package/dist/plan/prompts.d.ts +36 -0
  362. package/dist/plan/prompts.js +89 -0
  363. package/dist/plan/store.d.ts +37 -0
  364. package/dist/plan/store.js +90 -0
  365. package/dist/rpc.d.ts +58 -2
  366. package/dist/rpc.js +7 -0
  367. package/dist/sandbox/bash.d.ts +74 -0
  368. package/dist/sandbox/bash.js +126 -0
  369. package/dist/sandbox/detect.d.ts +48 -0
  370. package/dist/sandbox/detect.js +149 -0
  371. package/dist/sandbox/index.d.ts +5 -0
  372. package/dist/sandbox/index.js +5 -0
  373. package/dist/sandbox/profile.d.ts +33 -0
  374. package/dist/sandbox/profile.js +76 -0
  375. package/dist/sandbox/wrap.d.ts +29 -0
  376. package/dist/sandbox/wrap.js +117 -0
  377. package/dist/sdk.d.ts +33 -2
  378. package/dist/sdk.js +24 -2
  379. package/dist/session/types.d.ts +3 -1
  380. package/dist/session/types.js +1 -0
  381. package/dist/skills/builtin.d.ts +31 -0
  382. package/dist/skills/builtin.js +109 -0
  383. package/dist/skills/discover.d.ts +2 -1
  384. package/dist/skills/index-prompt.js +5 -8
  385. package/dist/tools/background-jobs.d.ts +71 -0
  386. package/dist/tools/background-jobs.js +198 -0
  387. package/dist/tools/bash.d.ts +53 -1
  388. package/dist/tools/bash.js +158 -7
  389. package/dist/tools/clipboard-image.d.ts +44 -0
  390. package/dist/tools/clipboard-image.js +148 -0
  391. package/dist/tools/edit.d.ts +2 -0
  392. package/dist/tools/edit.js +18 -3
  393. package/dist/tools/image-file.d.ts +38 -3
  394. package/dist/tools/image-file.js +81 -10
  395. package/dist/tools/image-resize.d.ts +58 -0
  396. package/dist/tools/image-resize.js +97 -0
  397. package/dist/tools/presets.d.ts +7 -2
  398. package/dist/tools/presets.js +11 -4
  399. package/dist/tools/read.d.ts +5 -2
  400. package/dist/tools/read.js +13 -10
  401. package/dist/tools/registry.d.ts +7 -1
  402. package/dist/tools/registry.js +27 -4
  403. package/dist/tools/task-ctl.d.ts +22 -0
  404. package/dist/tools/task-ctl.js +116 -0
  405. package/dist/tools/task.d.ts +22 -10
  406. package/dist/tools/task.js +66 -26
  407. package/dist/tools/todo.d.ts +22 -2
  408. package/dist/tools/todo.js +58 -14
  409. package/dist/tools/truncate.d.ts +15 -0
  410. package/dist/tools/truncate.js +25 -0
  411. package/dist/tools/types.d.ts +113 -0
  412. package/dist/tools/types.js +5 -0
  413. package/dist/tools/write.js +3 -0
  414. package/dist/tui/glyphs.d.ts +4 -0
  415. package/dist/tui/glyphs.js +4 -0
  416. package/dist/tui/keybindings.d.ts +6 -1
  417. package/dist/tui/keybindings.js +6 -1
  418. package/docs/acp.md +78 -0
  419. package/docs/agents.md +250 -0
  420. package/docs/codemode.md +12 -8
  421. package/docs/hooks.md +26 -22
  422. package/docs/permissions.md +78 -13
  423. package/docs/plan.md +107 -0
  424. package/docs/providers.md +228 -52
  425. package/docs/rewind-plan.md +181 -0
  426. package/docs/rpc.md +92 -5
  427. package/docs/sandbox.md +215 -0
  428. package/docs/session-format.md +41 -21
  429. package/docs/sessions.md +41 -1
  430. package/docs/tui-design.md +751 -0
  431. package/docs/tui.md +179 -21
  432. package/package.json +13 -2
@@ -1,20 +1,30 @@
1
1
  /**
2
- * 状态栏(设计 §12.6;终端界面视觉设计 v1 §3.10):编辑器下方一行,永远是最后一行。[B7]
2
+ * 状态栏(设计 §12.6;终端界面视觉设计 v1 §3.10;第五波 §1):编辑器下方,永远是最后一行。[B7][W5-A]
3
3
  *
4
- * 两区:左区「权限模式 · shift+tab 切换」,右区用量,中间用空格撑开;空隙不足 4 列时退回单区(` · ` 连接)。
5
- * `Accept edits · shift+tab 切换 sonnet-4-5 · medium · ↑12.3k ↓1.2k · cache 80% ♨ · $0.12 · ctx 34%`
4
+ * 两区:左区「权限模式 + shift+tab 切换」,右区信息,中间用空格撑开;空隙不足 4 列时退回单区。模式永远在最左;
5
+ * 同组项(模型与思考级别、目录与 git)以空格相连。compact 分隔符固定为 ` · `(tmux 宿主按此解析),full 按用户
6
+ * 样例用 ` | `。
6
7
  *
7
- * - 右区顺序固定:模型 · 思考级别(只显示值,off 不显示)· `↑` 输入(含缓存读写)`↓` 输出 · cache · 费用 ·
8
- * rebill · ctx · queue · codemode · 预设(非 default 才显示)· [宿主状态];分隔符固定为 ` · `,
9
- * 模式永远在最左(tmux 宿主可按此解析)。
10
- * - 用量来自 `session.getStats()`,只在 `refresh()` 时读取(事件驱动),渲染只拼字符串;
8
+ * 两种布局(`ui.statusLine`,§1.1):
9
+ * - `compact`(嵌入宿主缺省,即原单行状态栏):
10
+ * `Manual · shift+tab 切换 sonnet-4-5 · medium · ↑12.3k ↓1.2k · cache 80% ♨ · $0.12 · ctx 34% · proj ⎇ main 5ae9e54 +12 −3 · 2h24m`
11
+ * 右区顺序:模型 · 思考 · `↑ ↓` · cache · 费用 · rebill · ctx · git · 时长 · queue · codemode · 预设 · [宿主]。
12
+ * - `full`(独立终端缺省,用户样例):用量类项移到上方速率行(status-line.ts),本行为
13
+ * `Manual | 模型 思考 | Ctx 3.0% | 目录 ⎇ 分支 短提交 (+a,-d) | $费用 | 会话时长`;Ctx 一位小数、不换余量表。
14
+ *
15
+ * - 用量来自 `session.getStats()`,只在 `refresh()` 时读取(事件驱动),渲染只拼字符串;时长按渲染时刻算。
11
16
  * - [W3-C2] `cache` 是**最近一次**请求的命中率;端点三态 `cache —` / `cache 未报告`;保温中追加 `♨`;
12
- * 重计费 `rebill $x`(无价模型显示 token);codemode 生效时 `codemode on|only`,网络未隔离追加 `net!`;
17
+ * 重计费 `rebill $x`(无价模型显示 token);codemode 生效时 `codemode on|only`,网络未隔离追加 `net!`
18
+ * (S2:Node 权限模型与 OS 沙箱都不隔离网络时)。
19
+ * - 费用 = `stats.cost` + 外部 Agent 以美元计的用量(`stats.external`,W5-E;其它单位只在 /session 显示)。
13
20
  * - 着色:整行 dim;模式名正文色(Bypass permissions warning、Plan accent);模型 accent;ctx 按阈值
14
21
  * success / warning / error;rebill、queue warning;`net!` error。宽 ≥ 110 时 ctx 用余量表。
22
+ * - [W5-U] bash 在操作系统沙箱里跑时(S2)用量类项多一个 `沙箱`(与 codemode 同一丢弃优先级)。
23
+ * - [W5-U] 模型回退中(`model_fallback`)模型项显示 `主模型 → 回退模型`,切回主模型后恢复。
15
24
  * - 模型名缩写:宽 < 100 去掉供应商前缀,< 60 再去掉 `@渠道`,< 48 去掉版本后缀(第一个 `-数字` 起)。
16
- * - 宽度不够时按优先级丢弃(数字大的先丢):切换提示 12 → 宿主 11 → 预设 10 → rebill 9 → 费用 8 →
17
- * cache 7 → token 6 → 思考 5 → 队列 4 → codemode 3 → ctx 1 → 模型 0;模式永不丢。
25
+ * - ASCII:`⎇` → `git`(字形表 `branch`)、`−` → `-`、`♨` → `~`。
26
+ * - 宽度不够时按优先级丢弃(数字大的先丢,表见 §1.2);会变的数字按最宽形状占位(`reserve`),
27
+ * 数字变化不会让某项时有时无(40 列不抖动)。
18
28
  */
19
29
  import { permissionModeLabel } from "../../permissions/modes.js";
20
30
  import { formatModelRef } from "../../ai/providers/channels.js";
@@ -33,6 +43,16 @@ export function formatCost(cost) {
33
43
  return `$${cost.toFixed(2)}`;
34
44
  return `$${cost.toFixed(3)}`;
35
45
  }
46
+ /** 会话时长:`Ns` / `Nm` / `NhMm`。 */
47
+ export function formatDuration(ms) {
48
+ const seconds = Math.max(0, Math.floor(ms / 1000));
49
+ if (seconds < 60)
50
+ return `${seconds}s`;
51
+ const minutes = Math.floor(seconds / 60);
52
+ if (minutes < 60)
53
+ return `${minutes}m`;
54
+ return `${Math.floor(minutes / 60)}h${minutes % 60}m`;
55
+ }
36
56
  /** `cache 83%` / `cache —` / `cache 未报告`,保温计时中追加 ` ♨`。 */
37
57
  export function cacheText(cache, warm = "♨") {
38
58
  const rate = cache.lastHitRate ?? cache.hitRate;
@@ -43,10 +63,74 @@ export function cacheText(cache, warm = "♨") {
43
63
  : "—";
44
64
  return `cache ${value}${cache.warming.state === "scheduled" ? ` ${warm}` : ""}`;
45
65
  }
66
+ /** 状态费用:会话费用 + 外部 Agent 以美元计的用量;会话费用未知为 undefined。 */
67
+ export function statusCost(stats) {
68
+ if (stats.cost === undefined)
69
+ return undefined;
70
+ let external = 0;
71
+ for (const usage of Object.values(stats.external?.byAgent ?? {})) {
72
+ if (usage.unit === "usd")
73
+ external += usage.amount;
74
+ }
75
+ return stats.cost + external;
76
+ }
46
77
  /** 两区之间至少留的空格;不足时退回单区。 */
47
78
  const MIN_GAP = 4;
48
79
  /** ctx 改用余量表的最小宽度。 */
49
80
  const METER_WIDTH = 110;
81
+ /** 按优先级丢弃后排成一行(两区 / 单区);连接符都是 dim。 */
82
+ export function layoutRow(parts, width, theme, style = {}) {
83
+ const sep = style.sep ?? " · ";
84
+ const dim = (text) => (text === "" ? "" : theme.fg("dim", text));
85
+ /** 返回 [文本, 占位宽度]。 */
86
+ const join = (list) => {
87
+ let out = "";
88
+ let size = 0;
89
+ const add = (text, measured = visibleWidth(text)) => {
90
+ out += text;
91
+ size += measured;
92
+ };
93
+ list.forEach((p, i) => {
94
+ const prev = i === 0 ? undefined : list[i - 1];
95
+ const group = p.group === undefined ? undefined : style.groups?.[p.group];
96
+ const continues = prev !== undefined && p.group !== undefined && p.group === prev.group;
97
+ if (continues)
98
+ add(dim(group?.glue ?? " "));
99
+ else {
100
+ if (prev !== undefined) {
101
+ const prevGroup = prev.group === undefined ? undefined : style.groups?.[prev.group];
102
+ add(dim(prevGroup?.close ?? ""));
103
+ add(dim(p.lead ?? sep));
104
+ }
105
+ add(dim(group?.open ?? ""));
106
+ }
107
+ add(p.text, Math.max(visibleWidth(p.text), p.reserve ?? 0));
108
+ });
109
+ const last = list[list.length - 1];
110
+ if (last?.group !== undefined)
111
+ add(dim(style.groups?.[last.group]?.close ?? ""));
112
+ return [out, size];
113
+ };
114
+ let kept = [...parts];
115
+ const zone = (z) => kept.filter((p) => p.zone === z);
116
+ const fits = () => {
117
+ const l = join(zone("left"))[1];
118
+ const right = zone("right");
119
+ return l + (right.length === 0 ? 0 : sep.length + join(right)[1]) <= width;
120
+ };
121
+ while (!fits()) {
122
+ const droppable = kept.filter((p) => p.priority !== undefined);
123
+ if (droppable.length === 0)
124
+ break;
125
+ const drop = droppable.reduce((worst, p) => (p.priority > worst.priority ? p : worst));
126
+ kept = kept.filter((p) => p !== drop);
127
+ }
128
+ const l = join(zone("left"))[0];
129
+ const r = join(zone("right"))[0];
130
+ const gap = width - visibleWidth(l) - visibleWidth(r);
131
+ const line = r === "" ? l : gap >= MIN_GAP ? l + " ".repeat(gap) + r : l + dim(sep) + r;
132
+ return truncateToWidth(line, width);
133
+ }
50
134
  /** 按终端宽度缩写模型名(§3.10)。 */
51
135
  export function abbreviateModel(ref, width) {
52
136
  let out = ref;
@@ -58,6 +142,78 @@ export function abbreviateModel(ref, width) {
58
142
  out = out.replace(/-\d.*$/, "");
59
143
  return out;
60
144
  }
145
+ /** 用量类项的文本(已着色;普通项 dim)。 */
146
+ export function usageItems(stats, queue, source, theme) {
147
+ const g = theme.glyphs;
148
+ const dim = (text) => theme.fg("dim", text);
149
+ const out = {};
150
+ const t = stats.tokens;
151
+ const prompt = t.input + t.cacheRead + t.cacheWrite;
152
+ const cache = stats.cache;
153
+ if (prompt + t.output > 0) {
154
+ out.tokens = dim(`${g.arrowUp}${formatTokens(prompt)} ${g.arrowDown}${formatTokens(t.output)}`);
155
+ if (cache !== undefined)
156
+ out.cache = dim(cacheText(cache, g.warm));
157
+ else if (stats.cacheHitRate !== undefined) {
158
+ out.cache = dim(`cache ${Math.round(stats.cacheHitRate * 100)}%`);
159
+ }
160
+ const cost = statusCost(stats);
161
+ if (cost === undefined)
162
+ out.cost = dim("$?");
163
+ else if (cost > 0)
164
+ out.cost = dim(formatCost(cost));
165
+ if (cache !== undefined && cache.reBilledTokens > 0) {
166
+ const amount = cache.reBilledUsd === undefined
167
+ ? `${formatTokens(cache.reBilledTokens)} tok`
168
+ : formatCost(cache.reBilledUsd);
169
+ out.rebill = theme.fg("warning", `rebill ${amount}`);
170
+ }
171
+ }
172
+ if (queue > 0)
173
+ out.queue = theme.fg("warning", `queue ${queue}`);
174
+ const codemode = source.codemode?.();
175
+ if (codemode !== undefined) {
176
+ const strict = source.sandboxStrict?.() ?? true;
177
+ const label = dim(`codemode ${codemode}`);
178
+ out.codemode = strict ? label : `${label} ${theme.fg("error", "net!")}`;
179
+ }
180
+ if (source.bashSandbox?.() === true)
181
+ out.sandbox = dim("沙箱");
182
+ const preset = source.preset();
183
+ if (preset !== "default")
184
+ out.preset = dim(`preset ${preset}`);
185
+ const host = source.hostStatus?.();
186
+ if (host !== undefined && host.size > 0) {
187
+ const values = [...host.values()].filter((v) => v.trim() !== "");
188
+ if (values.length > 0)
189
+ out.host = dim(`[${values.join(" · ")}]`);
190
+ }
191
+ return out;
192
+ }
193
+ /** compact 布局的优先级(§1.2:现状顺序,git 三项与时长插在 ctx 之后)。 */
194
+ const COMPACT = {
195
+ model: 0,
196
+ ctx: 1,
197
+ duration: 3,
198
+ branch: 4,
199
+ dir: 5,
200
+ diff: 6,
201
+ codemode: 7,
202
+ queue: 8,
203
+ thinking: 9,
204
+ tokens: 10,
205
+ cache: 11,
206
+ cost: 12,
207
+ rebill: 13,
208
+ preset: 14,
209
+ host: 15,
210
+ hint: 16,
211
+ };
212
+ /** full 布局本行的优先级(§1.2 下行表)。 */
213
+ const FULL = { model: 0, ctx: 1, cost: 2, duration: 3, branch: 4, dir: 5, diff: 6, thinking: 7 };
214
+ const FULL_HINT = 12;
215
+ /** full 状态栏(用户样例):段间 ` | `。 */
216
+ const FULL_STYLE = { sep: " | " };
61
217
  export class StatusBar {
62
218
  source;
63
219
  theme;
@@ -74,106 +230,133 @@ export class StatusBar {
74
230
  setQueue(steering, followUp) {
75
231
  this.queue = steering + followUp;
76
232
  }
233
+ /** 当前统计(速率行共用,避免重复 getStats)。 */
234
+ current() {
235
+ this.stats ??= this.source.session().getStats();
236
+ return this.stats;
237
+ }
238
+ queued() {
239
+ return this.queue;
240
+ }
241
+ layout() {
242
+ return this.source.layout?.() ?? "compact";
243
+ }
77
244
  parts(width) {
78
245
  const theme = this.theme;
79
246
  const g = theme.glyphs;
80
- const session = this.source.session();
81
- const state = session.state;
82
- const stats = this.stats ?? session.getStats();
83
- this.stats = stats;
247
+ const state = this.source.session().state;
248
+ const stats = this.current();
249
+ const full = this.layout() === "full";
84
250
  const parts = [];
85
- /** 普通项整项 dim;`colored` 的项已自带颜色。 */
86
- const tone = (text, colored) => colored ? text : theme.fg("dim", text);
87
- const left = (text, priority, colored = false) => void parts.push({ text: tone(text, colored), priority, zone: "left" });
88
- const right = (text, priority, colored = false) => void parts.push({ text: tone(text, colored), priority, zone: "right" });
251
+ const dim = (text) => theme.fg("dim", text);
252
+ const right = (text, priority, extra = {}) => void parts.push({ text, priority, zone: "right", ...extra });
89
253
  const mode = permissionModeLabel(state.permissionMode);
90
- left(state.permissionMode === "full-auto"
91
- ? theme.fg("warning", mode)
92
- : state.permissionMode === "plan"
93
- ? theme.fg("accent", mode)
94
- : theme.fg("text", mode), undefined, true);
95
- if (width >= 40)
96
- left("shift+tab 切换", 12);
254
+ parts.push({
255
+ text: state.permissionMode === "full-auto"
256
+ ? theme.fg("warning", mode)
257
+ : state.permissionMode === "plan"
258
+ ? theme.fg("accent", mode)
259
+ : theme.fg("text", mode),
260
+ priority: undefined,
261
+ zone: "left",
262
+ });
263
+ if (width >= 40) {
264
+ parts.push({
265
+ text: dim("shift+tab 切换"),
266
+ priority: full ? FULL_HINT : COMPACT.hint,
267
+ zone: "left",
268
+ });
269
+ }
97
270
  const model = state.model === undefined ? "?" : abbreviateModel(formatModelRef(state.model), width);
98
- right(theme.fg("accent", model), 0, true);
99
- if (state.thinkingLevel !== "off")
100
- right(state.thinkingLevel, 5);
101
- const t = stats.tokens;
102
- const prompt = t.input + t.cacheRead + t.cacheWrite;
103
- const cache = stats.cache;
104
- if (prompt + t.output > 0) {
105
- right(`${g.arrowUp}${formatTokens(prompt)} ${g.arrowDown}${formatTokens(t.output)}`, 6);
106
- if (cache !== undefined)
107
- right(cacheText(cache, g.warm), 7);
108
- else if (stats.cacheHitRate !== undefined) {
109
- right(`cache ${Math.round(stats.cacheHitRate * 100)}%`, 7);
110
- }
111
- if (stats.cost === undefined)
112
- right("$?", 8);
113
- else if (stats.cost > 0)
114
- right(formatCost(stats.cost), 8);
115
- if (cache !== undefined && cache.reBilledTokens > 0) {
116
- const amount = cache.reBilledUsd === undefined
117
- ? `${formatTokens(cache.reBilledTokens)} tok`
118
- : formatCost(cache.reBilledUsd);
119
- right(theme.fg("warning", `rebill ${amount}`), 9, true);
120
- }
271
+ const p = full ? FULL : COMPACT;
272
+ const fallback = this.source.fallback?.();
273
+ // 回退中:`主模型 → 回退模型`(回退模型 warning),切回主模型后恢复原样
274
+ const modelText = fallback === undefined
275
+ ? theme.fg("accent", model)
276
+ : theme.fg("accent", abbreviateModel(fallback.from, width)) +
277
+ dim(g.ascii ? " -> " : " → ") +
278
+ theme.fg("warning", abbreviateModel(fallback.to, width));
279
+ right(modelText, p.model, full ? { group: "model" } : {});
280
+ if (state.thinkingLevel !== "off") {
281
+ right(dim(state.thinkingLevel), p.thinking, full ? { group: "model" } : {});
121
282
  }
122
- right(this.ctxText(stats.contextPercent, width), 1, true);
123
- if (this.queue > 0)
124
- right(theme.fg("warning", `queue ${this.queue}`), 4, true);
125
- const codemode = this.source.codemode?.();
126
- if (codemode !== undefined) {
127
- const strict = this.source.sandboxStrict?.() ?? true;
128
- const label = theme.fg("dim", `codemode ${codemode}`);
129
- right(strict ? label : `${label} ${theme.fg("error", "net!")}`, 3, true);
283
+ const usage = full ? {} : usageItems(stats, this.queue, this.source, theme);
284
+ if (usage.tokens !== undefined)
285
+ right(usage.tokens, COMPACT.tokens);
286
+ if (usage.cache !== undefined)
287
+ right(usage.cache, COMPACT.cache);
288
+ if (usage.cost !== undefined)
289
+ right(usage.cost, COMPACT.cost);
290
+ if (usage.rebill !== undefined)
291
+ right(usage.rebill, COMPACT.rebill);
292
+ right(this.ctxText(stats.contextPercent, width, full), p.ctx, full ? { reserve: 10 } : {});
293
+ this.gitParts(p, full).forEach((part) => parts.push(part));
294
+ if (full) {
295
+ const cost = statusCost(stats);
296
+ const used = stats.tokens.input + stats.tokens.output + stats.tokens.cacheRead > 0;
297
+ if (cost === undefined && used)
298
+ right(dim("$?"), FULL.cost);
299
+ else if (cost !== undefined && cost > 0)
300
+ right(dim(formatCost(cost)), FULL.cost);
130
301
  }
131
- const preset = this.source.preset();
132
- if (preset !== "default")
133
- right(`preset ${preset}`, 10);
134
- const host = this.source.hostStatus?.();
135
- if (host !== undefined && host.size > 0) {
136
- const values = [...host.values()].filter((v) => v.trim() !== "");
137
- if (values.length > 0)
138
- right(`[${values.join(" · ")}]`, 11);
302
+ const started = this.source.sessionStartedAt?.() ?? stats.telemetry?.sessionStartedAt;
303
+ if (started !== undefined) {
304
+ const now = this.source.now?.() ?? Date.now();
305
+ right(dim(formatDuration(now - started)), p.duration, { reserve: 6 });
139
306
  }
307
+ if (usage.queue !== undefined)
308
+ right(usage.queue, COMPACT.queue);
309
+ if (usage.codemode !== undefined)
310
+ right(usage.codemode, COMPACT.codemode);
311
+ if (usage.sandbox !== undefined)
312
+ right(usage.sandbox, COMPACT.codemode);
313
+ if (usage.preset !== undefined)
314
+ right(usage.preset, COMPACT.preset);
315
+ if (usage.host !== undefined)
316
+ right(usage.host, COMPACT.host);
140
317
  return parts;
141
318
  }
142
- ctxText(percent, width) {
319
+ /** `目录 ⎇ 分支 短提交 +a −b`:三项同组、各自按优先级丢弃。 */
320
+ gitParts(p, full) {
321
+ const git = this.source.git?.();
322
+ if (git === undefined)
323
+ return [];
324
+ const theme = this.theme;
325
+ const g = theme.glyphs;
326
+ const dim = (text) => theme.fg("dim", text);
327
+ const out = [];
328
+ const add = (text, priority) => void out.push({ text: dim(text), priority, zone: "right", group: "git" });
329
+ if (git.dir !== "")
330
+ add(git.dir, p.dir);
331
+ const info = git.info;
332
+ if (info === undefined)
333
+ return out;
334
+ const head = [info.branch, info.shortHead].filter((s) => s !== undefined).join(" ");
335
+ if (head !== "")
336
+ add(`${g.branch} ${head}`, p.branch);
337
+ if (info.insertions !== undefined && info.deletions !== undefined) {
338
+ const { insertions: a, deletions: d } = info;
339
+ add(full ? `(+${a},-${d})` : `+${a} ${g.ascii ? "-" : "−"}${d}`, p.diff);
340
+ }
341
+ return out;
342
+ }
343
+ ctxText(percent, width, decimal) {
143
344
  const theme = this.theme;
144
345
  if (percent === undefined)
145
- return theme.fg("dim", "ctx ?");
346
+ return theme.fg("dim", decimal ? "Ctx ?" : "ctx ?");
146
347
  const color = levelColor(percent / 100);
147
- if (width >= METER_WIDTH) {
348
+ // full 按用户样例总是 `Ctx 3.0%`;compact 宽屏换余量表(现状)
349
+ if (!decimal && width >= METER_WIDTH) {
148
350
  const meter = new Meter(percent / 100, { label: theme.fg("dim", "ctx"), theme });
149
351
  return meter.render(80)[0] ?? "";
150
352
  }
353
+ if (decimal)
354
+ return theme.fg("dim", "Ctx ") + theme.fg(color, `${percent.toFixed(1)}%`);
151
355
  return theme.fg("dim", "ctx ") + theme.fg(color, `${Math.round(percent)}%`);
152
356
  }
153
357
  render(width) {
154
- const theme = this.theme;
155
- const sep = theme.fg("dim", " · ");
156
- const join = (list) => list.map((p) => p.text).join(sep);
157
- let kept = this.parts(width);
158
- const zones = () => [
159
- join(kept.filter((p) => p.zone === "left")),
160
- join(kept.filter((p) => p.zone === "right")),
161
- ];
162
- const fits = () => {
163
- const [l, r] = zones();
164
- return visibleWidth(l) + (r === "" ? 0 : 3 + visibleWidth(r)) <= width;
165
- };
166
- while (!fits()) {
167
- const droppable = kept.filter((p) => p.priority !== undefined);
168
- if (droppable.length === 0)
169
- break;
170
- const drop = droppable.reduce((worst, p) => (p.priority > worst.priority ? p : worst));
171
- kept = kept.filter((p) => p !== drop);
172
- }
173
- const [l, r] = zones();
174
- const gap = width - visibleWidth(l) - visibleWidth(r);
175
- const line = r === "" ? l : gap >= MIN_GAP ? l + " ".repeat(gap) + r : l + sep + r;
176
- return [truncateToWidth(line, width)];
358
+ const style = this.layout() === "full" ? FULL_STYLE : {};
359
+ return [layoutRow(this.parts(width), width, this.theme, style)];
177
360
  }
178
361
  invalidate() {
179
362
  this.stats = undefined;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 速率行(第五波 §1.1,D1、D2):`ui.statusLine: "full"` 时在状态栏上方多一行。[W5-A]
3
+ *
4
+ * ```text
5
+ * tps: 100 tok/s • 546 tok / 5.5s (avg 100 · ttft 1.4s) ↑12.3k ↓1.2k · cache 83% ♨ · [-]
6
+ * ```
7
+ *
8
+ * - 左区(用户样例):`tps: <速率>`(流式中是最近 2 s 窗口的瞬时值、前缀 accent;结束后是该请求的平均值;
9
+ * 还没有请求时 `tps: —`)• `<输出 token> tok / <耗时>`(首 token 起算)`(avg <会话均速> · ttft <首 token 延迟>)`;
10
+ * `•` 走字形表(ASCII `*`);
11
+ * - 右区:从状态栏迁来的用量类项 `↑ ↓` · cache · rebill · queue · codemode · 预设 · [宿主],行尾 `[-]`
12
+ * (折叠提示:Ctrl+G 或 `/statusline compact`);
13
+ * - 数据:`StatusBar.current()` 的 `telemetry` 与用量(同一次 getStats);流式中 `telemetry_tick` 时刷新;
14
+ * - 丢弃顺序(§1.2,数字大先丢;`tps` 与 `[-]` 永不丢):tok/耗时 10 → codemode 9 → queue 8 → token 7 →
15
+ * cache 6 → rebill 5 → 预设 4 → 宿主 3 → avg 2 → ttft 1;数字按最宽形状占位,不随数值抖动;
16
+ * - `compact` 时不占行(render 返回空)。
17
+ */
18
+ import { type Component, type Theme } from "../../tui.js";
19
+ import { type StatusBar, type StatusBarSource } from "./status-bar.js";
20
+ /** 速率:< 10 保留一位小数,其余取整。 */
21
+ export declare function formatRate(tps: number): string;
22
+ /** 耗时:< 60 s 一位小数(`5.5s`),其余 `1m05s`。 */
23
+ export declare function formatSeconds(ms: number): string;
24
+ export declare const COLLAPSE_HINT = "[-]";
25
+ export declare class StatusLine implements Component {
26
+ private readonly bar;
27
+ private readonly source;
28
+ private readonly theme;
29
+ constructor(bar: StatusBar, source: StatusBarSource, theme: Theme);
30
+ private parts;
31
+ render(width: number): string[];
32
+ invalidate(): void;
33
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * 速率行(第五波 §1.1,D1、D2):`ui.statusLine: "full"` 时在状态栏上方多一行。[W5-A]
3
+ *
4
+ * ```text
5
+ * tps: 100 tok/s • 546 tok / 5.5s (avg 100 · ttft 1.4s) ↑12.3k ↓1.2k · cache 83% ♨ · [-]
6
+ * ```
7
+ *
8
+ * - 左区(用户样例):`tps: <速率>`(流式中是最近 2 s 窗口的瞬时值、前缀 accent;结束后是该请求的平均值;
9
+ * 还没有请求时 `tps: —`)• `<输出 token> tok / <耗时>`(首 token 起算)`(avg <会话均速> · ttft <首 token 延迟>)`;
10
+ * `•` 走字形表(ASCII `*`);
11
+ * - 右区:从状态栏迁来的用量类项 `↑ ↓` · cache · rebill · queue · codemode · 预设 · [宿主],行尾 `[-]`
12
+ * (折叠提示:Ctrl+G 或 `/statusline compact`);
13
+ * - 数据:`StatusBar.current()` 的 `telemetry` 与用量(同一次 getStats);流式中 `telemetry_tick` 时刷新;
14
+ * - 丢弃顺序(§1.2,数字大先丢;`tps` 与 `[-]` 永不丢):tok/耗时 10 → codemode 9 → queue 8 → token 7 →
15
+ * cache 6 → rebill 5 → 预设 4 → 宿主 3 → avg 2 → ttft 1;数字按最宽形状占位,不随数值抖动;
16
+ * - `compact` 时不占行(render 返回空)。
17
+ */
18
+ import { truncateToWidth } from "../../tui.js";
19
+ import { formatTokens, layoutRow, usageItems, } from "./status-bar.js";
20
+ /** 速率:< 10 保留一位小数,其余取整。 */
21
+ export function formatRate(tps) {
22
+ return tps < 10 ? tps.toFixed(1) : String(Math.round(tps));
23
+ }
24
+ /** 耗时:< 60 s 一位小数(`5.5s`),其余 `1m05s`。 */
25
+ export function formatSeconds(ms) {
26
+ const seconds = Math.max(0, ms) / 1000;
27
+ if (seconds < 60)
28
+ return `${seconds.toFixed(1)}s`;
29
+ const whole = Math.floor(seconds);
30
+ return `${Math.floor(whole / 60)}m${String(whole % 60).padStart(2, "0")}s`;
31
+ }
32
+ /** 占位宽度(常见的最宽形状):`tps: 999 tok/s`、`1.2k tok / 59.9s`、`avg 999`、`ttft 9.9s`。 */
33
+ const RESERVE = { tps: 14, amount: 16, avg: 7, ttft: 9 };
34
+ const PRIORITY = {
35
+ ttft: 1,
36
+ avg: 2,
37
+ host: 3,
38
+ preset: 4,
39
+ rebill: 5,
40
+ cache: 6,
41
+ tokens: 7,
42
+ queue: 8,
43
+ codemode: 9,
44
+ amount: 10,
45
+ };
46
+ export const COLLAPSE_HINT = "[-]";
47
+ /** 用户样例:`tps: 100 tok/s • 546 tok / 5.5s (avg 100 · ttft 1.4s)`;右区用量项仍以 ` · ` 分隔。 */
48
+ const RATE_STYLE = { groups: { stats: { glue: " · ", open: "(", close: ")" } } };
49
+ export class StatusLine {
50
+ bar;
51
+ source;
52
+ theme;
53
+ constructor(bar, source, theme) {
54
+ this.bar = bar;
55
+ this.source = source;
56
+ this.theme = theme;
57
+ }
58
+ parts() {
59
+ const theme = this.theme;
60
+ const dim = (text) => theme.fg("dim", text);
61
+ const stats = this.bar.current();
62
+ const telemetry = stats.telemetry;
63
+ const live = telemetry?.live;
64
+ const last = telemetry?.last;
65
+ const parts = [];
66
+ const left = (text, priority, reserve, extra = {}) => void parts.push({ text, priority, zone: "left", reserve, ...extra });
67
+ const right = (text, priority) => {
68
+ if (text !== undefined)
69
+ parts.push({ text, priority, zone: "right" });
70
+ };
71
+ let tps;
72
+ let amount;
73
+ if (live !== undefined) {
74
+ tps = live.tps;
75
+ amount = `${formatTokens(live.outputTokens)} tok / ${formatSeconds(live.elapsedMs)}`;
76
+ }
77
+ else if (last?.doneAt !== undefined) {
78
+ tps = last.tps;
79
+ if (last.outputTokens !== undefined && last.firstTokenAt !== undefined) {
80
+ amount = `${formatTokens(last.outputTokens)} tok / ${formatSeconds(last.doneAt - last.firstTokenAt)}`;
81
+ }
82
+ }
83
+ const label = live !== undefined ? theme.fg("accent", "tps:") : dim("tps:");
84
+ left(`${label} ${dim(tps === undefined ? "—" : `${formatRate(tps)} tok/s`)}`, undefined, RESERVE.tps);
85
+ if (amount !== undefined) {
86
+ left(dim(amount), PRIORITY.amount, RESERVE.amount, { lead: ` ${theme.glyphs.dot} ` });
87
+ }
88
+ const stat = { group: "stats", lead: " " };
89
+ if (telemetry?.avgTps !== undefined) {
90
+ left(dim(`avg ${formatRate(telemetry.avgTps)}`), PRIORITY.avg, RESERVE.avg, stat);
91
+ }
92
+ if (last?.ttftMs !== undefined) {
93
+ left(dim(`ttft ${formatSeconds(last.ttftMs)}`), PRIORITY.ttft, RESERVE.ttft, stat);
94
+ }
95
+ const usage = usageItems(stats, this.bar.queued(), this.source, theme);
96
+ right(usage.tokens, PRIORITY.tokens);
97
+ right(usage.cache, PRIORITY.cache);
98
+ right(usage.rebill, PRIORITY.rebill);
99
+ right(usage.queue, PRIORITY.queue);
100
+ right(usage.codemode, PRIORITY.codemode);
101
+ right(usage.sandbox, PRIORITY.codemode);
102
+ right(usage.preset, PRIORITY.preset);
103
+ right(usage.host, PRIORITY.host);
104
+ right(dim(COLLAPSE_HINT), undefined);
105
+ return parts;
106
+ }
107
+ render(width) {
108
+ if (this.bar.layout() !== "full")
109
+ return [];
110
+ return [truncateToWidth(layoutRow(this.parts(), width, this.theme, RATE_STYLE), width)];
111
+ }
112
+ invalidate() { }
113
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 消息区的子 Agent 折叠视图(docs/wave5-plan.md §7.5)。[W5-U]
3
+ *
4
+ * 消费 `subagent_start / update / end`,按 taskId 记下类型、状态、轮数、最近 3 个工具与用量;task 工具行
5
+ * (tool-view.ts)据 `parentToolCallId` 取来显示一行:
6
+ *
7
+ * ```
8
+ * ⏺ task 检查 src/tui 的测试覆盖缺口
9
+ * ⎿ ⠋ explore · 运行中 1m05s · 3 轮 · read grep bash · ↑12k ↓3.4k
10
+ * ```
11
+ *
12
+ * 后台任务的工具调用立即返回,工具行下面多一行跟随状态(`↳ t2 explore · 完成 · 7 轮 …`,运行中每秒刷新);
13
+ * 完成后父会话收到的 `<task-notification>` 只显示一行(`notificationSummary`),失败 / 停止另有一行提示。
14
+ * 外部 runner(claude / codex)同一组事件,用量可能缺。
15
+ */
16
+ import type { SessionEvent, SubagentStatus } from "../../agent/types.js";
17
+ import type { Usage } from "../../ai/types.js";
18
+ import { type Theme } from "../../tui.js";
19
+ export declare const RECENT_TOOLS = 3;
20
+ export interface SubagentState {
21
+ taskId: string;
22
+ parentToolCallId: string;
23
+ agent: string;
24
+ runner: string;
25
+ background: boolean;
26
+ status: SubagentStatus | "running";
27
+ turns: number;
28
+ /** 最近的工具名(最多 3 个,新的在后)。 */
29
+ tools: string[];
30
+ usage?: Usage;
31
+ startedAt: number;
32
+ endedAt?: number;
33
+ }
34
+ export declare class SubagentTracker {
35
+ private readonly now;
36
+ private readonly tasks;
37
+ private readonly byCall;
38
+ constructor(now?: () => number);
39
+ /** 子 Agent 事件:更新状态并返回它;其它事件返回 undefined。 */
40
+ onEvent(event: SessionEvent): SubagentState | undefined;
41
+ /** 运行中的任务。 */
42
+ running(): SubagentState[];
43
+ get(taskId: string): SubagentState | undefined;
44
+ /** task 工具调用对应的子 Agent(续聊时是同一 taskId)。 */
45
+ forToolCall(toolCallId: string): SubagentState | undefined;
46
+ clear(): void;
47
+ }
48
+ /** `explore · 运行中 1m05s · 3 轮 · read grep bash · ↑12k ↓3.4k`(已着色:类型 tool 色,其余 muted)。 */
49
+ export declare function subagentLine(state: SubagentState, theme: Theme, now: number): string;
50
+ /** 后台任务结束的一行提示。 */
51
+ export declare function backgroundEndText(state: SubagentState): string;
52
+ /**
53
+ * 父会话收到的 `<task-notification …>`(origin `task` 的用户消息)在消息区只显示一行:
54
+ * `t1 explore 完成 · 7 轮 · /tasks 查看输出`;不是通知格式返回 undefined。
55
+ */
56
+ export declare function notificationSummary(text: string): string | undefined;