@armadra/agent 0.6.1 → 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 (152) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/CHANGELOG.zh-CN.md +56 -0
  3. package/README.md +168 -575
  4. package/README.zh-CN.md +165 -596
  5. package/dist/agent/session-settings.d.ts +4 -1
  6. package/dist/agent/session-settings.js +7 -2
  7. package/dist/agent/session.d.ts +6 -0
  8. package/dist/agent/session.js +8 -0
  9. package/dist/agent/subagent-background.d.ts +75 -0
  10. package/dist/agent/subagent-background.js +209 -0
  11. package/dist/agent/subagent-registry.d.ts +24 -5
  12. package/dist/agent/subagent-registry.js +64 -92
  13. package/dist/agent/types-w5.d.ts +11 -1
  14. package/dist/agent/types.d.ts +5 -0
  15. package/dist/agents/builtin.js +0 -1
  16. package/dist/agents/external.js +0 -1
  17. package/dist/agents/parse.js +4 -3
  18. package/dist/agents/result.d.ts +7 -1
  19. package/dist/agents/result.js +20 -1
  20. package/dist/agents/task-control.d.ts +13 -2
  21. package/dist/agents/task-record.d.ts +13 -1
  22. package/dist/agents/task-record.js +32 -0
  23. package/dist/agents/types.d.ts +5 -1
  24. package/dist/ai/apis/chatgpt-backend.d.ts +7 -2
  25. package/dist/ai/apis/chatgpt-backend.js +12 -4
  26. package/dist/ai/providers/discovered-cache.d.ts +29 -12
  27. package/dist/ai/providers/discovered-cache.js +72 -12
  28. package/dist/ai/providers/registry.js +7 -6
  29. package/dist/ai/types.d.ts +5 -0
  30. package/dist/auth/chatgpt/backend-client.d.ts +14 -4
  31. package/dist/auth/chatgpt/backend-client.js +41 -8
  32. package/dist/auth/chatgpt/cli.d.ts +2 -1
  33. package/dist/auth/chatgpt/cli.js +14 -7
  34. package/dist/auth/chatgpt/follow.d.ts +16 -0
  35. package/dist/auth/chatgpt/follow.js +51 -0
  36. package/dist/auth/chatgpt/presets.d.ts +8 -0
  37. package/dist/auth/chatgpt/presets.js +12 -0
  38. package/dist/auth/testing/fake-oauth.d.ts +4 -1
  39. package/dist/auth/testing/fake-oauth.js +24 -1
  40. package/dist/bundle/ama.cjs +2016 -664
  41. package/dist/cli/compose-agents.d.ts +2 -1
  42. package/dist/cli/compose-agents.js +11 -2
  43. package/dist/cli/compose-providers.d.ts +2 -1
  44. package/dist/cli/compose-providers.js +6 -3
  45. package/dist/cli/startup-steps.js +4 -1
  46. package/dist/cli/subcommands/models-discover.d.ts +5 -2
  47. package/dist/cli/subcommands/models-discover.js +52 -10
  48. package/dist/config/json-schema.js +10 -2
  49. package/dist/config/key-docs.js +9 -3
  50. package/dist/config/merge.d.ts +1 -1
  51. package/dist/config/merge.js +20 -3
  52. package/dist/config/schema-w5.js +4 -2
  53. package/dist/config/schema-w6.d.ts +1 -1
  54. package/dist/config/schema-w6.js +2 -1
  55. package/dist/config/schema.js +2 -0
  56. package/dist/config/settings-registry.js +4 -0
  57. package/dist/config/types-w5.d.ts +10 -0
  58. package/dist/config/types-w5.js +2 -0
  59. package/dist/config/types-w6.d.ts +5 -0
  60. package/dist/config/types.d.ts +3 -1
  61. package/dist/git/info.d.ts +19 -0
  62. package/dist/git/info.js +64 -8
  63. package/dist/i18n/catalog.d.ts +52 -8
  64. package/dist/i18n/messages/agents.d.ts +56 -0
  65. package/dist/i18n/messages/agents.js +58 -2
  66. package/dist/i18n/messages/auth.d.ts +12 -0
  67. package/dist/i18n/messages/auth.js +20 -0
  68. package/dist/i18n/messages/config-keys.d.ts +8 -0
  69. package/dist/i18n/messages/config-keys.js +18 -10
  70. package/dist/i18n/messages/config.d.ts +8 -0
  71. package/dist/i18n/messages/interactive-startup.d.ts +0 -16
  72. package/dist/i18n/messages/interactive-startup.js +0 -16
  73. package/dist/i18n/messages/interactive.d.ts +23 -16
  74. package/dist/i18n/messages/interactive.js +25 -0
  75. package/dist/i18n/messages/print.d.ts +4 -0
  76. package/dist/i18n/messages/print.js +4 -0
  77. package/dist/i18n/messages/report.js +4 -4
  78. package/dist/i18n/messages/settings.d.ts +6 -0
  79. package/dist/i18n/messages/settings.js +6 -0
  80. package/dist/i18n/messages/subcommands-config.d.ts +4 -0
  81. package/dist/i18n/messages/subcommands-config.js +4 -0
  82. package/dist/i18n/messages/subcommands.d.ts +4 -0
  83. package/dist/index.d.ts +1 -0
  84. package/dist/modes/commands-core.js +8 -4
  85. package/dist/modes/interactive/agent-bar.d.ts +3 -1
  86. package/dist/modes/interactive/agent-bar.js +12 -5
  87. package/dist/modes/interactive/agent-ui.d.ts +21 -3
  88. package/dist/modes/interactive/agent-ui.js +81 -12
  89. package/dist/modes/interactive/agent-view.d.ts +4 -0
  90. package/dist/modes/interactive/agent-view.js +14 -1
  91. package/dist/modes/interactive/approval-dock.d.ts +51 -0
  92. package/dist/modes/interactive/approval-dock.js +112 -0
  93. package/dist/modes/interactive/approval-ui.d.ts +43 -0
  94. package/dist/modes/interactive/approval-ui.js +64 -0
  95. package/dist/modes/interactive/commands.js +4 -1
  96. package/dist/modes/interactive/event-notices.d.ts +6 -1
  97. package/dist/modes/interactive/event-notices.js +7 -1
  98. package/dist/modes/interactive/interactive-mode.d.ts +4 -2
  99. package/dist/modes/interactive/interactive-mode.js +33 -38
  100. package/dist/modes/interactive/key-dispatch.d.ts +21 -4
  101. package/dist/modes/interactive/key-dispatch.js +60 -8
  102. package/dist/modes/interactive/line/line-mode.js +5 -0
  103. package/dist/modes/interactive/run-indicator.d.ts +15 -0
  104. package/dist/modes/interactive/run-indicator.js +37 -5
  105. package/dist/modes/interactive/session-events.js +5 -1
  106. package/dist/modes/interactive/startup-header.d.ts +29 -14
  107. package/dist/modes/interactive/startup-header.js +93 -59
  108. package/dist/modes/interactive/startup-logo.d.ts +83 -0
  109. package/dist/modes/interactive/startup-logo.js +183 -0
  110. package/dist/modes/interactive/status-area.d.ts +18 -0
  111. package/dist/modes/interactive/status-area.js +67 -1
  112. package/dist/modes/interactive/status-bar.d.ts +9 -1
  113. package/dist/modes/interactive/status-bar.js +43 -11
  114. package/dist/modes/interactive/status-line.d.ts +2 -0
  115. package/dist/modes/interactive/status-line.js +11 -6
  116. package/dist/modes/interactive/status-quota.d.ts +44 -0
  117. package/dist/modes/interactive/status-quota.js +135 -0
  118. package/dist/modes/interactive/subagent-view.d.ts +1 -0
  119. package/dist/modes/interactive/subagent-view.js +8 -0
  120. package/dist/modes/interactive/task-background.d.ts +31 -0
  121. package/dist/modes/interactive/task-background.js +68 -0
  122. package/dist/modes/interactive/tool-view.d.ts +7 -1
  123. package/dist/modes/interactive/tool-view.js +24 -1
  124. package/dist/modes/print/print-mode.d.ts +11 -0
  125. package/dist/modes/print/print-mode.js +36 -1
  126. package/dist/modes/rpc/commands.d.ts +2 -1
  127. package/dist/modes/rpc/commands.js +9 -1
  128. package/dist/rpc.d.ts +13 -0
  129. package/dist/rpc.js +3 -0
  130. package/dist/tools/task-ctl.d.ts +2 -0
  131. package/dist/tools/task-ctl.js +7 -2
  132. package/dist/tools/task.d.ts +11 -0
  133. package/dist/tools/task.js +20 -2
  134. package/dist/tui/components/editor.d.ts +2 -0
  135. package/dist/tui/components/editor.js +4 -0
  136. package/dist/tui/components/loader.d.ts +5 -1
  137. package/dist/tui/components/loader.js +18 -5
  138. package/dist/tui/keybindings.d.ts +8 -3
  139. package/dist/tui/keybindings.js +8 -3
  140. package/docs/agents.md +52 -28
  141. package/docs/en/host-api.md +5 -1
  142. package/docs/en/providers.md +8 -4
  143. package/docs/en/rpc.md +19 -7
  144. package/docs/en/sessions.md +3 -1
  145. package/docs/en/tui.md +84 -66
  146. package/docs/host-api.md +5 -1
  147. package/docs/providers.md +29 -12
  148. package/docs/rpc.md +19 -7
  149. package/docs/sessions.md +2 -1
  150. package/docs/tui-design.md +42 -29
  151. package/docs/tui.md +67 -49
  152. package/package.json +1 -1
@@ -21,6 +21,10 @@
21
21
  * - [W5-H2] plan 模式产出计划、`plan.unattended: stop`(缺省)没人审批:stderr 一行(计划文件与审批办法)、
22
22
  * json 带 `planPending{planId, version, filePath}`、退出码 9。
23
23
  * - [W5-H2] `--image` / `@图片` 超限时按 config `images.resize` 缩放(缺省 auto)。
24
+ * - [W7-B2] 后台子 Agent(显式 `background:true` 或 `subagents.background: always`):主回合结束后若还有任务
25
+ * 在跑或通知待投递,stderr 一行提示并等它们结束、跑完通知回合再输出(最终文本 = 最后一条助手回复);
26
+ * 受 `--max-turns` / `--max-cost` / `limits.*` 约束(到限即停止等待,退出码 8),SIGINT / SIGTERM 照常中止
27
+ * (未结束的任务随会话关闭被停止);json 结果带 `tasks`(同 `getStats().tasks`)。
24
28
  * - 退出码:最终助手消息 `error / aborted` 或提示被拒 → 1;有工具调用被拒 → 7;到达预算 → 8;
25
29
  * 计划待审批 → 9;SIGINT 130、SIGTERM 143(先 abort)。
26
30
  */
@@ -29,6 +33,7 @@ import { ExitCode } from "../../cli/exit-codes.js";
29
33
  import { errorText, lastAssistant, onStdoutClosed, onTerminationSignals } from "../shared.js";
30
34
  import { toJsonLine, toWireEvent } from "./json-event.js";
31
35
  import { formatUsd } from "../../agent/limits.js";
36
+ import { registryOf } from "../../agent/subagent-registry.js";
32
37
  import { promptImages, sessionModel } from "../image-input.js";
33
38
  import { msg } from "../../i18n/index.js";
34
39
  export function joinPrompt(argument, piped) {
@@ -118,9 +123,15 @@ export async function runPrintMode(runtime, context) {
118
123
  const denied = [];
119
124
  let limit;
120
125
  let plan;
126
+ let stopWaiting = () => undefined;
127
+ const stopped = new Promise((resolve) => {
128
+ stopWaiting = resolve;
129
+ });
121
130
  const unsubscribe = session.subscribe((event) => {
122
- if (event.type === "limit_reached")
131
+ if (event.type === "limit_reached") {
123
132
  limit ??= event;
133
+ stopWaiting();
134
+ }
124
135
  else if (event.type === "plan_proposed")
125
136
  plan = event;
126
137
  else if (event.type === "plan_resolved" && event.planId === plan?.planId)
@@ -139,16 +150,25 @@ export async function runPrintMode(runtime, context) {
139
150
  let signalled;
140
151
  const offSignals = onTerminationSignals((code) => {
141
152
  signalled ??= code;
153
+ stopWaiting();
142
154
  void session.abort();
143
155
  });
144
156
  let stdoutClosed = false;
145
157
  const offEpipe = onStdoutClosed(() => {
146
158
  stdoutClosed = true;
159
+ stopWaiting();
147
160
  void session.abort();
148
161
  });
149
162
  let failure;
150
163
  try {
151
164
  await session.prompt(prompt, images.length > 0 ? { images } : {});
165
+ const ended = lastAssistant(session)?.stopReason;
166
+ if (signalled === undefined &&
167
+ !stdoutClosed &&
168
+ limit === undefined &&
169
+ ended !== "error" &&
170
+ ended !== "aborted")
171
+ await waitBackgroundTasks(session, stopped, io);
152
172
  }
153
173
  catch (error) {
154
174
  failure = errorText(error);
@@ -183,6 +203,7 @@ export async function runPrintMode(runtime, context) {
183
203
  cost: stats.cost,
184
204
  cacheHitRate: stats.cacheHitRate,
185
205
  ...(stats.cache !== undefined ? { cache: stats.cache } : {}),
206
+ ...(stats.tasks !== undefined ? { tasks: stats.tasks } : {}),
186
207
  ...(denied.length > 0 ? { deniedTools: denied } : {}),
187
208
  ...(plan !== undefined
188
209
  ? {
@@ -230,6 +251,20 @@ export async function runPrintMode(runtime, context) {
230
251
  return ExitCode.ToolDenied;
231
252
  return ExitCode.Ok;
232
253
  }
254
+ /**
255
+ * [W7-B2] 主回合结束后等后台任务与它们的通知回合(docs/agents-concurrency-plan.md §2.6、§6 Q5)。
256
+ * `settled()`(subagent-background.ts)等到没有运行中任务且通知投递链(含通知回合)结束;`stopped`:SIGINT /
257
+ * SIGTERM、stdout 关闭或预算到限时 resolve,立即停止等待。没有子 Agent 注册表时直接返回。
258
+ */
259
+ export async function waitBackgroundTasks(session, stopped, io) {
260
+ const registry = registryOf(session.state.sessionId);
261
+ if (registry === undefined)
262
+ return;
263
+ const running = registry.list().filter((task) => task.status === "running").length;
264
+ if (running > 0)
265
+ io.stderr(msg().print.print.waitingTasks(running));
266
+ await Promise.race([registry.settled(), stopped]);
267
+ }
233
268
  /** stderr 一行:计划已落盘待审批(-p 不替人批准)。 */
234
269
  export function describePlanPending(plan) {
235
270
  const where = plan.filePath ?? plan.planId;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * RPC 命令分派表(设计 §13.2、src/rpc.ts 的 33 条命令)。[B6]
2
+ * RPC 命令分派表(设计 §13.2、src/rpc.ts 的命令表,现 44 条)。[B6]
3
3
  *
4
4
  * - 提示类(prompt / steer / follow_up)不等运行结束:会话开始运行(`before_agent_start` /
5
5
  * `agent_start`)、入队或被处理后立刻应答 `{ disposition }`;运行中的进展走事件。
@@ -11,6 +11,7 @@
11
11
  * - [W5-F] 计划:声明 `plans` 能力后计划审批交客户端(`plan_proposed` → `plan_response`),未声明按
12
12
  * `plan.unattended`;`approve_fresh` 在这里新建会话并以计划全文开新回合。`get_tasks / get_agents`
13
13
  * 读 `RpcContext.tasks / agents`(W5-G 的注册表与发现结果;未装配时回空表)。
14
+ * - [W7-B2] `background_task { taskId? }` → `{ backgrounded }`(`session.backgroundTask`,B1)。
14
15
  */
15
16
  import type { AgentInfo } from "../../agents/types.js";
16
17
  import type { TaskRegistryView } from "../../tools/types.js";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * RPC 命令分派表(设计 §13.2、src/rpc.ts 的 33 条命令)。[B6]
2
+ * RPC 命令分派表(设计 §13.2、src/rpc.ts 的命令表,现 44 条)。[B6]
3
3
  *
4
4
  * - 提示类(prompt / steer / follow_up)不等运行结束:会话开始运行(`before_agent_start` /
5
5
  * `agent_start`)、入队或被处理后立刻应答 `{ disposition }`;运行中的进展走事件。
@@ -11,6 +11,7 @@
11
11
  * - [W5-F] 计划:声明 `plans` 能力后计划审批交客户端(`plan_proposed` → `plan_response`),未声明按
12
12
  * `plan.unattended`;`approve_fresh` 在这里新建会话并以计划全文开新回合。`get_tasks / get_agents`
13
13
  * 读 `RpcContext.tasks / agents`(W5-G 的注册表与发现结果;未装配时回空表)。
14
+ * - [W7-B2] `background_task { taskId? }` → `{ backgrounded }`(`session.backgroundTask`,B1)。
14
15
  */
15
16
  import { formatModelRef } from "../../ai/providers/channels.js";
16
17
  import { AgentSessionImpl } from "../../agent/session.js";
@@ -312,5 +313,12 @@ export const handlers = {
312
313
  get_agents: async (_p, ctx) => ({ agents: [...(ctx.agents?.() ?? [])] }),
313
314
  // [W6-T2] 轨迹(docs/wave6-plan.md §2.6;分页 / 增量 / 脱敏见 trace/query.ts)
314
315
  get_trace: async (p, ctx) => sessionTrace(ctx.session(), p ?? {}),
316
+ // [W7-B2] 前台任务转后台:工具调用立即返回、任务继续,完成后照常通知
317
+ background_task: async (p, ctx) => {
318
+ const taskId = p?.taskId;
319
+ if (taskId !== undefined && typeof taskId !== "string")
320
+ throw new AmaError("invalid_arguments", msg().print.rpc.badTaskId);
321
+ return { backgrounded: ctx.session().backgroundTask(taskId) };
322
+ },
315
323
  };
316
324
  export const RPC_COMMAND_TYPES = Object.keys(handlers);
package/dist/rpc.d.ts CHANGED
@@ -19,6 +19,9 @@
19
19
  * - [W6-C0] 第六波(docs/wave6-plan.md §2.6、§4.4):命令 `get_trace`(参数 `RpcGetTraceParams`、返回
20
20
  * `RpcW6Results`,W6-T2 实现,之前回 `not_implemented`);事件 `quota_update`;`permission_request.context`
21
21
  * 可带 `toolCallId`;`keySource` 可为 `oauth`。`RPC_PROTOCOL_VERSION` 不变。
22
+ * - [W7-B2] 后台子 Agent(docs/agents-concurrency-plan.md §2.5):命令 `background_task { taskId? }` →
23
+ * `RpcW7Results`(无 taskId = 全部前台运行中任务;对已结束 / 已在后台的任务回空表);事件
24
+ * `subagent_background` 由 `SessionEvent` 派生。`RPC_PROTOCOL_VERSION` 不变。
22
25
  */
23
26
  import type { AssistantEvent, ImageBlock, ModelThinkingLevel, Usage } from "./ai/types.js";
24
27
  import type { PlanData, PlanDecisionKind, QueueMode, SessionEvent, TodoItemView } from "./agent/types.js";
@@ -139,6 +142,16 @@ export interface RpcCommandMap {
139
142
  get_tasks: NoParams;
140
143
  get_agents: NoParams;
141
144
  get_trace: RpcGetTraceParams;
145
+ background_task: {
146
+ taskId?: string;
147
+ };
148
+ }
149
+ /** [W7-B2] 第七波命令成功时的 `data` 形状。 */
150
+ export interface RpcW7Results {
151
+ /** 实际转了后台的 taskId(含被打断的 `task_ctl wait`);没有可转的为空表。 */
152
+ background_task: {
153
+ backgrounded: string[];
154
+ };
142
155
  }
143
156
  /** [W6-C0] `get_trace` 的参数。 */
144
157
  export interface RpcGetTraceParams {
package/dist/rpc.js CHANGED
@@ -19,5 +19,8 @@
19
19
  * - [W6-C0] 第六波(docs/wave6-plan.md §2.6、§4.4):命令 `get_trace`(参数 `RpcGetTraceParams`、返回
20
20
  * `RpcW6Results`,W6-T2 实现,之前回 `not_implemented`);事件 `quota_update`;`permission_request.context`
21
21
  * 可带 `toolCallId`;`keySource` 可为 `oauth`。`RPC_PROTOCOL_VERSION` 不变。
22
+ * - [W7-B2] 后台子 Agent(docs/agents-concurrency-plan.md §2.5):命令 `background_task { taskId? }` →
23
+ * `RpcW7Results`(无 taskId = 全部前台运行中任务;对已结束 / 已在后台的任务回空表);事件
24
+ * `subagent_background` 由 `SessionEvent` 派生。`RPC_PROTOCOL_VERSION` 不变。
22
25
  */
23
26
  export const RPC_PROTOCOL_VERSION = 1;
@@ -7,6 +7,8 @@
7
7
  * 与父一致,运行时拒绝;
8
8
  * - `wait` / `output` 是轮询类调用(`annotations.pollable`,重复调用检测豁免);
9
9
  * - `send` = 续聊放后台:等价于 `task{taskId, prompt, background:true}`,完成后同样收到通知。
10
+ * - [W7-B1] `wait` 被转后台(`background()`,TUI `Ctrl+B` / RPC `background_task`)打断时立即返回固定
11
+ * 文案,要求模型不要再等。
10
12
  */
11
13
  import type { ToolDefinition } from "./types.js";
12
14
  export declare const TASK_CTL_TOOL = "task_ctl";
@@ -7,8 +7,10 @@
7
7
  * 与父一致,运行时拒绝;
8
8
  * - `wait` / `output` 是轮询类调用(`annotations.pollable`,重复调用检测豁免);
9
9
  * - `send` = 续聊放后台:等价于 `task{taskId, prompt, background:true}`,完成后同样收到通知。
10
+ * - [W7-B1] `wait` 被转后台(`background()`,TUI `Ctrl+B` / RPC `background_task`)打断时立即返回固定
11
+ * 文案,要求模型不要再等。
10
12
  */
11
- import { capTaskText, formatTokens } from "../agents/result.js";
13
+ import { capTaskText, formatTokens, waitDetachedText } from "../agents/result.js";
12
14
  import { taskControl } from "../agents/task-control.js";
13
15
  export const TASK_CTL_TOOL = "task_ctl";
14
16
  export const DEFAULT_WAIT_MS = 30_000;
@@ -96,7 +98,10 @@ export function createTaskCtlTool() {
96
98
  }
97
99
  case "wait": {
98
100
  const timeout = Math.min(Math.max(input.timeoutMs ?? DEFAULT_WAIT_MS, 0), MAX_WAIT_MS);
99
- const result = await control.wait(taskId, timeout);
101
+ const signal = control.detachSignal(taskId);
102
+ const result = await control.wait(taskId, timeout, { signal });
103
+ if (result === undefined && signal.aborted)
104
+ return { content: waitDetachedText(taskId) };
100
105
  if (result === undefined) {
101
106
  const turns = control.get(taskId)?.turns ?? 0;
102
107
  return {
@@ -5,6 +5,8 @@
5
5
  * - `agent`:子 Agent 类型(内置 general / explore / plan、`.ama/agents/*.md`)或外部 Agent;缺省
6
6
  * general。描述里列出可用类型(catalog 由 compose-agents.ts 经 `bindTaskAgents` 绑定;没绑定时
7
7
  * 只列内置类型)。
8
+ * - [W7-B1] 缺省前台 / 后台由 `subagents.background` 决定(交互 / RPC / ACP 后台,`-p` 前台),描述随之
9
+ * 二选一(`bindTaskBackground`);前台任务可被 `background()` 转后台,工具立即以固定文案返回。
8
10
  * - `background`:立即返回 taskId,完成后父会话收到 `<task-notification>`;`taskId`:向已有子会话
9
11
  * 追加消息(续聊,忽略 agent / tools / model);`isolation: "worktree"`:在独立 git worktree 里跑。
10
12
  * - **parallel**:同一回复里的多个 task 真并行(会话注册表的池限流,缺省 4);与 sequential 工具
@@ -33,9 +35,18 @@ export interface TaskInput {
33
35
  /** 预留(并发上限在会话的任务注册表,见 agent/subagent-registry.ts)。 */
34
36
  export interface TaskToolOptions {
35
37
  }
38
+ /** 缺省前台(`-p`、`subagents.background: never`)时的描述。 */
39
+ export declare const FOREGROUND_DESCRIPTION: string;
40
+ /** [W7-B1] 缺省后台(交互 / RPC / ACP,docs/agents-concurrency-plan.md §2.6)时的描述。 */
41
+ export declare const BACKGROUND_DESCRIPTION: string;
36
42
  /** 描述里类型清单的标题(清单本身另有 400 token 预算,见 agents/catalog.ts)。 */
37
43
  export declare const TASK_AGENTS_HEADING = "\nAgents:\n";
38
44
  /** 组装根把会话的类型目录绑到 task 工具上(描述里的类型清单)。 */
39
45
  export declare function bindTaskAgents(tool: ToolDefinition | undefined, catalog: AgentCatalog): void;
46
+ /**
47
+ * [W7-B1] 组装根把本进程的缺省前台 / 后台(`subagents.background` 按运行模式解析后)绑到 task 工具:
48
+ * 决定描述用哪一版。会话内不变(缓存前缀稳定);没绑定按缺省前台。
49
+ */
50
+ export declare function bindTaskBackground(tool: ToolDefinition | undefined, background: boolean): void;
40
51
  export declare function buildSubagentRequest(input: TaskInput, ctx: ToolContext): SubagentRequest | string;
41
52
  export declare function createTaskTool(_options?: TaskToolOptions): ToolDefinition<TaskInput>;
@@ -5,6 +5,8 @@
5
5
  * - `agent`:子 Agent 类型(内置 general / explore / plan、`.ama/agents/*.md`)或外部 Agent;缺省
6
6
  * general。描述里列出可用类型(catalog 由 compose-agents.ts 经 `bindTaskAgents` 绑定;没绑定时
7
7
  * 只列内置类型)。
8
+ * - [W7-B1] 缺省前台 / 后台由 `subagents.background` 决定(交互 / RPC / ACP 后台,`-p` 前台),描述随之
9
+ * 二选一(`bindTaskBackground`);前台任务可被 `background()` 转后台,工具立即以固定文案返回。
8
10
  * - `background`:立即返回 taskId,完成后父会话收到 `<task-notification>`;`taskId`:向已有子会话
9
11
  * 追加消息(续聊,忽略 agent / tools / model);`isolation: "worktree"`:在独立 git worktree 里跑。
10
12
  * - **parallel**:同一回复里的多个 task 真并行(会话注册表的池限流,缺省 4);与 sequential 工具
@@ -24,17 +26,32 @@ const THINKING = [
24
26
  "high",
25
27
  "xhigh",
26
28
  ];
27
- const BASE_DESCRIPTION = "Delegate to a sub-agent (fresh context; give full instructions). Returns its final report. " +
29
+ /** 缺省前台(`-p`、`subagents.background: never`)时的描述。 */
30
+ export const FOREGROUND_DESCRIPTION = "Delegate to a sub-agent (fresh context; give full instructions). Returns its final report. " +
28
31
  "Tasks in one reply run in parallel; writers should use isolation worktree. background: " +
29
32
  "returns a taskId now (see task_ctl); taskId: continue that task.";
33
+ /** [W7-B1] 缺省后台(交互 / RPC / ACP,docs/agents-concurrency-plan.md §2.6)时的描述。 */
34
+ export const BACKGROUND_DESCRIPTION = "Delegate to a sub-agent (fresh context; give full instructions). Runs in the background by " +
35
+ "default: returns a taskId; a <task-notification> follows when done, so keep working. " +
36
+ "background:false if your next step needs the result. taskId: continue a task. Tasks in one " +
37
+ "reply run in parallel; writers use isolation worktree.";
30
38
  /** 描述里类型清单的标题(清单本身另有 400 token 预算,见 agents/catalog.ts)。 */
31
39
  export const TASK_AGENTS_HEADING = "\nAgents:\n";
32
40
  const catalogs = new WeakMap();
41
+ const backgrounds = new WeakMap();
33
42
  /** 组装根把会话的类型目录绑到 task 工具上(描述里的类型清单)。 */
34
43
  export function bindTaskAgents(tool, catalog) {
35
44
  if (tool !== undefined)
36
45
  catalogs.set(tool, catalog);
37
46
  }
47
+ /**
48
+ * [W7-B1] 组装根把本进程的缺省前台 / 后台(`subagents.background` 按运行模式解析后)绑到 task 工具:
49
+ * 决定描述用哪一版。会话内不变(缓存前缀稳定);没绑定按缺省前台。
50
+ */
51
+ export function bindTaskBackground(tool, background) {
52
+ if (tool !== undefined)
53
+ backgrounds.set(tool, background);
54
+ }
38
55
  function fail(message) {
39
56
  return { content: message, isError: true };
40
57
  }
@@ -87,7 +104,8 @@ export function createTaskTool(_options = {}) {
87
104
  label: "Task",
88
105
  get description() {
89
106
  const catalog = catalogs.get(tool) ?? new AgentCatalog();
90
- return `${BASE_DESCRIPTION}${TASK_AGENTS_HEADING}${catalog.describe()}`;
107
+ const base = backgrounds.get(tool) === true ? BACKGROUND_DESCRIPTION : FOREGROUND_DESCRIPTION;
108
+ return `${base}${TASK_AGENTS_HEADING}${catalog.describe()}`;
91
109
  },
92
110
  parameters: {
93
111
  type: "object",
@@ -88,6 +88,8 @@ export declare class Editor implements Component, Focusable {
88
88
  col: number;
89
89
  };
90
90
  get isCompletionOpen(): boolean;
91
+ /** 正在用 ↑↓ 浏览输入历史(编辑后即退出)。 */
92
+ get isBrowsingHistory(): boolean;
91
93
  getHistory(): readonly string[];
92
94
  setHistory(entries: readonly string[]): void;
93
95
  addToHistory(text: string): void;
@@ -88,6 +88,10 @@ export class Editor {
88
88
  get isCompletionOpen() {
89
89
  return this.completion !== null;
90
90
  }
91
+ /** 正在用 ↑↓ 浏览输入历史(编辑后即退出)。 */
92
+ get isBrowsingHistory() {
93
+ return this.historyIndex !== -1;
94
+ }
91
95
  getHistory() {
92
96
  return this.history;
93
97
  }
@@ -2,7 +2,8 @@
2
2
  * 运行指示(设计 §12.1、§12.6;终端界面视觉设计 v1 §3.7)。[B4]
3
3
  *
4
4
  * 一行 `⠋ 动词 · 已用时 · 附加项…`:spinner `accent`、动词正文色、其余 `dim`,以 ` · ` 连接。
5
- * - `setVerb(verb, extras, { elapsed })` 换动词(思考中 / 回复中 / 运行 bash / 等待确认……);
5
+ * - `setVerb(verb, extras, { elapsed, optional })` 换动词(思考中 / 回复中 / 运行 bash / 等待确认……);
6
+ * `optional` 是排在附加项之后的可丢弃项(按键提示),整行放不下时从后往前丢,而不是被截断;
6
7
  * `setMessage(text)` 是只换动词的旧接口。
7
8
  * - 帧取 `theme.glyphs.spinner`(Unicode 10 帧 80 ms、ASCII 4 帧 250 ms);`frame` 暴露当前帧字形,
8
9
  * `onFrame` 让别的组件(运行中的工具摘要行)与 Loader 同帧换字,保证一帧只多改一行。
@@ -27,6 +28,8 @@ export interface LoaderOptions {
27
28
  export interface LoaderVerbOptions {
28
29
  /** 本动词是否显示已用时(缺省跟随 `showElapsed`)。 */
29
30
  elapsed?: boolean;
31
+ /** 可丢弃的附加项:宽度不够时从后往前整项丢掉(缺省没有)。 */
32
+ optional?: readonly string[];
30
33
  }
31
34
  export declare function formatElapsed(ms: number): string;
32
35
  export declare class Loader implements Component {
@@ -37,6 +40,7 @@ export declare class Loader implements Component {
37
40
  private startedAt;
38
41
  private verb;
39
42
  private extras;
43
+ private optional;
40
44
  private verbElapsed;
41
45
  private lastElapsed;
42
46
  private readonly listeners;
@@ -2,17 +2,21 @@
2
2
  * 运行指示(设计 §12.1、§12.6;终端界面视觉设计 v1 §3.7)。[B4]
3
3
  *
4
4
  * 一行 `⠋ 动词 · 已用时 · 附加项…`:spinner `accent`、动词正文色、其余 `dim`,以 ` · ` 连接。
5
- * - `setVerb(verb, extras, { elapsed })` 换动词(思考中 / 回复中 / 运行 bash / 等待确认……);
5
+ * - `setVerb(verb, extras, { elapsed, optional })` 换动词(思考中 / 回复中 / 运行 bash / 等待确认……);
6
+ * `optional` 是排在附加项之后的可丢弃项(按键提示),整行放不下时从后往前丢,而不是被截断;
6
7
  * `setMessage(text)` 是只换动词的旧接口。
7
8
  * - 帧取 `theme.glyphs.spinner`(Unicode 10 帧 80 ms、ASCII 4 帧 250 ms);`frame` 暴露当前帧字形,
8
9
  * `onFrame` 让别的组件(运行中的工具摘要行)与 Loader 同帧换字,保证一帧只多改一行。
9
10
  * - `animation: false`:spinner 固定为 `glyphs.spinnerStatic`,定时器每秒一次,已用时变了才请求重绘。
10
11
  * - `stop()` 后不再触发渲染。时钟可注入便于测试。
11
12
  */
12
- import { truncateToWidth } from "../ansi.js";
13
+ import { truncateToWidth, visibleWidth } from "../ansi.js";
13
14
  import { UNICODE_GLYPHS } from "../glyphs.js";
14
15
  /** Unicode 帧表(兼容旧导出;实际帧取 `theme.glyphs.spinner`)。 */
15
16
  export const LOADER_FRAMES = UNICODE_GLYPHS.spinner;
17
+ function sameList(a, b) {
18
+ return a.length === b.length && a.every((e, i) => e === b[i]);
19
+ }
16
20
  export function formatElapsed(ms) {
17
21
  const total = Math.max(0, Math.floor(ms / 1000));
18
22
  if (total < 60)
@@ -29,6 +33,7 @@ export class Loader {
29
33
  startedAt;
30
34
  verb;
31
35
  extras = [];
36
+ optional = [];
32
37
  verbElapsed;
33
38
  lastElapsed = "";
34
39
  listeners = new Set();
@@ -65,12 +70,13 @@ export class Loader {
65
70
  setVerb(verb, extras = [], options = {}) {
66
71
  const same = verb === this.verb &&
67
72
  options.elapsed === this.verbElapsed &&
68
- extras.length === this.extras.length &&
69
- extras.every((e, i) => e === this.extras[i]);
73
+ sameList(extras, this.extras) &&
74
+ sameList(options.optional ?? [], this.optional);
70
75
  if (same)
71
76
  return;
72
77
  this.verb = verb;
73
78
  this.extras = [...extras];
79
+ this.optional = [...(options.optional ?? [])];
74
80
  this.verbElapsed = options.elapsed;
75
81
  this.requestRender();
76
82
  }
@@ -142,7 +148,14 @@ export class Loader {
142
148
  for (const extra of this.extras)
143
149
  parts.push(dim(extra));
144
150
  const spinner = theme ? theme.fg("accent", this.frame) : this.frame;
145
- return [truncateToWidth(`${spinner} ${parts.join(dim(" · "))}`, width)];
151
+ const line = (items) => `${spinner} ${items.join(dim(" · "))}`;
152
+ for (const extra of this.optional) {
153
+ const next = [...parts, dim(extra)];
154
+ if (visibleWidth(line(next)) > width)
155
+ break;
156
+ parts.push(dim(extra));
157
+ }
158
+ return [truncateToWidth(line(parts), width)];
146
159
  }
147
160
  invalidate() { }
148
161
  }
@@ -45,10 +45,15 @@ export declare const DEFAULT_KEYBINDINGS: {
45
45
  /** [W5-U] 粘贴剪贴板图片(写进数据目录,输入框插入 `@路径`)。 */
46
46
  readonly "app.paste.image": readonly ["ctrl+v"];
47
47
  /**
48
- * [W6-A] 进入 Agent 栏:只在输入为空时生效(有字时 `Ctrl+B` 仍是光标左移、`↓` 仍是下移 / 历史);
49
- * `↓` 另要求栏可见。tmux 缺省前缀吃掉 `Ctrl+B` 时用 `↓`。
48
+ * [W6-A / W7-A] 进入 Agent 栏:输入为空、补全未开、有子 Agent 任务即可(不要求栏可见);有字时 `↓` 仍是
49
+ * 下移 / 历史,并给一次提示。`Ctrl+B` 不再进栏(tmux 缺省前缀会吃掉它),留给 `app.tasks.background`。
50
50
  */
51
- readonly "app.agents.focus": readonly ["ctrl+b", "down"];
51
+ readonly "app.agents.focus": readonly ["down"];
52
+ /**
53
+ * [W7-C] 有阻塞中的前台子 Agent 任务(或 `task_ctl wait`)时全部转后台,不看输入框;没有时落回编辑器
54
+ * (`tui.editor.cursorLeft` 的 `ctrl+b`)。tmux 里按 `C-b C-b` 透传。
55
+ */
56
+ readonly "app.tasks.background": readonly ["ctrl+b"];
52
57
  };
53
58
  export type ActionId = keyof typeof DEFAULT_KEYBINDINGS;
54
59
  export type KeybindingOverrides = Partial<Record<ActionId, readonly string[]>>;
@@ -48,10 +48,15 @@ export const DEFAULT_KEYBINDINGS = {
48
48
  /** [W5-U] 粘贴剪贴板图片(写进数据目录,输入框插入 `@路径`)。 */
49
49
  "app.paste.image": ["ctrl+v"],
50
50
  /**
51
- * [W6-A] 进入 Agent 栏:只在输入为空时生效(有字时 `Ctrl+B` 仍是光标左移、`↓` 仍是下移 / 历史);
52
- * `↓` 另要求栏可见。tmux 缺省前缀吃掉 `Ctrl+B` 时用 `↓`。
51
+ * [W6-A / W7-A] 进入 Agent 栏:输入为空、补全未开、有子 Agent 任务即可(不要求栏可见);有字时 `↓` 仍是
52
+ * 下移 / 历史,并给一次提示。`Ctrl+B` 不再进栏(tmux 缺省前缀会吃掉它),留给 `app.tasks.background`。
53
53
  */
54
- "app.agents.focus": ["ctrl+b", "down"],
54
+ "app.agents.focus": ["down"],
55
+ /**
56
+ * [W7-C] 有阻塞中的前台子 Agent 任务(或 `task_ctl wait`)时全部转后台,不看输入框;没有时落回编辑器
57
+ * (`tui.editor.cursorLeft` 的 `ctrl+b`)。tmux 里按 `C-b C-b` 透传。
58
+ */
59
+ "app.tasks.background": ["ctrl+b"],
55
60
  };
56
61
  export function isActionId(id) {
57
62
  return Object.prototype.hasOwnProperty.call(DEFAULT_KEYBINDINGS, id);
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.