@armadra/agent 0.6.8 → 0.7.1
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.
- package/CHANGELOG.md +69 -0
- package/CHANGELOG.zh-CN.md +50 -0
- package/README.md +4 -3
- package/README.zh-CN.md +1 -1
- package/dist/acp.d.ts +2 -2
- package/dist/acp.js +2 -2
- package/dist/ai/apis/shared.d.ts +7 -1
- package/dist/ai/apis/shared.js +10 -0
- package/dist/ai/fake/fake-provider.js +5 -1
- package/dist/ai/fake/fake-script.d.ts +5 -2
- package/dist/ai/fake/fake-script.js +6 -2
- package/dist/ai/types.d.ts +2 -1
- package/dist/auth/oauth/token-store.d.ts +2 -0
- package/dist/auth/oauth/token-store.js +7 -2
- package/dist/bundle/ama.cjs +48975 -47637
- package/dist/checkpoints/shadow-git.js +2 -1
- package/dist/cli/args.d.ts +6 -0
- package/dist/cli/args.js +46 -0
- package/dist/cli/bootstrap.js +13 -2
- package/dist/cli/compose-events.d.ts +10 -0
- package/dist/cli/compose-events.js +97 -0
- package/dist/cli/compose-session.d.ts +25 -4
- package/dist/cli/compose-session.js +130 -103
- package/dist/cli/compose.js +6 -1
- package/dist/cli/runtime.d.ts +3 -1
- package/dist/cli/subcommands/auth.d.ts +13 -1
- package/dist/cli/subcommands/auth.js +28 -1
- package/dist/config/auth-file.js +10 -2
- package/dist/config/fs-retry.d.ts +18 -0
- package/dist/config/fs-retry.js +33 -0
- package/dist/drivers/acp/client.d.ts +7 -2
- package/dist/drivers/acp/client.js +10 -1
- package/dist/drivers/acp/driver.d.ts +15 -4
- package/dist/drivers/acp/driver.js +110 -33
- package/dist/drivers/acp/testing/fake-agent-main.d.ts +1 -1
- package/dist/drivers/acp/testing/fake-agent-main.js +9 -2
- package/dist/drivers/acp/testing/fake-agent.d.ts +17 -2
- package/dist/drivers/acp/testing/fake-agent.js +108 -25
- package/dist/drivers/acp/types.d.ts +76 -12
- package/dist/drivers/acp/types.js +11 -3
- package/dist/drivers/jsonrpc.d.ts +13 -3
- package/dist/drivers/jsonrpc.js +40 -7
- package/dist/drivers/turn.d.ts +15 -2
- package/dist/drivers/turn.js +33 -4
- package/dist/i18n/catalog.d.ts +59 -16
- package/dist/i18n/catalog.js +4 -1
- package/dist/i18n/messages/acp.d.ts +125 -0
- package/dist/i18n/messages/acp.js +126 -0
- package/dist/i18n/messages/auth.d.ts +2 -0
- package/dist/i18n/messages/auth.js +4 -2
- package/dist/i18n/messages/cli-args.d.ts +2 -0
- package/dist/i18n/messages/cli-args.js +2 -0
- package/dist/i18n/messages/cli.d.ts +4 -0
- package/dist/i18n/messages/cli.js +2 -0
- package/dist/i18n/messages/print.d.ts +3 -33
- package/dist/i18n/messages/print.js +3 -33
- package/dist/modes/acp/acp-auth-gate.d.ts +40 -0
- package/dist/modes/acp/acp-auth-gate.js +201 -0
- package/dist/modes/acp/acp-config.d.ts +43 -0
- package/dist/modes/acp/acp-config.js +151 -0
- package/dist/modes/acp/acp-connection.d.ts +29 -0
- package/dist/modes/acp/acp-connection.js +37 -0
- package/dist/modes/acp/acp-events.d.ts +59 -23
- package/dist/modes/acp/acp-events.js +154 -88
- package/dist/modes/acp/acp-mode.d.ts +13 -2
- package/dist/modes/acp/acp-mode.js +23 -8
- package/dist/modes/acp/acp-server.d.ts +59 -32
- package/dist/modes/acp/acp-server.js +322 -128
- package/dist/modes/acp/acp-sessions.d.ts +80 -0
- package/dist/modes/acp/acp-sessions.js +157 -0
- package/dist/modes/acp/acp-tool-text.d.ts +23 -0
- package/dist/modes/acp/acp-tool-text.js +83 -0
- package/dist/modes/print/json-event.d.ts +2 -1
- package/dist/modes/print/json-event.js +6 -1
- package/dist/tools/edit.d.ts +6 -2
- package/dist/tools/edit.js +19 -2
- package/dist/tools/types.d.ts +11 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/write.d.ts +1 -0
- package/dist/tools/write.js +21 -3
- package/docs/acp.md +143 -29
- package/docs/agents.md +2 -0
- package/docs/codemode.md +1 -1
- package/docs/en/acp.md +192 -0
- package/docs/en/sessions.md +1 -1
- package/docs/sessions.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,75 @@ English · [简体中文](CHANGELOG.zh-CN.md)
|
|
|
5
5
|
> This file is in English starting with 0.6.0. Release notes for 0.1 through 0.5.1 are in Chinese in
|
|
6
6
|
> [CHANGELOG.zh-CN.md](CHANGELOG.zh-CN.md). New entries go into both files.
|
|
7
7
|
|
|
8
|
+
## 0.7.1 (2026-10-05)
|
|
9
|
+
|
|
10
|
+
- **Windows: concurrent OAuth refresh**: while one ama process releases the `auth.json.lock`, another one opening it
|
|
11
|
+
got EPERM (the file is "delete pending" on NTFS) and failed the refresh; it now keeps waiting for the lock. Replacing
|
|
12
|
+
or reading `auth.json` while another process has it open retries briefly on EPERM / EACCES / EBUSY (Windows only).
|
|
13
|
+
- **Checkpoints (shadow-git)**: the 3-second snapshot budget no longer includes creating the shadow repository the first
|
|
14
|
+
time (a dozen `git` processes, several seconds on Windows), which could downgrade a session to `tools` on its first turn.
|
|
15
|
+
|
|
16
|
+
## 0.7.0 (2026-10-04)
|
|
17
|
+
|
|
18
|
+
ACP completion: `ama --mode acp` as an agent for editors (Zed and other ACP clients) and `AcpClient` / `AcpDriver` as a
|
|
19
|
+
client, checked in-repo against the official ACP v1 schema 1.24.1. Docs: docs/acp.md (English: docs/en/acp.md).
|
|
20
|
+
|
|
21
|
+
- **No model, no exit**: without a model `ama --mode acp` no longer exits with code 4. It answers `initialize` (two
|
|
22
|
+
terminal auth methods when the client declares `clientCapabilities.auth.terminal`: their `args`,
|
|
23
|
+
`--acp-terminal-auth chatgpt` / `api-key`, are appended to the configured command as the spec says, and ama then runs
|
|
24
|
+
`ama auth login chatgpt` / `ama auth set` instead of ACP mode; the start-up `--auth-file` / profile `authFile` is passed on), answers session
|
|
25
|
+
methods with -32000 (the no-model guidance, `data.authMethods`) and retries start-up on them at most once per second;
|
|
26
|
+
once a model is available the same connection is handed to the normal ACP server (no new `initialize`). `authenticate`
|
|
27
|
+
answers -32602; closing stdin exits 0. stdout is taken over before start-up in ACP mode. `ama auth set` without a
|
|
28
|
+
provider on a TTY now lets you pick one with the arrow keys (non-TTY is still a usage error).
|
|
29
|
+
- **Several sessions**: every ACP session stays open in memory (an empty one can be switched back to); one turn runs at
|
|
30
|
+
a time and `session/prompt` for another session is queued (FIFO) instead of failing busy. `session/new` / `load` /
|
|
31
|
+
`resume` / `list` / `set_mode` / `set_config_option` / `close` work while a turn runs. Permission modes are kept per
|
|
32
|
+
session and applied when its turn starts. `session/cancel` on a queued prompt answers `cancelled`. `session/list`
|
|
33
|
+
filters by `cwd`, pages 50 at a time with `nextCursor` (an invalid cursor is invalid params) and strips embedded
|
|
34
|
+
resources from titles; `session_info_update` (title, `updatedAt`) is sent at the end of each turn. A refusal (Anthropic
|
|
35
|
+
`stop_reason: "refusal"`, new `StopReason` value) answers `refusal`; elsewhere the message still ends as an error
|
|
36
|
+
(`stopReasonOf()` tells them apart; the fake provider script accepts `stopReason: "refusal"`). `mcpServers` /
|
|
37
|
+
`additionalDirectories` are ignored with one stderr line. **Behavior change**: after `session/close`, requests for
|
|
38
|
+
that id answer -32002 (it used to reopen); open it again with `session/load` / `session/resume`.
|
|
39
|
+
- **Tool calls in detail**: `tool_call` carries `name`; each tool call inside a codemode script is listed as its own
|
|
40
|
+
`tool_call` (title prefixed `codemode › `, `_meta.ama.parentToolCallId` points at the outer call) and closes on its
|
|
41
|
+
own, and permission requests use the requesting call's id, so they no longer point at unknown ids. While a permission
|
|
42
|
+
request is open the call goes back to `pending`, then `in_progress` once allowed. `edit` / `write` fill the new
|
|
43
|
+
`ToolResult.fileChange` (raw before / after text with BOM and CRLF, `oldText: null` for a new file, omitted above
|
|
44
|
+
256 KiB per side; never persisted and stripped from RPC / stream-json events), and the completed update carries a
|
|
45
|
+
`diff` plus the first 4 KB of text, with `locations[].line` at the first changed line. Replayed tool results
|
|
46
|
+
(`session/load`) carry their first 4 KB of text (no diff). Permission modes get display names and localized
|
|
47
|
+
descriptions.
|
|
48
|
+
- **Config options and command list**: session-open results carry `configOptions` — `mode` (the permission mode, the same state as `modes`; clients with config options such
|
|
49
|
+
as Zed ignore `modes`), `model` (grouped by provider,
|
|
50
|
+
values `provider/model-id`, the same "configured" view as the TUI `/model` picker: only providers with a key, an OAuth
|
|
51
|
+
login or local, `models.enabled` respected, `fake` hidden by the usual rule) and `thinking` (category `thought_level`,
|
|
52
|
+
only the levels the current model supports); no boolean options. `session/set_config_option`
|
|
53
|
+
switches them (unknown ids / values answer -32602) and model / thinking level changes send `config_option_update`.
|
|
54
|
+
After a session opens, `available_commands_update` lists skills as `skill:<name>` and prompt templates as `<name>`
|
|
55
|
+
(`argument-hint` as `input.hint`); built-in slash commands are not listed. Prompt templates in
|
|
56
|
+
`LoadedResources.prompts` now carry `description` / `argumentHint`.
|
|
57
|
+
- **`$/cancel_request` both ways**: `JsonRpcPeer` takes `cancelRequests` (on for both ACP sides, off by default so the
|
|
58
|
+
Codex app-server wire is byte-for-byte unchanged): an aborted outgoing request notifies the peer, a peer-cancelled
|
|
59
|
+
incoming request aborts its `ctx.signal` and answers -32800. A prompt withdrawn with `$/cancel_request` stops the turn
|
|
60
|
+
and answers -32800; a permission request the agent no longer needs is withdrawn so the client can close its dialog.
|
|
61
|
+
- **Client side (`task(agent="acp:…")`)**: `AcpClient` declares `clientCapabilities.session.configOptions: {}`; an agent
|
|
62
|
+
withdrawing a pending permission request closes the approval and answers `cancelled`. `AcpDriver` falls back to a
|
|
63
|
+
`mode`-category config option when an agent has no `modes`, reports -32000 as `agent_auth_required` listing the
|
|
64
|
+
agent's auth methods (terminal ones with the command to run), treats -32800 after a cancel as `cancelled`, and counts
|
|
65
|
+
`diff` paths in `filesTouched`. Docs: docs/agents.md.
|
|
66
|
+
- **Types and tests**: the ACP types gain `authenticate`, `$/cancel_request`, -32800, terminal auth methods, client
|
|
67
|
+
`session` / `auth` capabilities, tool call `name` / `_meta`, `config_option_update` and `ACP_META_KEY` (exported from
|
|
68
|
+
`@armadra/agent/acp`); the prompt `usage` is marked UNSTABLE; select config options are either all flat or all grouped
|
|
69
|
+
(the fake agent's `model` option is now grouped). The fake ACP agent adds `--config-only`, `--auth-required` and
|
|
70
|
+
`[cancel-request]`. Every ACP line in the tests and golden recordings is validated against the bundled schema.
|
|
71
|
+
- **Zed run-through fixes**: terminal auth `args` are now `--acp-terminal-auth chatgpt|api-key` because clients append them
|
|
72
|
+
to the configured command; config options gain `mode` (Zed ignores `modes` once `configOptions` exist); a session's own
|
|
73
|
+
mode changes are remembered for the queue; `session/load` / `resume` of an unknown UUID (an empty session that was never
|
|
74
|
+
written before ama restarted) opens a new empty session with that id instead of -32002; tool call ids that the upstream reuses in a later turn get a `#n`
|
|
75
|
+
suffix on the wire so they stay unique within the session (Zed merged them into one entry).
|
|
76
|
+
|
|
8
77
|
## 0.6.8 (2026-10-04)
|
|
9
78
|
|
|
10
79
|
- **ACP client: elicitation and session config options**: `AcpClient` takes an optional `onElicitation(params, signal)`;
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -4,6 +4,56 @@
|
|
|
4
4
|
|
|
5
5
|
> 从 0.6.0 起 [CHANGELOG.md](CHANGELOG.md) 为英文,本文件保留中文记录(0.1–0.5.1 的完整历史在此)。新条目两份都要加。
|
|
6
6
|
|
|
7
|
+
## 0.7.1(2026-10-05)
|
|
8
|
+
|
|
9
|
+
- **Windows:并发刷新 OAuth**:一个 ama 进程刚释放 `auth.json.lock` 时,另一个进程打开它会报 EPERM(NTFS 上文件处于删除挂起),刷新
|
|
10
|
+
直接失败;现在当作锁被占用继续等。别的进程正打开 `auth.json` 时的覆盖 / 读取遇到 EPERM / EACCES / EBUSY 短暂重试(只在 Windows)。
|
|
11
|
+
- **检查点(影子 git)**:3 秒的快照耗时上限不再算上第一次建影子仓库(十来个 `git` 子进程,Windows 上要好几秒),以前可能第一回合就把
|
|
12
|
+
会话降级为 `tools`。
|
|
13
|
+
|
|
14
|
+
## 0.7.0(2026-10-04)
|
|
15
|
+
|
|
16
|
+
ACP 补全:`ama --mode acp` 作为编辑器(Zed 等 ACP 客户端)的 Agent,`AcpClient` / `AcpDriver` 作为客户端,仓库内对照官方 ACP v1
|
|
17
|
+
schema 1.24.1 逐条校验。文档:docs/acp.md(英文:docs/en/acp.md)。
|
|
18
|
+
|
|
19
|
+
- **没有模型不退出**:`ama --mode acp` 没有模型时不再以退出码 4 结束,照常回 `initialize`(客户端声明
|
|
20
|
+
`clientCapabilities.auth.terminal` 时给两条 terminal 型认证方法:按规范其 `args`(`--acp-terminal-auth chatgpt` / `api-key`)
|
|
21
|
+
追加在配置好的启动命令后面,ama 见到后不进 ACP 模式、改跑 `ama auth login chatgpt` / `ama auth set`;启动时的
|
|
22
|
+
`--auth-file` / profile 的 `authFile` 一并带上),会话方法回 -32000(无模型引导,`data.authMethods`)并以至多每秒一次重试启动;
|
|
23
|
+
有了模型就把同一条连接交给正常的 ACP 服务端(不必重新 `initialize`)。`authenticate` 回 -32602;stdin 关闭退出 0。
|
|
24
|
+
ACP 模式在启动前就接管 stdout。`ama auth set` 不给供应商且在 TTY 下时可用方向键选择(非 TTY 仍是用法错误)。
|
|
25
|
+
- **多会话**:每个 ACP 会话常驻内存(空会话切走再切回也在);同一时刻只跑一个回合,对别的会话的 `session/prompt` 进先进先出队列,
|
|
26
|
+
不再报 busy;`session/new` / `load` / `resume` / `list` / `set_mode` / `set_config_option` / `close` 在运行中也可调。权限模式按会话记,
|
|
27
|
+
轮到它跑时再应用。排队中的提示收到 `session/cancel` 回 `cancelled`。`session/list` 按 `cwd` 过滤、每页 50 条带 `nextCursor`
|
|
28
|
+
(非法 cursor 为 invalid params),标题去掉嵌入资源块;每回合结束发 `session_info_update`(标题、`updatedAt`)。拒答(Anthropic
|
|
29
|
+
`stop_reason: "refusal"`,`StopReason` 新值)回 `refusal`,其它地方消息仍按出错收尾(以 `stopReasonOf()` 区分;假供应商脚本支持
|
|
30
|
+
`stopReason: "refusal"`)。`mcpServers` / `additionalDirectories` 忽略并在 stderr 记一行。**行为变化**:`session/close` 之后对该 id
|
|
31
|
+
的请求回 -32002(以前会重新打开),要再用先 `session/load` / `session/resume`。
|
|
32
|
+
- **工具调用可视化**:`tool_call` 带 `name`;codemode 脚本里的每次内层调用单列为一条 `tool_call`(标题前缀 `codemode › `,
|
|
33
|
+
`_meta.ama.parentToolCallId` 指向外层调用)并各自收口,权限请求用发起调用的 id,不再指向未公布的 id。权限询问期间调用回到
|
|
34
|
+
`pending`,允许后再 `in_progress`。`edit` / `write` 填新的 `ToolResult.fileChange`(改前 / 改后的磁盘原文,BOM 与 CRLF 原样,
|
|
35
|
+
新文件 `oldText: null`,单侧超过 256 KiB 不填;不落盘,RPC / stream-json 事件里去掉),完成更新带 `diff` 与前 4 KB 文本,
|
|
36
|
+
`locations[].line` 为首个改动行。`session/load` 回放的工具结果带前 4 KB 文本(无 diff)。权限模式带显示名与随界面语言的说明。
|
|
37
|
+
- **配置项与命令表**:开会话答复带 `configOptions`——`mode`(权限模式,与 `modes` 同一状态;有配置项的客户端如 Zed 不再看 `modes`)、`model`(按供应商分组,值 `provider/model-id`,与 TUI `/model` 的「已配置」
|
|
38
|
+
视图同一口径:只列有 key、OAuth 已登录或本地的供应商,遵守 `models.enabled`,`fake` 按既有规则藏起)与 `thinking`(category
|
|
39
|
+
`thought_level`,只列当前模型支持的级别);没有 boolean 项。`session/set_config_option` 切换(未知 id / 值回
|
|
40
|
+
-32602),模型 / 思考级别变化发 `config_option_update`。会话打开后发 `available_commands_update`:Skill 列为 `skill:<名字>`,提示模板
|
|
41
|
+
列为 `<名字>`(`argument-hint` 作 `input.hint`),不列内置斜杠命令。`LoadedResources.prompts` 的提示模板多带 `description` /
|
|
42
|
+
`argumentHint`。
|
|
43
|
+
- **双向 `$/cancel_request`**:`JsonRpcPeer` 新选项 `cancelRequests`(ACP 两侧开,缺省关闭,Codex app-server 线路逐字节不变):本端
|
|
44
|
+
abort 的出站请求会通知对端,对端撤回的入站请求 abort 其 `ctx.signal` 并回 -32800。以 `$/cancel_request` 撤回的 prompt 停止回合并答
|
|
45
|
+
-32800;不再需要的权限请求由 Agent 撤回,客户端可以关掉对话框。
|
|
46
|
+
- **客户端侧(`task(agent="acp:…")`)**:`AcpClient` 声明 `clientCapabilities.session.configOptions: {}`;Agent 撤回挂起的权限请求时
|
|
47
|
+
审批关掉、答 `cancelled`。`AcpDriver` 在 Agent 没有 `modes` 时退到 category `mode` 的配置项,-32000 报 `agent_auth_required` 并列出
|
|
48
|
+
Agent 的认证方法(terminal 型附命令),取消后的 -32800 视为 `cancelled`,`diff` 的路径计入 `filesTouched`。文档:docs/agents.md。
|
|
49
|
+
- **类型与测试**:ACP 类型补 `authenticate`、`$/cancel_request`、-32800、terminal 型认证方法、客户端 `session` / `auth` 能力、
|
|
50
|
+
tool call 的 `name` / `_meta`、`config_option_update` 与 `ACP_META_KEY`(`@armadra/agent/acp` 导出);回合 `usage` 注明 UNSTABLE;
|
|
51
|
+
select 配置项的选项须全部平铺或全部分组(假 Agent 的 `model` 项改为分组)。假 ACP Agent 加 `--config-only`、`--auth-required` 与
|
|
52
|
+
`[cancel-request]`。测试与黄金记录里的每条 ACP 线路都按随仓库的 schema 校验。
|
|
53
|
+
- **Zed 实测修正**:terminal 认证的 `args` 改为 `--acp-terminal-auth chatgpt|api-key`(客户端是追加到启动命令后面);配置项补 `mode`(有
|
|
54
|
+
`configOptions` 时 Zed 不看 `modes`);会话自己换的模式记进排队重放;`session/load` / `resume` 找不到的 UUID(重启前从未落盘的空会话)按原 id
|
|
55
|
+
新建空会话,不再 -32002;上游在后续回合复用的工具调用 id 在线上加 `#n` 后缀,保持会话内唯一(Zed 曾把它们合成一条)。
|
|
56
|
+
|
|
7
57
|
## 0.6.8(2026-10-04)
|
|
8
58
|
|
|
9
59
|
- **ACP 客户端:elicitation 与会话配置项**:`AcpClient` 可选构造参数 `onElicitation(params, signal)`,给了才在 `initialize`
|
package/README.md
CHANGED
|
@@ -172,7 +172,7 @@ await session.dispose();
|
|
|
172
172
|
| Entry point | Use | Docs |
|
|
173
173
|
| -------------------- | ----------------------------------------------------------------- | ------------------------------------------ |
|
|
174
174
|
| `ama --mode rpc` | JSONL over stdio for hosts; types in `@armadra/agent/rpc` | [docs/en/rpc.md](docs/en/rpc.md) |
|
|
175
|
-
| `ama --mode acp` | ACP agent for editors and Armadra; client in `@armadra/agent/acp` | [docs/acp.md](docs/acp.md)
|
|
175
|
+
| `ama --mode acp` | ACP agent for editors and Armadra; client in `@armadra/agent/acp` | [docs/en/acp.md](docs/en/acp.md) |
|
|
176
176
|
| `--profile <file>` | Host adapter: canvas tools, approvals, injected messages, status | [docs/en/host-api.md](docs/en/host-api.md) |
|
|
177
177
|
| `@armadra/agent/tui` | The terminal component library | [docs/en/tui.md](docs/en/tui.md) |
|
|
178
178
|
|
|
@@ -218,7 +218,7 @@ Common flags: `--model`, `--thinking`, `--permission-mode`, `--allow` / `--deny`
|
|
|
218
218
|
|
|
219
219
|
## Documentation
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
Seven docs have English versions; the rest are in Chinese.
|
|
222
222
|
|
|
223
223
|
**User docs**
|
|
224
224
|
|
|
@@ -231,7 +231,8 @@ Six user docs have English versions; the rest are in Chinese.
|
|
|
231
231
|
**Integration docs**
|
|
232
232
|
|
|
233
233
|
- [RPC protocol](docs/en/rpc.md) ([中文](docs/rpc.md)), [Host adapter API](docs/en/host-api.md) ([中文](docs/host-api.md))
|
|
234
|
-
- [ACP](docs/acp.md)
|
|
234
|
+
- [ACP](docs/en/acp.md) ([中文](docs/acp.md)): `ama --mode acp`, sessions, tool calls, sign-in, deviations, the ACP client
|
|
235
|
+
- [Session file format](docs/session-format.md) (Chinese)
|
|
235
236
|
|
|
236
237
|
**Design and research** (Chinese)
|
|
237
238
|
|
package/README.zh-CN.md
CHANGED
|
@@ -229,7 +229,7 @@ Armadra 用 `ama --profile <路径>` 加 `coordinator` 预设启动 ama:协调
|
|
|
229
229
|
**集成文档**
|
|
230
230
|
|
|
231
231
|
- [RPC 协议](docs/rpc.md)([English](docs/en/rpc.md))、[宿主适配器 API](docs/host-api.md)([English](docs/en/host-api.md))
|
|
232
|
-
- [ACP](docs/acp.md)
|
|
232
|
+
- [ACP](docs/acp.md)([English](docs/en/acp.md))、[会话文件格式](docs/session-format.md)
|
|
233
233
|
|
|
234
234
|
**设计与研究**
|
|
235
235
|
|
package/dist/acp.d.ts
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export type * from "./drivers/types.js";
|
|
10
10
|
export type * from "./drivers/acp/types.js";
|
|
11
|
-
export { ACP_METHODS, ACP_PROTOCOL_VERSION, RPC_ERRORS } from "./drivers/acp/types.js";
|
|
11
|
+
export { ACP_META_KEY, ACP_METHODS, ACP_PROTOCOL_VERSION, RPC_ERRORS, } from "./drivers/acp/types.js";
|
|
12
12
|
export { WRITE_CHUNK_BYTES, createLineReader, writeChunked, type LineReader, } from "./modes/rpc/jsonl.js";
|
|
13
13
|
export { JsonRpcPeer, RpcError, type IncomingRequestContext, type JsonRpcPeerOptions, type RpcId, } from "./drivers/jsonrpc.js";
|
|
14
14
|
export { AcpClient, unattendedOutcome, type AcpClientHandlers, type AcpClientOptions, } from "./drivers/acp/client.js";
|
|
15
15
|
export { AcpDriver } from "./drivers/acp/driver.js";
|
|
16
16
|
export { runFakeAcpAgent, type FakeAcpAgentOptions } from "./drivers/acp/testing/fake-agent.js";
|
|
17
|
-
/** 假 ACP Agent 的可执行入口(`node <path> [--minimal]`);只在已编译的包里存在。 */
|
|
17
|
+
/** 假 ACP Agent 的可执行入口(`node <path> [--minimal] [--config-options] [--config-only] [--auth-required]`);只在已编译的包里存在。 */
|
|
18
18
|
export declare function fakeAcpAgentPath(): string;
|
package/dist/acp.js
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* - 假 ACP Agent(进程内 `runFakeAcpAgent`,或 `fakeAcpAgentPath()` 起子进程),用于黄金记录。
|
|
8
8
|
*/
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
export { ACP_METHODS, ACP_PROTOCOL_VERSION, RPC_ERRORS } from "./drivers/acp/types.js";
|
|
10
|
+
export { ACP_META_KEY, ACP_METHODS, ACP_PROTOCOL_VERSION, RPC_ERRORS, } from "./drivers/acp/types.js";
|
|
11
11
|
export { WRITE_CHUNK_BYTES, createLineReader, writeChunked, } from "./modes/rpc/jsonl.js";
|
|
12
12
|
export { JsonRpcPeer, RpcError, } from "./drivers/jsonrpc.js";
|
|
13
13
|
export { AcpClient, unattendedOutcome, } from "./drivers/acp/client.js";
|
|
14
14
|
export { AcpDriver } from "./drivers/acp/driver.js";
|
|
15
15
|
export { runFakeAcpAgent } from "./drivers/acp/testing/fake-agent.js";
|
|
16
|
-
/** 假 ACP Agent 的可执行入口(`node <path> [--minimal]`);只在已编译的包里存在。 */
|
|
16
|
+
/** 假 ACP Agent 的可执行入口(`node <path> [--minimal] [--config-options] [--config-only] [--auth-required]`);只在已编译的包里存在。 */
|
|
17
17
|
export function fakeAcpAgentPath() {
|
|
18
18
|
return fileURLToPath(new URL("./drivers/acp/testing/fake-agent-main.js", import.meta.url));
|
|
19
19
|
}
|
package/dist/ai/apis/shared.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - 流式期间 `arguments` 随增量更新为「到目前为止」的部分对象。
|
|
9
9
|
*/
|
|
10
10
|
import type { AssistantEventStreamImpl } from "../event-stream.js";
|
|
11
|
-
import type { AssistantMessage, Model, StreamOptions, TextBlock, ThinkingBlock, ToolCallBlock } from "../types.js";
|
|
11
|
+
import type { AssistantMessage, Model, StopReason, StreamOptions, TextBlock, ThinkingBlock, ToolCallBlock } from "../types.js";
|
|
12
12
|
export declare function createOutput(model: Model): AssistantMessage;
|
|
13
13
|
/** 缺 key 时同步抛 `AmaError{code:"no_api_key"}`(§3.1 流契约唯一的同步异常)。 */
|
|
14
14
|
export declare function requireApiKey(model: Model, options: StreamOptions): void;
|
|
@@ -49,6 +49,12 @@ export declare const STREAM_ENDED_MESSAGE = "Stream ended before completion";
|
|
|
49
49
|
export declare function finishDone(stream: AssistantEventStreamImpl, tracker: BlockTracker, model: Model, reason: "stop" | "length" | "toolUse"): void;
|
|
50
50
|
/** 出错或中止:关块、写 errorMessage、发 error(signal 已中止 → aborted)。 */
|
|
51
51
|
export declare function finishError(stream: AssistantEventStreamImpl, tracker: BlockTracker, model: Model, error: unknown, signal: AbortSignal): void;
|
|
52
|
+
/**
|
|
53
|
+
* 供应商以安全理由拒答(Anthropic `stop_reason: "refusal"`,docs/acp-plan.md D12):消息照旧以
|
|
54
|
+
* `error` 收尾(TUI / print / 重试的口径不变),`rawStopReason` 记原值;需要区分的消费者(ACP 的
|
|
55
|
+
* `refusal` 停止原因)用 {@link stopReasonOf}。这是 `refusal` 的唯一映射处。[ACP-B]
|
|
56
|
+
*/
|
|
57
|
+
export declare function stopReasonOf(message: Pick<AssistantMessage, "stopReason" | "rawStopReason">): StopReason;
|
|
52
58
|
/** 内部用:可携带供应商原始 stop reason 的错误(content_filter、refusal 等)。 */
|
|
53
59
|
export declare class ProviderStopError extends Error {
|
|
54
60
|
constructor(message: string);
|
package/dist/ai/apis/shared.js
CHANGED
|
@@ -170,6 +170,16 @@ export function finishError(stream, tracker, model, error, signal) {
|
|
|
170
170
|
finalizeUsage(model, output.usage);
|
|
171
171
|
stream.push({ type: "error", reason: aborted ? "aborted" : "error", message: output });
|
|
172
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* 供应商以安全理由拒答(Anthropic `stop_reason: "refusal"`,docs/acp-plan.md D12):消息照旧以
|
|
175
|
+
* `error` 收尾(TUI / print / 重试的口径不变),`rawStopReason` 记原值;需要区分的消费者(ACP 的
|
|
176
|
+
* `refusal` 停止原因)用 {@link stopReasonOf}。这是 `refusal` 的唯一映射处。[ACP-B]
|
|
177
|
+
*/
|
|
178
|
+
export function stopReasonOf(message) {
|
|
179
|
+
return message.stopReason === "error" && message.rawStopReason === "refusal"
|
|
180
|
+
? "refusal"
|
|
181
|
+
: message.stopReason;
|
|
182
|
+
}
|
|
173
183
|
/** 内部用:可携带供应商原始 stop reason 的错误(content_filter、refusal 等)。 */
|
|
174
184
|
export class ProviderStopError extends Error {
|
|
175
185
|
constructor(message) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { appendFileSync } from "node:fs";
|
|
17
17
|
import { AssistantEventStreamImpl } from "../event-stream.js";
|
|
18
|
-
import { BlockTracker, createOutput, finishDone, finishError } from "../apis/shared.js";
|
|
18
|
+
import { BlockTracker, ProviderStopError, createOutput, finishDone, finishError, } from "../apis/shared.js";
|
|
19
19
|
import { contentText, normalizeContext } from "../context.js";
|
|
20
20
|
import { describeFakeError, loadFakeScript, parseFakeScript, } from "./fake-script.js";
|
|
21
21
|
export const FAKE_PROVIDER_ID = "fake";
|
|
@@ -222,6 +222,10 @@ export class FakeProvider {
|
|
|
222
222
|
usage.cacheWrite = response.usage?.cacheWrite ?? 0;
|
|
223
223
|
if (error)
|
|
224
224
|
throw new Error(error.message);
|
|
225
|
+
if (response.stopReason === "refusal") {
|
|
226
|
+
tracker.output.rawStopReason = "refusal";
|
|
227
|
+
throw new ProviderStopError("The model refused to respond (refusal)");
|
|
228
|
+
}
|
|
225
229
|
const reason = response.stopReason ?? (tracker.hasToolCalls ? "toolUse" : "stop");
|
|
226
230
|
finishDone(stream, tracker, model, reason);
|
|
227
231
|
}
|
|
@@ -46,8 +46,11 @@ export interface FakeResponse {
|
|
|
46
46
|
/** 依次产出的块;与 `text` 简写二选一(都给时 text 在后)。 */
|
|
47
47
|
steps?: FakeStep[];
|
|
48
48
|
text?: string;
|
|
49
|
-
/**
|
|
50
|
-
|
|
49
|
+
/**
|
|
50
|
+
* 缺省:有工具调用 → toolUse,否则 stop。`refusal` 模拟 Anthropic 的拒答:产出完 steps 后以
|
|
51
|
+
* error 收尾、`rawStopReason: "refusal"`(与真实协议同形)。
|
|
52
|
+
*/
|
|
53
|
+
stopReason?: "stop" | "length" | "toolUse" | "refusal";
|
|
51
54
|
usage?: Partial<Pick<Usage, "input" | "output" | "cacheRead" | "cacheWrite">>;
|
|
52
55
|
error?: FakeError;
|
|
53
56
|
/** start 之前的延迟(毫秒,可被 abort 打断)。 */
|
|
@@ -66,8 +66,12 @@ function checkResponse(value, path) {
|
|
|
66
66
|
fail(`${path}.text`, "must be a string");
|
|
67
67
|
}
|
|
68
68
|
const stop = value["stopReason"];
|
|
69
|
-
if (stop !== undefined &&
|
|
70
|
-
|
|
69
|
+
if (stop !== undefined &&
|
|
70
|
+
stop !== "stop" &&
|
|
71
|
+
stop !== "length" &&
|
|
72
|
+
stop !== "toolUse" &&
|
|
73
|
+
stop !== "refusal") {
|
|
74
|
+
fail(`${path}.stopReason`, "must be stop / length / toolUse / refusal");
|
|
71
75
|
}
|
|
72
76
|
const error = value["error"];
|
|
73
77
|
if (error !== undefined) {
|
package/dist/ai/types.d.ts
CHANGED
|
@@ -118,7 +118,8 @@ export interface Usage {
|
|
|
118
118
|
/** [W6-C0] 订阅计费(ChatGPT 登录,W6-O):`cost` 为 0,统计单列「订阅」不折算美元。 */
|
|
119
119
|
billing?: "subscription";
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
/** `refusal`:供应商以安全理由拒答(目前只有 Anthropic 的 `stop_reason: "refusal"` 会映射到它)。 */
|
|
122
|
+
export type StopReason = "stop" | "length" | "toolUse" | "aborted" | "error" | "refusal";
|
|
122
123
|
/** 工具声明(发给供应商的形状)。 */
|
|
123
124
|
export interface ToolDecl {
|
|
124
125
|
name: string;
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* - 写:读-改-写 `auth.json` 整个文件,经 `writeAuthFile`(同目录临时文件 0600 → rename → chmod),其它条目原样保留;
|
|
7
7
|
* - 锁:`<auth.json>.lock`,`open(O_CREAT|O_EXCL)`(flag `wx`)写 `{ pid, at }`;等待 ≤ 15 s;锁文件超过 60 s 且
|
|
8
8
|
* pid 已死视为陈旧,删除后重试。取锁后由调用方**重读文件**再决定要不要刷新。
|
|
9
|
+
* - Windows:锁文件删除挂起时 `open(wx)` 报 EPERM / EACCES,按「被占用」继续等;读 / rename 的同类瞬时错误
|
|
10
|
+
* 短暂重试(`config/fs-retry.ts`)。
|
|
9
11
|
*/
|
|
10
12
|
import { type OAuthAuthEntry } from "../../config/types-w6.js";
|
|
11
13
|
export declare const LOCK_WAIT_MS = 15000;
|
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
* - 写:读-改-写 `auth.json` 整个文件,经 `writeAuthFile`(同目录临时文件 0600 → rename → chmod),其它条目原样保留;
|
|
7
7
|
* - 锁:`<auth.json>.lock`,`open(O_CREAT|O_EXCL)`(flag `wx`)写 `{ pid, at }`;等待 ≤ 15 s;锁文件超过 60 s 且
|
|
8
8
|
* pid 已死视为陈旧,删除后重试。取锁后由调用方**重读文件**再决定要不要刷新。
|
|
9
|
+
* - Windows:锁文件删除挂起时 `open(wx)` 报 EPERM / EACCES,按「被占用」继续等;读 / rename 的同类瞬时错误
|
|
10
|
+
* 短暂重试(`config/fs-retry.ts`)。
|
|
9
11
|
*/
|
|
10
12
|
import { closeSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeSync, } from "node:fs";
|
|
11
13
|
import { dirname } from "node:path";
|
|
12
14
|
import { writeAuthFile } from "../../config/auth-file.js";
|
|
15
|
+
import { isTransientFsError, retryTransientFs } from "../../config/fs-retry.js";
|
|
13
16
|
import { CONFIG_FILE_VERSION } from "../../config/types.js";
|
|
14
17
|
import { isOAuthEntry } from "../../config/types-w6.js";
|
|
15
18
|
import { AmaError } from "../../errors.js";
|
|
@@ -18,7 +21,7 @@ export const LOCK_STALE_MS = 60_000;
|
|
|
18
21
|
const LOCK_POLL_MS = 50;
|
|
19
22
|
function readRaw(path) {
|
|
20
23
|
try {
|
|
21
|
-
const value = JSON.parse(readFileSync(path, "utf8"));
|
|
24
|
+
const value = JSON.parse(retryTransientFs(() => readFileSync(path, "utf8")));
|
|
22
25
|
return typeof value === "object" && value !== null && typeof value.providers === "object"
|
|
23
26
|
? value
|
|
24
27
|
: undefined;
|
|
@@ -85,7 +88,9 @@ function tryAcquire(path) {
|
|
|
85
88
|
return true;
|
|
86
89
|
}
|
|
87
90
|
catch (error) {
|
|
88
|
-
|
|
91
|
+
const code = error.code;
|
|
92
|
+
// Windows:上一个持锁进程刚删掉锁文件(删除挂起)时 open(wx) 报 EPERM / EACCES——同样是「被占用」
|
|
93
|
+
if (code === "EEXIST" || isTransientFsError(code))
|
|
89
94
|
return false;
|
|
90
95
|
throw error;
|
|
91
96
|
}
|