@armadra/agent 0.6.2 → 0.6.4

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 (147) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/CHANGELOG.zh-CN.md +59 -0
  3. package/README.md +168 -575
  4. package/README.zh-CN.md +165 -596
  5. package/dist/agent/queue.d.ts +9 -0
  6. package/dist/agent/queue.js +27 -0
  7. package/dist/agent/session-subagent.d.ts +1 -0
  8. package/dist/agent/session-subagent.js +9 -0
  9. package/dist/agent/session.d.ts +11 -0
  10. package/dist/agent/session.js +29 -2
  11. package/dist/agent/subagent-background.d.ts +75 -0
  12. package/dist/agent/subagent-background.js +209 -0
  13. package/dist/agent/subagent-direct.d.ts +5 -2
  14. package/dist/agent/subagent-direct.js +34 -1
  15. package/dist/agent/subagent-registry.d.ts +26 -6
  16. package/dist/agent/subagent-registry.js +66 -94
  17. package/dist/agent/types-w5.d.ts +11 -1
  18. package/dist/agent/types.d.ts +12 -0
  19. package/dist/agents/builtin.js +0 -1
  20. package/dist/agents/external.js +0 -1
  21. package/dist/agents/parse.js +4 -3
  22. package/dist/agents/result.d.ts +7 -1
  23. package/dist/agents/result.js +20 -1
  24. package/dist/agents/task-control.d.ts +13 -2
  25. package/dist/agents/task-record.d.ts +16 -4
  26. package/dist/agents/task-record.js +32 -0
  27. package/dist/agents/types.d.ts +5 -1
  28. package/dist/ai/apis/chatgpt-rate-limits.js +7 -2
  29. package/dist/ai/providers/discovered-cache.d.ts +10 -4
  30. package/dist/ai/providers/discovered-cache.js +30 -14
  31. package/dist/ai/types.d.ts +2 -1
  32. package/dist/auth/chatgpt/backend-client.d.ts +6 -5
  33. package/dist/auth/chatgpt/backend-client.js +8 -7
  34. package/dist/bundle/ama.cjs +2034 -552
  35. package/dist/cli/compose-agents.d.ts +2 -1
  36. package/dist/cli/compose-agents.js +11 -2
  37. package/dist/cli/subcommands/models-discover.d.ts +1 -0
  38. package/dist/cli/subcommands/models-discover.js +12 -2
  39. package/dist/config/json-schema.js +10 -2
  40. package/dist/config/key-docs.js +9 -2
  41. package/dist/config/merge.d.ts +1 -1
  42. package/dist/config/merge.js +20 -3
  43. package/dist/config/schema-w5.js +4 -2
  44. package/dist/config/schema.js +4 -0
  45. package/dist/config/settings-registry.js +5 -0
  46. package/dist/config/types-w5.d.ts +10 -0
  47. package/dist/config/types-w5.js +2 -0
  48. package/dist/config/types.d.ts +5 -1
  49. package/dist/drivers/runner.js +29 -2
  50. package/dist/git/info.d.ts +19 -0
  51. package/dist/git/info.js +64 -8
  52. package/dist/i18n/catalog.d.ts +58 -8
  53. package/dist/i18n/messages/agents.d.ts +60 -0
  54. package/dist/i18n/messages/agents.js +62 -2
  55. package/dist/i18n/messages/config-keys.d.ts +8 -0
  56. package/dist/i18n/messages/config-keys.js +18 -10
  57. package/dist/i18n/messages/config.d.ts +8 -0
  58. package/dist/i18n/messages/interactive-startup.d.ts +0 -16
  59. package/dist/i18n/messages/interactive-startup.js +0 -16
  60. package/dist/i18n/messages/interactive-view.d.ts +8 -0
  61. package/dist/i18n/messages/interactive-view.js +10 -0
  62. package/dist/i18n/messages/interactive.d.ts +33 -16
  63. package/dist/i18n/messages/interactive.js +31 -0
  64. package/dist/i18n/messages/print.d.ts +4 -0
  65. package/dist/i18n/messages/print.js +4 -0
  66. package/dist/i18n/messages/report.d.ts +8 -0
  67. package/dist/i18n/messages/report.js +12 -4
  68. package/dist/i18n/messages/settings.d.ts +8 -0
  69. package/dist/i18n/messages/settings.js +8 -0
  70. package/dist/i18n/messages/subcommands-config.d.ts +4 -0
  71. package/dist/i18n/messages/subcommands-config.js +4 -0
  72. package/dist/i18n/messages/subcommands.d.ts +4 -0
  73. package/dist/index.d.ts +1 -0
  74. package/dist/modes/commands-core.js +19 -4
  75. package/dist/modes/interactive/agent-bar.d.ts +3 -1
  76. package/dist/modes/interactive/agent-bar.js +12 -5
  77. package/dist/modes/interactive/agent-ui.d.ts +21 -3
  78. package/dist/modes/interactive/agent-ui.js +82 -12
  79. package/dist/modes/interactive/agent-view.d.ts +9 -0
  80. package/dist/modes/interactive/agent-view.js +28 -4
  81. package/dist/modes/interactive/approval-dock.d.ts +51 -0
  82. package/dist/modes/interactive/approval-dock.js +112 -0
  83. package/dist/modes/interactive/approval-ui.d.ts +43 -0
  84. package/dist/modes/interactive/approval-ui.js +64 -0
  85. package/dist/modes/interactive/commands.js +4 -1
  86. package/dist/modes/interactive/event-notices.d.ts +6 -1
  87. package/dist/modes/interactive/event-notices.js +7 -1
  88. package/dist/modes/interactive/interactive-mode.d.ts +4 -2
  89. package/dist/modes/interactive/interactive-mode.js +46 -39
  90. package/dist/modes/interactive/interrupt-send.d.ts +25 -0
  91. package/dist/modes/interactive/interrupt-send.js +32 -0
  92. package/dist/modes/interactive/key-dispatch.d.ts +29 -5
  93. package/dist/modes/interactive/key-dispatch.js +97 -8
  94. package/dist/modes/interactive/line/line-mode.d.ts +1 -0
  95. package/dist/modes/interactive/line/line-mode.js +34 -7
  96. package/dist/modes/interactive/run-indicator.d.ts +34 -2
  97. package/dist/modes/interactive/run-indicator.js +62 -8
  98. package/dist/modes/interactive/session-events.js +5 -1
  99. package/dist/modes/interactive/startup-header.d.ts +29 -14
  100. package/dist/modes/interactive/startup-header.js +93 -59
  101. package/dist/modes/interactive/startup-logo.d.ts +83 -0
  102. package/dist/modes/interactive/startup-logo.js +183 -0
  103. package/dist/modes/interactive/status-area.d.ts +18 -0
  104. package/dist/modes/interactive/status-area.js +67 -1
  105. package/dist/modes/interactive/status-bar.d.ts +13 -2
  106. package/dist/modes/interactive/status-bar.js +60 -17
  107. package/dist/modes/interactive/status-line.d.ts +11 -8
  108. package/dist/modes/interactive/status-line.js +52 -39
  109. package/dist/modes/interactive/status-quota.d.ts +58 -0
  110. package/dist/modes/interactive/status-quota.js +155 -0
  111. package/dist/modes/interactive/subagent-view.d.ts +1 -0
  112. package/dist/modes/interactive/subagent-view.js +8 -0
  113. package/dist/modes/interactive/task-background.d.ts +31 -0
  114. package/dist/modes/interactive/task-background.js +68 -0
  115. package/dist/modes/interactive/tool-view.d.ts +7 -1
  116. package/dist/modes/interactive/tool-view.js +24 -1
  117. package/dist/modes/print/print-mode.d.ts +11 -0
  118. package/dist/modes/print/print-mode.js +36 -1
  119. package/dist/modes/rpc/commands.d.ts +4 -1
  120. package/dist/modes/rpc/commands.js +24 -2
  121. package/dist/rpc.d.ts +20 -0
  122. package/dist/rpc.js +3 -0
  123. package/dist/tools/task-ctl.d.ts +2 -0
  124. package/dist/tools/task-ctl.js +7 -2
  125. package/dist/tools/task.d.ts +11 -0
  126. package/dist/tools/task.js +20 -2
  127. package/dist/tools/types.d.ts +6 -0
  128. package/dist/tui/components/editor.d.ts +2 -0
  129. package/dist/tui/components/editor.js +4 -0
  130. package/dist/tui/components/loader.d.ts +5 -1
  131. package/dist/tui/components/loader.js +18 -5
  132. package/dist/tui/keybindings.d.ts +15 -3
  133. package/dist/tui/keybindings.js +15 -3
  134. package/docs/agents.md +52 -28
  135. package/docs/en/host-api.md +5 -1
  136. package/docs/en/providers.md +1 -1
  137. package/docs/en/rpc.md +28 -14
  138. package/docs/en/sessions.md +3 -1
  139. package/docs/en/tui.md +106 -70
  140. package/docs/host-api.md +5 -1
  141. package/docs/providers.md +4 -2
  142. package/docs/rpc.md +28 -14
  143. package/docs/session-format.md +1 -1
  144. package/docs/sessions.md +2 -1
  145. package/docs/tui-design.md +42 -29
  146. package/docs/tui.md +106 -70
  147. package/package.json +1 -1
package/docs/en/tui.md CHANGED
@@ -15,16 +15,12 @@ Running `ama` directly in a terminal (stdin / stdout both TTYs, `TERM` not `dumb
15
15
  The visual spec (colors, glyphs, screen-by-screen mockups) is in [tui-design.md](../tui-design.md) (Chinese). Hierarchy is expressed by indentation: column 0 holds the user `›`, the tool `⏺` and notice symbols, column 2 the result connector `⎿`, column 4 the tool output; the structure stays readable without colors (`NO_COLOR`, `capture-pane` without `-e`).
16
16
 
17
17
  ```
18
- ╭──────────────────────────────────────────────────────────────╮
19
- │ ✻ ama 0.3.0 │ ← startup header (normal)
20
- │ │
21
- │ Model anthropic/claude-sonnet-4-5 · thinking medium │
22
- │ Dir ~/Projects/demo · trusted (trust.json) │
23
- │ Mode Accept edits · preset default │
24
- │ Loaded AGENTS.md · 2 Skills │
25
- │ │
26
- │ /help commands · Shift+Tab mode · Ctrl+O expand tool output │
27
- ╰──────────────────────────────────────────────────────────────╯
18
+ ▄███▄ ██▄ ▄██ ▄███▄ ama 0.6.2 ← startup header (normal)
19
+ ██▀ ▀██ ███▄ ▄███ ██▀ ▀██ anthropic/claude-sonnet-4-5@messages · thinking medium
20
+ ███████ ██ ▀█▀ ██ ███████ ~/Projects/demo · trusted (trust.json)
21
+ ██ ██ ██ ██ ██ ██ Accept edits · preset default
22
+ ▀▀ ▀▀ ▀▀ ▀▀ ▀▀ ▀▀ AGENTS.md · 2 Skill
23
+ /help commands · Shift+Tab mode · Ctrl+O expand tool output
28
24
 
29
25
  › read the README ← user message (continuation lines indented 2)
30
26
 
@@ -43,24 +39,27 @@ The visual spec (colors, glyphs, screen-by-screen mockups) is in [tui-design.md]
43
39
  ────────────────────────────────────────────────
44
40
  › Type a message, / commands, @ files, Shift+Enter newline ← input box (placeholder)
45
41
  ────────────────────────────────────────────────
46
- tps: 100 tok/s • 546 tok / 5.5s (avg 100 · ttft 1.4s) ↑12k ↓1.2k · cache 80% ♨ · [-] ← rate line (full)
47
- Accept edits claude-opus-5-5 medium | Ctx 3.0% | proj ⎇ main 5ae9e54 (+12,-3) | $0.26 | 2h24m
42
+ codemode on tps: 100 tok/s • 546 tok / 5.5s (avg 100 · ttft 1.4s) · ↑12k ↓1.2k · cache 80% ♨ · [-] ← rate line (full)
43
+ Accept edits | shift+tab to cycle claude-opus-5-5 medium | Ctx 3.0% | proj ⎇ main 5ae9e54 ↑2 (+12,-3) | $0.26 | 2h24m
44
+ Session: 10.0% | Reset: 2h 18m | Weekly: 31.0% | Weekly Reset: 6d 5h ← subscription quota line (full, ChatGPT subscription model)
48
45
  ```
49
46
 
50
- - **User messages**: start with `›`, continuation lines indented 2 columns; steers while running are marked `↳ steer`, messages queued after this turn `↳ after`, and messages injected by the host (the Armadra canvas) `↳ host` (the `origin` in the session file stays steer / followUp / host).
47
+ - **User messages**: start with `›`, continuation lines indented 2 columns; steers while running are marked `↳ steer`, messages queued after this turn `↳ after`, messages injected by the host (the Armadra canvas) `↳ host`, and the new turn opened by interrupt-and-send `↳ interrupt` (the `origin` in the session file stays steer / followUp / host / interrupt).
51
48
  - **Thinking blocks**: `ui.showThinking` = `collapsed` (default: "thinking…" → "thinking · 1.2k tokens", `Ctrl+O` expands it to an indented body of at most 60 lines) / `full` (always expanded) / `hidden`.
52
49
  - **Tool calls**: titled `⏺ tool name summary`; `⏺` is the accent color while running, green on success, red on failure. The second line after `⎿` is the result summary: lines read, `N changes · +a −b`, `exit 0 · 2.1s · 48 lines`, matches and files, `N inner calls · M lines of script output`, `sub-agent · running 1m05s` / `done · 1m42s · ↑28k ↓4.1k`; while running the summary line carries a spinner in the same frame as the bottom and the seconds. Bodies show the first 3 lines folded; `edit` shows a diff (first 12 lines, with line numbers at ≥ 60 columns); running `bash` scrolls its last 8 lines. `Ctrl+O` expands / folds everything (thinking blocks included). Inner calls of a codemode script hang under the outer call (folded, only the titles and summaries of the latest 5 are listed).
53
50
  - **Notices**: `✗` errors, `↻ retry n/m`, `!` warnings (cache misses, remaining context), `⛔` hook blocks, host notifications, explanations of denied or timed-out approvals; compaction / branch summaries are left-bar cards (`▎ context compacted 128k → 24k tokens`).
54
- - **Running**: `⠋ verb · elapsed · …`, with the verb taken from the deepest current state: waiting for confirmation (approval open), `running bash` / `running 3 tools`, `retry 2/3 · in 2s`, compacting context, replying `· ↓≈1.2k` (estimated tokens of this output), thinking.
55
- - **Status bar**: always the last line, with the mode always on the far left. In `compact` the separator is always `·` (embedding hosts parse it), in `full` it is `|`. The layout follows `ui.statusLine`: `full` (two lines) by default in a standalone terminal, `compact` (one line, same layout as before) by default in an embedding host with a profile; `Ctrl+G` or `/statusline [full|compact]` switches at runtime for this session only. With `full` the input box is the 4th line from the bottom (`compact` keeps it 3rd from the bottom).
56
- - **`full` top line (rate line)**: `tps: <rate> tok/s • <output tokens> tok / <elapsed> (avg <session average> · ttft <time to first token>)`. While streaming the rate is the instantaneous value over the last 2 s (`tps:` in the accent color); afterwards it is the request's average; whole replies generated in under 0.25 s get no rate and show `—`; elapsed time starts at the first token; in ASCII `•` becomes `*`. The right side holds usage items: `↑` input (including cache reads and writes) `↓` output · cache · re-billing · queue count · codemode · tool preset (when not default) · host status, with `[-]` at the end hinting that it folds. Only chat requests count (compaction summaries, warming and the classifier do not). When narrow, these drop in order: output / elapsed, codemode, queue count, tokens, cache, re-billing, preset, host status, avg, ttft; `tps` and `[-]` never drop.
57
- - **`full` bottom line**: on the left `permission mode | shift+tab to switch`, on the right `model thinking-level | Ctx 3.0% | <dir name> ⎇ <branch> <short commit> (+a,-d) | $cost | session duration` (Ctx with one decimal, no meter even when wide); when narrow, these drop in order: the switch hint, thinking level, line changes, directory name, branch and commit, duration, cost, context, model.
58
- - **`compact`**: one line; on the right model · thinking level · `↑ ↓` · cache · cost · re-billing · context usage · dir ⎇ branch commit +a −b · session duration · queue count · codemode · preset · host status; when narrow, these drop in order: the switch hint, host status, preset, re-billing, cost, cache, tokens, thinking level, queue count, codemode, line changes, directory name, branch and commit, duration, context, model.
59
- - **git**: branch and short commit are read directly from `.git/HEAD` (worktrees understood; detached shows only the short commit; outside git the whole part is omitted, leaving only the directory name). `+a −b` is the working tree (staged included) line diff against HEAD, computed in the background with `git diff --numstat HEAD` after a turn ends, a writing tool finishes, a rewind or `/tree`, at most once every 10 seconds; if it takes longer than 2 seconds or fails, line changes are hidden for the rest of the session; `AMA_STATUS_GIT=0` turns it off.
51
+ - **Running**: `⠋ verb · elapsed · …`, with the verb taken from the deepest current state: waiting for confirmation (approval open), `running bash` / `running 3 tools`, `retry 2/3 · in 2s`, compacting context, replying `· ↓≈1.2k` (estimated tokens of this output), thinking. While a foreground sub-agent task blocks the turn, `Ctrl+B to background` is appended; while the agent bar has tasks, `↓ Agent bar` is appended (`↓ handle approval` instead when an approval is docked): `⠏ running task · 4s · Esc to interrupt · Ctrl+B to background · ↓ Agent bar`; items are dropped whole from the end when the line does not fit. With text in the input box, `Enter queue · Ctrl+X interrupt & send` comes first (`Enter interrupt & send · Ctrl+X queue` with `ui.enterWhileRunning: "interrupt"`); with queued steers the queue's last line reads `Alt+↑ take back · Ctrl+X send now · Esc refill and interrupt`.
52
+ - **Status bar**: the mode is always on the far left; `full` splits into two sides — the left column holds state and switches (permission mode, the `shift+tab` hint, codemode, sandbox, preset, fallback), the right column holds metrics and the model (tps, usage, model, Ctx, git, cost, duration, quota), right-aligned, and a line with nothing on the left sits flush right; the status bar is the last line except for the subscription quota line in the `full` layout (`compact` always keeps it last). In `compact` the separator is always `·` (embedding hosts parse it), in `full` it is `|`. The layout follows `ui.statusLine`: `full` (two lines) by default in a standalone terminal, `compact` (one line, same layout as before) by default in an embedding host with a profile; `Ctrl+G` or `/statusline [full|compact]` switches at runtime for this session only. With `full` the input box is the 4th line from the bottom (`compact` keeps it 3rd from the bottom).
53
+ - **`full` top line (rate line)**: the left side holds switches: `codemode on|only` (with `net!` when the network is not isolated) · `sandbox` · `preset <name>` (when not default) · `→ <fallback model>` (during a fallback, yellow) · queue count · host status; it is empty when there are none. The right side is `tps: <rate> tok/s • <output tokens> tok / <elapsed> (avg <session average> · ttft <time to first token>) · ↑<input> ↓<output> · cache · re-billing · [-]`. While streaming the rate is the instantaneous value over the last 2 s (`tps:` in the accent color); afterwards it is the request's average; whole replies generated in under 0.25 s get no rate and show `—`; elapsed time starts at the first token; `↑` input includes cache reads and writes; in ASCII `•` becomes `*` and `→` becomes `->`; `[-]` at the end hints that it folds. Only chat requests count (compaction summaries, warming and the classifier do not). When narrow, the right-side metrics drop first (output / elapsed, tokens, cache, re-billing, avg, ttft), then the left-side switches (host status, queue count, preset, fallback, sandbox, codemode); `tps` and `[-]` never drop.
54
+ - **`full` bottom line**: on the left `permission mode | shift+tab to switch`, on the right `model thinking-level | Ctx 3.0% | <dir name> ⎇ <branch> <short commit> ↑N ↓N (+a,-d) | $cost | session duration` (Ctx with one decimal, no meter even when wide); when narrow, these drop in order: the switch hint, thinking level, line changes, directory name, branch and commit, duration, cost, context, model.
55
+ - **Subscription quota line** (third `full` line, below the status bar): when the current model uses a ChatGPT subscription (the `chatgpt` provider) it shows `Session: <used %> | Reset: <time to reset> | Weekly: <used %> | Weekly Reset: <time to reset>` (Chinese labels in the Chinese interface), from the latest `quota_update` (the codex flavor's `x-codex-primary/secondary-*` response headers and `codex.rate_limits` events; siwc only has it after a 429). Reset times are relative (`2h 18m`, `6d 5h`) and refresh once a minute. Before the first request the codex flavor shows "Quota: shown after the first request" (the first request brings the quota back, so holding the line avoids the row count jumping); siwc without data takes no line (it only gets a quota when over the limit, so a placeholder would stay forever); non-subscription models show nothing. Below 80 columns it compresses to `5h 10% ↻2h18m · wk 31% ↻6d5h`, dropping the reset times first when narrower. The quota line is right-aligned. Labels follow the window length rather than the primary / secondary slot: 300 minutes is `Session`, 10080 minutes is `Weekly`, any other length shows the length itself (`1d: `); without a length, primary counts as Session and secondary as Weekly (or the other one if that name is taken); windows are ordered shortest first. A window with 0 % used, no length and no reset time is the server saying "no such window" and is not shown (some plans get a single weekly window from codex, sent as primary). `Ctrl+G` / `/statusline compact` folds the quota line together with the rate line (`compact` stays a single line; hosts anchor on "last line = status bar").
56
+ - **Colors** (`full`): labels, units, separators and parentheses dim gray; the rate number purple, output / elapsed / avg blue, ttft purple; model and thinking level blue; Ctx and quota percentages by threshold green / yellow / red (≥ 70% yellow, ≥ 90% red); directory and branch green, short commit dim, `↑N` ahead orange, `↓N` behind red, `(+a,-d)` green / red; cost yellow; duration and reset times purple. All come from theme semantic colors with dark / light and 16-color mappings; `NO_COLOR` and ASCII drop the colors and keep the structure. `compact` colors are unchanged.
57
+ - **`compact`**: one line; on the right model · thinking level · `↑ ↓` · cache · cost · re-billing · context usage · dir ⎇ branch commit +a −b · session duration · queue count · codemode · preset · host status · a short subscription quota item (`5h 10% wk 31%`, only with quota data; no `·` inside the item); when narrow, these drop in order: the quota item, the switch hint, host status, preset, re-billing, cost, cache, tokens, thinking level, queue count, codemode, line changes, directory name, branch and commit, duration, context, model.
58
+ - **git**: branch and short commit are read directly from `.git/HEAD` (worktrees understood; detached shows only the short commit; outside git the whole part is omitted, leaving only the directory name). `+a −b` is the working tree (staged included) line diff against HEAD, computed in the background with `git diff --numstat HEAD` after a turn ends, a writing tool finishes, a rewind or `/tree`, at most once every 10 seconds; if it takes longer than 2 seconds or fails, line changes are hidden for the rest of the session; `AMA_STATUS_GIT=0` turns it off. In `full`, `↑N` / `↓N` count the commits the current branch is ahead of / behind its upstream: when the branch has an upstream in the git config (`branch.<name>.merge`), the same throttled cycle then runs `git rev-list --left-right --count @{upstream}...HEAD` (same 2-second timeout; after a timeout it stops for the session); zero, no upstream or detached shows nothing; ASCII uses `^N` / `vN`.
60
59
  - **Cost** includes sub-tasks, warming, the classifier and external agent usage priced in USD (other units only in `/session`); **duration** counts from when this process opened the current session (`Ns` / `Nm` / `NhMm`).
61
60
  - When bash commands run in the OS sandbox (`sandbox.bash: "auto"` and available on this machine, see [sandbox.md](../sandbox.md), Chinese), the usage items gain a sandbox marker, dropped first together with codemode when space runs out.
62
- - During a model fallback (`fallbackModel`: when the main model is overloaded or retries are exhausted, one retry with the fallback model) the model item shows `main model → fallback model` (the fallback in yellow); it disappears once the fallback model replies and the main model is restored, and the message area gets an explanatory line.
63
- - Model names abbreviate with width (provider dropped below 100 columns, channel below 60, version suffix below 48); `compact` at ≥ 110 columns shows context as a meter `ctx ▮▮▮▯▯▯▯▯▯▯ 34%`; changing numbers reserve their widest shape, so items never flicker in and out as values change. In ASCII mode `⎇` → `git`, `−` → `-`, `♨` → `~`.
61
+ - During a model fallback (`fallbackModel`: when the main model is overloaded or retries are exhausted, one retry with the fallback model) the `compact` model item shows `main model → fallback model` (the fallback in yellow), while `full` keeps only the main model there and shows `→ fallback model` on the left of the rate line; it disappears once the fallback model replies and the main model is restored, and the message area gets an explanatory line.
62
+ - Model names abbreviate with width (provider dropped below 100 columns, channel below 60, version suffix below 48); `compact` at ≥ 110 columns shows context as a meter `ctx ▮▮▮▯▯▯▯▯▯▯ 34%`; changing numbers reserve their widest shape, so items never flicker in and out as values change. In ASCII mode `⎇` → `git`, `−` → `-`, `♨` → `~`, `↻` → `@`.
64
63
  - **Exit**: a session summary line and the resume command are appended at the end of the message area and stay in the terminal scrollback:
65
64
 
66
65
  ```
@@ -117,26 +116,44 @@ Trade-offs: the status bar shows the latest hit rate (the session total lives in
117
116
 
118
117
  ## Keys
119
118
 
120
- | Key | Effect |
121
- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
122
- | Enter | Send; while running = steer (inserted into the current turn) |
123
- | Alt+Enter | While running, queue after this turn (followUp); when idle, same as Enter |
124
- | Shift+Enter / Ctrl+J | New line |
125
- | Esc | Interrupt: queued messages go back into the input box, then the current run stops; closes completion first when it is open |
126
- | Esc Esc (idle) | Empty input: open the rewind list (same as `/rewind`); with text: clear it and save it into input history |
127
- | Alt+↑ | Recall the last queued message |
128
- | Shift+Tab / Tab | Cycle permission modes Manual → Accept edits → Plan → Auto → Bypass permissions (Tab only on an empty input with completion closed, otherwise still completion; entering Bypass asks to confirm, see "Entering Bypass" below) |
129
- | Ctrl+O | Expand / fold tool output and thinking blocks |
130
- | Ctrl+L / Ctrl+T | Pick model / thinking level |
131
- | Ctrl+G | Bottom info line two lines (full) ↔ one line (compact), this session only |
132
- | Ctrl+V | Paste an image from the clipboard: saved in the data directory, `@<path>` inserted at the cursor (same as `/paste`) |
133
- | Ctrl+C | Clear the input; on an empty input, press again within 1.5 seconds to quit (exit code 130) |
134
- | Ctrl+D | Quit on an empty input |
135
- | Tab | Complete |
136
- | ↑ / ↓ | Browse history on a single line (`<data dir>/history`, 500 entries) |
137
- | Ctrl+B / ↓ (empty input) | Enter the agent bar (when there are sub-agent tasks; with text Ctrl+B still moves the cursor left, use ↓ in tmux), see "Sub-agents" (from wave 6 W6-A) |
138
-
139
- Keys can be overridden in `~/.config/ama/keybindings.json`: keys are action ids (`app.interrupt`, `app.rewind`, `app.message.followUp`, `app.statusLine.toggle`, `app.paste.image`, `app.agents.focus`, `tui.editor.newLine` …), values are a key or an array of keys, and an empty array disables the action. `app.rewind` is the key double-pressed while idle (Esc by default, at most 800 ms apart).
119
+ | Key | Effect |
120
+ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
121
+ | Enter | Send; while running = steer (queued until the next delivery point: the end of this model reply or of this tool batch) |
122
+ | Ctrl+X | While running, interrupt and send now: stop the current turn (model stream and running tools; tools record "aborted by user") and start a new turn with the input at once, queued steers first; with an empty input only the queued steers are sent now. When idle, same as Enter. Swapped with Enter under `ui.enterWhileRunning: "interrupt"` |
123
+ | Alt+Enter | While running, queue after this turn (followUp); when idle, same as Enter |
124
+ | Shift+Enter / Ctrl+J | New line |
125
+ | Esc | Interrupt: queued messages go back into the input box, then the current run stops (with foreground sub-agent tasks; background tasks keep running, as the hint says); closes completion first when it is open |
126
+ | Esc Esc (idle) | Empty input: open the rewind list (same as `/rewind`); with text: clear it and save it into input history |
127
+ | Alt+↑ | Recall the last queued message |
128
+ | Shift+Tab / Tab | Cycle permission modes Manual → Accept edits → Plan → Auto → Bypass permissions (Tab only on an empty input with completion closed, otherwise still completion; entering Bypass asks to confirm, see "Entering Bypass" below) |
129
+ | Ctrl+O | Expand / fold tool output and thinking blocks |
130
+ | Ctrl+L / Ctrl+T | Pick model / thinking level |
131
+ | Ctrl+G | Bottom info line two lines (full) ↔ one line (compact), this session only |
132
+ | Ctrl+V | Paste an image from the clipboard: saved in the data directory, `@<path>` inserted at the cursor (same as `/paste`) |
133
+ | Ctrl+C | Clear the input; on an empty input, press again within 1.5 seconds to quit (exit code 130) |
134
+ | Ctrl+D | Quit on an empty input |
135
+ | Tab | Complete |
136
+ | ↑ / ↓ | Browse history on a single line (`<data dir>/history`, 500 entries) |
137
+ | ↓ (empty input) | Enter the agent bar (whenever there are sub-agent tasks); with text it still moves down / through history and hints once, see "Sub-agents" |
138
+ | Ctrl+B | When foreground sub-agent tasks (or a `task_ctl wait`) block the turn, move them all to the background, whatever is in the input box; otherwise cursor left. In tmux press `C-b C-b`, see "Sub-agents" |
139
+
140
+ Keys can be overridden in `~/.config/ama/keybindings.json`: keys are action ids (`app.interrupt`, `app.rewind`, `app.message.followUp`, `app.message.interrupt`, `app.statusLine.toggle`, `app.paste.image`, `app.agents.focus`, `app.tasks.background`, `tui.editor.newLine` …), values are a key or an array of keys, and an empty array disables the action. `app.rewind` is the key double-pressed while idle (Esc by default, at most 800 ms apart).
141
+
142
+ ### Interrupt and send now
143
+
144
+ While running, Enter **queues** by default (steer): the message waits for the next delivery point, which can take a long time while the model writes a long reply or a tool runs a five-minute command. To change course right away, write the message and press **`Ctrl+X`** (action `app.message.interrupt`):
145
+
146
+ - The current turn stops at once: the model stream is cut, running tools finish as interrupted (every tool call has exactly one result, recorded as "aborted by user"), and the interrupted reply stays in the session under the abort rules. Then, **without pressing Enter again**, the text is sent as the user message of a new turn (`origin: "interrupt"` in the session, `↳ interrupt` in the message area).
147
+ - Queued steers come along: they are joined in front of the text in queue order (blank line between); messages queued after this turn (Alt+Enter) stay queued and are delivered after the new turn as usual.
148
+ - Only the foreground stops: foreground sub-agent tasks stop with the main turn; background tasks are not affected.
149
+ - Nothing happens while completion, an approval dialog or a picker is open; slash commands still run as commands; with an empty input only the queued steers are sent now (a hint says when there is nothing to send).
150
+ - The request prefix does not change: the new turn's request starts with every message of the interrupted request, so the cache keeps hitting.
151
+ - Esc is unchanged: it still interrupts and puts queued messages back into the input box (see interrupt to withdraw below); it never sends.
152
+ - To make Enter interrupt instead: set `ui.enterWhileRunning` to `interrupt` in `/config` (applies at once); Enter then interrupts and sends and `Ctrl+X` queues.
153
+
154
+ Why `Ctrl+X` by default: macOS Terminal, iTerm2, tmux and Windows Terminal all pass it through and nothing else is bound to it; `Ctrl+Enter` is indistinguishable from Enter without the kitty keyboard protocol, `Ctrl+S` may be swallowed by XOFF flow control, `Alt+Enter` is already followUp, `Alt+letter` sends no Meta by default on macOS, and `Ctrl+]` is awkward on non-US layouts. Rebind it in `keybindings.json`.
155
+
156
+ In line mode (`--no-tui`), typing `/interrupt <text>` while running does the same (when idle it is an ordinary prompt); the TUI accepts the command too. Over RPC it is `interrupt: true` on `prompt` / `steer`, see [rpc.md](rpc.md).
140
157
 
141
158
  ## Rewind
142
159
 
@@ -315,12 +332,17 @@ Sub-agents started by the `task` tool ([agents.md](../agents.md), Chinese) fold
315
332
  ⏺ task check test coverage gaps in src/tui
316
333
  ⎿ ⠋ explore · running 1m05s · 3 turns · read grep bash · ↑12k ↓3.4k
317
334
  ⏺ task background review
318
- ⎿ done · 0.0s
335
+ ⎿ started in the background
319
336
  ↳ t2 explore · running 40s · 1 turn · read
337
+ ⏺ task scan
338
+ ⎿ moved to the background · 12s
339
+ ↳ t3 explore · running 30s · 2 turns · grep
320
340
  ```
321
341
 
322
- - The status line shows the type (external agents show a runner such as `claude (claude)`), status and elapsed time, turns, the latest 3 tools and usage; after a foreground task ends it is replaced by the result summary. A background task's (`background: true`) tool call returns immediately, with an extra follow-up status line below (refreshed every second while running); when it completes, the `<task-notification>` the model receives shows in the message area as a single line (e.g. "↳ sub-agent notification: t2 explore done · 7 turns · see /tasks for output"), plus a yellow notice on failure or stop.
323
- - `/tasks`: focus the agent bar (below); `/tasks <id>` opens that task's sub-agent view directly; `/tasks stop <id>` stops it. With `ui.agentBar: "off"` (the default in embedding hosts) `/tasks` is still the task picker (newest on top, Enter shows the output, running tasks can be stopped). Line mode: `/tasks` lists, `/tasks <id>` shows output, `/tasks stop <id>` stops.
342
+ - The status line shows the type (external agents show a runner such as `claude (claude)`), status and elapsed time, turns, the latest 3 tools and usage; after a foreground task ends it is replaced by the result summary. A background task's tool call (background is the default in the interactive UI, see [agents.md](../agents.md) "foreground and background", Chinese) returns immediately; the summary line says "started in the background", with an extra follow-up status line below (refreshed every second while running). A foreground task moved to the background shows "moved to the background · elapsed" with the same follow-up line (the explanation meant for the model is hidden; `Ctrl+O` shows it). A task moved by the auto timeout (`subagents.autoBackgroundAfterMs`) or by the host also gets a one-line notice; when it completes, the `<task-notification>` the model receives shows in the message area as a single line (e.g. "↳ sub-agent notification: t2 explore done · 7 turns · see /tasks for output"), plus a yellow notice on failure or stop.
343
+ - `/tasks`: focus the agent bar (below); `/tasks <id>` opens that task's sub-agent view directly; `/tasks stop <id>` stops it; `/tasks bg [id]` moves it to the background (without an id: every blocking foreground task, same as `Ctrl+B`). With `ui.agentBar: "off"` `/tasks` is still the task picker (newest on top, Enter shows the output, running tasks can be stopped). Line mode: `/tasks` lists, `/tasks <id>` shows output, `/tasks stop <id>` stops, `/tasks bg [id]` moves to the background (typed while running it is still a command, not a steer).
344
+ - Moving to the background (`Ctrl+B`, key action `app.tasks.background`): while the main turn waits for a foreground task (`task` with `background: false`, the `-p` default, or `task_ctl wait`), press it and the tool call returns at once, the task keeps running, the main turn carries on and you can keep sending messages; when the task ends the `<task-notification>` arrives and opens a turn as usual. The hint says "Moved to the background: t2; you'll be notified when it finishes". With nothing to move, `Ctrl+B` falls through to the editor (cursor left) and is not swallowed. tmux's default prefix is `C-b`: in tmux press `C-b C-b` (default `send-prefix`) to pass it to ama, or press `b` in the agent bar; you can also rebind it in `keybindings.json`.
345
+ - Esc interrupts only the foreground: Esc while running stops the main turn and the sub-tasks still in the foreground; tasks already in the background keep running, and the hint says "Interrupted (background task t2 keeps running; Esc doesn't affect it)".
324
346
  - `/agents`: the available types: name, runner, source (built-in / user / project / profile / host), external agents marked installed with a version or not installed, plus a one-line description.
325
347
  - Notices reported by external agents themselves (budget exhausted, timeout, mode downgrade …) show in the message area as a single line `[claude · t3] …`.
326
348
 
@@ -335,11 +357,13 @@ Above the status line (below the hint line) the bar lists sub-agent tasks, one l
335
357
  1 more
336
358
  ```
337
359
 
338
- - States: queued (the concurrency pool is full) / running (elapsed time, turns, latest tool) / awaiting approval (the approval dialog currently holds its request) / done / failed / stopped (plus out of turns and interrupted); `⏺` is the accent color while running, green when done, red on failure, yellow / dim otherwise; `*` in ASCII.
360
+ - States: queued (the concurrency pool is full) / running (elapsed time, turns, latest tool) / awaiting approval (the approval dialog currently holds its request, or it is docked in the bar; the row is yellow) / done / failed / stopped (plus out of turns and interrupted); `⏺` is the accent color while running, green when done, red on failure, yellow / dim otherwise; `*` in ASCII.
339
361
  - When it shows: while any task is queued, running or awaiting approval; tasks that ended in this session and have not been looked at in the view stay until viewed, at most 10 minutes. Tasks already finished when a session is resumed are not shown (`/tasks` lists them).
340
- - Entering: `Ctrl+B` with an empty input box (whenever there are tasks), or `↓` (while the bar is visible) — tmux's default prefix swallows `Ctrl+B`, so use `↓` there; with text in the input box `Ctrl+B` still moves the cursor left and `↓` still moves down / through history. The key action is `app.agents.focus`, configurable in `keybindings.json`.
341
- - In the bar: `↑` `↓` select (lists every task of the session, the window scrolls along; `↑` on the first item returns to the input box), Enter opens the sub-agent view, Esc / `Ctrl+B` return to the input box; typing returns to the input box with the text filled in. The last line is a key hint.
342
- - Embedding hosts (with a profile) default to `ui.agentBar: "off"`: no bar, and `Ctrl+B` / `↓` go to the editor as usual.
362
+ - Entering: press `↓` with an empty input box and no completion open, whenever the session has tasks (even after the bar has collapsed, same as `/tasks`); the same inside and outside tmux. It works while a turn runs too; the `↓ Agent bar` at the end of the running line is the reminder. The key action is `app.agents.focus` (only `down` by default), configurable in `keybindings.json`.
363
+ - When the key does not get you in, a one-line hint shows for 3 seconds: text in the input box — "Input is not empty; clear it and press ↓ for the Agent bar" (once per draft, with the cursor on the last line; `↓` still moves down); the bar is off — "Agent bar is off (ui.agentBar); use /tasks"; no tasks — "No sub-agent tasks yet". While browsing input history with `↑` `↓`, `↓` only steps through history.
364
+ - `Ctrl+B` does not enter the bar; it moves foreground tasks to the background (above). For the old "`Ctrl+B` enters the bar", set `"app.agents.focus": ["down", "ctrl+b"]` in `keybindings.json` and rebind `app.tasks.background`.
365
+ - In the bar: `↑` `↓` select (lists every task of the session, the window scrolls along; `↑` on the first item returns to the input box), Enter opens the sub-agent view (a docked approval of the selected task pops up right away), `b` / `Ctrl+B` moves the selected foreground task to the background (a one-line hint when it is not running in the foreground), `x` stops the selected task (the first press hints "Press x again to stop t2"; it stops only on a second press within 1.5 seconds), Esc returns to the input box; other letters return to the input box with the text filled in. The last line is the key hint `↑↓ select · Enter open · b background · x stop · Esc back`; narrow screens drop the `b` / `x` items.
366
+ - Embedding hosts (with a profile) no longer turn the bar off by default; a host that shows sub-tasks itself and does not want the bar sets `ui.agentBar: "off"` in its profile (see [host-api.md](host-api.md) "Embedding in Armadra"). With the bar off it is not shown, and `↓` with tasks points to `/tasks`.
343
367
 
344
368
  ### Sub-agent view
345
369
 
@@ -360,8 +384,18 @@ t2 explore · running 1m05s · 3 turns · ↑12k ↓3.4k · Esc back · /tasks s
360
384
  - The body follows live: for ama sub-agents it shows every message and tool call of the sub-session (rendered like the message area); when the sub-session handle has been released (at most 16 are kept) or the session was resumed, the sub-session file is loaded read-only and live events are attached when the task runs again. External agents (claude / codex / ACP) show the live output held in this process's memory (text, thinking, tool start / end, turns, notices; at most 2000 items / 1 MB, never written to disk); after ama restarts only one line remains, saying to use the original CLI's resume <session id> for the full text.
361
385
  - With an empty input box: `↑` / PgUp scroll up (pausing follow, with "follow paused · End to resume" at the bottom), `↓` / PgDn scroll down, End (or `f` while paused) resumes following; `←` `→` switch to the previous / next task; Esc returns to the main screen. With text in the input box, Esc clears it first.
362
386
  - Enter sends the input to this sub-agent (recorded in the sub-session as a user message with `origin: "direct"`, see [session-format.md](../session-format.md), Chinese): ama sub-agent running → delivered when its current turn ends; external agent running or task still queued → continued after this run ends; finished → continued in the background (like `task_ctl send`; the main session receives the `<task-notification>` as usual when it completes). A line at the bottom reports the result. The parent session's model does not know you talked to the sub-agent directly; the result comes back through the completion notification.
363
- - Nothing is interrupted from the view: Esc only goes back. To stop the task use `/tasks stop <id>`, which also works in the view's input box (the only command the view accepts).
364
- - When the viewed task waits for approval the title says "awaiting approval", and the approval dialog pops up over the view as usual (with the `[task:<type>]` origin).
387
+ - `Ctrl+X` interrupts this sub-agent and sends now: an ama sub-agent that is running stops its current turn (tools finish as interrupted) and starts a new turn with the message at once; the task completes and notifies the main session as usual. An external agent whose driver can interrupt a single turn (ACP `session/cancel`, Claude stream-json interrupt, Codex `turn/interrupt`) is interrupted and its next turn starts with the message (and any messages queued before it); when it cannot (oneshot, host drivers) or the task is still waiting in the concurrency pool, the message is queued instead with a hint; a finished task continues in the background as with Enter. Swapped with Enter under `ui.enterWhileRunning: "interrupt"`. The main session is not affected.
388
+ - Esc in the view interrupts nothing: it only goes back. To stop the task use `/tasks stop <id>`; to move it to the background use `Ctrl+B` or `/tasks bg [id]`. Both work in the view's input box (the only commands the view accepts).
389
+ - When the viewed task waits for approval the title says "awaiting approval", and the approval dialog pops up over the view as usual (with the `[task:<type>]` origin); if its approval is docked in the bar, opening the view pops it up.
390
+
391
+ ### Docked approvals of background tasks
392
+
393
+ When a background task (including one moved to the background) needs approval, it does not interrupt what you are doing:
394
+
395
+ - While the main session is running, the input box has a draft, or another overlay is open, no dialog pops up; the request is **docked**: the task's row in the agent bar says "needs approval" (yellow) and the running line shows `↓ handle approval`; the sub-task waits meanwhile.
396
+ - It pops up on its own once the main session is idle, the input box is empty and no overlay is open; entering the bar, selecting it and pressing Enter (opening the view) pops it up immediately.
397
+ - If the main session or a foreground task asks for approval while one is docked: approvals are serialized, so the docked one pops up first and theirs follow; the main session's approvals are never held back.
398
+ - Approvals of foreground tasks and of the main session pop up immediately as before; timeouts (deny after 10 minutes by default), aborts and unattended rules are unchanged. RPC clients receive `permission_request` as usual and decide how to present it.
365
399
 
366
400
  Origin labels on approval boxes:
367
401
 
@@ -413,12 +447,13 @@ Requires memory to be enabled (`ama memory enable` or `--memory`, see [memory.md
413
447
 
414
448
  ## Startup screen
415
449
 
416
- `ui.quietStartup` / `--quiet-startup`: `normal` shows a boxed startup header: title, model and thinking level, directory (`~` abbreviated) and trust state, permission mode / preset / codemode, loaded context files / Skills / prompt templates / hooks, warning count and common keys; below 56 columns or with `ui.compact` the box is dropped and each item takes one line. `header` is a single line `✻ ama version · model · mode · /help` (the profile default); `silent` shows nothing. When `--resume` has no id, the model has no key, the session directory does not exist or project resources need trust, a small selection / input prompt appears before the interface starts, collapsing into one line on screen once answered.
450
+ `ui.quietStartup` / `--quiet-startup`: `normal` shows an "AMA" logo with an info column: version, model and thinking level, directory (`~` abbreviated) and trust state, permission mode / preset / codemode, loaded context files / Skills / prompt templates / hooks, warning count and common keys. At 72 columns or wider the logo sits on the left and the info on the right; at 48–71 columns the logo is on top; below 48 columns a two-line header is shown instead (version · model · thinking / mode · directory · trust). `ui.logo: "off"` or `ui.compact` shows only the info column. The logo takes the theme's accent → user → tool colors letter by letter; ASCII mode swaps in a glyph made of `_ / \ |`. On startup a one-off "light-up" sweep plays for about a second (the glyph starts dim, a highlight band sweeps left to right, then it settles); it redraws in place and leaves no frames in the scrollback, and any key settles it at once while the key still goes to the input box. The settled frame is shown directly with `ui.animation: false`, `NO_COLOR` / a colorless terminal, a non-TTY, an embedding host (profile.host), a `CI` environment, a prompt given on the command line (`ama "…"`), a terminal shorter than 16 rows or content taller than one screen; the line interface, `-p`, RPC and ACP draw no startup header. `header` is a single line `✻ ama version · model · mode · /help` (the profile default); `silent` shows nothing. When `--resume` has no id, the model has no key, the session directory does not exist or project resources need trust, a small selection / input prompt appears before the interface starts, collapsing into one line on screen once answered.
417
451
 
418
452
  ## In tmux / Armadra terminal nodes
419
453
 
420
454
  - Bracketed paste: enabled at startup; pasted multi-line content enters the input box as a whole (folded into a paste placeholder with the line count beyond 10 lines or 1 000 characters), and an Enter right after a paste sends directly, which suits writes from external programs.
421
455
  - Terminal capabilities are not queried, and mouse and the Kitty keyboard protocol are not enabled, so no replies get mixed into input; tmux ≥ 3.4 passes synchronized output through, and older versions display fine too.
456
+ - tmux's default prefix `C-b` is taken by the tmux client: to move tasks to the background press `C-b C-b` (`send-prefix` passes it through), or `↓` into the agent bar and press `b`; entering the bar uses `↓`, which the prefix does not affect.
422
457
  - When the window size changes the last screen is redrawn in full; history in the scrollback is unaffected.
423
458
  - Automatic fallback: non-TTY, `TERM=dumb`, `--no-tui` or a failed terminal initialization use line mode, with the same commands and approval prompts.
424
459
 
@@ -430,8 +465,9 @@ The `ui` section of `config.json` (settable at project level too):
430
465
  | ----------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
431
466
  | `ui.theme` | `dark` | `dark` / `light` / `auto`; auto only looks at `COLORFGBG` (no terminal query) and uses dark when unsure; configuring it explicitly is recommended |
432
467
  | `ui.ascii` | auto-detected | ASCII glyphs (`›` → `>`, `⏺` → `*`, `⎿` → `L`, box lines → `+ - \|`, a 4-frame spinner) |
433
- | `ui.compact` | `false` | No blank lines between message blocks, no box around the startup header |
434
- | `ui.animation` | `true` | `false`: the spinner stays still as `·` while running and redraws only when seconds change |
468
+ | `ui.compact` | `false` | No blank lines between message blocks, no logo in the startup header |
469
+ | `ui.logo` | `auto` | The "AMA" logo in the startup header; `off` shows only the info column |
470
+ | `ui.animation` | `true` | `false`: the spinner stays still as `·` while running and redraws only when seconds change; the startup logo does not animate |
435
471
  | `ui.markdown` | `true` | `false`: assistant text is not rendered as Markdown |
436
472
  | `ui.showThinking` | `collapsed` | See "Layout" |
437
473
  | `ui.quietStartup` | `normal` | See "Startup screen" |
@@ -505,29 +541,29 @@ tui.setFocus(editor);
505
541
  tui.start();
506
542
  ```
507
543
 
508
- | Export | Purpose |
509
- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
510
- | `Component`, `Focusable`, `CURSOR_MARKER` | The component contract: `render(width)` returns lines (each with visible width ≤ width), `handleInput?(data)`, `invalidate()`; the focused component emits `CURSOR_MARKER` at the cursor |
511
- | `TUI` | Root container and differential rendering (main screen, synchronized output): `addChild`, `start` / `stop`, `requestRender`, `setFocus`, `addInputListener`, `showOverlay` |
512
- | `ProcessTerminal`, `MemoryTerminal`, `VirtualScreen` | A real terminal (raw mode, bracketed paste); an in-memory terminal and a VT screen (tests, frame goldens) |
513
- | `Container`, `Text`, `TruncatedText`, `Markdown`, `Box`, `Card`, `Spacer` | Basic components; `Card` is a left-bar card, `Box` accepts `borderColor` |
514
- | `Loader` | Running indicator: `setVerb(verb, extras, { elapsed })`, `frame` / `onFrame` (changes glyph in the same frame as other components), `animation: false` |
515
- | `Editor`, `EditorBuffer`, `PasteStore` | Multi-line editor (history, the `AutocompleteProvider` completion interface, paste folding) |
516
- | `SelectList` | Filterable selection list: groups, badges, number keys, `stacked`, `currentValue` (✓), `footer` key hints |
517
- | `KeyValue`, `Meter` | Two-column aligned key-value table (`wrap` wraps aligned to the value column); a meter (`levelColor` threshold coloring) |
518
- | `compositeOverlays`, `OverlayOptions` | Overlay compositing (centered / bottom-anchored) |
519
- | `createTheme`, `plainTheme`, `detectCapabilities`, `Theme` | Themes and color capability detection (`NO_COLOR`, 16 / 256 / truecolor); 14 semantic colors, `resolveThemeName("auto")` |
520
- | `Theme.glyphs`, `UNICODE_GLYPHS`, `ASCII_GLYPHS`, `detectAscii` | Glyph tables (`›` `⏺` `⎿` `✻` `▎`, box lines, spinner frames …) with ASCII fallback; `createTheme(name, { ascii })` |
521
- | `Keybindings`, `DEFAULT_KEYBINDINGS`, `loadKeybindingsFile` | Action id → keys, overridden by `keybindings.json` |
522
- | `parseKey`, `matchesKey`, `StdinBuffer` | Key sequence parsing and Esc timeout splitting (`AMA_TUI_ESC_TIMEOUT`) |
523
- | `visibleWidth`, `truncateToWidth`, `wrapTextWithAnsi`, `sliceByColumn` … | Width computation and truncation aware of ANSI and wide characters |
544
+ | Export | Purpose |
545
+ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
546
+ | `Component`, `Focusable`, `CURSOR_MARKER` | The component contract: `render(width)` returns lines (each with visible width ≤ width), `handleInput?(data)`, `invalidate()`; the focused component emits `CURSOR_MARKER` at the cursor |
547
+ | `TUI` | Root container and differential rendering (main screen, synchronized output): `addChild`, `start` / `stop`, `requestRender`, `setFocus`, `addInputListener`, `showOverlay` |
548
+ | `ProcessTerminal`, `MemoryTerminal`, `VirtualScreen` | A real terminal (raw mode, bracketed paste); an in-memory terminal and a VT screen (tests, frame goldens) |
549
+ | `Container`, `Text`, `TruncatedText`, `Markdown`, `Box`, `Card`, `Spacer` | Basic components; `Card` is a left-bar card, `Box` accepts `borderColor` |
550
+ | `Loader` | Running indicator: `setVerb(verb, extras, { elapsed, optional })` (`optional` extras are dropped whole when the line does not fit), `frame` / `onFrame` (changes glyph in the same frame as other components), `animation: false` |
551
+ | `Editor`, `EditorBuffer`, `PasteStore` | Multi-line editor (history, the `AutocompleteProvider` completion interface, paste folding) |
552
+ | `SelectList` | Filterable selection list: groups, badges, number keys, `stacked`, `currentValue` (✓), `footer` key hints |
553
+ | `KeyValue`, `Meter` | Two-column aligned key-value table (`wrap` wraps aligned to the value column); a meter (`levelColor` threshold coloring) |
554
+ | `compositeOverlays`, `OverlayOptions` | Overlay compositing (centered / bottom-anchored) |
555
+ | `createTheme`, `plainTheme`, `detectCapabilities`, `Theme` | Themes and color capability detection (`NO_COLOR`, 16 / 256 / truecolor); 14 semantic colors, `resolveThemeName("auto")` |
556
+ | `Theme.glyphs`, `UNICODE_GLYPHS`, `ASCII_GLYPHS`, `detectAscii` | Glyph tables (`›` `⏺` `⎿` `✻` `▎`, box lines, spinner frames …) with ASCII fallback; `createTheme(name, { ascii })` |
557
+ | `Keybindings`, `DEFAULT_KEYBINDINGS`, `loadKeybindingsFile` | Action id → keys, overridden by `keybindings.json` |
558
+ | `parseKey`, `matchesKey`, `StdinBuffer` | Key sequence parsing and Esc timeout splitting (`AMA_TUI_ESC_TIMEOUT`) |
559
+ | `visibleWidth`, `truncateToWidth`, `wrapTextWithAnsi`, `sliceByColumn` … | Width computation and truncation aware of ANSI and wide characters |
524
560
 
525
561
  ## Testing
526
562
 
527
563
  Frame goldens all live in `test/fixtures/tui/`; `MemoryTerminal` reconstructs the screen (without color, verifying only layout and glyphs):
528
564
 
529
565
  - `src/modes/interactive/interactive-mode.test.ts`: a complete read-file run at 80x24 and 40x24 (startup, input, tool running, finish, `Ctrl+O` expand, exit summary) → `run-*.txt`; approvals, cache notices and more.
530
- - `src/modes/interactive/interactive-frames.test.ts`: startup headers (`startup-normal-*`, `header-quiet-*`), tool hierarchy (`tools-*`), notices (`notices-*`), running verbs (`loader-verbs-*`), the `/session` panel (`panel-session-*`), a whole run in ASCII mode (`ascii-run-*`).
566
+ - `src/modes/interactive/interactive-frames.test.ts`: startup headers (`startup-normal-*`, `header-quiet-*`; logo variants and the animation in `startup-logo.test.ts` / `startup-logo-*`), tool hierarchy (`tools-*`), notices (`notices-*`), running verbs (`loader-verbs-*`), the `/session` panel (`panel-session-*`), a whole run in ASCII mode (`ascii-run-*`).
531
567
  - Wave 5 (W5-U): `plan-dialog.test.ts` (`plan-dialog-*`: four options, execution mode, feedback, external editor, ASCII, 40 columns), `approval-origin.test.ts` (`approval-origin-*`, `approval-task-agent-*`, `approval-first-run-*`, `approval-task-external-*` and the first-run merge), `subagent-view.test.ts` (`subagent-view-*`), `tasks-panel.test.ts` (`tasks-picker-*`, `tasks-output-*`, `agents-panel-*`), `harness-notices.test.ts` (`harness-notices-*`), `interactive-w5.test.ts` (plan → approval → execution, `/plan`, background tasks into `/tasks`, Ctrl+V; `interactive-plan-*`, `interactive-tasks-*`).
532
568
  - `src/tui/tui-frames.test.ts`: component level (conversation, Markdown, editor placeholder / multi-line / paste / completion); `status-widths.txt` of `status-bar.test.ts`; approvals and mode pickers in `approval-dialog.test.ts` and `pickers.test.ts`.
533
569
 
package/docs/host-api.md CHANGED
@@ -159,4 +159,8 @@ interface WarmDecision {
159
159
 
160
160
  ## 嵌入 Armadra
161
161
 
162
- Armadra 用 profile 启动 ama:`ama --profile <path>`,profile 的 `host` 指向它的适配器(`ama-armadra.cjs`),另带 instructions、skillDirs、hooksFile、authFile、sessionDir、`trustProject`。适配器在 `ARMADRA_NODE_ID` 缺失时返回 `undefined`,同一个 profile 在画布外退化为普通 ama。契约细节见 Armadra 仓库 [docs/design/coordinator-agent.md](https://github.com/yovinchen/Armadra/blob/main/docs/design/coordinator-agent.md)。
162
+ Armadra 用 profile 启动 ama:`ama --profile <path>`,profile 的 `host` 指向它的适配器(`ama-armadra.cjs`),另带 instructions、skillDirs、hooksFile、authFile、sessionDir、`trustProject`。适配器在 `ARMADRA_NODE_ID` 缺失时返回 `undefined`,同一个 profile 在画布外退化为普通 ama。
163
+
164
+ 有 profile 时的界面缺省:`ui.quietStartup: "header"`、`ui.statusLine: "compact"`(最后一行是状态栏,宿主按 `·` 解析)。Agent 栏(`ui.agentBar`)不再缺省关闭,与独立终端一样是 `auto`;宿主自己展示子任务、不要栏时在 profile 的 `config` 指向的配置文件里写 `{ "ui": { "agentBar": "off" } }`。
165
+
166
+ 契约细节见 Armadra 仓库 [docs/design/coordinator-agent.md](https://github.com/yovinchen/Armadra/blob/main/docs/design/coordinator-agent.md)。
package/docs/providers.md CHANGED
@@ -189,8 +189,10 @@ ama auth logout chatgpt # siwc 先撤销 refresh token 再删本
189
189
  模型列表接口(siwc `GET /v1/models`、codex `GET /models?client_version=…`,只读、不消耗额度;失败静默,改提示
190
190
  `ama models discover chatgpt`),把账户可用的 slug 与显示名连同 flavor、时间戳缓存到
191
191
  `<dataDir>/models/discovered/chatgpt.json`——返回 0 个也写空表,免得残留另一种登录方式的缓存;`ama models discover
192
- chatgpt` 也重写这份缓存,`ama auth logout chatgpt` 删掉它。组装注册表时缓存并入模型表为空的供应商,元数据用 models.dev
193
- 快照补全(codex 后端另给的上下文窗口、输入模态、推理强度也存进缓存,models.dev 补不到时用它),`/model` 选择器、
192
+ chatgpt` 也重写这份缓存,`ama auth logout chatgpt` 删掉它。组装注册表时缓存并入模型表为空的供应商,后端给的上下文窗口、
193
+ 输入模态、推理强度(codex 后端给;siwc 条目带了也取)存进缓存并优先于 models.dev 快照——订阅后端的生效窗口(如 272k)
194
+ 可能远小于 models.dev 记的 API 版窗口,压缩阈值按后端窗口算;后端没给的字段(输出上限等)用 models.dev 补,两边都没有
195
+ 上下文窗口时按保守缺省 128k。旧版本写的缓存不含窗口就照旧用 models.dev,重新 `ama models discover chatgpt` 即刷新。`/model` 选择器、
194
196
  `ama models list` 照常列出;缓存的 flavor 与当前登录不符时视为过期、不并入(选择器提示重新发现)。缓存里没有的 slug
195
197
  仍可 `--model chatgpt/<slug>` 使用。
196
198
 
package/docs/rpc.md CHANGED
@@ -36,16 +36,18 @@
36
36
 
37
37
  ### 提示
38
38
 
39
- | 命令 | 参数 | `data` |
40
- | ------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
41
- | `prompt` | `message: string`、`images?: ImageBlock[]`、`streamingBehavior?: "steer" \| "followUp"` | `{ disposition: "started" \| "queued" \| "handled" }` |
42
- | `steer` | `message`、`images?` | 同上 |
43
- | `follow_up` | `message`、`images?` | 同上 |
44
- | `abort` | — | `{}`(回到空闲后应答;不清队列) |
45
- | `clear_queue` | — | `{ steering: string[], followUp: string[] }`(被清掉的文本) |
39
+ | 命令 | 参数 | `data` |
40
+ | ------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
41
+ | `prompt` | `message: string`、`images?: ImageBlock[]`、`streamingBehavior?: "steer" \| "followUp"`、`interrupt?: boolean` | `{ disposition: "started" \| "queued" \| "handled" }` |
42
+ | `steer` | `message`、`images?`、`interrupt?: boolean` | 同上 |
43
+ | `follow_up` | `message`、`images?` | 同上 |
44
+ | `abort` | — | `{}`(回到空闲后应答;不清队列) |
45
+ | `clear_queue` | — | `{ steering: string[], followUp: string[] }`(被清掉的文本) |
46
46
 
47
47
  提示类命令**不等运行结束**:会话开始运行(`before_agent_start` / `agent_start`)、消息入队或被处理(例如斜杠命令、Hook 阻止)后立刻应答,运行进展走事件。运行中发 `prompt` 且不带 `streamingBehavior` → 失败,`code: "busy"`;带上 `steer` / `followUp` 则入队。应答发出之后的运行失败以 `{"type":"notification","level":"error","message":…}` 报告。
48
48
 
49
+ **打断并立即发送**:`prompt` / `steer` 带 `interrupt: true`(优先于 `streamingBehavior`)时,运行中先取走排队的 steer、中止当前回合(模型流断开,正在执行的工具按中断收尾,每个工具调用恰有一个结果 `aborted by user`,被打断的 assistant 消息以 `stopReason: "aborted"` 落盘),再立刻以「排队的 steer… + 本条」(空行拼接)开新回合,应答 `{ disposition: "started" }`;新回合的 user 消息 `origin: "interrupt"`。排在本轮之后的 followUp 留在队列,新回合结束后照常投递;后台子 Agent 不受影响。空闲时等同不带。`interrupt` 不是布尔 → `invalid_arguments`;运行中但本条与排队的 steer 都为空 → `invalid_arguments`(不中断)。事件顺序:`queue_update`(steer 被取走)→ 旧回合 `message_end`(aborted)→ `agent_settled` → `agent_start` → 应答 → 新的 user `message_end` ……(黄金记录 `test/fixtures/rpc/interrupt.out.jsonl`)。新请求以被打断那次请求的全部消息为前缀,缓存照常命中。SDK 对应 `session.prompt(text, { interrupt: true })` / `session.steer(text, { interrupt: true })`。
50
+
49
51
  ### 状态
50
52
 
51
53
  | 命令 | 参数 | `data` |
@@ -159,7 +161,17 @@
159
161
  - 整个 `data` 都经过脱敏;节点里只有 id、时间、计数与用量,正文只在 `previews` 里。运行中没有结果的工具标 `running`。
160
162
  - 错误:`invalid_arguments`(参数越界、`before` 不是本分支的回合 id、`before` 与 `since` 同用)、`task_not_found`。
161
163
 
162
- 合计 43 条命令,名字即 `RpcCommandMap` 的键。
164
+ ### 后台子 Agent(第七波)
165
+
166
+ | 命令 | 参数 | `data` |
167
+ | ----------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
168
+ | `background_task` | `taskId?` | `{ backgrounded: string[] }`:实际转了后台的 `taskId`。不给 `taskId` = 全部前台运行中任务;已结束、已在后台或不存在的任务回空表;`taskId` 不是字符串 → `invalid_arguments` |
169
+
170
+ 被转的前台任务不中断,其 `task` 调用立即以 `tool_execution_end` 返回(结果文本以 `[task tN] Moved to the background` 开头,
171
+ `details.status: "running"`),随后发 `subagent_background`;任务结束时照常 `subagent_end`,父会话空闲后收到 `origin: "task"`
172
+ 的通知消息。语义与交互界面的 `Ctrl+B` 相同,见 [agents.md](agents.md)「前台与后台」。
173
+
174
+ 合计 44 条命令,名字即 `RpcCommandMap` 的键。
163
175
 
164
176
  ## 事件
165
177
 
@@ -204,11 +216,12 @@
204
216
 
205
217
  `task` / `task_ctl` 起的子 Agent(ama 子会话与外部 Agent 同一组事件,见 [agents.md](agents.md)「子 Agent」):
206
218
 
207
- | 事件 | 字段 |
208
- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
209
- | `subagent_start` | `taskId`、`parentToolCallId`、`agent`、`runner`(`ama` / `claude` / `codex` / `acp:<程序>`)、`description`、`background`、`model?`、`sessionFile?`、`cwd`;同一 `taskId` 续聊时再发一次 |
210
- | `subagent_update` | `taskId`、`kind: tool \| text \| turn`、`toolName?`、`textDelta?`(≥ 250 ms 合并)、`turn`、`usage?` |
211
- | `subagent_end` | `taskId`、`status: completed \| failed \| aborted \| max_turns \| interrupted`、`usage?`、`cache?`、`outputFile?`、`worktree?: { branch, changed }` |
219
+ | 事件 | 字段 |
220
+ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
221
+ | `subagent_start` | `taskId`、`parentToolCallId`、`agent`、`runner`(`ama` / `claude` / `codex` / `acp:<程序>`)、`description`、`background`、`model?`、`sessionFile?`、`cwd`;同一 `taskId` 续聊时再发一次 |
222
+ | `subagent_update` | `taskId`、`kind: tool \| text \| turn`、`toolName?`、`textDelta?`(≥ 250 ms 合并)、`turn`、`usage?` |
223
+ | `subagent_background` | `taskId`、`parentToolCallId`、`reason: user \| timeout \| host`(前台任务转后台:交互界面手动、`subagents.autoBackgroundAfterMs` 到时、RPC / SDK 调用;第七波) |
224
+ | `subagent_end` | `taskId`、`status: completed \| failed \| aborted \| max_turns \| interrupted`、`usage?`、`cache?`、`outputFile?`、`worktree?: { branch, changed }` |
212
225
 
213
226
  子会话与外部 Agent 的审批照常以 `permission_request` 发给本连接,`context`(可选)标出来源(第六波起本会话工具调用的审批也带
214
227
  `context.toolCallId`——触发审批的工具调用 id,轨迹据此算审批等待;外部 Agent 的请求不带):
@@ -225,7 +238,8 @@
225
238
  只有类型目录,见 `cachedAgentInfos`,`src/agents/external.ts`)。
226
239
  `test/fixtures/rpc/subagent.out.jsonl` 是一次前台 `task(agent="explore")` 加 `get_tasks` / `get_agents` 的黄金记录(只保留
227
240
  响应、`tool_execution_*`、`subagent_*` 与 `agent_settled`),由
228
- `src/agent/subagent-rpc.test.ts` 用 `UPDATE_GOLDEN=1` 更新。
241
+ `src/agent/subagent-rpc.test.ts` 用 `UPDATE_GOLDEN=1` 更新。`test/fixtures/rpc/background.out.jsonl` 是前台 `task` 运行中
242
+ `background_task` 转后台、随后任务结束并投递通知回合的黄金记录,由 `src/modes/rpc/rpc-background.test.ts` 更新。
229
243
 
230
244
  ### 速率遥测(第五波)
231
245
 
@@ -48,7 +48,7 @@
48
48
  ### 消息
49
49
 
50
50
  - `system`:`{ role: "system", sections, toolsAdded?, toolsRemoved?, timestamp }`。首条是全量:系统提示的命名节(固定顺序 `preamble → tools → rules → project_context → skills → memory → hooks → cwd → host → role`;`memory` 是第六波的记忆索引节,未开启记忆时不出现)与工具声明表;之后只落**补丁**——节名级替换、值为 `null` 表示删除该节,`toolsAdded` / `toolsRemoved` 增删工具。依次重放得到当前系统提示;请求时由协议层重装全量。首条 system 消息在首次请求前落盘,也就是建文件的时刻。
51
- - `user`:`content`、`origin?`(缺省 = 普通用户输入;`steer` / `followUp` 是运行中插话与排队,`host` 是宿主 `sendUser` 注入,`direct` 是用户在子 Agent 视图里直接发给该子 Agent 的消息(第六波 W6-A,写在子会话里;投影与普通 user 消息相同),其它字符串原样记录)。
51
+ - `user`:`content`、`origin?`(缺省 = 普通用户输入;`steer` / `followUp` 是运行中插话与排队,`host` 是宿主 `sendUser` 注入,`interrupt` 是打断并立即发送开的新回合(TUI `Ctrl+X`、line 模式 `/interrupt`、RPC / SDK 的 `interrupt: true`;投影与普通 user 消息相同),`direct` 是用户在子 Agent 视图里直接发给该子 Agent 的消息(第六波 W6-A,写在子会话里;投影与普通 user 消息相同),其它字符串原样记录)。
52
52
  - `assistant`:`content`(文本 / 思考 / 工具调用块)、`api`、`provider`、`model`、`usage`、`stopReason`,以及可选的 `responseId`、`thinkingLevel`、`providerThinkingLevel`、`rawStopReason`、`errorMessage`。`usage` 是 `{ input, output, cacheRead, cacheWrite, cacheWrite1h?, reasoning?, totalTokens, cost?, cacheReported? }`:`input` 不含缓存部分,`cost` 是 `{ input, output, cacheRead, cacheWrite, total }`(美元,模型无价格时缺省),`cacheReported` 表示原始响应里出现过缓存字段。
53
53
  - `toolResult`:对应工具调用的结果(`toolCallId`、`toolName`、`content`、`isError`、`details?`)。
54
54
 
package/docs/sessions.md CHANGED
@@ -46,7 +46,8 @@ packy/deepseek-v4-flash 1 3 1 4.6k 980 2k 0 30.8
46
46
  | 错误 / 重试 | `stopReason: "error"` 的 assistant;`context_edit{reason:"retry"}`(自动重试剔除的失败尝试) |
47
47
  | 渠道 | 最近一条 `model_change` 与请求同 provider / model 时取它的 `channel` |
48
48
 
49
- `task` 子会话是独立文件,按它自己的 cwd 计入。
49
+ `task` 子会话是独立文件,按它自己的 cwd 计入。后台任务完成后父会话里的通知消息(`origin: "task"`)同样开启一个回合,
50
+ 按父会话计入;`-p` 等后台任务时(见 [agents.md](agents.md)「前台与后台」)这些通知回合也写进同一个会话文件。
50
51
 
51
52
  ### 性能与索引
52
53