@armadra/agent 0.5.0 → 0.6.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 (471) hide show
  1. package/CHANGELOG.md +177 -294
  2. package/CHANGELOG.zh-CN.md +442 -0
  3. package/README.md +408 -364
  4. package/README.zh-CN.md +705 -0
  5. package/dist/agent/session-plan.js +2 -2
  6. package/dist/agent/session-state.d.ts +3 -0
  7. package/dist/agent/session-state.js +20 -0
  8. package/dist/agent/session-subagent.d.ts +15 -1
  9. package/dist/agent/session-subagent.js +26 -3
  10. package/dist/agent/session-telemetry.d.ts +16 -2
  11. package/dist/agent/session-telemetry.js +29 -7
  12. package/dist/agent/session-tools.js +5 -2
  13. package/dist/agent/session-trace-writer.d.ts +27 -0
  14. package/dist/agent/session-trace-writer.js +253 -0
  15. package/dist/agent/session.d.ts +2 -0
  16. package/dist/agent/session.js +12 -0
  17. package/dist/agent/subagent-direct.d.ts +20 -0
  18. package/dist/agent/subagent-direct.js +76 -0
  19. package/dist/agent/subagent-registry.d.ts +8 -1
  20. package/dist/agent/subagent-registry.js +28 -1
  21. package/dist/agent/system-prompt.d.ts +3 -1
  22. package/dist/agent/system-prompt.js +3 -0
  23. package/dist/agent/tool-runner.d.ts +2 -1
  24. package/dist/agent/tool-runner.js +5 -4
  25. package/dist/agent/types-w6.d.ts +39 -0
  26. package/dist/agent/types-w6.js +4 -0
  27. package/dist/agent/types.d.ts +5 -2
  28. package/dist/agents/external.js +1 -1
  29. package/dist/agents/task-record.d.ts +74 -1
  30. package/dist/agents/task-record.js +100 -0
  31. package/dist/ai/apis/cache-params.js +2 -1
  32. package/dist/ai/apis/chatgpt-backend.d.ts +52 -0
  33. package/dist/ai/apis/chatgpt-backend.js +224 -0
  34. package/dist/ai/apis/chatgpt-rate-limits.d.ts +26 -0
  35. package/dist/ai/apis/chatgpt-rate-limits.js +91 -0
  36. package/dist/ai/apis/openai-responses-request.d.ts +2 -1
  37. package/dist/ai/apis/openai-responses-request.js +8 -2
  38. package/dist/ai/apis/openai-responses.d.ts +2 -0
  39. package/dist/ai/apis/openai-responses.js +51 -16
  40. package/dist/ai/overflow.d.ts +1 -1
  41. package/dist/ai/overflow.js +10 -7
  42. package/dist/ai/providers/auth.d.ts +15 -2
  43. package/dist/ai/providers/auth.js +69 -2
  44. package/dist/ai/providers/builtin.js +27 -0
  45. package/dist/ai/providers/catalog-data.js +1 -0
  46. package/dist/ai/providers/enrich.js +2 -7
  47. package/dist/ai/providers/models-dev-cache.js +15 -19
  48. package/dist/ai/providers/models-dev-snapshot.js +8 -7
  49. package/dist/ai/providers/models-dev.js +5 -15
  50. package/dist/ai/providers/suggest.js +2 -16
  51. package/dist/ai/types.d.ts +27 -1
  52. package/dist/auth/chatgpt/backend-client.d.ts +26 -0
  53. package/dist/auth/chatgpt/backend-client.js +74 -0
  54. package/dist/auth/chatgpt/claims.d.ts +17 -0
  55. package/dist/auth/chatgpt/claims.js +46 -0
  56. package/dist/auth/chatgpt/cli.d.ts +33 -0
  57. package/dist/auth/chatgpt/cli.js +252 -0
  58. package/dist/auth/chatgpt/doctor.d.ts +17 -0
  59. package/dist/auth/chatgpt/doctor.js +36 -0
  60. package/dist/auth/chatgpt/host-id.d.ts +7 -0
  61. package/dist/auth/chatgpt/host-id.js +26 -0
  62. package/dist/auth/chatgpt/login.d.ts +30 -0
  63. package/dist/auth/chatgpt/login.js +130 -0
  64. package/dist/auth/chatgpt/presets.d.ts +66 -0
  65. package/dist/auth/chatgpt/presets.js +120 -0
  66. package/dist/auth/chatgpt/quota-text.d.ts +13 -0
  67. package/dist/auth/chatgpt/quota-text.js +36 -0
  68. package/dist/auth/oauth/browser.d.ts +12 -0
  69. package/dist/auth/oauth/browser.js +27 -0
  70. package/dist/auth/oauth/callback-server.d.ts +47 -0
  71. package/dist/auth/oauth/callback-server.js +147 -0
  72. package/dist/auth/oauth/flows.d.ts +62 -0
  73. package/dist/auth/oauth/flows.js +113 -0
  74. package/dist/auth/oauth/jwt.d.ts +24 -0
  75. package/dist/auth/oauth/jwt.js +81 -0
  76. package/dist/auth/oauth/live.d.ts +21 -0
  77. package/dist/auth/oauth/live.js +25 -0
  78. package/dist/auth/oauth/oidc.d.ts +26 -0
  79. package/dist/auth/oauth/oidc.js +72 -0
  80. package/dist/auth/oauth/pkce.d.ts +15 -0
  81. package/dist/auth/oauth/pkce.js +20 -0
  82. package/dist/auth/oauth/refresh.d.ts +35 -0
  83. package/dist/auth/oauth/refresh.js +129 -0
  84. package/dist/auth/oauth/token-client.d.ts +33 -0
  85. package/dist/auth/oauth/token-client.js +121 -0
  86. package/dist/auth/oauth/token-store.d.ts +23 -0
  87. package/dist/auth/oauth/token-store.js +125 -0
  88. package/dist/auth/testing/fake-oauth.d.ts +73 -0
  89. package/dist/auth/testing/fake-oauth.js +271 -0
  90. package/dist/auth/testing/refresh-child.d.ts +5 -0
  91. package/dist/auth/testing/refresh-child.js +16 -0
  92. package/dist/bundle/ama.cjs +28307 -13241
  93. package/dist/checkpoints/backend.js +6 -5
  94. package/dist/checkpoints/restore.js +3 -2
  95. package/dist/checkpoints/settings.js +2 -1
  96. package/dist/checkpoints/shadow-git.js +7 -6
  97. package/dist/checkpoints/shadow-restore.js +3 -2
  98. package/dist/checkpoints/tracker.js +4 -3
  99. package/dist/cli/args.d.ts +9 -2
  100. package/dist/cli/args.js +74 -27
  101. package/dist/cli/bootstrap.js +53 -31
  102. package/dist/cli/choice-prompt.d.ts +58 -0
  103. package/dist/cli/choice-prompt.js +142 -0
  104. package/dist/cli/codemode-notice.js +2 -2
  105. package/dist/cli/compose-agents.js +2 -1
  106. package/dist/cli/compose-extensions.js +2 -0
  107. package/dist/cli/compose-memory.d.ts +53 -0
  108. package/dist/cli/compose-memory.js +127 -0
  109. package/dist/cli/compose-providers.d.ts +5 -0
  110. package/dist/cli/compose-providers.js +32 -2
  111. package/dist/cli/compose-session.d.ts +5 -0
  112. package/dist/cli/compose-session.js +27 -13
  113. package/dist/cli/compose-store.d.ts +2 -0
  114. package/dist/cli/compose-store.js +9 -5
  115. package/dist/cli/compose.d.ts +2 -0
  116. package/dist/cli/compose.js +13 -5
  117. package/dist/cli/default-model.d.ts +0 -2
  118. package/dist/cli/default-model.js +5 -8
  119. package/dist/cli/deps.d.ts +5 -1
  120. package/dist/cli/exit-codes.d.ts +1 -1
  121. package/dist/cli/exit-codes.js +7 -16
  122. package/dist/cli/from-prompt.js +3 -2
  123. package/dist/cli/help-text.d.ts +2 -1
  124. package/dist/cli/help-text.js +5 -99
  125. package/dist/cli/main.d.ts +5 -0
  126. package/dist/cli/main.js +21 -2
  127. package/dist/cli/proxy.js +14 -9
  128. package/dist/cli/runtime.d.ts +5 -0
  129. package/dist/cli/startup-screen.js +20 -27
  130. package/dist/cli/startup-steps.js +6 -8
  131. package/dist/cli/subcommands/auth.d.ts +6 -3
  132. package/dist/cli/subcommands/auth.js +52 -22
  133. package/dist/cli/subcommands/config-set.d.ts +21 -0
  134. package/dist/cli/subcommands/config-set.js +137 -0
  135. package/dist/cli/subcommands/config.d.ts +2 -1
  136. package/dist/cli/subcommands/config.js +51 -37
  137. package/dist/cli/subcommands/doctor.d.ts +2 -1
  138. package/dist/cli/subcommands/doctor.js +97 -58
  139. package/dist/cli/subcommands/init.d.ts +1 -1
  140. package/dist/cli/subcommands/init.js +8 -7
  141. package/dist/cli/subcommands/memory.d.ts +18 -0
  142. package/dist/cli/subcommands/memory.js +189 -0
  143. package/dist/cli/subcommands/model-meta.js +11 -9
  144. package/dist/cli/subcommands/models-cache-probe.d.ts +1 -1
  145. package/dist/cli/subcommands/models-cache-probe.js +26 -32
  146. package/dist/cli/subcommands/models-discover.d.ts +3 -0
  147. package/dist/cli/subcommands/models-discover.js +44 -21
  148. package/dist/cli/subcommands/models.d.ts +1 -1
  149. package/dist/cli/subcommands/models.js +29 -22
  150. package/dist/cli/subcommands/probe-runner.js +8 -7
  151. package/dist/cli/subcommands/providers-list.js +32 -21
  152. package/dist/cli/subcommands/providers-plan.js +24 -25
  153. package/dist/cli/subcommands/providers-probe.js +5 -6
  154. package/dist/cli/subcommands/providers.d.ts +2 -2
  155. package/dist/cli/subcommands/providers.js +48 -59
  156. package/dist/cli/subcommands/sessions-export.d.ts +1 -1
  157. package/dist/cli/subcommands/sessions-export.js +10 -9
  158. package/dist/cli/subcommands/sessions-search.d.ts +1 -1
  159. package/dist/cli/subcommands/sessions-search.js +11 -10
  160. package/dist/cli/subcommands/sessions-trace.d.ts +30 -0
  161. package/dist/cli/subcommands/sessions-trace.js +156 -0
  162. package/dist/cli/subcommands/sessions.d.ts +4 -3
  163. package/dist/cli/subcommands/sessions.js +40 -37
  164. package/dist/cli/subcommands/stats.d.ts +1 -1
  165. package/dist/cli/subcommands/stats.js +49 -30
  166. package/dist/cli/system-prompt-arg.js +3 -2
  167. package/dist/codemode/capability.js +3 -2
  168. package/dist/compaction/post-compact.d.ts +1 -0
  169. package/dist/compaction/post-compact.js +15 -1
  170. package/dist/compaction/prune-tier.d.ts +1 -1
  171. package/dist/compaction/prune-tier.js +3 -3
  172. package/dist/compaction/serialize.js +2 -1
  173. package/dist/config/auth-file.d.ts +12 -2
  174. package/dist/config/auth-file.js +32 -10
  175. package/dist/config/checker.js +12 -11
  176. package/dist/config/context-files.js +3 -2
  177. package/dist/config/edit.d.ts +106 -0
  178. package/dist/config/edit.js +350 -0
  179. package/dist/config/init.d.ts +4 -3
  180. package/dist/config/init.js +10 -18
  181. package/dist/config/json-schema.d.ts +2 -1
  182. package/dist/config/json-schema.js +101 -68
  183. package/dist/config/key-docs.d.ts +18 -7
  184. package/dist/config/key-docs.js +43 -128
  185. package/dist/config/load.js +7 -6
  186. package/dist/config/merge.d.ts +4 -1
  187. package/dist/config/merge.js +42 -22
  188. package/dist/config/paths.js +6 -5
  189. package/dist/config/profile.d.ts +5 -1
  190. package/dist/config/profile.js +7 -2
  191. package/dist/config/schema-w5.js +3 -2
  192. package/dist/config/schema-w6.d.ts +19 -0
  193. package/dist/config/schema-w6.js +115 -0
  194. package/dist/config/schema.js +34 -20
  195. package/dist/config/settings-registry.d.ts +78 -0
  196. package/dist/config/settings-registry.js +184 -0
  197. package/dist/config/types-w6.d.ts +109 -0
  198. package/dist/config/types-w6.js +19 -0
  199. package/dist/config/types.d.ts +11 -8
  200. package/dist/config/types.js +1 -0
  201. package/dist/drivers/acp/client.js +1 -1
  202. package/dist/drivers/acp/driver.js +13 -8
  203. package/dist/drivers/agents.js +4 -3
  204. package/dist/drivers/base.js +3 -2
  205. package/dist/drivers/host-runners.js +2 -1
  206. package/dist/drivers/native/claude-stream.js +12 -11
  207. package/dist/drivers/native/codex-app-server.js +17 -12
  208. package/dist/drivers/native/oneshot.js +8 -7
  209. package/dist/drivers/pool.js +1 -1
  210. package/dist/drivers/runner.d.ts +3 -1
  211. package/dist/drivers/runner.js +68 -20
  212. package/dist/drivers/turn.js +1 -1
  213. package/dist/hooks/config.js +3 -2
  214. package/dist/hooks/protocol.js +18 -12
  215. package/dist/host/api-impl.js +11 -10
  216. package/dist/host/loader.js +9 -8
  217. package/dist/host/types.d.ts +3 -0
  218. package/dist/i18n/catalog.d.ts +2169 -0
  219. package/dist/i18n/catalog.js +72 -0
  220. package/dist/i18n/format.d.ts +21 -0
  221. package/dist/i18n/format.js +58 -0
  222. package/dist/i18n/index.d.ts +58 -0
  223. package/dist/i18n/index.js +72 -0
  224. package/dist/i18n/messages/agents.d.ts +84 -0
  225. package/dist/i18n/messages/agents.js +85 -0
  226. package/dist/i18n/messages/approval.d.ts +99 -0
  227. package/dist/i18n/messages/approval.js +100 -0
  228. package/dist/i18n/messages/auth.d.ts +187 -0
  229. package/dist/i18n/messages/auth.js +189 -0
  230. package/dist/i18n/messages/cli-args.d.ts +46 -0
  231. package/dist/i18n/messages/cli-args.js +46 -0
  232. package/dist/i18n/messages/cli-help.d.ts +12 -0
  233. package/dist/i18n/messages/cli-help.js +244 -0
  234. package/dist/i18n/messages/cli.d.ts +336 -0
  235. package/dist/i18n/messages/cli.js +311 -0
  236. package/dist/i18n/messages/config-keys.d.ts +289 -0
  237. package/dist/i18n/messages/config-keys.js +292 -0
  238. package/dist/i18n/messages/config.d.ts +502 -0
  239. package/dist/i18n/messages/config.js +239 -0
  240. package/dist/i18n/messages/drivers.d.ts +123 -0
  241. package/dist/i18n/messages/drivers.js +124 -0
  242. package/dist/i18n/messages/errors.d.ts +83 -0
  243. package/dist/i18n/messages/errors.js +171 -0
  244. package/dist/i18n/messages/interactive-line.d.ts +61 -0
  245. package/dist/i18n/messages/interactive-line.js +69 -0
  246. package/dist/i18n/messages/interactive-startup.d.ts +103 -0
  247. package/dist/i18n/messages/interactive-startup.js +104 -0
  248. package/dist/i18n/messages/interactive-view.d.ts +140 -0
  249. package/dist/i18n/messages/interactive-view.js +141 -0
  250. package/dist/i18n/messages/interactive.d.ts +493 -0
  251. package/dist/i18n/messages/interactive.js +248 -0
  252. package/dist/i18n/messages/memory.d.ts +123 -0
  253. package/dist/i18n/messages/memory.js +128 -0
  254. package/dist/i18n/messages/panels.d.ts +190 -0
  255. package/dist/i18n/messages/panels.js +189 -0
  256. package/dist/i18n/messages/permissions.d.ts +126 -0
  257. package/dist/i18n/messages/permissions.js +151 -0
  258. package/dist/i18n/messages/plan.d.ts +123 -0
  259. package/dist/i18n/messages/plan.js +124 -0
  260. package/dist/i18n/messages/print.d.ts +86 -0
  261. package/dist/i18n/messages/print.js +91 -0
  262. package/dist/i18n/messages/report.d.ts +391 -0
  263. package/dist/i18n/messages/report.js +490 -0
  264. package/dist/i18n/messages/rewind.d.ts +198 -0
  265. package/dist/i18n/messages/rewind.js +217 -0
  266. package/dist/i18n/messages/session.d.ts +230 -0
  267. package/dist/i18n/messages/session.js +258 -0
  268. package/dist/i18n/messages/settings.d.ts +348 -0
  269. package/dist/i18n/messages/settings.js +336 -0
  270. package/dist/i18n/messages/subcommands-config.d.ts +111 -0
  271. package/dist/i18n/messages/subcommands-config.js +127 -0
  272. package/dist/i18n/messages/subcommands.d.ts +548 -0
  273. package/dist/i18n/messages/subcommands.js +474 -0
  274. package/dist/i18n/messages/trace.d.ts +243 -0
  275. package/dist/i18n/messages/trace.js +238 -0
  276. package/dist/i18n/types.d.ts +15 -0
  277. package/dist/i18n/types.js +7 -0
  278. package/dist/index.d.ts +6 -0
  279. package/dist/index.js +2 -0
  280. package/dist/memory/edit.d.ts +24 -0
  281. package/dist/memory/edit.js +63 -0
  282. package/dist/memory/frontmatter.d.ts +37 -0
  283. package/dist/memory/frontmatter.js +79 -0
  284. package/dist/memory/index.d.ts +28 -0
  285. package/dist/memory/index.js +126 -0
  286. package/dist/memory/lock.d.ts +17 -0
  287. package/dist/memory/lock.js +62 -0
  288. package/dist/memory/paths.d.ts +57 -0
  289. package/dist/memory/paths.js +175 -0
  290. package/dist/memory/report.d.ts +36 -0
  291. package/dist/memory/report.js +86 -0
  292. package/dist/memory/runtime.d.ts +42 -0
  293. package/dist/memory/runtime.js +36 -0
  294. package/dist/memory/secrets.d.ts +8 -0
  295. package/dist/memory/secrets.js +27 -0
  296. package/dist/memory/section.d.ts +28 -0
  297. package/dist/memory/section.js +59 -0
  298. package/dist/memory/store.d.ts +70 -0
  299. package/dist/memory/store.js +284 -0
  300. package/dist/memory/tool.d.ts +31 -0
  301. package/dist/memory/tool.js +94 -0
  302. package/dist/modes/acp/acp-events.js +2 -1
  303. package/dist/modes/acp/acp-server.js +13 -12
  304. package/dist/modes/commands-core.d.ts +17 -0
  305. package/dist/modes/commands-core.js +101 -61
  306. package/dist/modes/image-input.js +20 -6
  307. package/dist/modes/interactive/agent-bar.d.ts +95 -0
  308. package/dist/modes/interactive/agent-bar.js +248 -0
  309. package/dist/modes/interactive/agent-panels.js +27 -26
  310. package/dist/modes/interactive/agent-transcript.d.ts +47 -0
  311. package/dist/modes/interactive/agent-transcript.js +191 -0
  312. package/dist/modes/interactive/agent-ui.d.ts +36 -1
  313. package/dist/modes/interactive/agent-ui.js +180 -14
  314. package/dist/modes/interactive/agent-view.d.ts +72 -0
  315. package/dist/modes/interactive/agent-view.js +281 -0
  316. package/dist/modes/interactive/approval-dialog.js +46 -34
  317. package/dist/modes/interactive/clipboard-paste.d.ts +2 -2
  318. package/dist/modes/interactive/clipboard-paste.js +9 -4
  319. package/dist/modes/interactive/commands.d.ts +22 -1
  320. package/dist/modes/interactive/commands.js +93 -38
  321. package/dist/modes/interactive/completion.js +2 -1
  322. package/dist/modes/interactive/config-panel.d.ts +70 -0
  323. package/dist/modes/interactive/config-panel.js +319 -0
  324. package/dist/modes/interactive/config-ui.d.ts +96 -0
  325. package/dist/modes/interactive/config-ui.js +397 -0
  326. package/dist/modes/interactive/confirm-dialog.d.ts +49 -0
  327. package/dist/modes/interactive/confirm-dialog.js +120 -0
  328. package/dist/modes/interactive/event-notices.js +11 -9
  329. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  330. package/dist/modes/interactive/interactive-mode.js +25 -22
  331. package/dist/modes/interactive/key-dispatch.d.ts +18 -0
  332. package/dist/modes/interactive/key-dispatch.js +39 -13
  333. package/dist/modes/interactive/line/line-editor.js +2 -1
  334. package/dist/modes/interactive/line/line-mode.d.ts +2 -0
  335. package/dist/modes/interactive/line/line-mode.js +24 -4
  336. package/dist/modes/interactive/line/line-render.js +25 -24
  337. package/dist/modes/interactive/memory-panel.d.ts +64 -0
  338. package/dist/modes/interactive/memory-panel.js +221 -0
  339. package/dist/modes/interactive/message-view.d.ts +4 -2
  340. package/dist/modes/interactive/message-view.js +42 -33
  341. package/dist/modes/interactive/panels.js +39 -31
  342. package/dist/modes/interactive/pickers.d.ts +2 -1
  343. package/dist/modes/interactive/pickers.js +10 -6
  344. package/dist/modes/interactive/plan-command.d.ts +1 -1
  345. package/dist/modes/interactive/plan-command.js +24 -27
  346. package/dist/modes/interactive/plan-dialog.js +18 -17
  347. package/dist/modes/interactive/plan-flow.js +6 -5
  348. package/dist/modes/interactive/rewind-command.d.ts +1 -1
  349. package/dist/modes/interactive/rewind-command.js +18 -16
  350. package/dist/modes/interactive/rewind-flow.d.ts +0 -1
  351. package/dist/modes/interactive/rewind-flow.js +13 -14
  352. package/dist/modes/interactive/rewind-list.js +7 -6
  353. package/dist/modes/interactive/rewind-panel.js +40 -40
  354. package/dist/modes/interactive/rewind-text.d.ts +5 -3
  355. package/dist/modes/interactive/rewind-text.js +48 -38
  356. package/dist/modes/interactive/run-indicator.js +15 -12
  357. package/dist/modes/interactive/session-events.js +3 -2
  358. package/dist/modes/interactive/startup-header.js +22 -23
  359. package/dist/modes/interactive/startup-ui.js +41 -29
  360. package/dist/modes/interactive/status-area.d.ts +2 -0
  361. package/dist/modes/interactive/status-area.js +8 -2
  362. package/dist/modes/interactive/status-bar.js +4 -3
  363. package/dist/modes/interactive/subagent-view.js +9 -7
  364. package/dist/modes/interactive/tasks-report.d.ts +6 -0
  365. package/dist/modes/interactive/tasks-report.js +28 -31
  366. package/dist/modes/interactive/terminal-setup.d.ts +9 -0
  367. package/dist/modes/interactive/terminal-setup.js +23 -0
  368. package/dist/modes/interactive/tool-summary.js +19 -17
  369. package/dist/modes/interactive/tool-view.js +7 -4
  370. package/dist/modes/interactive/trace-view.d.ts +89 -0
  371. package/dist/modes/interactive/trace-view.js +332 -0
  372. package/dist/modes/print/print-mode.js +14 -15
  373. package/dist/modes/rpc/commands.js +7 -3
  374. package/dist/modes/rpc/rpc-mode.js +9 -3
  375. package/dist/modes/session-report.d.ts +2 -0
  376. package/dist/modes/session-report.js +101 -105
  377. package/dist/modes/startup-ui-text.js +11 -6
  378. package/dist/permissions/bypass.d.ts +30 -0
  379. package/dist/permissions/bypass.js +45 -0
  380. package/dist/permissions/memory-class.d.ts +23 -0
  381. package/dist/permissions/memory-class.js +54 -0
  382. package/dist/permissions/modes.d.ts +3 -3
  383. package/dist/permissions/modes.js +21 -16
  384. package/dist/permissions/pipeline.d.ts +1 -1
  385. package/dist/permissions/pipeline.js +9 -2
  386. package/dist/permissions/preview.js +49 -42
  387. package/dist/permissions/rules.js +5 -2
  388. package/dist/permissions/types.d.ts +4 -0
  389. package/dist/plan/store.js +2 -1
  390. package/dist/rpc.d.ts +39 -1
  391. package/dist/rpc.js +3 -0
  392. package/dist/sandbox/bash.js +11 -8
  393. package/dist/sandbox/detect.js +14 -11
  394. package/dist/sdk.d.ts +17 -1
  395. package/dist/sdk.js +29 -7
  396. package/dist/session/export.js +31 -30
  397. package/dist/session/manager.d.ts +1 -1
  398. package/dist/session/manager.js +5 -3
  399. package/dist/session/reuse.js +5 -4
  400. package/dist/session/scan.js +3 -2
  401. package/dist/session/stats-aggregate.d.ts +3 -1
  402. package/dist/session/stats-aggregate.js +5 -1
  403. package/dist/session/stats-index.js +2 -1
  404. package/dist/session/stats-scan.d.ts +4 -1
  405. package/dist/session/stats-scan.js +6 -2
  406. package/dist/session/store.d.ts +11 -0
  407. package/dist/session/store.js +48 -1
  408. package/dist/session/types.d.ts +2 -0
  409. package/dist/skills/builtin.js +2 -1
  410. package/dist/tools/image-file.d.ts +26 -1
  411. package/dist/tools/image-file.js +55 -13
  412. package/dist/tools/presets.d.ts +14 -1
  413. package/dist/tools/presets.js +3 -3
  414. package/dist/tools/registry.js +1 -1
  415. package/dist/tools/truncate.d.ts +1 -1
  416. package/dist/tools/truncate.js +2 -2
  417. package/dist/tools/types.d.ts +17 -1
  418. package/dist/trace/build-index.d.ts +91 -0
  419. package/dist/trace/build-index.js +127 -0
  420. package/dist/trace/build-nodes.d.ts +24 -0
  421. package/dist/trace/build-nodes.js +279 -0
  422. package/dist/trace/build-util.d.ts +43 -0
  423. package/dist/trace/build-util.js +120 -0
  424. package/dist/trace/build.d.ts +82 -0
  425. package/dist/trace/build.js +382 -0
  426. package/dist/trace/detail.d.ts +35 -0
  427. package/dist/trace/detail.js +205 -0
  428. package/dist/trace/flatten.d.ts +69 -0
  429. package/dist/trace/flatten.js +173 -0
  430. package/dist/trace/format.d.ts +47 -0
  431. package/dist/trace/format.js +329 -0
  432. package/dist/trace/html-template.d.ts +19 -0
  433. package/dist/trace/html-template.js +150 -0
  434. package/dist/trace/html.d.ts +104 -0
  435. package/dist/trace/html.js +292 -0
  436. package/dist/trace/preview.d.ts +29 -0
  437. package/dist/trace/preview.js +59 -0
  438. package/dist/trace/query-session.d.ts +14 -0
  439. package/dist/trace/query-session.js +38 -0
  440. package/dist/trace/query.d.ts +55 -0
  441. package/dist/trace/query.js +188 -0
  442. package/dist/trace/session.d.ts +40 -0
  443. package/dist/trace/session.js +168 -0
  444. package/dist/trace/types.d.ts +243 -0
  445. package/dist/trace/types.js +13 -0
  446. package/dist/tui/components/editor-paste.d.ts +2 -0
  447. package/dist/tui/components/editor-paste.js +6 -3
  448. package/dist/tui/components/editor.js +2 -2
  449. package/dist/tui/components/loader.d.ts +3 -0
  450. package/dist/tui/components/loader.js +14 -0
  451. package/dist/tui/components/settings-list.d.ts +61 -0
  452. package/dist/tui/components/settings-list.js +135 -0
  453. package/dist/tui/keybindings.d.ts +6 -1
  454. package/dist/tui/keybindings.js +18 -6
  455. package/dist/tui.d.ts +1 -0
  456. package/dist/tui.js +2 -0
  457. package/docs/en/host-api.md +167 -0
  458. package/docs/en/permissions.md +214 -0
  459. package/docs/en/providers.md +625 -0
  460. package/docs/en/rpc.md +361 -0
  461. package/docs/en/sessions.md +219 -0
  462. package/docs/en/tui.md +527 -0
  463. package/docs/host-api.md +18 -17
  464. package/docs/memory.md +172 -0
  465. package/docs/permissions.md +3 -2
  466. package/docs/providers.md +67 -1
  467. package/docs/rpc.md +31 -3
  468. package/docs/session-format.md +8 -2
  469. package/docs/sessions.md +29 -1
  470. package/docs/tui.md +189 -21
  471. package/package.json +8 -3
@@ -10,32 +10,75 @@
10
10
  * - `/rewind`(无参数)打开回滚列表与确认面板(rewind-flow.ts);带参数走 commands-core,对话变了时
11
11
  * 重画消息区并回填原消息。
12
12
  * - 不是命令(含模板与 `/skill:`)返回 false,调用方把整行当提示发出。
13
+ * - [W6-C0] 面板钩子:`/config`(W6-S)、`/trace`(W6-T1)、`/memory`(W6-M)按 `PANEL_COMMANDS` 表分派到
14
+ * `CommandUi` 的可选钩子;`/tasks` 无参在有 `agentBar` 时聚焦 Agent 栏、`/tasks <id>` 在有 `agentView` 时
15
+ * 直接进子 Agent 视图(W6-A)。钩子没装时回落 commands-core(line 模式同一套文本)。
13
16
  */
17
+ import { msg } from "../../i18n/index.js";
14
18
  import { formatModelRef } from "../../ai/providers/channels.js";
15
19
  import { AgentSessionImpl } from "../../agent/session.js";
20
+ import { THINKING_LEVELS } from "../../ai/thinking.js";
16
21
  import { listSessions } from "../../cli/compose-store.js";
17
22
  import { hideFakeProvider } from "../../cli/fake-visibility.js";
18
- import { AUTO_LAYER_TEXT, permissionModeLabel } from "../../permissions/modes.js";
23
+ import { autoLayerText, permissionModeLabel } from "../../permissions/modes.js";
19
24
  import { BUILTIN_COMMANDS, parseSlash, runSlashCommand, } from "../commands-core.js";
20
25
  import { contentText } from "./message-view.js";
21
26
  import { cachePanel, permissionsPanel, sessionPanel } from "./panels.js";
22
27
  import { modelItems, permissionPickerSpec, sessionItems, thinkingItems, treeItems, } from "./pickers.js";
28
+ /** 说明随界面语言(取用时求值)。 */
29
+ function command(name, key) {
30
+ return {
31
+ name,
32
+ get description() {
33
+ return msg().interactive.commands[key];
34
+ },
35
+ };
36
+ }
23
37
  export const INTERACTIVE_COMMANDS = [
24
- { name: "tree", description: "浏览会话树,回到某条消息之前重写" },
25
- { name: "permissions", description: "权限模式、判定顺序与规则" },
38
+ command("tree", "treeDescription"),
39
+ command("permissions", "permissionsDescription"),
26
40
  ];
27
41
  /** 补全与 /help 用的完整命令表。 */
28
42
  export const ALL_COMMANDS = [...BUILTIN_COMMANDS, ...INTERACTIVE_COMMANDS];
29
- export const KEY_HINTS = [
30
- "Enter 发送(运行中 = 插话) Alt+Enter 排到本轮之后 Shift+Enter / Ctrl+J 换行",
31
- "Esc 中断(排队消息回填编辑器) Alt+↑ 取回最后一条排队消息",
32
- "空闲时 Esc Esc:输入框为空 = 回滚(/rewind),有字 = 清空(↑ 取回)",
33
- "Shift+Tab 切换权限模式 Ctrl+L 模型 Ctrl+T 思考级别 Ctrl+O 展开工具输出与思考",
34
- "审批:1–3 或 ↑↓ Enter 选择,y 允许 a 本会话允许同类 n / Esc 拒绝 v 完整输入",
35
- "计划审批:1 批准 2 新上下文执行 3 继续修改 4 放弃并退出 Plan e 编辑计划 Esc 留在 Plan",
36
- "Ctrl+V 粘贴剪贴板图片(插入 @路径) Ctrl+G 底部信息行两行 / 一行",
37
- "Ctrl+C 清空输入(再按退出) Ctrl+D 空输入时退出 Tab 补全 @ 引用文件",
38
- ].join("\n");
43
+ /** `/help` 末尾的按键说明(多行)。 */
44
+ export function keyHints() {
45
+ return msg().interactive.commands.keyHints;
46
+ }
47
+ /**
48
+ * [W6-C0] 面板类命令 → `CommandUi` 钩子。钩子存在就调用并返回 true;不存在返回 false(回落 commands-core)。
49
+ */
50
+ export const PANEL_COMMANDS = {
51
+ config: async (ui, args) => {
52
+ if (ui.configPanel === undefined)
53
+ return false;
54
+ await ui.configPanel(args);
55
+ return true;
56
+ },
57
+ trace: async (ui, args) => {
58
+ if (ui.traceView === undefined)
59
+ return false;
60
+ await ui.traceView(args === "" ? undefined : args);
61
+ return true;
62
+ },
63
+ memory: async (ui, args) => {
64
+ if (ui.memoryPanel === undefined)
65
+ return false;
66
+ await ui.memoryPanel(args);
67
+ return true;
68
+ },
69
+ tasks: async (ui, args) => {
70
+ const parts = args.split(/\s+/).filter((s) => s !== "");
71
+ if (parts.length === 0 && ui.agentBar !== undefined) {
72
+ await ui.agentBar();
73
+ return true;
74
+ }
75
+ if (parts.length === 1 && parts[0] !== "stop" && ui.agentView !== undefined) {
76
+ await ui.agentView(parts[0]);
77
+ return true;
78
+ }
79
+ return false;
80
+ },
81
+ };
39
82
  function homeOf(ui) {
40
83
  return ui.home !== undefined ? { home: ui.home } : {};
41
84
  }
@@ -51,25 +94,23 @@ export function activeBranchIds(session) {
51
94
  return new Set(session.entries.map((e) => e.id));
52
95
  }
53
96
  export function permissionsText(runtime, session) {
97
+ const m = msg().interactive.commands;
98
+ const p = msg().panels.permissions;
54
99
  const rules = runtime.permission.rules;
55
100
  const mode = session.state.permissionMode;
56
- const order = mode === "auto"
57
- ? "deny 规则 → Hook deny → 危险命令确认 → 规则层(受保护路径、项目外写入、网络、删除类)→ Hook ask → allow 规则 / Hook allow / 本会话记忆 → 静态判定(只读、项目内写入、安全名单)→ 模型分类器 → 询问"
58
- : mode === "allowlist"
59
- ? "deny 规则 → Hook deny → 危险命令(拒绝)→ 只读工具 / allow 规则 / Hook allow 放行 → 其余拒绝(从不询问)"
60
- : "deny 规则 → Hook deny → 危险命令确认 → 权限模式 → allow 规则 / Hook allow / 本会话记忆 → 询问";
101
+ const order = mode === "auto" ? p.orderAuto : mode === "allowlist" ? p.orderAllowlist : p.orderDefault;
61
102
  const lines = [
62
- `权限模式:${permissionModeLabel(mode)}(${mode})`,
63
- `判定顺序:${order}`,
64
- rules.length === 0 ? "规则:(无)" : `规则(${rules.length}):`,
103
+ m.permissionsMode(permissionModeLabel(mode), mode),
104
+ m.permissionsOrder(order),
105
+ rules.length === 0 ? m.rulesNone : m.rules(rules.length),
65
106
  ...rules.map((r) => ` ${r.effect === "deny" ? "deny " : "allow"} ${r.raw} [${r.source}]`),
66
107
  ];
67
108
  const recent = runtime.permission.autoDecisions?.() ?? [];
68
109
  if (recent.length > 0) {
69
- lines.push(`最近的 auto 判定(${recent.length}):`);
110
+ lines.push(m.recentAuto(recent.length));
70
111
  for (const d of recent) {
71
- const cached = d.cached === true ? "(缓存)" : "";
72
- lines.push(` ${AUTO_LAYER_TEXT[d.layer]} ${d.decision} ${d.toolName} ${d.summary} — ${d.reason}${cached}`);
112
+ const cached = d.cached === true ? p.cached : "";
113
+ lines.push(` ${autoLayerText(d.layer)} ${d.decision} ${d.toolName} ${d.summary} — ${d.reason}${cached}`);
73
114
  }
74
115
  }
75
116
  return lines.join("\n");
@@ -78,7 +119,7 @@ async function pickTree(ui, title) {
78
119
  const session = ui.session();
79
120
  const items = treeItems(session.entries, activeBranchIds(session), ui.now());
80
121
  if (items.length === 0) {
81
- ui.notice("info", "还没有用户消息");
122
+ ui.notice("info", msg().interactive.commands.noUserMessages);
82
123
  return undefined;
83
124
  }
84
125
  const active = [...items].reverse().find((i) => i.label.includes("● "));
@@ -94,14 +135,14 @@ async function pickTree(ui, title) {
94
135
  async function treeCommand(ui) {
95
136
  const session = ui.session();
96
137
  if (!(session instanceof AgentSessionImpl)) {
97
- ui.notice("warn", "当前会话不支持 /tree");
138
+ ui.notice("warn", msg().interactive.commands.treeUnsupported);
98
139
  return;
99
140
  }
100
141
  if (session.state.isStreaming) {
101
- ui.notice("warn", "运行中不能切换分支(先 Esc 中断)");
142
+ ui.notice("warn", msg().interactive.commands.treeBusy);
102
143
  return;
103
144
  }
104
- const entry = await pickTree(ui, "会话树");
145
+ const entry = await pickTree(ui, msg().interactive.commands.treeTitle);
105
146
  if (entry === undefined)
106
147
  return;
107
148
  await session.navigate(entry.parentId);
@@ -110,12 +151,13 @@ async function treeCommand(ui) {
110
151
  }
111
152
  async function handlePick(what, ui) {
112
153
  const session = ui.session();
154
+ const m = msg().interactive.commands;
113
155
  switch (what) {
114
156
  case "model": {
115
157
  const current = session.state.model;
116
158
  const ref = current === undefined ? undefined : `${current.provider}/${current.id}`;
117
159
  const picked = await ui.pick({
118
- title: "选择模型",
160
+ title: m.modelTitle,
119
161
  items: await modelItems(ui.env === undefined
120
162
  ? ui.runtime.providers
121
163
  : hideFakeProvider(ui.runtime.providers, ui.env)),
@@ -126,7 +168,7 @@ async function handlePick(what, ui) {
126
168
  if (picked === undefined)
127
169
  return;
128
170
  await session.setModel(picked.value);
129
- ui.notice("info", `模型:${picked.value}`);
171
+ ui.notice("info", m.modelSet(picked.value));
130
172
  return;
131
173
  }
132
174
  case "session": {
@@ -136,26 +178,26 @@ async function handlePick(what, ui) {
136
178
  });
137
179
  const others = items.filter((i) => i.id !== session.state.sessionId);
138
180
  if (others.length === 0) {
139
- ui.notice("info", "本目录没有其它会话");
181
+ ui.notice("info", m.noOtherSessions);
140
182
  return;
141
183
  }
142
184
  const picked = await ui.pick({
143
- title: "恢复会话",
185
+ title: m.resumeTitle,
144
186
  items: sessionItems(others, ui.now()),
145
187
  filterable: true,
146
188
  });
147
189
  if (picked === undefined)
148
190
  return;
149
191
  const next = await ui.switchSession({ kind: "resume", id: picked.value });
150
- ui.notice("info", `已恢复会话 ${next.state.sessionId.slice(0, 8)}`);
192
+ ui.notice("info", m.resumed(next.state.sessionId.slice(0, 8)));
151
193
  return;
152
194
  }
153
195
  case "tree": {
154
- const entry = await pickTree(ui, "从哪条消息之前分叉");
196
+ const entry = await pickTree(ui, m.forkTitle);
155
197
  if (entry === undefined)
156
198
  return;
157
199
  const next = await ui.switchSession(entry.parentId === null ? { kind: "new" } : { kind: "fork", entryId: entry.parentId });
158
- ui.notice("info", `已分叉到新会话 ${next.state.sessionId.slice(0, 8)}`);
200
+ ui.notice("info", m.forked(next.state.sessionId.slice(0, 8)));
159
201
  ui.setEditorText(userEntryText(entry) ?? "");
160
202
  return;
161
203
  }
@@ -164,8 +206,13 @@ async function handlePick(what, ui) {
164
206
  if (picked === undefined)
165
207
  return;
166
208
  const mode = picked.value;
209
+ const current = session.state.permissionMode;
210
+ if (mode !== current && ui.confirmMode !== undefined && !(await ui.confirmMode(mode))) {
211
+ ui.notice("info", m.modeCancelled(permissionModeLabel(current)));
212
+ return;
213
+ }
167
214
  session.setPermissionMode(mode);
168
- ui.notice("info", `权限模式:${permissionModeLabel(mode)}`);
215
+ ui.notice("info", m.modeSet(permissionModeLabel(mode)));
169
216
  return;
170
217
  }
171
218
  case "thinking": {
@@ -173,9 +220,13 @@ async function handlePick(what, ui) {
173
220
  const found = model === undefined ? undefined : ui.runtime.providers.findModel(formatModelRef(model));
174
221
  const reasoning = found?.ok === true ? found.model.reasoning : true;
175
222
  const picked = await ui.pick({
176
- title: "思考级别",
223
+ title: m.thinkingTitle,
177
224
  items: thinkingItems(reasoning),
178
225
  selected: session.state.thinkingLevel,
226
+ currentValue: session.state.thinkingLevel,
227
+ filterable: false,
228
+ numberKeys: true,
229
+ footer: m.numberFooter("↑↓", THINKING_LEVELS.length),
179
230
  });
180
231
  if (picked !== undefined)
181
232
  session.setThinkingLevel(picked.value);
@@ -191,6 +242,9 @@ export async function runInteractiveCommand(line, ui) {
191
242
  try {
192
243
  if (ui.extra !== undefined && (await ui.extra(parsed.name, parsed.args)))
193
244
  return true;
245
+ const panel = PANEL_COMMANDS[parsed.name];
246
+ if (panel !== undefined && (await panel(ui, parsed.args)))
247
+ return true;
194
248
  if (parsed.name === "tree") {
195
249
  await treeCommand(ui);
196
250
  return true;
@@ -222,6 +276,7 @@ export async function runInteractiveCommand(line, ui) {
222
276
  runtime: ui.runtime,
223
277
  session: () => ui.session(),
224
278
  switchSession: (request) => ui.switchSession(request),
279
+ ...(ui.confirmMode !== undefined ? { confirmPermissionMode: ui.confirmMode } : {}),
225
280
  });
226
281
  if (result === undefined)
227
282
  return false;
@@ -240,7 +295,7 @@ export async function runInteractiveCommand(line, ui) {
240
295
  ui.reload();
241
296
  if (parsed.name === "help") {
242
297
  const extra = INTERACTIVE_COMMANDS.map((c) => `/${c.name} ${c.description}`);
243
- ui.notice("info", [result.message ?? "", ...extra, "", KEY_HINTS].join("\n"));
298
+ ui.notice("info", [result.message ?? "", ...extra, "", keyHints()].join("\n"));
244
299
  }
245
300
  else if (result.message !== undefined)
246
301
  ui.notice("info", result.message);
@@ -7,6 +7,7 @@
7
7
  * 最多 50 项;查询含 glob 字符时按 glob(tools/glob.ts)匹配相对路径,否则按「文件名前缀 → 路径前缀 →
8
8
  * 文件名子串 → 路径子串」排序。目录以 `/` 结尾,可继续补全。文件表按 cwd 缓存 15 秒、最多 20 000 项。
9
9
  */
10
+ import { msg } from "../../i18n/index.js";
10
11
  import { GlobMatcher } from "../../tools/glob.js";
11
12
  import { walk } from "../../tools/ignore.js";
12
13
  export const MAX_FILE_ITEMS = 50;
@@ -82,7 +83,7 @@ export class InteractiveCompletion {
82
83
  : command.description, command.name);
83
84
  }
84
85
  for (const prompt of this.source.prompts())
85
- push(prompt.name, "提示模板", prompt.name);
86
+ push(prompt.name, msg().interactive.completion.promptTemplate, prompt.name);
86
87
  for (const skill of this.source.skills()) {
87
88
  push(`skill:${skill.name}`, skill.description, `skill:${skill.name}`);
88
89
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * `/config` settings panel component (docs/wave6-plan.md §6.3, D24-D26). [W6-S]
3
+ *
4
+ * Bottom overlay with the card bar: title + write target (`Tab` switches user / project), a search line,
5
+ * the grouped `SettingsList`, a rule, the selected key's description (key-docs, dim, wrapped), one notice
6
+ * line and the key hints. What a key press *does* to a setting is the controller's job (config-ui.ts):
7
+ * the panel calls `activate(key)` for Enter / Space, `unset(key)` for a confirmed Backspace / Delete,
8
+ * `commit(key, text)` for the inline number / text editor, `switchScope()` for Tab and `close()` for Esc.
9
+ *
10
+ * Keys: ↑↓ (and Ctrl+P / Ctrl+N) move; `/` starts a search (typing filters, Esc clears it first, a
11
+ * second Esc closes); Backspace / Delete asks once, then unsets; inside the editor Enter saves (an
12
+ * invalid value stays in the box with the error in red), Esc cancels.
13
+ */
14
+ import { type Component, type Focusable, type Keybindings, type SemanticColor, type SettingsRow, type Theme } from "../../tui.js";
15
+ import { type ConfigSnapshot, type EditScope, type SettingValue } from "../../config/edit.js";
16
+ import { type SettingSpec } from "../../config/settings-registry.js";
17
+ export interface ConfigPanelDeps {
18
+ theme: Theme;
19
+ keybindings?: Keybindings;
20
+ /** Terminal rows (the panel fits in `rows - 1`). */
21
+ rows?(): number;
22
+ /** Write target paths for the title (already shortened with ~). */
23
+ paths: Readonly<Record<EditScope, string>>;
24
+ /** Embedded host (profile): the title says changes go to the user config. */
25
+ embedded?: boolean;
26
+ activate(key: string, scope: EditScope): void | Promise<void>;
27
+ unset(key: string, scope: EditScope): void | Promise<void>;
28
+ /** Inline editor: returns an error message to keep the box open. */
29
+ commit(key: string, text: string, scope: EditScope): Promise<string | undefined>;
30
+ close(): void;
31
+ requestRender(): void;
32
+ }
33
+ /** Display text of a setting value (unset ASCII shows the detected value). */
34
+ export declare function settingValueText(spec: SettingSpec, value: SettingValue, theme: Theme): string;
35
+ /** Why the row cannot be changed in `scope` (undefined: editable). */
36
+ export declare function blockedReason(spec: SettingSpec, value: SettingValue, scope: EditScope): string | undefined;
37
+ /** Panel rows for a snapshot (pure; frame tests use it directly). */
38
+ export declare function settingRows(snapshot: ConfigSnapshot, scope: EditScope, theme: Theme): SettingsRow[];
39
+ /** Hint text for an "edited elsewhere" row id (undefined for settings). */
40
+ export declare function hintOf(id: string): string | undefined;
41
+ export declare class ConfigPanel implements Component, Focusable {
42
+ private snapshot;
43
+ private readonly deps;
44
+ focused: boolean;
45
+ scope: EditScope;
46
+ private readonly list;
47
+ private searching;
48
+ private edit;
49
+ private pendingUnset;
50
+ private notice;
51
+ private busy;
52
+ constructor(snapshot: ConfigSnapshot, deps: ConfigPanelDeps);
53
+ /** Selected row id (tests). */
54
+ get selectedKey(): string | undefined;
55
+ get editing(): boolean;
56
+ refresh(snapshot?: ConfigSnapshot): void;
57
+ select(key: string): void;
58
+ setNotice(text: string | undefined, color?: SemanticColor): void;
59
+ /** Open the inline editor for a number / text key. */
60
+ startEdit(key: string, initial: string): void;
61
+ handleInput(data: string): void;
62
+ private move;
63
+ /** Run a controller action; input is ignored until it settles. */
64
+ private run;
65
+ private editInput;
66
+ render(width: number): string[];
67
+ private body;
68
+ private description;
69
+ invalidate(): void;
70
+ }
@@ -0,0 +1,319 @@
1
+ /**
2
+ * `/config` settings panel component (docs/wave6-plan.md §6.3, D24-D26). [W6-S]
3
+ *
4
+ * Bottom overlay with the card bar: title + write target (`Tab` switches user / project), a search line,
5
+ * the grouped `SettingsList`, a rule, the selected key's description (key-docs, dim, wrapped), one notice
6
+ * line and the key hints. What a key press *does* to a setting is the controller's job (config-ui.ts):
7
+ * the panel calls `activate(key)` for Enter / Space, `unset(key)` for a confirmed Backspace / Delete,
8
+ * `commit(key, text)` for the inline number / text editor, `switchScope()` for Tab and `close()` for Esc.
9
+ *
10
+ * Keys: ↑↓ (and Ctrl+P / Ctrl+N) move; `/` starts a search (typing filters, Esc clears it first, a
11
+ * second Esc closes); Backspace / Delete asks once, then unsets; inside the editor Enter saves (an
12
+ * invalid value stays in the box with the error in red), Esc cancels.
13
+ */
14
+ import { Card, SettingsList, defaultKeybindings, isPasteData, isPrintableText, matchesKey, truncateToWidth, unwrapPaste, wrapTextWithAnsi, } from "../../tui.js";
15
+ import { msg } from "../../i18n/index.js";
16
+ import { formatSettingValue, getConfigValue, overriddenFor, } from "../../config/edit.js";
17
+ import { SETTING_HINTS, settingDoc, settingDynamicDefault, settingsRegistry, } from "../../config/settings-registry.js";
18
+ const ELSEWHERE = "elsewhere:";
19
+ /** Display text of a setting value (unset ASCII shows the detected value). */
20
+ export function settingValueText(spec, value, theme) {
21
+ if (value.value === undefined && spec.key === "ui.ascii")
22
+ return msg().settings.auto(String(theme.glyphs.ascii));
23
+ return formatSettingValue(value.value);
24
+ }
25
+ function lockReason(value) {
26
+ const reasons = msg().settings.lockReason;
27
+ if (value.source === "env")
28
+ return reasons.env(value.envName ?? "");
29
+ if (value.source === "cli")
30
+ return reasons.cli;
31
+ if (value.source === "profile")
32
+ return reasons.profile;
33
+ return reasons.project;
34
+ }
35
+ /** Why the row cannot be changed in `scope` (undefined: editable). */
36
+ export function blockedReason(spec, value, scope) {
37
+ const s = msg().settings;
38
+ if (overriddenFor(value, scope) !== undefined)
39
+ return s.errors.locked(s.labels[spec.key] ?? spec.key, lockReason(value));
40
+ if (scope === "project" && spec.project === "deny")
41
+ return s.errors.projectDenied(spec.key);
42
+ return undefined;
43
+ }
44
+ /** Panel rows for a snapshot (pure; frame tests use it directly). */
45
+ export function settingRows(snapshot, scope, theme) {
46
+ const s = msg().settings;
47
+ const labels = s.labels;
48
+ const rows = settingsRegistry().map((spec) => {
49
+ const value = getConfigValue(snapshot, spec.key);
50
+ const locked = overriddenFor(value, scope) !== undefined;
51
+ const tag = spec.prefix === true ? `${s.apply[spec.apply]} · ${s.prefixTag}` : s.apply[spec.apply];
52
+ let note;
53
+ let noteColor;
54
+ if (locked) {
55
+ note = s.locked(value.envName ?? value.source);
56
+ noteColor = "warning";
57
+ }
58
+ else if (scope === "project" && spec.project !== "any")
59
+ note = s.projectNote[spec.project];
60
+ else if (value.source !== "default")
61
+ note = s.source(value.source);
62
+ const row = {
63
+ id: spec.key,
64
+ group: s.groups[spec.group],
65
+ label: labels[spec.key] ?? spec.key,
66
+ value: settingValueText(spec, value, theme),
67
+ tag,
68
+ search: [...(spec.options ?? []), settingDoc(spec.key) ?? ""].join(" "),
69
+ };
70
+ if (value.value === undefined)
71
+ row.valueColor = "dim";
72
+ if (note !== undefined)
73
+ row.note = note;
74
+ if (noteColor !== undefined)
75
+ row.noteColor = noteColor;
76
+ if (scope === "project" && spec.project === "deny")
77
+ row.dim = true;
78
+ return row;
79
+ });
80
+ for (const hint of SETTING_HINTS) {
81
+ rows.push({
82
+ id: `${ELSEWHERE}${hint.key}`,
83
+ group: s.groups.elsewhere,
84
+ label: hint.key,
85
+ value: s.hints[hint.hint],
86
+ dim: true,
87
+ });
88
+ }
89
+ return rows;
90
+ }
91
+ /** Hint text for an "edited elsewhere" row id (undefined for settings). */
92
+ export function hintOf(id) {
93
+ if (!id.startsWith(ELSEWHERE))
94
+ return undefined;
95
+ const key = id.slice(ELSEWHERE.length);
96
+ const hint = SETTING_HINTS.find((h) => h.key === key);
97
+ return hint === undefined ? undefined : msg().settings.hints[hint.hint];
98
+ }
99
+ class Lines {
100
+ build;
101
+ constructor(build) {
102
+ this.build = build;
103
+ }
104
+ render(width) {
105
+ return this.build(width);
106
+ }
107
+ invalidate() { }
108
+ }
109
+ /** Fixed lines besides the list: title (+ embedded), search, rule, description (≤ 3), notice, footer. */
110
+ const CHROME = 8;
111
+ export class ConfigPanel {
112
+ snapshot;
113
+ deps;
114
+ focused = false;
115
+ scope = "user";
116
+ list;
117
+ searching = false;
118
+ edit;
119
+ pendingUnset;
120
+ notice;
121
+ busy = false;
122
+ constructor(snapshot, deps) {
123
+ this.snapshot = snapshot;
124
+ this.deps = deps;
125
+ this.list = new SettingsList({ theme: deps.theme, emptyText: msg().settings.noMatches });
126
+ this.refresh(snapshot);
127
+ }
128
+ /** Selected row id (tests). */
129
+ get selectedKey() {
130
+ return this.list.selectedRow?.id;
131
+ }
132
+ get editing() {
133
+ return this.edit !== undefined;
134
+ }
135
+ refresh(snapshot = this.snapshot) {
136
+ this.snapshot = snapshot;
137
+ this.list.setRows(settingRows(snapshot, this.scope, this.deps.theme));
138
+ }
139
+ select(key) {
140
+ this.list.selectId(key);
141
+ }
142
+ setNotice(text, color = "dim") {
143
+ this.notice = text === undefined ? undefined : { text, color };
144
+ }
145
+ /** Open the inline editor for a number / text key. */
146
+ startEdit(key, initial) {
147
+ this.edit = { key, text: initial };
148
+ }
149
+ handleInput(data) {
150
+ if (this.busy)
151
+ return;
152
+ const keys = this.deps.keybindings ?? defaultKeybindings;
153
+ if (this.edit !== undefined)
154
+ return this.editInput(data);
155
+ const row = this.list.selectedRow;
156
+ const unsetKey = matchesKey(data, "backspace") || matchesKey(data, "delete");
157
+ if (!unsetKey || this.searching)
158
+ this.pendingUnset = undefined;
159
+ if (keys.matches(data, "tui.select.up"))
160
+ return this.move(-1);
161
+ if (keys.matches(data, "tui.select.down"))
162
+ return this.move(1);
163
+ if (matchesKey(data, "escape") || keys.matches(data, "app.clear")) {
164
+ if (this.searching || this.list.query !== "") {
165
+ this.searching = false;
166
+ this.list.setQuery("");
167
+ return;
168
+ }
169
+ return this.deps.close();
170
+ }
171
+ if (matchesKey(data, "tab")) {
172
+ this.scope = this.scope === "user" ? "project" : "user";
173
+ this.notice = undefined;
174
+ this.refresh();
175
+ return;
176
+ }
177
+ if (this.searching) {
178
+ if (matchesKey(data, "enter") && row !== undefined)
179
+ return this.run(this.deps.activate(row.id, this.scope));
180
+ if (matchesKey(data, "backspace"))
181
+ return this.list.setQuery([...this.list.query].slice(0, -1).join(""));
182
+ const text = isPasteData(data) ? unwrapPaste(data) : isPrintableText(data) ? data : "";
183
+ if (text !== "")
184
+ this.list.setQuery(this.list.query + text.replace(/\s+/g, " "));
185
+ return;
186
+ }
187
+ if (data === "/") {
188
+ this.searching = true;
189
+ return;
190
+ }
191
+ if (row === undefined)
192
+ return;
193
+ if (matchesKey(data, "enter") || matchesKey(data, "space")) {
194
+ this.notice = undefined;
195
+ const hint = hintOf(row.id);
196
+ if (hint !== undefined)
197
+ return this.setNotice(hint);
198
+ return this.run(this.deps.activate(row.id, this.scope));
199
+ }
200
+ if (unsetKey && hintOf(row.id) === undefined) {
201
+ if (this.pendingUnset === row.id) {
202
+ this.pendingUnset = undefined;
203
+ return this.run(this.deps.unset(row.id, this.scope));
204
+ }
205
+ this.pendingUnset = row.id;
206
+ this.setNotice(msg().settings.confirmUnset(row.id), "warning");
207
+ }
208
+ }
209
+ move(delta) {
210
+ this.list.move(delta);
211
+ this.pendingUnset = undefined;
212
+ if (!this.searching)
213
+ this.notice = undefined;
214
+ }
215
+ /** Run a controller action; input is ignored until it settles. */
216
+ run(result) {
217
+ if (!(result instanceof Promise))
218
+ return;
219
+ this.busy = true;
220
+ void result.finally(() => {
221
+ this.busy = false;
222
+ this.deps.requestRender();
223
+ });
224
+ }
225
+ editInput(data) {
226
+ const edit = this.edit;
227
+ if (matchesKey(data, "escape") ||
228
+ (this.deps.keybindings ?? defaultKeybindings).matches(data, "app.clear")) {
229
+ this.edit = undefined;
230
+ return;
231
+ }
232
+ if (matchesKey(data, "enter")) {
233
+ this.busy = true;
234
+ void this.deps
235
+ .commit(edit.key, edit.text, this.scope)
236
+ .then((error) => {
237
+ if (error === undefined)
238
+ this.edit = undefined;
239
+ else
240
+ edit.error = error;
241
+ })
242
+ .finally(() => {
243
+ this.busy = false;
244
+ this.deps.requestRender();
245
+ });
246
+ return;
247
+ }
248
+ if (matchesKey(data, "backspace"))
249
+ edit.text = [...edit.text].slice(0, -1).join("");
250
+ else if (isPasteData(data))
251
+ edit.text += unwrapPaste(data).replace(/\s+/g, " ");
252
+ else if (isPrintableText(data))
253
+ edit.text += data;
254
+ else
255
+ return;
256
+ delete edit.error;
257
+ }
258
+ render(width) {
259
+ const { theme } = this.deps;
260
+ const s = msg().settings;
261
+ const rows = this.deps.rows?.();
262
+ const embedded = this.deps.embedded === true ? 1 : 0;
263
+ if (rows !== undefined)
264
+ this.list.maxVisible = rows - 1 - CHROME - embedded;
265
+ const subtitle = `${s.writeTo(s.scope[this.scope], this.deps.paths[this.scope])} ${s.tabSwitch}`;
266
+ const card = new Card(new Lines((w) => this.body(w)), { theme, title: s.title, subtitle });
267
+ return card.render(width);
268
+ }
269
+ body(width) {
270
+ const { theme } = this.deps;
271
+ const s = msg().settings;
272
+ const out = [];
273
+ if (this.deps.embedded === true)
274
+ out.push(theme.fg("warning", s.embedded));
275
+ const query = this.list.query;
276
+ out.push(this.searching || query !== ""
277
+ ? `${s.searchPrompt(query)}${this.searching && this.edit === undefined ? theme.fg("accent", "_") : ""}`
278
+ : theme.fg("dim", s.searchPlaceholder));
279
+ out.push(...this.list.render(width));
280
+ out.push(theme.fg("border", theme.glyphs.rule.repeat(Math.max(1, width))));
281
+ out.push(...this.description(width));
282
+ if (this.edit !== undefined) {
283
+ const label = s.labels[this.edit.key] ?? this.edit.key;
284
+ const box = `${s.editPrompt(label)}${this.edit.text}${theme.fg("accent", "_")}`;
285
+ out.push(truncateToWidth(box, width));
286
+ if (this.edit.error !== undefined)
287
+ out.push(theme.fg("error", truncateToWidth(this.edit.error, width)));
288
+ }
289
+ else if (this.notice !== undefined) {
290
+ out.push(...wrapTextWithAnsi(theme.fg(this.notice.color, this.notice.text), width).slice(0, 2));
291
+ }
292
+ const footer = this.edit !== undefined
293
+ ? s.footerEdit
294
+ : this.searching
295
+ ? s.footerSearch
296
+ : width < 72
297
+ ? s.footerCompact
298
+ : s.footer;
299
+ out.push(theme.fg("dim", truncateToWidth(footer, width)));
300
+ return out;
301
+ }
302
+ description(width) {
303
+ const { theme } = this.deps;
304
+ const row = this.list.selectedRow;
305
+ if (row === undefined || hintOf(row.id) !== undefined)
306
+ return [];
307
+ const doc = settingDoc(row.id) ?? "";
308
+ const value = getConfigValue(this.snapshot, row.id);
309
+ const dynamic = value.value === undefined ? settingDynamicDefault(row.id) : undefined;
310
+ const text = dynamic === undefined ? doc : `${doc} · ${dynamic}`;
311
+ const spec = settingsRegistry().find((x) => x.key === row.id);
312
+ const blocked = spec === undefined ? undefined : blockedReason(spec, value, this.scope);
313
+ const lines = blocked === undefined ? [] : [theme.fg("warning", truncateToWidth(blocked, width))];
314
+ if (text !== "")
315
+ lines.push(...wrapTextWithAnsi(theme.fg("dim", text), width));
316
+ return lines.slice(0, 3);
317
+ }
318
+ invalidate() { }
319
+ }