@armadra/agent 0.6.2 → 0.6.3

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 (130) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/CHANGELOG.zh-CN.md +44 -0
  3. package/README.md +168 -575
  4. package/README.zh-CN.md +165 -596
  5. package/dist/agent/session.d.ts +6 -0
  6. package/dist/agent/session.js +8 -0
  7. package/dist/agent/subagent-background.d.ts +75 -0
  8. package/dist/agent/subagent-background.js +209 -0
  9. package/dist/agent/subagent-registry.d.ts +24 -5
  10. package/dist/agent/subagent-registry.js +64 -92
  11. package/dist/agent/types-w5.d.ts +11 -1
  12. package/dist/agent/types.d.ts +5 -0
  13. package/dist/agents/builtin.js +0 -1
  14. package/dist/agents/external.js +0 -1
  15. package/dist/agents/parse.js +4 -3
  16. package/dist/agents/result.d.ts +7 -1
  17. package/dist/agents/result.js +20 -1
  18. package/dist/agents/task-control.d.ts +13 -2
  19. package/dist/agents/task-record.d.ts +13 -1
  20. package/dist/agents/task-record.js +32 -0
  21. package/dist/agents/types.d.ts +5 -1
  22. package/dist/ai/providers/discovered-cache.d.ts +10 -4
  23. package/dist/ai/providers/discovered-cache.js +30 -14
  24. package/dist/auth/chatgpt/backend-client.d.ts +6 -5
  25. package/dist/auth/chatgpt/backend-client.js +8 -7
  26. package/dist/bundle/ama.cjs +1691 -500
  27. package/dist/cli/compose-agents.d.ts +2 -1
  28. package/dist/cli/compose-agents.js +11 -2
  29. package/dist/cli/subcommands/models-discover.d.ts +1 -0
  30. package/dist/cli/subcommands/models-discover.js +12 -2
  31. package/dist/config/json-schema.js +9 -2
  32. package/dist/config/key-docs.js +2 -2
  33. package/dist/config/merge.d.ts +1 -1
  34. package/dist/config/merge.js +20 -3
  35. package/dist/config/schema-w5.js +4 -2
  36. package/dist/config/schema.js +2 -0
  37. package/dist/config/settings-registry.js +4 -0
  38. package/dist/config/types-w5.d.ts +10 -0
  39. package/dist/config/types-w5.js +2 -0
  40. package/dist/config/types.d.ts +3 -1
  41. package/dist/git/info.d.ts +19 -0
  42. package/dist/git/info.js +64 -8
  43. package/dist/i18n/catalog.d.ts +45 -8
  44. package/dist/i18n/messages/agents.d.ts +56 -0
  45. package/dist/i18n/messages/agents.js +58 -2
  46. package/dist/i18n/messages/config-keys.d.ts +6 -0
  47. package/dist/i18n/messages/config-keys.js +16 -10
  48. package/dist/i18n/messages/config.d.ts +6 -0
  49. package/dist/i18n/messages/interactive-startup.d.ts +0 -16
  50. package/dist/i18n/messages/interactive-startup.js +0 -16
  51. package/dist/i18n/messages/interactive.d.ts +23 -16
  52. package/dist/i18n/messages/interactive.js +25 -0
  53. package/dist/i18n/messages/print.d.ts +4 -0
  54. package/dist/i18n/messages/print.js +4 -0
  55. package/dist/i18n/messages/report.js +4 -4
  56. package/dist/i18n/messages/settings.d.ts +6 -0
  57. package/dist/i18n/messages/settings.js +6 -0
  58. package/dist/i18n/messages/subcommands-config.d.ts +4 -0
  59. package/dist/i18n/messages/subcommands-config.js +4 -0
  60. package/dist/i18n/messages/subcommands.d.ts +4 -0
  61. package/dist/index.d.ts +1 -0
  62. package/dist/modes/commands-core.js +8 -4
  63. package/dist/modes/interactive/agent-bar.d.ts +3 -1
  64. package/dist/modes/interactive/agent-bar.js +12 -5
  65. package/dist/modes/interactive/agent-ui.d.ts +21 -3
  66. package/dist/modes/interactive/agent-ui.js +81 -12
  67. package/dist/modes/interactive/agent-view.d.ts +4 -0
  68. package/dist/modes/interactive/agent-view.js +14 -1
  69. package/dist/modes/interactive/approval-dock.d.ts +51 -0
  70. package/dist/modes/interactive/approval-dock.js +112 -0
  71. package/dist/modes/interactive/approval-ui.d.ts +43 -0
  72. package/dist/modes/interactive/approval-ui.js +64 -0
  73. package/dist/modes/interactive/commands.js +4 -1
  74. package/dist/modes/interactive/event-notices.d.ts +6 -1
  75. package/dist/modes/interactive/event-notices.js +7 -1
  76. package/dist/modes/interactive/interactive-mode.d.ts +4 -2
  77. package/dist/modes/interactive/interactive-mode.js +33 -38
  78. package/dist/modes/interactive/key-dispatch.d.ts +21 -4
  79. package/dist/modes/interactive/key-dispatch.js +60 -8
  80. package/dist/modes/interactive/line/line-mode.js +5 -0
  81. package/dist/modes/interactive/run-indicator.d.ts +15 -0
  82. package/dist/modes/interactive/run-indicator.js +37 -5
  83. package/dist/modes/interactive/session-events.js +5 -1
  84. package/dist/modes/interactive/startup-header.d.ts +29 -14
  85. package/dist/modes/interactive/startup-header.js +93 -59
  86. package/dist/modes/interactive/startup-logo.d.ts +83 -0
  87. package/dist/modes/interactive/startup-logo.js +183 -0
  88. package/dist/modes/interactive/status-area.d.ts +18 -0
  89. package/dist/modes/interactive/status-area.js +67 -1
  90. package/dist/modes/interactive/status-bar.d.ts +9 -1
  91. package/dist/modes/interactive/status-bar.js +43 -11
  92. package/dist/modes/interactive/status-line.d.ts +2 -0
  93. package/dist/modes/interactive/status-line.js +11 -6
  94. package/dist/modes/interactive/status-quota.d.ts +44 -0
  95. package/dist/modes/interactive/status-quota.js +135 -0
  96. package/dist/modes/interactive/subagent-view.d.ts +1 -0
  97. package/dist/modes/interactive/subagent-view.js +8 -0
  98. package/dist/modes/interactive/task-background.d.ts +31 -0
  99. package/dist/modes/interactive/task-background.js +68 -0
  100. package/dist/modes/interactive/tool-view.d.ts +7 -1
  101. package/dist/modes/interactive/tool-view.js +24 -1
  102. package/dist/modes/print/print-mode.d.ts +11 -0
  103. package/dist/modes/print/print-mode.js +36 -1
  104. package/dist/modes/rpc/commands.d.ts +2 -1
  105. package/dist/modes/rpc/commands.js +9 -1
  106. package/dist/rpc.d.ts +13 -0
  107. package/dist/rpc.js +3 -0
  108. package/dist/tools/task-ctl.d.ts +2 -0
  109. package/dist/tools/task-ctl.js +7 -2
  110. package/dist/tools/task.d.ts +11 -0
  111. package/dist/tools/task.js +20 -2
  112. package/dist/tui/components/editor.d.ts +2 -0
  113. package/dist/tui/components/editor.js +4 -0
  114. package/dist/tui/components/loader.d.ts +5 -1
  115. package/dist/tui/components/loader.js +18 -5
  116. package/dist/tui/keybindings.d.ts +8 -3
  117. package/dist/tui/keybindings.js +8 -3
  118. package/docs/agents.md +52 -28
  119. package/docs/en/host-api.md +5 -1
  120. package/docs/en/providers.md +1 -1
  121. package/docs/en/rpc.md +19 -7
  122. package/docs/en/sessions.md +3 -1
  123. package/docs/en/tui.md +84 -66
  124. package/docs/host-api.md +5 -1
  125. package/docs/providers.md +4 -2
  126. package/docs/rpc.md +19 -7
  127. package/docs/sessions.md +2 -1
  128. package/docs/tui-design.md +42 -29
  129. package/docs/tui.md +67 -49
  130. package/package.json +1 -1
package/docs/agents.md CHANGED
@@ -38,7 +38,7 @@ model: fast # inherit(缺省)| fast | strong(models.aliases)| provider/m
38
38
  thinking: low
39
39
  max-turns: 20 # 缺省 30
40
40
  isolation: none # none(缺省)| worktree
41
- background: false
41
+ background: false # 不写 = 按 config subagents.background
42
42
  runner: ama # ama(缺省)| claude | codex | acp:<程序>
43
43
  ---
44
44
 
@@ -60,16 +60,16 @@ runner: ama # ama(缺省)| claude | codex | acp:<程序>
60
60
 
61
61
  ### `task` 参数
62
62
 
63
- | 参数 | 说明 |
64
- | ------------------------------------------------ | -------------------------------------------------------------------------- |
65
- | `prompt` | 必填,完整的任务说明 |
66
- | `agent` | 类型名,缺省 `general`;也可以是外部 Agent(见「外部 Agent」节) |
67
- | `description` | 显示用的短标签 |
68
- | `background` | `true`:立即返回 `taskId`,完成后父会话收到通知;缺省取类型的 `background` |
69
- | `taskId` | 续聊:向已有任务的子会话追加一条消息(忽略 `agent` / `tools` / `model`) |
70
- | `isolation` | `worktree`:在独立 git worktree 里运行 |
71
- | `budgetUsd` | 外部 Agent 的美元预算 |
72
- | `tools` / `model` / `thinkingLevel` / `maxTurns` | 保留的高级参数(描述里不展开) |
63
+ | 参数 | 说明 |
64
+ | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
65
+ | `prompt` | 必填,完整的任务说明 |
66
+ | `agent` | 类型名,缺省 `general`;也可以是外部 Agent(见「外部 Agent」节) |
67
+ | `description` | 显示用的短标签 |
68
+ | `background` | `true`:立即返回 `taskId`,完成后父会话收到通知;`false`:等结果。缺省取类型的 `background`,类型没写时按 `subagents.background`(见下文「前台与后台」) |
69
+ | `taskId` | 续聊:向已有任务的子会话追加一条消息(忽略 `agent` / `tools` / `model`) |
70
+ | `isolation` | `worktree`:在独立 git worktree 里运行 |
71
+ | `budgetUsd` | 外部 Agent 的美元预算 |
72
+ | `tools` / `model` / `thinkingLevel` / `maxTurns` | 保留的高级参数(描述里不展开) |
73
73
 
74
74
  同一条回复里的多个 `task` **并行**执行,由会话的任务池限流(`subagents.maxConcurrent`,缺省 4);排队超过
75
75
  `subagents.maxPending`(缺省 16)直接报错,提示模型不要重试。并行且会写文件的任务请用 `isolation: "worktree"`。
@@ -79,9 +79,31 @@ runner: ama # ama(缺省)| claude | codex | acp:<程序>
79
79
  全文写到会话目录的 `outputs/<会话 id>-<taskId>.md`(内存会话写到系统临时目录)。轮数用尽且最后一步停在工具结果上时,
80
80
  ama 以「不允许调用工具」再跑一轮要最终报告,结果前加 `[Turn limit reached; …]`,状态 `max_turns`。
81
81
 
82
+ ### 前台与后台
83
+
84
+ 是否后台的优先级:调用参数 `background` > 类型定义的 `background:` > config `subagents.background`。
85
+ `subagents.background` 缺省 `auto`:交互界面、RPC、ACP 下后台(模型需要结果才写 `background: false`),`-p` 下前台;
86
+ `always` / `never` 固定。两种缺省对应两版 `task` 工具描述,会话内不变,不影响缓存前缀稳定。
87
+
88
+ 前台任务运行中可以转后台,任务不中断,`task` 工具调用立即返回一段固定英文结果(含 `taskId` 与输出文件),完成后照常发
89
+ `<task-notification>`:
90
+
91
+ - 交互界面:`Ctrl+B` / `/tasks bg [id]` / Agent 栏里按 `b`(见 [tui.md](tui.md)「子 Agent」);
92
+ - RPC:`background_task { taskId? }`([rpc.md](rpc.md)),SDK:`session.backgroundTask(taskId?)`,返回实际转了的 `taskId`;
93
+ 不给 `taskId` 时转全部前台运行中任务,正在 `task_ctl wait` 的等待也一并打断;
94
+ - 自动:`subagents.autoBackgroundAfterMs` 大于 0 时,前台任务运行超过该毫秒数自动转后台(缺省 0 关闭)。
95
+
96
+ 转后台发 `subagent_background { taskId, parentToolCallId, reason }` 事件(`user` / `timeout` / `host`)。
97
+ 父会话 `Esc` 中断只连带中止仍在前台的任务,后台任务不受影响;停止后台任务用 `task_ctl stop` 或 `/tasks stop`。
98
+
99
+ `-p` 下(缺省前台)显式 `background: true` 仍生效:主回合结束后若还有任务在跑或通知待投递,stderr 一行提示,等它们结束、
100
+ 跑完通知回合再输出,输出的文本是最后一条助手回复;等待受 `--max-turns` / `--max-cost` / `limits.*` 约束(到限停止等待、
101
+ 退出码 8),`Ctrl+C` / SIGTERM 照常中止(未结束的任务随会话关闭被停止,退出码 130 / 143)。`--output-format json` 的结果带
102
+ `tasks`(同 `getStats().tasks`)。
103
+
82
104
  ### 后台任务与 `task_ctl`
83
105
 
84
- `background: true` 立即返回 `taskId` 与输出文件路径。任务完成后,ama 在父会话空闲时投递一条 user 消息(`origin: "task"`)
106
+ 后台任务立即返回 `taskId` 与输出文件路径。任务完成后,ama 在父会话空闲时投递一条 user 消息(`origin: "task"`)
85
107
  并开始新回合;父正忙则等这一轮结束再投递,不打断。多条通知按完成顺序到达:
86
108
 
87
109
  ```text
@@ -95,13 +117,13 @@ ama 以「不允许调用工具」再跑一轮要最终报告,结果前加 `[T
95
117
 
96
118
  `task_ctl` 的动作:
97
119
 
98
- | `action` | 说明 |
99
- | -------- | ---------------------------------------------------------------------------------- |
100
- | `list` | 列出本会话的任务:编号、类型、状态、轮数、token、耗时、是否后台、描述 |
101
- | `wait` | 等任务结束(`timeoutMs` 缺省 30 000,最多 600 000);超时说明仍在运行 |
102
- | `stop` | 停止任务,返回终态 |
103
- | `output` | 运行中返回已有输出,结束后返回最终文本(同样有 50 KB 上限) |
104
- | `send` | 向任务追加一条消息并放到后台运行(等价于 `task{taskId, prompt, background:true}`) |
120
+ | `action` | 说明 |
121
+ | -------- | ------------------------------------------------------------------------------------------------------- |
122
+ | `list` | 列出本会话的任务:编号、类型、状态、轮数、token、耗时、是否后台、描述 |
123
+ | `wait` | 等任务结束(`timeoutMs` 缺省 30 000,最多 600 000);超时说明仍在运行;被转后台时立即返回并说明不必再等 |
124
+ | `stop` | 停止任务,返回终态 |
125
+ | `output` | 运行中返回已有输出,结束后返回最终文本(同样有 50 KB 上限) |
126
+ | `send` | 向任务追加一条消息并放到后台运行(等价于 `task{taskId, prompt, background:true}`) |
105
127
 
106
128
  ### 续聊、保留与 resume
107
129
 
@@ -122,20 +144,22 @@ worktree 里的编辑不记进父会话的检查点。注意:worktree 不共
122
144
 
123
145
  ### 事件与统计
124
146
 
125
- RPC / SDK 事件 `subagent_start` / `subagent_update` / `subagent_end` 见 [rpc.md](rpc.md)「子 Agent 事件」。
147
+ RPC / SDK 事件 `subagent_start` / `subagent_update` / `subagent_background` / `subagent_end` 见 [rpc.md](rpc.md)「子 Agent 事件」。
126
148
  `getStats().tasks` 给出任务总数、运行中数量与按状态的计数;子会话的缓存命中与重计费仍汇总在 `cache.subagents`。
127
149
  RPC `get_tasks` / `get_agents` 返回任务快照与可用类型(来源、定义文件路径)。交互界面的 `/tasks`、`/agents` 与 task 工具行的折叠显示见 [tui.md](tui.md)「子 Agent」。
128
150
 
129
151
  ### 配置
130
152
 
131
- | 键 | 说明 |
132
- | --------------------------------- | ------------------------------------------- |
133
- | `subagents.maxConcurrent` | 同时运行的子 Agent,缺省 4 |
134
- | `subagents.maxPending` | 排队上限,缺省 16 |
135
- | `subagents.defaultModel` | 子 Agent 缺省模型,不设继承父会话 |
136
- | `agents.dirs` | 追加的定义目录 |
137
- | `agents.<类型>.model` | 某个类型的模型(如让 `explore` 用便宜模型) |
138
- | `models.aliases.fast` / `.strong` | 定义文件里 `model: fast / strong` 的映射 |
153
+ | 键 | 说明 |
154
+ | --------------------------------- | -------------------------------------------------------------------------------- |
155
+ | `subagents.maxConcurrent` | 同时运行的子 Agent,缺省 4 |
156
+ | `subagents.maxPending` | 排队上限,缺省 16 |
157
+ | `subagents.defaultModel` | 子 Agent 缺省模型,不设继承父会话 |
158
+ | `subagents.background` | `auto`(缺省)\| `always` \| `never`,见「前台与后台」;用户 / 项目 / 宿主级都认 |
159
+ | `subagents.autoBackgroundAfterMs` | 前台任务运行超过该毫秒数自动转后台,缺省 0(关闭);用户 / 项目级都认 |
160
+ | `agents.dirs` | 追加的定义目录 |
161
+ | `agents.<类型>.model` | 某个类型的模型(如让 `explore` 用便宜模型) |
162
+ | `models.aliases.fast` / `.strong` | 定义文件里 `model: fast / strong` 的映射 |
139
163
 
140
164
  ### 限制
141
165
 
@@ -164,4 +164,8 @@ Return `"warm"` / `"stop"` (a Promise is fine). `"stop"` skips the request and s
164
164
 
165
165
  ## Embedding in Armadra
166
166
 
167
- Armadra starts ama with a profile: `ama --profile <path>`. The profile's `host` points to its adapter (`ama-armadra.cjs`) and also carries instructions, skillDirs, hooksFile, authFile, sessionDir and `trustProject`. The adapter returns `undefined` when `ARMADRA_NODE_ID` is missing, so the same profile behaves as plain ama outside the canvas. Contract details are in [docs/design/coordinator-agent.md](https://github.com/yovinchen/Armadra/blob/main/docs/design/coordinator-agent.md) in the Armadra repository.
167
+ Armadra starts ama with a profile: `ama --profile <path>`. The profile's `host` points to its adapter (`ama-armadra.cjs`) and also carries instructions, skillDirs, hooksFile, authFile, sessionDir and `trustProject`. The adapter returns `undefined` when `ARMADRA_NODE_ID` is missing, so the same profile behaves as plain ama outside the canvas.
168
+
169
+ Interface defaults with a profile: `ui.quietStartup: "header"` and `ui.statusLine: "compact"` (the last line is the status bar, which the host parses by `·`). The agent bar (`ui.agentBar`) is no longer off by default; it is `auto` as in a standalone terminal. A host that shows sub-tasks itself and does not want the bar writes `{ "ui": { "agentBar": "off" } }` into the config file its profile's `config` points to.
170
+
171
+ Contract details are in [docs/design/coordinator-agent.md](https://github.com/yovinchen/Armadra/blob/main/docs/design/coordinator-agent.md) in the Armadra repository.
@@ -170,7 +170,7 @@ ama auth logout chatgpt # siwc revokes the refresh token first, t
170
170
  | Quota | only known when exceeded (429); set a weekly cap for ama under ChatGPT → Settings → Usage → App limits | response headers, `codex.rate_limits` events, `ama auth status` queries `wham/usage` |
171
171
  | Logout | calls `revocation_endpoint`, then deletes locally | deletes locally only |
172
172
 
173
- **Model list**: `chatgpt` has no built-in model table (`chatgpt/<slug>` accepts any slug). After a successful `ama auth login chatgpt`, ama deletes the old cache and calls the model list endpoint once (siwc `GET /v1/models`, codex `GET /models?client_version=…`; read-only, no usage consumed; failures are silent and the output suggests `ama models discover chatgpt` instead) and caches the slugs and display names available to the account, with the flavor and a timestamp, in `<dataDir>/models/discovered/chatgpt.json` — an empty list is written as well when no model comes back, so no cache from the other sign-in method is left behind; `ama models discover chatgpt` rewrites the cache and `ama auth logout chatgpt` deletes it. When the registry is assembled the cache is merged into providers whose model table is empty, with metadata filled from the models.dev snapshot (the context window, input modalities and reasoning efforts reported by the codex backend are cached too and used when models.dev has nothing), so the `/model` picker and `ama models list` show the models; a cache whose flavor differs from the current sign-in counts as stale and is not merged (the picker suggests discovering again). Slugs missing from the cache still work with `--model chatgpt/<slug>`.
173
+ **Model list**: `chatgpt` has no built-in model table (`chatgpt/<slug>` accepts any slug). After a successful `ama auth login chatgpt`, ama deletes the old cache and calls the model list endpoint once (siwc `GET /v1/models`, codex `GET /models?client_version=…`; read-only, no usage consumed; failures are silent and the output suggests `ama models discover chatgpt` instead) and caches the slugs and display names available to the account, with the flavor and a timestamp, in `<dataDir>/models/discovered/chatgpt.json` — an empty list is written as well when no model comes back, so no cache from the other sign-in method is left behind; `ama models discover chatgpt` rewrites the cache and `ama auth logout chatgpt` deletes it. When the registry is assembled the cache is merged into providers whose model table is empty, and the context window, input modalities and reasoning efforts reported by the backend (codex reports them; siwc entries are read the same way when present) are cached and take precedence over the models.dev snapshot — the subscription backend's effective window (such as 272k) can be far smaller than the API window models.dev lists, and the compaction threshold follows the backend window; fields the backend leaves out (such as the output limit) come from models.dev, and when neither has a context window a conservative 128k is used. A cache written by an older version without windows keeps using models.dev until `ama models discover chatgpt` refreshes it. This way the `/model` picker and `ama models list` show the models; a cache whose flavor differs from the current sign-in counts as stale and is not merged (the picker suggests discovering again). Slugs missing from the cache still work with `--model chatgpt/<slug>`.
174
174
 
175
175
  **codex `client_version`**: the codex backend filters models by `client_version` (each model has a minimum client version; omitting the parameter is a 400), and ama sends a Codex CLI version (default `0.160.0`), not its own version. If codex returns no models at login or discover time, that version is most likely too old: set a newer Codex CLI version with `ama config set auth.chatgpt.codexClientVersion <version>` (user level) or the environment variable `AMA_CHATGPT_CODEX_CLIENT_VERSION` (takes precedence), then run `ama models discover chatgpt`. Inference requests carry no version.
176
176
 
package/docs/en/rpc.md CHANGED
@@ -171,7 +171,18 @@ After a session switch the server re-subscribes to events and sends `session_sta
171
171
  - Errors: `invalid_arguments` (out-of-range parameters, `before` not a turn id on this branch, `before` together with
172
172
  `since`) and `task_not_found`.
173
173
 
174
- 43 commands in total; their names are the keys of `RpcCommandMap`.
174
+ ### Background sub-agents (wave 7)
175
+
176
+ | Command | Parameters | `data` |
177
+ | ----------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
178
+ | `background_task` | `taskId?` | `{ backgrounded: string[] }`: the task ids actually moved. Without `taskId`, every running foreground task; an empty list for finished, already-background or unknown tasks; a non-string `taskId` → `invalid_arguments` |
179
+
180
+ A moved foreground task is not interrupted: its `task` call returns at once with `tool_execution_end` (the result text starts with
181
+ `[task tN] Moved to the background`, `details.status: "running"`), followed by `subagent_background`; when the task ends you get
182
+ `subagent_end` as usual and, once the parent session is idle, the notification message with `origin: "task"`. Same semantics as
183
+ `Ctrl+B` in the interactive UI.
184
+
185
+ 44 commands in total; their names are the keys of `RpcCommandMap`.
175
186
 
176
187
  ## Events
177
188
 
@@ -216,13 +227,14 @@ There is also the non-session event `{"type":"notification","level":"info"|"warn
216
227
 
217
228
  Sub-agents started by `task` / `task_ctl` (ama sub-sessions and external agents share the same events, see [agents.md](../agents.md), Chinese):
218
229
 
219
- | Event | Fields |
220
- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
221
- | `subagent_start` | `taskId`, `parentToolCallId`, `agent`, `runner` (`ama` / `claude` / `codex` / `acp:<program>`), `description`, `background`, `model?`, `sessionFile?`, `cwd`; sent again when the same `taskId` is continued |
222
- | `subagent_update` | `taskId`, `kind: tool \| text \| turn`, `toolName?`, `textDelta?` (merged over ≥ 250 ms), `turn`, `usage?` |
223
- | `subagent_end` | `taskId`, `status: completed \| failed \| aborted \| max_turns \| interrupted`, `usage?`, `cache?`, `outputFile?`, `worktree?: { branch, changed }` |
230
+ | Event | Fields |
231
+ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
232
+ | `subagent_start` | `taskId`, `parentToolCallId`, `agent`, `runner` (`ama` / `claude` / `codex` / `acp:<program>`), `description`, `background`, `model?`, `sessionFile?`, `cwd`; sent again when the same `taskId` is continued |
233
+ | `subagent_update` | `taskId`, `kind: tool \| text \| turn`, `toolName?`, `textDelta?` (merged over ≥ 250 ms), `turn`, `usage?` |
234
+ | `subagent_background` | `taskId`, `parentToolCallId`, `reason: user \| timeout \| host` (a foreground task moved to the background: by hand in the interactive UI, when `subagents.autoBackgroundAfterMs` elapses, or by an RPC / SDK call; wave 7) |
235
+ | `subagent_end` | `taskId`, `status: completed \| failed \| aborted \| max_turns \| interrupted`, `usage?`, `cache?`, `outputFile?`, `worktree?: { branch, changed }` |
224
236
 
225
- Approvals of sub-sessions and external agents are sent to this connection as `permission_request` as usual, with an optional `context` marking the origin (since wave 6, approvals of this session's own tool calls also carry `context.toolCallId`, the id of the tool call that triggered the approval, which traces use to compute approval wait time; external agent requests do not carry it): `depth` (1 = from a task sub-agent), `taskId` (the originating task), `origin` (permission requests from external agents: `agent`, `sessionId` (the external CLI's own session id), `toolCall: { title, kind, locations?, inputSummary? }`, `options`). Dialogs use it to show `[task:<agent>]` or `[claude · session abc1]`. For external agent requests `toolName` is `agent:<id>` and the answer applies to this one request only ("allow for this session" is remembered by the external agent itself); the first run of an external agent in a session additionally gets one confirmation with `toolName: "task"`, `input: { agent, mode, note }` (`context.taskId`). `test/fixtures/rpc/external.out.jsonl` is the golden record of the three approvals of `task(agent="acp:ama")` (the task tool, the first run, the child ama's bash), updated by `src/agents/external-rpc.test.ts` with `UPDATE_GOLDEN=1`. After a background task completes, the parent session receives a user message with `origin: "task"` (`<task-notification …>…</task-notification>`) and starts a new turn as usual. Task and type lists are returned by `get_tasks` / `get_agents` (shapes `TaskInfo` / `AgentInfo`; empty without the task tool), with data from the current session's `taskRegistryView(sessionId)` / `sessionAgents(sessionId)` (`src/agent/subagent-registry.ts`). `get_agents` also includes external agents (`installed` / `version` come from PATH and a `--version` probe, cached asynchronously when the session is created and refreshed when external tasks end or host injections change; before the cache is ready there is only the type catalog, see `cachedAgentInfos` in `src/agents/external.ts`). `test/fixtures/rpc/subagent.out.jsonl` is the golden record of a foreground `task(agent="explore")` plus `get_tasks` / `get_agents` (keeping only responses, `tool_execution_*`, `subagent_*` and `agent_settled`), updated by `src/agent/subagent-rpc.test.ts` with `UPDATE_GOLDEN=1`.
237
+ Approvals of sub-sessions and external agents are sent to this connection as `permission_request` as usual, with an optional `context` marking the origin (since wave 6, approvals of this session's own tool calls also carry `context.toolCallId`, the id of the tool call that triggered the approval, which traces use to compute approval wait time; external agent requests do not carry it): `depth` (1 = from a task sub-agent), `taskId` (the originating task), `origin` (permission requests from external agents: `agent`, `sessionId` (the external CLI's own session id), `toolCall: { title, kind, locations?, inputSummary? }`, `options`). Dialogs use it to show `[task:<agent>]` or `[claude · session abc1]`. For external agent requests `toolName` is `agent:<id>` and the answer applies to this one request only ("allow for this session" is remembered by the external agent itself); the first run of an external agent in a session additionally gets one confirmation with `toolName: "task"`, `input: { agent, mode, note }` (`context.taskId`). `test/fixtures/rpc/external.out.jsonl` is the golden record of the three approvals of `task(agent="acp:ama")` (the task tool, the first run, the child ama's bash), updated by `src/agents/external-rpc.test.ts` with `UPDATE_GOLDEN=1`. After a background task completes, the parent session receives a user message with `origin: "task"` (`<task-notification …>…</task-notification>`) and starts a new turn as usual. Task and type lists are returned by `get_tasks` / `get_agents` (shapes `TaskInfo` / `AgentInfo`; empty without the task tool), with data from the current session's `taskRegistryView(sessionId)` / `sessionAgents(sessionId)` (`src/agent/subagent-registry.ts`). `get_agents` also includes external agents (`installed` / `version` come from PATH and a `--version` probe, cached asynchronously when the session is created and refreshed when external tasks end or host injections change; before the cache is ready there is only the type catalog, see `cachedAgentInfos` in `src/agents/external.ts`). `test/fixtures/rpc/subagent.out.jsonl` is the golden record of a foreground `task(agent="explore")` plus `get_tasks` / `get_agents` (keeping only responses, `tool_execution_*`, `subagent_*` and `agent_settled`), updated by `src/agent/subagent-rpc.test.ts` with `UPDATE_GOLDEN=1`. `test/fixtures/rpc/background.out.jsonl` is the golden record of `background_task` moving a running foreground `task` to the background, the task ending and its notification turn, updated by `src/modes/rpc/rpc-background.test.ts`.
226
238
 
227
239
  ### Throughput telemetry (wave 5)
228
240
 
@@ -51,7 +51,9 @@ Top 5 tool calls
51
51
  | Errors / retries | Assistant messages with `stopReason: "error"`; `context_edit{reason:"retry"}` (failed attempts removed by automatic retry) |
52
52
  | Channel | The `channel` of the latest `model_change` with the same provider / model as the request |
53
53
 
54
- `task` sub-sessions are separate files and count under their own cwd.
54
+ `task` sub-sessions are separate files and count under their own cwd. The notification message a parent session receives when a
55
+ background task finishes (`origin: "task"`) also starts a turn and counts under the parent; when `-p` waits for background tasks,
56
+ those notification turns are written to the same session file.
55
57
 
56
58
  ### Performance and index
57
59
 
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
 
@@ -44,23 +40,26 @@ The visual spec (colors, glyphs, screen-by-screen mockups) is in [tui-design.md]
44
40
  › Type a message, / commands, @ files, Shift+Enter newline ← input box (placeholder)
45
41
  ────────────────────────────────────────────────
46
42
  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
43
+ Accept edits 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
47
  - **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).
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).
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.
52
+ - **Status bar**: the mode is always on the far left; 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).
56
53
  - **`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.
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. A window that is not 5 hours / 7 days is labelled with its actual length. `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
61
  - 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`, `−` → `-`, `♨` → `~`.
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,27 @@ 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 (inserted into the current turn) |
122
+ | Alt+Enter | While running, queue after this turn (followUp); when idle, same as Enter |
123
+ | Shift+Enter / Ctrl+J | New line |
124
+ | 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 |
125
+ | Esc Esc (idle) | Empty input: open the rewind list (same as `/rewind`); with text: clear it and save it into input history |
126
+ | Alt+↑ | Recall the last queued message |
127
+ | 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) |
128
+ | Ctrl+O | Expand / fold tool output and thinking blocks |
129
+ | Ctrl+L / Ctrl+T | Pick model / thinking level |
130
+ | Ctrl+G | Bottom info line two lines (full) ↔ one line (compact), this session only |
131
+ | Ctrl+V | Paste an image from the clipboard: saved in the data directory, `@<path>` inserted at the cursor (same as `/paste`) |
132
+ | Ctrl+C | Clear the input; on an empty input, press again within 1.5 seconds to quit (exit code 130) |
133
+ | Ctrl+D | Quit on an empty input |
134
+ | Tab | Complete |
135
+ | ↑ / ↓ | Browse history on a single line (`<data dir>/history`, 500 entries) |
136
+ | ↓ (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" |
137
+ | 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" |
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`, `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).
140
140
 
141
141
  ## Rewind
142
142
 
@@ -315,12 +315,17 @@ Sub-agents started by the `task` tool ([agents.md](../agents.md), Chinese) fold
315
315
  ⏺ task check test coverage gaps in src/tui
316
316
  ⎿ ⠋ explore · running 1m05s · 3 turns · read grep bash · ↑12k ↓3.4k
317
317
  ⏺ task background review
318
- ⎿ done · 0.0s
318
+ ⎿ started in the background
319
319
  ↳ t2 explore · running 40s · 1 turn · read
320
+ ⏺ task scan
321
+ ⎿ moved to the background · 12s
322
+ ↳ t3 explore · running 30s · 2 turns · grep
320
323
  ```
321
324
 
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.
325
+ - 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.
326
+ - `/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).
327
+ - 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`.
328
+ - 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
329
  - `/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
330
  - Notices reported by external agents themselves (budget exhausted, timeout, mode downgrade …) show in the message area as a single line `[claude · t3] …`.
326
331
 
@@ -335,11 +340,13 @@ Above the status line (below the hint line) the bar lists sub-agent tasks, one l
335
340
  1 more
336
341
  ```
337
342
 
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.
343
+ - 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
344
  - 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.
345
+ - 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`.
346
+ - 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.
347
+ - `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`.
348
+ - 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.
349
+ - 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
350
 
344
351
  ### Sub-agent view
345
352
 
@@ -360,8 +367,17 @@ t2 explore · running 1m05s · 3 turns · ↑12k ↓3.4k · Esc back · /tasks s
360
367
  - 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
368
  - 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
369
  - 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).
370
+ - Nothing is interrupted from the view: Esc 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).
371
+ - 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.
372
+
373
+ ### Docked approvals of background tasks
374
+
375
+ When a background task (including one moved to the background) needs approval, it does not interrupt what you are doing:
376
+
377
+ - 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.
378
+ - 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.
379
+ - 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.
380
+ - 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
381
 
366
382
  Origin labels on approval boxes:
367
383
 
@@ -413,12 +429,13 @@ Requires memory to be enabled (`ama memory enable` or `--memory`, see [memory.md
413
429
 
414
430
  ## Startup screen
415
431
 
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.
432
+ `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
433
 
418
434
  ## In tmux / Armadra terminal nodes
419
435
 
420
436
  - 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
437
  - 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.
438
+ - 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
439
  - When the window size changes the last screen is redrawn in full; history in the scrollback is unaffected.
423
440
  - Automatic fallback: non-TTY, `TERM=dumb`, `--no-tui` or a failed terminal initialization use line mode, with the same commands and approval prompts.
424
441
 
@@ -430,8 +447,9 @@ The `ui` section of `config.json` (settable at project level too):
430
447
  | ----------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
431
448
  | `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
449
  | `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 |
450
+ | `ui.compact` | `false` | No blank lines between message blocks, no logo in the startup header |
451
+ | `ui.logo` | `auto` | The "AMA" logo in the startup header; `off` shows only the info column |
452
+ | `ui.animation` | `true` | `false`: the spinner stays still as `·` while running and redraws only when seconds change; the startup logo does not animate |
435
453
  | `ui.markdown` | `true` | `false`: assistant text is not rendered as Markdown |
436
454
  | `ui.showThinking` | `collapsed` | See "Layout" |
437
455
  | `ui.quietStartup` | `normal` | See "Startup screen" |
@@ -505,29 +523,29 @@ tui.setFocus(editor);
505
523
  tui.start();
506
524
  ```
507
525
 
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 |
526
+ | Export | Purpose |
527
+ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
528
+ | `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 |
529
+ | `TUI` | Root container and differential rendering (main screen, synchronized output): `addChild`, `start` / `stop`, `requestRender`, `setFocus`, `addInputListener`, `showOverlay` |
530
+ | `ProcessTerminal`, `MemoryTerminal`, `VirtualScreen` | A real terminal (raw mode, bracketed paste); an in-memory terminal and a VT screen (tests, frame goldens) |
531
+ | `Container`, `Text`, `TruncatedText`, `Markdown`, `Box`, `Card`, `Spacer` | Basic components; `Card` is a left-bar card, `Box` accepts `borderColor` |
532
+ | `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` |
533
+ | `Editor`, `EditorBuffer`, `PasteStore` | Multi-line editor (history, the `AutocompleteProvider` completion interface, paste folding) |
534
+ | `SelectList` | Filterable selection list: groups, badges, number keys, `stacked`, `currentValue` (✓), `footer` key hints |
535
+ | `KeyValue`, `Meter` | Two-column aligned key-value table (`wrap` wraps aligned to the value column); a meter (`levelColor` threshold coloring) |
536
+ | `compositeOverlays`, `OverlayOptions` | Overlay compositing (centered / bottom-anchored) |
537
+ | `createTheme`, `plainTheme`, `detectCapabilities`, `Theme` | Themes and color capability detection (`NO_COLOR`, 16 / 256 / truecolor); 14 semantic colors, `resolveThemeName("auto")` |
538
+ | `Theme.glyphs`, `UNICODE_GLYPHS`, `ASCII_GLYPHS`, `detectAscii` | Glyph tables (`›` `⏺` `⎿` `✻` `▎`, box lines, spinner frames …) with ASCII fallback; `createTheme(name, { ascii })` |
539
+ | `Keybindings`, `DEFAULT_KEYBINDINGS`, `loadKeybindingsFile` | Action id → keys, overridden by `keybindings.json` |
540
+ | `parseKey`, `matchesKey`, `StdinBuffer` | Key sequence parsing and Esc timeout splitting (`AMA_TUI_ESC_TIMEOUT`) |
541
+ | `visibleWidth`, `truncateToWidth`, `wrapTextWithAnsi`, `sliceByColumn` … | Width computation and truncation aware of ANSI and wide characters |
524
542
 
525
543
  ## Testing
526
544
 
527
545
  Frame goldens all live in `test/fixtures/tui/`; `MemoryTerminal` reconstructs the screen (without color, verifying only layout and glyphs):
528
546
 
529
547
  - `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-*`).
548
+ - `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
549
  - 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
550
  - `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
551
 
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