@armadra/agent 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,8 @@
7
7
  * 与父一致,运行时拒绝;
8
8
  * - `wait` / `output` 是轮询类调用(`annotations.pollable`,重复调用检测豁免);
9
9
  * - `send` = 续聊放后台:等价于 `task{taskId, prompt, background:true}`,完成后同样收到通知。
10
+ * - [#242] `coordinator` 预设下 `wait` 的超时封顶 `COORDINATOR_WAIT_MAX_MS`(协调者不阻塞,见
11
+ * `bindTaskCtlWaitCap`);其它预设不变。
10
12
  * - [W7-B1] `wait` 被转后台(`background()`,TUI `Ctrl+B` / RPC `background_task`)打断时立即返回固定
11
13
  * 文案,要求模型不要再等。
12
14
  */
@@ -16,6 +18,17 @@ import { nestedTaskRejection } from "./task.js";
16
18
  export const TASK_CTL_TOOL = "task_ctl";
17
19
  export const DEFAULT_WAIT_MS = 30_000;
18
20
  export const MAX_WAIT_MS = 600_000;
21
+ /** [#242] `coordinator` 预设下 `wait` 的超时上限(毫秒)。 */
22
+ export const COORDINATOR_WAIT_MAX_MS = 5_000;
23
+ const waitCaps = new WeakMap();
24
+ /**
25
+ * [#242] 组装根把 `wait` 的超时上限绑到 `task_ctl` 工具(`coordinator` 预设);没绑定按 `MAX_WAIT_MS`。
26
+ * 工具描述与参数 schema 不变(其它预设逐字节不变);被封顶时在超时回复里说明。
27
+ */
28
+ export function bindTaskCtlWaitCap(tool, capMs) {
29
+ if (tool !== undefined)
30
+ waitCaps.set(tool, capMs);
31
+ }
19
32
  function fail(message) {
20
33
  return { content: message, isError: true };
21
34
  }
@@ -46,7 +59,7 @@ async function send(input, ctx) {
46
59
  return { content: result.text, isError: result.isError };
47
60
  }
48
61
  export function createTaskCtlTool() {
49
- return {
62
+ const tool = {
50
63
  name: TASK_CTL_TOOL,
51
64
  label: "TaskCtl",
52
65
  description: "Manage sub-agent tasks started by task: list them, wait for one, stop it, read its " +
@@ -98,15 +111,20 @@ export function createTaskCtlTool() {
98
111
  };
99
112
  }
100
113
  case "wait": {
101
- const timeout = Math.min(Math.max(input.timeoutMs ?? DEFAULT_WAIT_MS, 0), MAX_WAIT_MS);
114
+ const cap = Math.min(waitCaps.get(tool) ?? MAX_WAIT_MS, MAX_WAIT_MS);
115
+ const wanted = Math.min(Math.max(input.timeoutMs ?? DEFAULT_WAIT_MS, 0), MAX_WAIT_MS);
116
+ const timeout = Math.min(wanted, cap);
102
117
  const signal = control.detachSignal(taskId);
103
118
  const result = await control.wait(taskId, timeout, { signal });
104
119
  if (result === undefined && signal.aborted)
105
120
  return { content: waitDetachedText(taskId) };
106
121
  if (result === undefined) {
107
122
  const turns = control.get(taskId)?.turns ?? 0;
123
+ const capped = wanted > timeout
124
+ ? ` Waits are capped at ${Math.round(timeout / 1000)}s here; do not block on it: keep working, a <task-notification> follows when it finishes.`
125
+ : "";
108
126
  return {
109
- content: `Task ${taskId} is still running (${turns} turns so far). Wait again or stop it.`,
127
+ content: `Task ${taskId} is still running (${turns} turns so far). Wait again or stop it.${capped}`,
110
128
  };
111
129
  }
112
130
  return {
@@ -119,4 +137,5 @@ export function createTaskCtlTool() {
119
137
  }
120
138
  },
121
139
  };
140
+ return tool;
122
141
  }
@@ -71,7 +71,7 @@ Read-only commands (`src/permissions/readonly-bash.ts`): first they pass auto's
71
71
  | `tree`, `file`, `jq` | `tree -o`, `file -C`, `jq -i` do not count |
72
72
  | `git status / log / show / diff / rev-parse / blame / ls-files / branch` | Only the git global options `-C` and `--no-pager` are allowed; `--output`, `--ext-diff`, `--textconv` and branch-changing options do not count |
73
73
 
74
- This is narrower than auto's safe list: no test / build runners (`npm test` and `cargo build` run project scripts), and no `env` / `printenv` (they would print secrets in environment variables into the context). Reading secret paths (`cat .env`) is not read-only.
74
+ This is narrower than auto's safe list: no test / build runners (`npm test` and `cargo build` run project scripts). `env` / `printenv` (they would print secrets in environment variables into the context) are on neither list. Reading secret paths (`cat .env`) is not read-only.
75
75
 
76
76
  ## Network class: web_fetch
77
77
 
@@ -175,15 +175,15 @@ No model call; allowed when satisfied:
175
175
 
176
176
  The safe list (`src/permissions/auto-safe.ts`, each entry with positive and negative tests):
177
177
 
178
- | Command | Restrictions |
179
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
180
- | `ls`, `cat`, `head`, `tail`, `wc`, `grep`, `egrep`, `fgrep`, `rg`, `pwd`, `echo`, `printf`, `which`, `env` and `printenv` (printing only), `true`, `false`, `sort`, `cut`, `tr`, `diff`, `basename`, `dirname`, `realpath`, `stat`, `file`, `du`, `df`, `date`, `whoami`, `uname`, `cd` / `pushd` / `popd` (later relative paths resolve against the new directory) | `env FOO=1 cmd` is judged as `cmd` |
181
- | `find` | Without `-exec`, `-execdir`, `-ok`, `-okdir`, `-delete`, `-fprint*`, `-fls` |
182
- | `git status / diff / log / show / rev-parse / blame / ls-files` (`diff / log / show` without `--output`, `--ext-diff`, `--textconv`; `rg` without `--pre`; `sort` without `-o`; `date` without `-s`) | Only `-C dir` and `--no-pager` before the subcommand (`-c` can change the pager and is not safe) |
183
- | `git branch` | Listing only: without `-d / -D / --delete / -m / -M / -c / -C / -f / -u` etc. |
184
- | `npm / pnpm / yarn test`, `… run test / lint / typecheck / build`, `pnpm / yarn lint / typecheck / build`, `npm t` | |
185
- | `node --test`, `tsc --noEmit`, `vitest run`, `pnpm vitest run`, `pnpm exec vitest run`, `pytest`, `python -m pytest` | |
186
- | `cargo test / check / build / clippy`, `go test / build / vet`, `make test / check / lint / build` | |
178
+ | Command | Restrictions |
179
+ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
180
+ | `ls`, `cat`, `head`, `tail`, `wc`, `grep`, `egrep`, `fgrep`, `rg`, `pwd`, `echo`, `printf`, `which`, `true`, `false`, `sort`, `cut`, `tr`, `diff`, `basename`, `dirname`, `realpath`, `stat`, `file`, `du`, `df`, `date`, `whoami`, `uname`, `cd` / `pushd` / `popd` (later relative paths resolve against the new directory) | `env FOO=1 cmd` is judged as `cmd`; `env` / `printenv` print the whole environment (secrets included) and are not on the list |
181
+ | `find` | Without `-exec`, `-execdir`, `-ok`, `-okdir`, `-delete`, `-fprint*`, `-fls` |
182
+ | `git status / diff / log / show / rev-parse / blame / ls-files` (`diff / log / show` without `--output`, `--ext-diff`, `--textconv`; `rg` without `--pre`; `sort` without `-o`; `date` without `-s`) | Only `-C dir` and `--no-pager` before the subcommand (`-c` can change the pager and is not safe) |
183
+ | `git branch` | Listing only: without `-d / -D / --delete / -m / -M / -c / -C / -f / -u` etc. |
184
+ | `npm / pnpm / yarn test`, `… run test / lint / typecheck / build`, `pnpm / yarn lint / typecheck / build`, `npm t` | |
185
+ | `node --test`, `tsc --noEmit`, `vitest run`, `pnpm vitest run`, `pnpm exec vitest run`, `pytest`, `python -m pytest` | |
186
+ | `cargo test / check / build / clippy`, `go test / build / vet`, `make test / check / lint / build` | |
187
187
 
188
188
  Extending it: the user-level config `permission.autoSafeCommands` adds entries, e.g. `["just test", "bun test", "make fmt"]`, matched by word prefix (`just test` matches `just test --verbose`); entries containing `*` match the whole segment as a glob (`bun run test*`). Project config cannot add entries (that would loosen). Added commands still pass ① first: network, deletion and writes outside the project still ask.
189
189
 
@@ -147,9 +147,9 @@ These are deliberate choices, not omissions:
147
147
 
148
148
  The model uses ACP agents through `task(agent="acp:<program>")` (ama itself is `task(agent="acp:ama")`; see "Using them in task" in [agents.md](../../guides/agents.md), Chinese).
149
149
 
150
- `AcpClient` (`@armadra/agent/acp`): `initialize`, `newSession`, `resumeSession` (preferred, no replay), `loadSession`, `listSessions`, `closeSession`, `prompt`, `setMode`, `cancel`.
150
+ `AcpClient` (`@armadra/agent/acp`): `initialize`, `authenticate`, `logout`, `newSession`, `resumeSession` (preferred, no replay), `loadSession`, `listSessions`, `listAllSessions`, `closeSession`, `prompt`, `setMode`, `cancel`.
151
151
 
152
- - Declared client capabilities: `session.configOptions: {}` (select options; boolean is not declared); `fs` is not declared (`fs/*` requests from the agent answer method not found) and `terminal` only when `onTerminal` is given (see below), and neither is `auth.terminal` (ama has no interactive terminal to lend to the agent).
152
+ - Declared client capabilities: `session.configOptions: {}` (select options; boolean is not declared); `fs` is not declared (`fs/*` requests from the agent answer method not found) and `terminal` only when `onTerminal` is given (see below), and `auth.terminal` is not declared by default either (ama has no interactive terminal to lend to the agent; a host that has one passes `authTerminal: true`, see below).
153
153
  - `$/cancel_request` is on in both directions: aborting the `signal` of a request after it was sent tells the agent to withdraw it; when the agent withdraws a pending `session/request_permission` / `elicitation/create`, the handler's `signal` aborts and the answer is `cancelled` / `cancel` (a choice the handler makes afterwards does not count).
154
154
  - `session/request_permission` goes to `onPermission`; without a handler the first `reject_once` is answered (unattended).
155
155
  - `cancel(sessionId)` sends `session/cancel` and answers `cancelled` to that session's pending permission requests (required by the spec).
@@ -158,7 +158,11 @@ The model uses ACP agents through `task(agent="acp:<program>")` (ama itself is `
158
158
  - `elicitation/create` (the agent asking a human for structured input): only when the constructor is given `onElicitation(params, signal)` does `initialize` declare `clientCapabilities.elicitation` and accept this request; otherwise it is not declared and the request answers method not found (as in older versions). Answers are normalized to `{ action: "accept" | "decline" | "cancel", content? }` (`content` only with accept; unknown actions count as cancel); pending ones answer `{ action: "cancel" }` on `cancel(sessionId)` and when the connection closes. ama itself, as a client, gives no handler and never fills in forms for a human.
159
159
  - Session config options: the `configOptions` of session-open answers (new / load / resume) are passed through; `setConfigOption(sessionId, configId, value)` sends `session/set_config_option`, and the answer is the new state of all options.
160
160
  - `terminal/*` (the agent asking the host for a terminal): only when the constructor is given `onTerminal(method, params, signal)` does `initialize` declare `clientCapabilities.terminal: true`; `terminal/create|output|wait_for_exit|kill|release` are then handed to it with the params untouched, its return value is the answer and throwing `RpcError` answers with that error. Without it `terminal: false` is declared and the requests answer method not found (as in older versions). Permissions, cwd restriction and output limits are the host's job; ama itself, as a client, gives no handler.
161
- - `AcpClient.features`: `{ mcpServers, elicitation, configOptions, terminal }`, for feature detection by hosts.
161
+ - Authentication: `authenticate(methodId, signal?)` sends `authenticate { methodId }`; `logout(signal?)` sends `logout` (available when the agent declares `agentCapabilities.auth.logout`, see `supportsLogout()`). `authMethods` keeps the id, name, description and type from `initialize`, plus `args` and `env` for terminal methods (`isTerminalAuthMethod` tells them apart). The spec forbids passing a terminal method to `authenticate`: when the method is known to be a terminal one, `authenticate` throws -32602 locally without sending anything, and the host runs the agent command plus the method's `args` and `env` in a terminal (exit code 0 is success). Only the constructor option `authTerminal: true` declares `clientCapabilities.auth.terminal`, which is what makes the agent offer terminal methods. On -32000 (auth_required) when opening a session or sending a prompt, `isAuthRequiredError(error)` recognizes it and the host shows `authMethods` to the user. ama itself, as a client, passes no `authTerminal` and never signs in for a human.
162
+ - `elicitation/complete` (agent → client notification: a URL elicitation finished out of band): only when the constructor is given `onElicitationComplete({ elicitationId })` is it handled, and `initialize` then declares `elicitation` as `{ form: {}, url: {} }` (with only `onElicitation` it stays `{}`). If a request with the same `elicitationId` is still unanswered, its handler's `signal` aborts and it answers `{ action: "cancel" }`, so a host can dismiss the card; notifications without a string `elicitationId` are ignored.
163
+ - Form fields: besides `string` / `number` / `integer` / `boolean`, `type: "array"` is a string multi-select (`items: { type: "string", enum }` or titled `items: { anyOf: [{ const, title }] }`, optionally with `minItems` / `maxItems`), and a `string` field may carry `oneOf: [{ const, title }]` (titled single select). In the answer's `content` a multi-select value is an array of strings. `multiSelectOptions(field)` / `singleSelectOptions(field)` normalize those forms into a `{ const, title }` list (undefined for other controls); unknown `type`s are kept as is and must not be rendered as a known control.
164
+ - Session management: `listSessions(cwd?, cursor?, signal?)` sends `session/list` (`supportsList()`), `listAllSessions(cwd?, signal?, maxPages = 100)` follows `nextCursor` to fetch them all; `closeSession(sessionId, signal?)` sends `session/close` (`supportsClose()`). The spec makes closing equivalent to cancelling first, so `closeSession` first answers that session's pending permission requests with `cancelled` and its elicitations with `cancel`; other sessions are untouched. An agent that did not declare the capability answers -32601 and one that needs sign-in answers -32000; both codes are thrown as they are.
165
+ - `AcpClient.features`: `{ mcpServers, elicitation, configOptions, terminal, auth, elicitationComplete }`, for feature detection by hosts.
162
166
 
163
167
  `AcpDriver` implements the driver contract (`AgentDriver`) on top of the client:
164
168
 
@@ -86,7 +86,8 @@ ama 再跑一轮、在消息里要求「不要调用工具、直接给最终报
86
86
 
87
87
  是否后台的优先级:调用参数 `background` > 类型定义的 `background:` > config `subagents.background`。
88
88
  `subagents.background` 缺省 `auto`:交互界面、RPC、ACP 下后台(模型需要结果才写 `background: false`),`-p` 下前台;
89
- `always` / `never` 固定。两种缺省对应两版 `task` 工具描述,会话内不变,不影响缓存前缀稳定。
89
+ `always` / `never` 固定。
90
+ `coordinator` 工具预设(协调者只分派、不阻塞)下缺省固定为后台,不受 `-p` 与 `subagents.background` 影响。两种缺省对应两版 `task` 工具描述,会话内不变,不影响缓存前缀稳定。
90
91
 
91
92
  前台任务运行中可以转后台,任务不中断,`task` 工具调用立即返回一段固定英文结果(含 `taskId` 与输出文件),完成后照常发
92
93
  `<task-notification>`:
@@ -128,6 +129,8 @@ ama 再跑一轮、在消息里要求「不要调用工具、直接给最终报
128
129
  | `output` | 运行中返回已有输出,结束后返回最终文本(同样有 50 KB 上限) |
129
130
  | `send` | 向任务追加一条消息并放到后台运行(等价于 `task{taskId, prompt, background:true}`) |
130
131
 
132
+ `coordinator` 预设下 `wait` 的超时封顶 5 000 毫秒(含缺省与更大的 `timeoutMs`),超时回复里说明被封顶、完成后会有通知;其它预设不变。
133
+
131
134
  ### 续聊、保留与 resume
132
135
 
133
136
  每个任务有会话内唯一的 `taskId`(`t1`、`t2` …)。子会话结束后不立即释放,最多保留 4 个(`subagents.retainSessions`;
@@ -66,7 +66,7 @@ plan 模式的第 ③ 步看输入(实现 `src/permissions/pipeline.ts` 的 `p
66
66
  | `tree`、`file`、`jq` | `tree -o`、`file -C`、`jq -i` 不算 |
67
67
  | `git status / log / show / diff / rev-parse / blame / ls-files / branch` | git 全局选项只允许 `-C`、`--no-pager`;`--output`、`--ext-diff`、`--textconv` 与改分支的选项不算 |
68
68
 
69
- 比 auto 的安全名单窄:不含测试 / 构建运行器(`npm test`、`cargo build` 会执行项目脚本),也不含 `env` / `printenv`(会把环境变量里的密钥打进上下文)。读机密路径(`cat .env`)不算只读。
69
+ 比 auto 的安全名单窄:不含测试 / 构建运行器(`npm test`、`cargo build` 会执行项目脚本)。`env` / `printenv`(会把环境变量里的密钥打进上下文)两份名单都没有。读机密路径(`cat .env`)不算只读。
70
70
 
71
71
  ## 网络类:web_fetch
72
72
 
@@ -174,15 +174,15 @@ default / auto-edit 下,满足下面全部条件的 bash 调用免审批(`Pe
174
174
 
175
175
  安全名单(`src/permissions/auto-safe.ts`,每条都有正反例测试):
176
176
 
177
- | 命令 | 限制 |
178
- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
179
- | `ls`、`cat`、`head`、`tail`、`wc`、`grep`、`egrep`、`fgrep`、`rg`、`pwd`、`echo`、`printf`、`which`、`env` 与 `printenv`(只打印)、`true`、`false`、`sort`、`cut`、`tr`、`diff`、`basename`、`dirname`、`realpath`、`stat`、`file`、`du`、`df`、`date`、`whoami`、`uname`、`cd` / `pushd` / `popd`(之后的相对路径按新目录解析) | `env FOO=1 cmd` 按 `cmd` 判断 |
180
- | `find` | 不带 `-exec`、`-execdir`、`-ok`、`-okdir`、`-delete`、`-fprint*`、`-fls` |
181
- | `git status / diff / log / show / rev-parse / blame / ls-files`(`diff / log / show` 不带 `--output`、`--ext-diff`、`--textconv`;`rg` 不带 `--pre`;`sort` 不带 `-o`;`date` 不带 `-s`) | 子命令前只允许 `-C dir`、`--no-pager`(`-c` 能改 pager,不算安全) |
182
- | `git branch` | 只列出:不带 `-d / -D / --delete / -m / -M / -c / -C / -f / -u` 等 |
183
- | `npm / pnpm / yarn test`、`… run test / lint / typecheck / build`、`pnpm / yarn lint / typecheck / build`、`npm t` | |
184
- | `node --test`、`tsc --noEmit`、`vitest run`、`pnpm vitest run`、`pnpm exec vitest run`、`pytest`、`python -m pytest` | |
185
- | `cargo test / check / build / clippy`、`go test / build / vet`、`make test / check / lint / build` | |
177
+ | 命令 | 限制 |
178
+ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
179
+ | `ls`、`cat`、`head`、`tail`、`wc`、`grep`、`egrep`、`fgrep`、`rg`、`pwd`、`echo`、`printf`、`which`、`true`、`false`、`sort`、`cut`、`tr`、`diff`、`basename`、`dirname`、`realpath`、`stat`、`file`、`du`、`df`、`date`、`whoami`、`uname`、`cd` / `pushd` / `popd`(之后的相对路径按新目录解析) | `env FOO=1 cmd` 按 `cmd` 判断;`env` / `printenv` 会打印整个环境(含密钥),不在名单里 |
180
+ | `find` | 不带 `-exec`、`-execdir`、`-ok`、`-okdir`、`-delete`、`-fprint*`、`-fls` |
181
+ | `git status / diff / log / show / rev-parse / blame / ls-files`(`diff / log / show` 不带 `--output`、`--ext-diff`、`--textconv`;`rg` 不带 `--pre`;`sort` 不带 `-o`;`date` 不带 `-s`) | 子命令前只允许 `-C dir`、`--no-pager`(`-c` 能改 pager,不算安全) |
182
+ | `git branch` | 只列出:不带 `-d / -D / --delete / -m / -M / -c / -C / -f / -u` 等 |
183
+ | `npm / pnpm / yarn test`、`… run test / lint / typecheck / build`、`pnpm / yarn lint / typecheck / build`、`npm t` | |
184
+ | `node --test`、`tsc --noEmit`、`vitest run`、`pnpm vitest run`、`pnpm exec vitest run`、`pytest`、`python -m pytest` | |
185
+ | `cargo test / check / build / clippy`、`go test / build / vet`、`make test / check / lint / build` | |
186
186
 
187
187
  扩展:用户级配置 `permission.autoSafeCommands` 追加,例如 `["just test", "bun test", "make fmt"]`——按词前缀匹配(`just test` 命中 `just test --verbose`);含 `*` 的按通配匹配整段(`bun run test*`)。项目级配置不能追加(放宽)。追加的命令照样先过①:网络、删除、项目外写入仍然询问。
188
188
 
@@ -153,10 +153,10 @@ stdin 关闭后等已开始的运行结束再退出(0);`ama` 进程的 std
153
153
 
154
154
  模型经 `task(agent="acp:<程序>")` 使用 ACP Agent(ama 自己是 `task(agent="acp:ama")`,见 [agents.md](../guides/agents.md)「在 task 里使用」)。
155
155
 
156
- `AcpClient`(`@armadra/agent/acp`):`initialize`、`newSession`、`resumeSession`(优先,不回放)、`loadSession`、`listSessions`、`closeSession`、`prompt`、`setMode`、`cancel`。
156
+ `AcpClient`(`@armadra/agent/acp`):`initialize`、`authenticate`、`logout`、`newSession`、`resumeSession`(优先,不回放)、`loadSession`、`listSessions`、`listAllSessions`、`closeSession`、`prompt`、`setMode`、`cancel`。
157
157
 
158
158
  - 声明的客户端能力:`session.configOptions: {}`(接 select 型配置项,不声明 boolean);不声明 `fs`(Agent 发来的 `fs/*` 请求回 method not found);`terminal` 只在构造参数给了
159
- `onTerminal` 时声明(见下),也不声明 `auth.terminal`(ama 没有可借给 Agent 的交互终端)。
159
+ `onTerminal` 时声明(见下),`auth.terminal` 缺省也不声明(ama 没有可借给 Agent 的交互终端;宿主有交互终端时构造参数给 `authTerminal: true`,见下)。
160
160
  - 线路两侧开 `$/cancel_request`:本端请求的 `signal` 在发出后 abort 会通知 Agent 撤回;Agent 撤回挂起的
161
161
  `session/request_permission` / `elicitation/create` 时处理器的 `signal` abort,答 `cancelled` / `cancel`(处理器之后给的选择不作数)。
162
162
  - `session/request_permission` 交给 `onPermission`;没有处理器时回首个 `reject_once`(无人值守)。
@@ -175,7 +175,11 @@ stdin 关闭后等已开始的运行结束再退出(0);`ama` 进程的 std
175
175
  `clientCapabilities.terminal: true`,并把 `terminal/create|output|wait_for_exit|kill|release` 连同原样参数交给它,返回值即答复,
176
176
  抛 `RpcError` 回对应错误;没给时声明 `terminal: false`、请求回 method not found(与旧版相同)。权限、cwd 限制与输出上限由宿主负责;
177
177
  ama 自己作客户端时不给处理器。
178
- - `AcpClient.features`:`{ mcpServers, elicitation, configOptions, terminal }`,宿主据此做特性检测。
178
+ - 认证:`authenticate(methodId, signal?)` 发 `authenticate { methodId }`;`logout(signal?)` 发 `logout`(Agent 声明 `agentCapabilities.auth.logout` 才可用,`supportsLogout()`)。`authMethods` 保留 `initialize` 答的 id、name、description、type,terminal 型还有 `args`、`env`(`isTerminalAuthMethod` 判断类型)。terminal 型方法规范禁止经 `authenticate`:已知是 terminal 型时 `authenticate` 本地抛 -32602、不发请求,由宿主在终端里跑 agent 命令加该方法的 `args`、`env`,退出码 0 即成功。构造参数 `authTerminal: true` 才声明 `clientCapabilities.auth.terminal`,Agent 据此才给 terminal 型方法。开会话或发提示遇 -32000(auth_required)用 `isAuthRequiredError(error)` 判断,再把 `authMethods` 交给用户。ama 自己作客户端不给 `authTerminal`、不替人登录。
179
+ - `elicitation/complete`(Agent → 客户端通知,URL 型 elicitation 在带外完成):构造参数给了 `onElicitationComplete({ elicitationId })` 才接,并把 `initialize` 的 `elicitation` 声明成 `{ form: {}, url: {} }`(只给 `onElicitation` 时仍是 `{}`)。同一 `elicitationId` 的请求若还没答,它处理器的 `signal` 随之 abort、答 `{ action: "cancel" }`,宿主据此收掉卡片;参数里没有字符串 `elicitationId` 的通知被忽略。
180
+ - 表单字段:除 `string` / `number` / `integer` / `boolean` 外,`type: "array"` 是字符串多选(`items: { type: "string", enum }` 或带标题的 `items: { anyOf: [{ const, title }] }`,可带 `minItems` / `maxItems`),`string` 字段可带 `oneOf: [{ const, title }]`(带标题的单选)。答复 `content` 里多选值是字符串数组。`multiSelectOptions(field)` / `singleSelectOptions(field)` 把这几种写法收成 `{ const, title }` 列表(不是对应控件返回 undefined);不认识的 `type` 原样保留,不要当已知控件渲染。
181
+ - 会话管理:`listSessions(cwd?, cursor?, signal?)` 发 `session/list`(`supportsList()`),`listAllSessions(cwd?, signal?, maxPages = 100)` 跟 `nextCursor` 翻页取全;`closeSession(sessionId, signal?)` 发 `session/close`(`supportsClose()`)。规范要求关闭等同先取消,所以 `closeSession` 先让该会话挂起的权限请求答 `cancelled`、elicitation 答 `cancel`,别的会话不受影响。Agent 没声明能力会答 -32601,需要登录时答 -32000,错误码都原样抛出。
182
+ - `AcpClient.features`:`{ mcpServers, elicitation, configOptions, terminal, auth, elicitationComplete }`,宿主据此做特性检测。
179
183
 
180
184
  `AcpDriver` 在客户端之上实现驱动契约(`AgentDriver`):
181
185
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armadra/agent",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "A coding and coordination agent that runs standalone or embedded in Armadra",
5
5
  "type": "module",
6
6
  "keywords": [