@armadra/agent 0.10.0 → 0.11.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/CHANGELOG.zh-CN.md +18 -0
  3. package/README.md +5 -0
  4. package/README.zh-CN.md +4 -0
  5. package/dist/bundle/ama.cjs +1606 -423
  6. package/dist/cli/compose.d.ts +3 -0
  7. package/dist/cli/compose.js +1 -0
  8. package/dist/drivers/agents.js +1 -1
  9. package/dist/drivers/host-runners.d.ts +26 -1
  10. package/dist/drivers/host-runners.js +147 -0
  11. package/dist/hooks/matcher.d.ts +2 -2
  12. package/dist/hooks/matcher.js +3 -3
  13. package/dist/host/api-impl.d.ts +3 -0
  14. package/dist/host/api-impl.js +7 -2
  15. package/dist/host/types.d.ts +37 -1
  16. package/dist/host/types.js +5 -0
  17. package/dist/host.d.ts +1 -1
  18. package/dist/host.js +1 -1
  19. package/dist/i18n/catalog.d.ts +5 -0
  20. package/dist/i18n/messages/approval.d.ts +5 -0
  21. package/dist/i18n/messages/approval.js +5 -0
  22. package/dist/i18n/messages/drivers.d.ts +2 -0
  23. package/dist/i18n/messages/drivers.js +2 -0
  24. package/dist/i18n/messages/interactive-view.d.ts +5 -0
  25. package/dist/i18n/messages/interactive-view.js +5 -0
  26. package/dist/i18n/messages/interactive.d.ts +4 -0
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/modes/commands-core.d.ts +7 -0
  30. package/dist/modes/commands-core.js +23 -5
  31. package/dist/modes/interactive/approval-dialog.js +17 -4
  32. package/dist/modes/interactive/commands.d.ts +2 -0
  33. package/dist/modes/interactive/commands.js +5 -1
  34. package/dist/modes/interactive/interactive-mode.js +2 -2
  35. package/dist/modes/interactive/tool-summary.d.ts +2 -0
  36. package/dist/modes/interactive/tool-summary.js +27 -0
  37. package/dist/modes/rpc/commands.js +4 -4
  38. package/dist/permissions/classifier.js +1 -1
  39. package/dist/permissions/network-class.d.ts +28 -0
  40. package/dist/permissions/network-class.js +91 -0
  41. package/dist/permissions/pipeline.d.ts +1 -1
  42. package/dist/permissions/pipeline.js +24 -7
  43. package/dist/permissions/preview.js +3 -0
  44. package/dist/permissions/rules.d.ts +1 -0
  45. package/dist/permissions/rules.js +5 -1
  46. package/dist/tools/presets.d.ts +1 -1
  47. package/dist/tools/presets.js +2 -2
  48. package/dist/tools/registry.d.ts +3 -1
  49. package/dist/tools/registry.js +4 -2
  50. package/dist/tools/types.d.ts +4 -1
  51. package/dist/tools/web/fetch.d.ts +48 -0
  52. package/dist/tools/web/fetch.js +307 -0
  53. package/dist/tools/web/html-to-markdown.d.ts +31 -0
  54. package/dist/tools/web/html-to-markdown.js +522 -0
  55. package/dist/tools/web/url-guard.d.ts +59 -0
  56. package/dist/tools/web/url-guard.js +221 -0
  57. package/docs/en/guides/permissions.md +23 -1
  58. package/docs/en/reference/host-api.md +61 -19
  59. package/docs/en/reference/rpc.md +7 -7
  60. package/docs/guides/agents.md +14 -0
  61. package/docs/guides/hooks.md +86 -8
  62. package/docs/guides/permissions.md +23 -1
  63. package/docs/guides/sandbox.md +6 -0
  64. package/docs/reference/host-api.md +61 -19
  65. package/docs/reference/rpc.md +7 -7
  66. package/package.json +1 -1
@@ -204,6 +204,12 @@ which needs the user's approval.]`。按文本猜测,可能误报(同样的
204
204
  文件,不是终端。不用 `--unshare-pid`:沙箱里能看到并向同用户的进程发信号。
205
205
  - 后台 bash 由 ama 打开输出文件再交给子进程,输出目录无论在不在可写列表都能写。
206
206
  - SDK(`createAgent`)不读 `sandbox.bash`,bash 不包装、权限照旧。
207
+ - **`web_fetch` 不在沙箱里**:它在 ama 进程内发请求,`sandbox.network: deny` 只断 bash 的网,不断 `web_fetch`。
208
+ 这是有意的分工:bash 断网换免审批,联网读取改走显式受控的 `web_fetch`(权限类 `network`,default 询问、`-p`
209
+ 拒绝、执行层拒私网与元数据地址,见 [permissions.md](permissions.md)「网络类」)。外泄面:获批的 `web_fetch`
210
+ 能把上下文里的内容拼进 URL 的路径与查询串带出去(查询串 ≤ 1024 字符);`auto` 的分类器对像密钥的查询串询问,
211
+ 但它不是安全边界。不需要联网时用 `tools.disabled: ["web_fetch"]` 或 `permission.deny: ["web_fetch"]` 关掉,
212
+ 只允许少数站点时配 `--permission-mode allowlist` 加 `--allow 'web_fetch(<域名>)'`。
207
213
 
208
214
  ### 测试
209
215
 
@@ -1,6 +1,6 @@
1
1
  # 宿主适配器 API(@armadra/agent/host)
2
2
 
3
- 宿主适配器是一个本地 JS 模块,ama 启动时加载它并交给它一个 `HostApi`:它可以注册工具、追加系统提示、观察事件、回答审批、注入用户消息、在界面上显示通知与状态。Armadra 画布就是以宿主适配器的形式接入的(画布工具 `canvas_*` / `context_*` 都由适配器注册)。类型定义在 `src/host/types.ts`,从 `@armadra/agent/host` 导出,`HOST_API_VERSION = 1`。设计依据见 [design.md](../design/design.md) §6.2、§6.3、§11.1 第 13 步。
3
+ 宿主适配器是一个本地 JS 模块,ama 启动时加载它并交给它一个 `HostApi`:它可以注册工具、追加系统提示、观察事件、回答审批、注入用户消息、在界面上显示通知与状态。Armadra 画布就是以宿主适配器的形式接入的(画布工具 `canvas_*` / `context_*` 都由适配器注册)。类型定义在 `src/host/types.ts`,从 `@armadra/agent/host` 导出,`HOST_API_VERSION = 1`、`HOST_API_MINOR = 1`(host-api 1.1,见「版本」)。设计依据见 [design.md](../design/design.md) §6.2、§6.3、§11.1 第 13 步。
4
4
 
5
5
  ## 模块形状
6
6
 
@@ -46,26 +46,40 @@ export function create(api) {
46
46
  | `create()` 抛错或 10 秒内未返回 | 6 |
47
47
  | 返回的适配器缺 `id` | 6 |
48
48
 
49
+ ## 版本
50
+
51
+ - **主版本** `HOST_API_VERSION`(`api.version`):宿主导出的 `hostApi` 必须与它严格相等,只在破坏性变化(删改已有成员或语义)时递增,并要求 ama 破坏性版本升级(`pnpm release:check`)。
52
+ - **小版本** `HOST_API_MINOR`(`api.minor`):只加不改的可选成员每加一批递增一次,旧宿主不受影响,不改主版本。旧运行时没有 `api.minor`,按 0 算。
53
+ - 宿主按成员是否存在做特性探测(如 `typeof api.runners?.onDone === "function"`),比按 `api.agent.version` 比较更可靠。
54
+
55
+ | 小版本 | ama 起始版本 | 新增 |
56
+ | ------ | ------------ | ----------------------------------------------------------------- |
57
+ | 0 | — | 现有全部成员(`cache?`、`runners?.provide` 等可选面同样要先判断) |
58
+ | 1 | 未发布 | `minor`、`runners?.onDone`(见「runner 完成事件」) |
59
+
49
60
  ## HostApi
50
61
 
51
- | 成员 | 说明 |
52
- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
53
- | `version` | `HOST_API_VERSION` |
54
- | `agent` | `{ name: "ama", version }` |
55
- | `env` | 启动时环境变量的冻结副本 |
56
- | `mode` | `interactive` / `line` / `print` / `rpc` |
57
- | `session.id()` / `file()` / `cwd()` / `model()` | 当前会话(换会话后跟随新会话);`file()` 在首次请求落盘前为 `undefined` |
58
- | `tools.register(tool)` | 注册工具(形状见下);名字须匹配 `^[a-z][a-z0-9_]{1,63}$`,同名已存在抛 `tool_exists`。建议加前缀(`canvas_*`) |
59
- | `tools.disable(name)` | 隐藏内置工具(如 Armadra 禁用 `task`),系统提示的工具节随之不再列它 |
60
- | `tools.list()` | 当前全部工具名 |
61
- | `instructions.add(source)` | 追加到系统提示最后的 `host` 节;`{ kind: "file", path }` 或 `{ kind: "text", text, name? }` |
62
- | `events.on(name, handler)` | 观察事件(下表),返回注销函数 |
63
- | `approvals.setBroker(broker)` | 设置审批回答者(见「审批」) |
64
- | `messages.sendUser(text, origin?)` | 以 user 消息注入:空闲时开始一次运行(`"started"`),运行中按 steer 入队(`"queued"`);`origin` 缺省 `"host"`,落盘在消息上,界面标 `↳ host` |
65
- | `ui.notify(message, level?)` | 交互 / line 模式进消息区;rpc 模式变成 `notification` 事件(stderr 另有一份);print 模式写 stderr |
66
- | `ui.setStatus(key, text?)` | 状态栏的宿主项;`text` 为空或缺省则移除该键 |
67
- | `log(level, message, detail?)` | 日志;`warn` / `error` 写 stderr |
68
- | `cache?.onWarmingDecision(handler)` | 缓存保温的否决钩子(见「缓存保温」);可选面,旧版本运行时没有它,用前判断 `api.cache !== undefined` |
62
+ | 成员 | 说明 |
63
+ | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
64
+ | `version` | `HOST_API_VERSION` |
65
+ | `agent` | `{ name: "ama", version }` |
66
+ | `env` | 启动时环境变量的冻结副本 |
67
+ | `mode` | `interactive` / `line` / `print` / `rpc` |
68
+ | `session.id()` / `file()` / `cwd()` / `model()` | 当前会话(换会话后跟随新会话);`file()` 在首次请求落盘前为 `undefined` |
69
+ | `tools.register(tool)` | 注册工具(形状见下);名字须匹配 `^[a-z][a-z0-9_]{1,63}$`,同名已存在抛 `tool_exists`。建议加前缀(`canvas_*`) |
70
+ | `tools.disable(name)` | 隐藏内置工具(如 Armadra 禁用 `task`),系统提示的工具节随之不再列它 |
71
+ | `tools.list()` | 当前全部工具名 |
72
+ | `instructions.add(source)` | 追加到系统提示最后的 `host` 节;`{ kind: "file", path }` 或 `{ kind: "text", text, name? }` |
73
+ | `events.on(name, handler)` | 观察事件(下表),返回注销函数 |
74
+ | `approvals.setBroker(broker)` | 设置审批回答者(见「审批」) |
75
+ | `messages.sendUser(text, origin?)` | 以 user 消息注入:空闲时开始一次运行(`"started"`),运行中按 steer 入队(`"queued"`);`origin` 缺省 `"host"`,落盘在消息上,界面标 `↳ host` |
76
+ | `ui.notify(message, level?)` | 交互 / line 模式进消息区;rpc 模式变成 `notification` 事件(stderr 另有一份);print 模式写 stderr |
77
+ | `ui.setStatus(key, text?)` | 状态栏的宿主项;`text` 为空或缺省则移除该键 |
78
+ | `log(level, message, detail?)` | 日志;`warn` / `error` 写 stderr |
79
+ | `cache?.onWarmingDecision(handler)` | 缓存保温的否决钩子(见「缓存保温」);可选面,旧版本运行时没有它,用前判断 `api.cache !== undefined` |
80
+ | `runners?.provide(runner)` | 注入子 Agent runner:`id` 即 `task(agent=<id>)` 的名字,`description` 进 task 工具描述;有宿主时 ama 不自起外部 CLI,只认这里注入的;返回注销函数 |
81
+ | `runners?.onDone(handler)` | 宿主 runner 每轮结束时回调(见「runner 完成事件」),返回注销函数;host-api 1.1 起,用前判断 `typeof api.runners?.onDone === "function"` |
82
+ | `minor?` | `HOST_API_MINOR`(见「版本」);旧运行时缺省 |
69
83
 
70
84
  `create()` 期间会话还没组装完:`session.*` 返回启动时确定的值,`sendUser` 会以 `busy` 拒绝——要在启动时发消息,等 `session_start` 事件之后再调用。
71
85
 
@@ -99,6 +113,8 @@ interface ToolResult {
99
113
 
100
114
  宿主工具与内置工具走同一条路径:schema 校验 → 命令式 Hook PreToolUse → 权限管线(按 `permission` 分类)→ 审批 → 执行 → PostToolUse。在 codemode 脚本里也能以 `tools.<name>()` 调用。
101
115
 
116
+ 导出的 `ToolPermission` 类型多了 `"network"`(内置 `web_fetch` 用,按 execute 判定、plan 下询问,见 [permissions.md](../guides/permissions.md)「网络类」);宿主经 `tools.register` 注册的工具仍只接受 `read` / `write` / `execute`,`HOST_API_VERSION` 不变。
117
+
102
118
  ## 事件
103
119
 
104
120
  `events.on` 的处理器只观察:抛错只记日志,不影响运行;处理器依次调用并等待,`session_shutdown` 被 await(退出前可以做清理)。
@@ -152,6 +168,32 @@ interface WarmDecision {
152
168
 
153
169
  返回 `"warm"` / `"stop"`(可以是 Promise)。返回 `"stop"` 即不发并停止本轮保温(内置决策本是 warm 时,停止原因记为 `declined`);返回 `"warm"` 可以覆盖内置的 stop。处理器出错回落内置决策。注册多个时最后注册且未注销的那个生效;返回值是注销函数。保温机制本身见 [providers.md](../guides/providers.md)「缓存」。
154
170
 
171
+ ## runner 完成事件
172
+
173
+ `api.runners.onDone(handler)`(host-api 1.1):经 `runners.provide` 注入的 runner 每跑完一轮,以下面的载荷调用一次 `handler`。一轮 = `start()` 之后的第一轮,或 `send` / `interrupt` 之后的下一轮;以 ama 对该轮 `wait()` 的落定为准(返回、抛错、或任务被中断)。
174
+
175
+ ```ts
176
+ interface RunnerDoneEvent {
177
+ runner: string; // 宿主 runner 的 id(即 task(agent=<id>))
178
+ taskId?: string; // ama 的任务 id(SubagentRunRequest.taskId);直接调用 runner 时缺省
179
+ sessionId: string; // runner 自己的会话 id(RunnerHandle.id)
180
+ round: number; // 第几轮,从 1 起
181
+ status: "completed" | "failed" | "aborted" | "max_turns" | "interrupted";
182
+ isError: boolean; // status 为 failed 或 aborted
183
+ text?: string; // wait() 返回的结果文本
184
+ error?: string; // wait() 抛出的错误信息
185
+ startedAt: number; // 这一轮开始(start / send / interrupt 的时刻),epoch 毫秒
186
+ endedAt: number; // 落定时刻
187
+ durationMs: number;
188
+ }
189
+ ```
190
+
191
+ - `status` 与任务注册表一致:任务的 `signal` 已 abort 或调过 `stop()` → `aborted`;否则取结果自报的终态(`max_turns` / `interrupted` 等);再按 `isError` 分 `failed` / `completed`。`wait()` 抛错且未中断 → `failed`,带 `error`。
192
+ - 只观察:处理器不被 await,抛错(含 Promise 拒绝)只经 `log` 记一条 error,不影响 `wait()` 的结果与其它处理器。
193
+ - 同一轮里 ama 可能多次调用 `wait()`(任务入口与注册表各一次),事件只发一次;该轮落定后、没有新的 `send` / `interrupt` 时再调用 `wait()` 不再发。运行中的 `interrupt` 由当前这一轮跟到结束,不另起一轮。
194
+ - 只覆盖宿主注入的 runner;ama 自己的子会话(`general` / `explore` / `plan`)仍看 `subagent_start` / `subagent_end` 事件。
195
+ - 用途:宿主不必轮询「协调者是否还在等这个任务」——收到事件即知道 ama 这一侧的任务结束了。
196
+
155
197
  ## 退出
156
198
 
157
199
  - 进程退出:`session_shutdown` 事件(await)→ SessionEnd Hook(`reason: "exit"`)→ `adapter.dispose()` → 会话 dispose。`dispose` 抛错只记 warning。
@@ -119,13 +119,13 @@
119
119
 
120
120
  ### 工具、权限、发现
121
121
 
122
- | 命令 | 参数 | `data` |
123
- | --------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
124
- | `get_tools` | — | `{ tools: { name, description, parameters, permission, active }[] }`(注册表全部工具,`active` 表示模型当前能看到) |
125
- | `set_active_tools` | `names: string[]` | `{ names }`(生效后的活动工具名) |
126
- | `set_permission_mode` | `mode: plan \| allowlist \| default \| auto-edit \| auto \| full-auto` | `{ mode }`(未知模式 → `invalid_arguments`) |
127
- | `get_commands` | — | `{ commands: { name, description?, source: "builtin" \| "template" \| "skill" }[] }`;Skill 名写作 `skill:<名>` |
128
- | `get_skills` | — | `{ skills: { name, description, location, … }[] }`(已发现的 Skill;`location` 是 SKILL.md 路径) |
122
+ | 命令 | 参数 | `data` |
123
+ | --------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
124
+ | `get_tools` | — | `{ tools: { name, description, parameters, permission, active }[] }`(注册表全部工具,`active` 表示模型当前能看到) |
125
+ | `set_active_tools` | `names: string[]` | `{ names }`(生效后的活动工具名) |
126
+ | `set_permission_mode` | `mode: plan \| allowlist \| default \| auto-edit \| auto \| full-auto` | `{ mode }`(未知模式 → `invalid_arguments`) |
127
+ | `get_commands` | — | `{ commands: { name, description?, source: "builtin" \| "template" \| "skill" }[] }`;Skill 名写作 `skill:<名>`;只列当前可用的内置命令(记忆关闭时没有 `memory`,task 不可调时没有 `tasks`) |
128
+ | `get_skills` | — | `{ skills: { name, description, location, … }[] }`(已发现的 Skill;`location` 是 SKILL.md 路径) |
129
129
 
130
130
  ### 计划与任务(第五波)
131
131
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armadra/agent",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "A coding and coordination agent that runs standalone or embedded in Armadra",
5
5
  "type": "module",
6
6
  "keywords": [