@armadra/agent 0.3.0 → 0.4.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.
- package/CHANGELOG.md +84 -0
- package/README.md +93 -44
- package/dist/agent/retry.d.ts +1 -1
- package/dist/agent/retry.js +2 -1
- package/dist/agent/session-cache.js +1 -1
- package/dist/agent/session-classifier.d.ts +18 -0
- package/dist/agent/session-classifier.js +103 -0
- package/dist/agent/session-core.d.ts +13 -0
- package/dist/agent/session-tools.js +24 -3
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.js +12 -3
- package/dist/agent/tool-runner.js +15 -2
- package/dist/agent/types.d.ts +9 -1
- package/dist/ai/apis/anthropic-messages.js +3 -2
- package/dist/ai/apis/google-generative-ai.js +3 -2
- package/dist/ai/apis/openai-completions.js +3 -2
- package/dist/ai/apis/openai-responses.js +3 -2
- package/dist/ai/http.d.ts +28 -6
- package/dist/ai/http.js +41 -8
- package/dist/ai/providers/registry.d.ts +8 -1
- package/dist/ai/providers/registry.js +30 -22
- package/dist/ai/providers/suggest.d.ts +18 -0
- package/dist/ai/providers/suggest.js +72 -0
- package/dist/ai/sse.d.ts +6 -2
- package/dist/ai/sse.js +22 -2
- package/dist/ai/types.d.ts +9 -2
- package/dist/bundle/ama.cjs +15279 -10151
- package/dist/cli/args.d.ts +19 -4
- package/dist/cli/args.js +83 -10
- package/dist/cli/bootstrap.js +27 -3
- package/dist/cli/codemode-notice.d.ts +20 -0
- package/dist/cli/codemode-notice.js +55 -0
- package/dist/cli/compose-session.d.ts +5 -0
- package/dist/cli/compose-session.js +32 -1
- package/dist/cli/compose-store.d.ts +1 -1
- package/dist/cli/compose-store.js +3 -1
- package/dist/cli/compose.d.ts +14 -4
- package/dist/cli/compose.js +29 -10
- package/dist/cli/default-model.d.ts +38 -1
- package/dist/cli/default-model.js +96 -9
- package/dist/cli/deps.d.ts +36 -0
- package/dist/cli/exit-codes.d.ts +2 -0
- package/dist/cli/exit-codes.js +3 -0
- package/dist/cli/fake-visibility.d.ts +13 -0
- package/dist/cli/fake-visibility.js +26 -0
- package/dist/cli/from-prompt.d.ts +18 -0
- package/dist/cli/from-prompt.js +49 -0
- package/dist/cli/main.d.ts +9 -2
- package/dist/cli/main.js +77 -3
- package/dist/cli/proxy.d.ts +51 -0
- package/dist/cli/proxy.js +135 -0
- package/dist/cli/startup-screen.d.ts +29 -0
- package/dist/cli/startup-screen.js +47 -0
- package/dist/cli/startup-steps.d.ts +1 -1
- package/dist/cli/startup-steps.js +14 -12
- package/dist/cli/subcommands/config.d.ts +19 -3
- package/dist/cli/subcommands/config.js +108 -21
- package/dist/cli/subcommands/context.js +4 -1
- package/dist/cli/subcommands/doctor.js +12 -1
- package/dist/cli/subcommands/init.js +2 -1
- package/dist/cli/subcommands/models-discover.d.ts +15 -9
- package/dist/cli/subcommands/models-discover.js +53 -46
- package/dist/cli/subcommands/probe-runner.d.ts +96 -0
- package/dist/cli/subcommands/probe-runner.js +264 -0
- package/dist/cli/subcommands/providers-probe.d.ts +34 -0
- package/dist/cli/subcommands/providers-probe.js +87 -0
- package/dist/cli/subcommands/providers.d.ts +3 -2
- package/dist/cli/subcommands/providers.js +48 -45
- package/dist/cli/subcommands/sessions-export.d.ts +10 -0
- package/dist/cli/subcommands/sessions-export.js +59 -0
- package/dist/cli/subcommands/sessions-search.d.ts +13 -0
- package/dist/cli/subcommands/sessions-search.js +103 -0
- package/dist/cli/subcommands/sessions.d.ts +3 -2
- package/dist/cli/subcommands/sessions.js +22 -1
- package/dist/cli/subcommands/stats.d.ts +17 -0
- package/dist/cli/subcommands/stats.js +198 -0
- package/dist/cli/system-prompt-arg.d.ts +11 -0
- package/dist/cli/system-prompt-arg.js +34 -0
- package/dist/codemode/modes.d.ts +4 -11
- package/dist/codemode/modes.js +5 -27
- package/dist/codemode/tool.d.ts +18 -14
- package/dist/codemode/tool.js +71 -28
- package/dist/config/init.d.ts +6 -2
- package/dist/config/init.js +12 -5
- package/dist/config/json-schema.d.ts +1 -0
- package/dist/config/json-schema.js +36 -3
- package/dist/config/key-docs.d.ts +22 -0
- package/dist/config/key-docs.js +105 -0
- package/dist/config/merge.d.ts +9 -8
- package/dist/config/merge.js +22 -7
- package/dist/config/schema.d.ts +1 -1
- package/dist/config/schema.js +24 -5
- package/dist/config/types.d.ts +34 -6
- package/dist/config/types.js +17 -1
- package/dist/modes/commands-core.js +6 -5
- package/dist/modes/interactive/approval-dialog.d.ts +21 -5
- package/dist/modes/interactive/approval-dialog.js +106 -27
- package/dist/modes/interactive/commands.d.ts +11 -2
- package/dist/modes/interactive/commands.js +56 -16
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +65 -81
- package/dist/modes/interactive/key-dispatch.d.ts +3 -1
- package/dist/modes/interactive/key-dispatch.js +5 -6
- package/dist/modes/interactive/line/line-mode.d.ts +1 -0
- package/dist/modes/interactive/line/line-mode.js +6 -4
- package/dist/modes/interactive/line/line-render.d.ts +4 -0
- package/dist/modes/interactive/line/line-render.js +28 -6
- package/dist/modes/interactive/message-view.d.ts +48 -9
- package/dist/modes/interactive/message-view.js +238 -44
- package/dist/modes/interactive/panels.d.ts +18 -0
- package/dist/modes/interactive/panels.js +143 -0
- package/dist/modes/interactive/pickers.d.ts +23 -2
- package/dist/modes/interactive/pickers.js +48 -15
- package/dist/modes/interactive/run-indicator.d.ts +51 -0
- package/dist/modes/interactive/run-indicator.js +189 -0
- package/dist/modes/interactive/startup-header.d.ts +40 -0
- package/dist/modes/interactive/startup-header.js +169 -0
- package/dist/modes/interactive/status-bar.d.ts +18 -15
- package/dist/modes/interactive/status-bar.js +98 -56
- package/dist/modes/interactive/tool-summary.d.ts +46 -0
- package/dist/modes/interactive/tool-summary.js +218 -0
- package/dist/modes/interactive/tool-view.d.ts +48 -15
- package/dist/modes/interactive/tool-view.js +203 -145
- package/dist/modes/print/print-mode.d.ts +31 -4
- package/dist/modes/print/print-mode.js +116 -7
- package/dist/modes/rpc/commands.js +5 -0
- package/dist/permissions/auto-safe.d.ts +60 -0
- package/dist/permissions/auto-safe.js +529 -0
- package/dist/permissions/classifier.d.ts +64 -0
- package/dist/permissions/classifier.js +184 -0
- package/dist/permissions/dangerous.d.ts +5 -0
- package/dist/permissions/dangerous.js +1 -1
- package/dist/permissions/modes.d.ts +30 -0
- package/dist/permissions/modes.js +78 -0
- package/dist/permissions/pipeline.d.ts +31 -4
- package/dist/permissions/pipeline.js +196 -6
- package/dist/permissions/protected.d.ts +19 -0
- package/dist/permissions/protected.js +74 -0
- package/dist/permissions/rules.js +3 -0
- package/dist/permissions/types.d.ts +50 -3
- package/dist/permissions/types.js +3 -0
- package/dist/sdk.d.ts +9 -3
- package/dist/sdk.js +10 -2
- package/dist/session/export.d.ts +32 -0
- package/dist/session/export.js +187 -0
- package/dist/session/redact.d.ts +15 -0
- package/dist/session/redact.js +55 -0
- package/dist/session/reuse.d.ts +33 -0
- package/dist/session/reuse.js +86 -0
- package/dist/session/scan.d.ts +34 -0
- package/dist/session/scan.js +140 -0
- package/dist/session/search.d.ts +52 -0
- package/dist/session/search.js +211 -0
- package/dist/session/stats-aggregate.d.ts +63 -0
- package/dist/session/stats-aggregate.js +163 -0
- package/dist/session/stats-index.d.ts +26 -0
- package/dist/session/stats-index.js +91 -0
- package/dist/session/stats-scan.d.ts +54 -0
- package/dist/session/stats-scan.js +236 -0
- package/dist/tools/presets.d.ts +35 -8
- package/dist/tools/presets.js +56 -17
- package/dist/tui/component.d.ts +8 -2
- package/dist/tui/component.js +3 -1
- package/dist/tui/components/box.d.ts +6 -1
- package/dist/tui/components/box.js +16 -6
- package/dist/tui/components/card.d.ts +23 -0
- package/dist/tui/components/card.js +37 -0
- package/dist/tui/components/editor-history.d.ts +6 -0
- package/dist/tui/components/editor-history.js +45 -0
- package/dist/tui/components/editor-paste.d.ts +1 -1
- package/dist/tui/components/editor-paste.js +4 -4
- package/dist/tui/components/editor.d.ts +11 -5
- package/dist/tui/components/editor.js +52 -58
- package/dist/tui/components/key-value.d.ts +3 -0
- package/dist/tui/components/key-value.js +16 -6
- package/dist/tui/components/loader.d.ts +37 -7
- package/dist/tui/components/loader.js +84 -21
- package/dist/tui/components/markdown.d.ts +5 -1
- package/dist/tui/components/markdown.js +45 -21
- package/dist/tui/components/meter.d.ts +3 -3
- package/dist/tui/components/meter.js +13 -11
- package/dist/tui/components/select-list.d.ts +23 -1
- package/dist/tui/components/select-list.js +76 -13
- package/dist/tui/glyphs.d.ts +68 -0
- package/dist/tui/glyphs.js +114 -0
- package/dist/tui/theme.d.ts +30 -6
- package/dist/tui/theme.js +103 -17
- package/dist/tui.d.ts +4 -2
- package/dist/tui.js +3 -1
- package/docs/codemode.md +23 -9
- package/docs/hooks.md +10 -10
- package/docs/permissions.md +148 -0
- package/docs/providers.md +33 -10
- package/docs/rpc.md +36 -36
- package/docs/session-format.md +2 -1
- package/docs/sessions.md +134 -0
- package/docs/tui.md +139 -62
- package/package.json +3 -1
package/docs/codemode.md
CHANGED
|
@@ -2,23 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
设计依据见 [design.md](design.md) §5.5、§5.6、§9.1。
|
|
4
4
|
|
|
5
|
-
`codemode` 工具让模型写一段 JavaScript,在脚本里经 `tools.*`
|
|
5
|
+
`codemode` 工具让模型写一段 JavaScript,在脚本里经 `tools.*` 编排多次工具调用,只有脚本输出回到模型。长流程、工具密集的任务里,它把多次往返合成一次,减少往返次数与缓存读取;短任务里模型照常直接调用工具,codemode 只多占约 400 token 前缀,所以 `default` 预设在网络隔离的运行时上缺省开着它。
|
|
6
6
|
|
|
7
7
|
## 打开
|
|
8
8
|
|
|
9
|
-
| 写法
|
|
10
|
-
|
|
|
11
|
-
| `--tools-preset codemode`(`only
|
|
12
|
-
| `--codemode on` / `codemode.mode: "on"`
|
|
13
|
-
| `--codemode off` /
|
|
9
|
+
| 写法 | 模型看到的工具 |
|
|
10
|
+
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `--tools-preset codemode-only`(`only`;旧名 `codemode` 仍可用) | 只有 `codemode`;全部内置工具与宿主工具只能在脚本里调用,声明列在 `codemode` 描述里 |
|
|
12
|
+
| `--codemode on` / `codemode.mode: "on"` | 预设的工具 + `codemode`;其它工具描述不变,`codemode` 描述一行列出可在脚本里调用的直接工具(参数相同)与仅脚本可调的工具名 |
|
|
13
|
+
| `--codemode off` / `codemode.mode: "off"` | 不注册 `codemode` |
|
|
14
|
+
|
|
15
|
+
`codemode.mode` 不写时**跟随预设**:
|
|
16
|
+
|
|
17
|
+
| 预设 | Node ≥ 25(沙箱隔离网络) | Node 22 / 24 |
|
|
18
|
+
| ------------------------- | ------------------------- | ---------------------------------------------------------------------- |
|
|
19
|
+
| `default` | `on` | `off`,启动时提示一次(每个配置目录一次,记在数据目录 `notices.json`) |
|
|
20
|
+
| `codemode-only` | `only` | `only`(`execute` 类,见下) |
|
|
21
|
+
| `minimal` / `coordinator` | `off` | `off` |
|
|
22
|
+
|
|
23
|
+
缺省配置里不写 `codemode.mode`(`ama init` 生成的 `config.json` 也不写),所以这张映射以后调整时老用户同样生效。`ama config show` / `ama doctor` 显示生效模式与原因(跟随哪个预设、Node 是否隔离网络)。
|
|
24
|
+
|
|
25
|
+
`coordinator` 预设即使显式 `on`,脚本里能调用的工具也只限它的活动集(read 与宿主工具):`tools.bash`、`tools.write` 在脚本里同样不存在,协调者「不写文件、不跑 bash」的约定不能经 codemode 绕过。
|
|
14
26
|
|
|
15
27
|
`codemode` 本身的权限类随沙箱能力:网络隔离(Node ≥ 25,见下文沙箱)时是 `read` 类,`default` 权限模式下免审批——脚本只能经 `tools.*` 做事,每次内层调用仍逐个经过权限管线;网络未隔离(Node 22 / 24)时是 `execute` 类,`default` 模式下每次都要审批,`-p` 等无人值守场景直接拒绝,此时常用做法是在配置里放行它:
|
|
16
28
|
|
|
17
29
|
```json
|
|
18
|
-
{ "version": 1, "tools": { "preset": "codemode" }, "permission": { "allow": ["codemode"] } }
|
|
30
|
+
{ "version": 1, "tools": { "preset": "codemode-only" }, "permission": { "allow": ["codemode"] } }
|
|
19
31
|
```
|
|
20
32
|
|
|
21
|
-
其它配置:`codemode.inlineBudget
|
|
33
|
+
其它配置:`codemode.inlineBudget`(`only` 模式在描述里内联声明的预算,估算 token,缺省 3000,超出只列名字;`on` 模式不内联)、`codemode.requireStrict`(见下文沙箱)。项目级配置只能把 `codemode.mode` 设为 `off`。
|
|
34
|
+
|
|
35
|
+
`on` 模式的前缀开销:去重前 `codemode` 描述把已直接暴露的六个工具的声明又内联一遍,其它工具各追加一行提示,系统提示 + 工具表比 `off` 多约 1356 token;现在只多约 390 token(字符 / 4 估算,测试锁定 ≤ 500)。升级后续接的旧会话因为描述字节变化会有一次缓存未命中。
|
|
22
36
|
|
|
23
37
|
## 脚本
|
|
24
38
|
|
|
@@ -82,7 +96,7 @@
|
|
|
82
96
|
- 空环境启动,拿不到密钥、会话文件与环境变量(Windows 上 libuv 会从父进程补入 PATH、SYSTEMROOT、USERPROFILE 等系统变量,不含密钥);不授予文件写、子进程、worker、addon、inspector 权限;Node 22.0–22.12 用 `--experimental-permission`;嵌入 Electron 时设 `ELECTRON_RUN_AS_NODE=1`。
|
|
83
97
|
- 子进程里用 `node:vm` 建只含 ECMAScript 内建对象的上下文(`codeGeneration: { strings: false, wasm: false }`,沙箱对象空原型);全局函数都在上下文内定义,只经一个宿主函数交换 JSON 字符串;子进程主 realm 也禁止字符串生成代码,经构造器链逃逸拿不到 `Function("return process")`。
|
|
84
98
|
- `tools.*` 经 stdin / stdout 的 JSON 行协议回调父进程执行。
|
|
85
|
-
- 网络:Node ≥ 25 的权限模型同时拒绝网络(strict);Node 22 / 24 不管网络,脚本若逃出 `vm` 就能联网——此时工具描述标注 `network not isolated`,`codemode.requireStrict: true` 时直接不注册 `codemode` 并给出 warning(codemode 预设随之回退到 default)。
|
|
99
|
+
- 网络:Node ≥ 25 的权限模型同时拒绝网络(strict);Node 22 / 24 不管网络,脚本若逃出 `vm` 就能联网——此时工具描述标注 `network not isolated`,`codemode.requireStrict: true` 时直接不注册 `codemode` 并给出 warning(codemode-only 预设随之回退到 default)。
|
|
86
100
|
|
|
87
101
|
| 实测(`--permission` + 只读入口) | Node 22.19 | Node 24.21 | Node 26.10 |
|
|
88
102
|
| --------------------------------- | ---------- | ---------- | ---------- |
|
package/docs/hooks.md
CHANGED
|
@@ -58,16 +58,16 @@
|
|
|
58
58
|
|
|
59
59
|
stdin 是一个 JSON 对象,写完即关闭。所有事件共有:
|
|
60
60
|
|
|
61
|
-
| 字段 | 说明
|
|
62
|
-
| ---------------- |
|
|
63
|
-
| `hookEventName` | 事件名
|
|
64
|
-
| `sessionId` | 会话 id(子会话为子会话自己的)
|
|
65
|
-
| `sessionFile` | 会话文件(落盘前缺省)
|
|
66
|
-
| `cwd` | 会话 cwd
|
|
67
|
-
| `model` | `{ provider, id }`
|
|
68
|
-
| `permissionMode` | `plan` / `default` / `auto-edit` / `full-auto` |
|
|
69
|
-
| `depth` | 主会话 0,`task` 子会话 1
|
|
70
|
-
| `host` | 宿主适配器 id(激活时)
|
|
61
|
+
| 字段 | 说明 |
|
|
62
|
+
| ---------------- | --------------------------------------------------------------------- |
|
|
63
|
+
| `hookEventName` | 事件名 |
|
|
64
|
+
| `sessionId` | 会话 id(子会话为子会话自己的) |
|
|
65
|
+
| `sessionFile` | 会话文件(落盘前缺省) |
|
|
66
|
+
| `cwd` | 会话 cwd |
|
|
67
|
+
| `model` | `{ provider, id }` |
|
|
68
|
+
| `permissionMode` | `plan` / `allowlist` / `default` / `auto-edit` / `auto` / `full-auto` |
|
|
69
|
+
| `depth` | 主会话 0,`task` 子会话 1 |
|
|
70
|
+
| `host` | 宿主适配器 id(激活时) |
|
|
71
71
|
|
|
72
72
|
事件特有:
|
|
73
73
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# 权限模式与 auto 判定
|
|
2
|
+
|
|
3
|
+
本文写 ama 的六种权限模式、每次工具调用的判定顺序,以及 `auto` 模式「规则层 → 静态判定 → 模型分类器」三层怎样决定放行还是询问。总体设计见 [design.md](design.md) §6.3、§7;Hook 的输入输出见 [hooks.md](hooks.md)。
|
|
4
|
+
|
|
5
|
+
## 模式
|
|
6
|
+
|
|
7
|
+
| 值 | 显示名 | 读 | 写(项目内) | 执行(bash 等) | 何时用 |
|
|
8
|
+
| ----------- | ------------------ | --- | --------------------------- | ------------------------------ | -------------------------------- |
|
|
9
|
+
| `default` | Manual | ✓ | 询问 | 询问 | 缺省 |
|
|
10
|
+
| `auto-edit` | Accept edits | ✓ | ✓ | 询问 | 放心让它改代码,命令逐条看 |
|
|
11
|
+
| `plan` | Plan | ✓ | 拒绝 | 拒绝 | 只读调研、出方案 |
|
|
12
|
+
| `auto` | Auto | ✓ | ✓(受保护路径与项目外询问) | 安全名单放行,其余由分类器判断 | 推荐:常规操作不打扰,有风险才问 |
|
|
13
|
+
| `full-auto` | Bypass permissions | ✓ | ✓ | ✓ | 一次性沙箱、容器 |
|
|
14
|
+
| `allowlist` | Allowlist only | ✓ | 只放行 allow 规则命中的 | 只放行 allow 规则命中的 | CI:从不询问,没列出的直接拒绝 |
|
|
15
|
+
|
|
16
|
+
所有模式下,deny 规则、Hook deny 都先判定并直接拒绝;危险命令表(`rm -rf /`、`git push --force`、`curl … | sh` 等,见 README「安全」)一律询问,`allowlist` 与无人值守时变为拒绝。
|
|
17
|
+
|
|
18
|
+
设置方式:`--permission-mode <值>`、配置 `permission.mode`、交互界面 `/permission`(选择器)或 `Shift+Tab`、RPC `set_permission_mode`、SDK `permission.mode`。
|
|
19
|
+
|
|
20
|
+
### 严格度与项目级配置
|
|
21
|
+
|
|
22
|
+
严格度从严到宽:`plan < allowlist < default < auto-edit < auto < full-auto`。项目级 `.ama/config.json` 只能把模式往严的方向改;另外**不能设 `auto` 或 `full-auto`**(这两种由 ama 自己或什么都不判断就放行,必须由用户级配置、命令行或 profile 打开),设了会被忽略并给 warning。
|
|
23
|
+
|
|
24
|
+
`allowlist` 排在 `plan` 与 `default` 之间:它放行的调用是 `plan` 放行的(只读工具)加上 allow 规则明确列出的;`default` 放行的集合包含它(只读 + allow 规则),其余在 `default` 下询问、在 `allowlist` 下拒绝。所以 `plan ⊆ allowlist ⊆ default`。读工具在 `allowlist` 下照常放行,与其它模式一致;要连读都限制,用 deny 规则。
|
|
25
|
+
|
|
26
|
+
### 界面
|
|
27
|
+
|
|
28
|
+
- 状态栏显示显示名:`mode:Auto`;`Bypass permissions` 标黄。
|
|
29
|
+
- `/permission` 不带参数打开选择器:标题 `Mode`,每项「显示名 + 一行说明」,右侧是数字快捷键 1–6,当前模式打勾,配置里的缺省模式标 `Default`,Auto 标 `Recommended`。line 模式 `/permission` 打印同样的列表。
|
|
30
|
+
- `Shift+Tab` 循环:Manual → Accept edits → Plan → Auto → Bypass permissions → Manual。`Allowlist only` 不在循环里,只能显式选。
|
|
31
|
+
|
|
32
|
+
## 判定顺序
|
|
33
|
+
|
|
34
|
+
一次工具调用(模型直接发起或 codemode / task 里嵌套发起都一样):
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
schema 校验
|
|
38
|
+
→ 命令式 Hook PreToolUse(deny 一票否决;allow / ask 交给管线)
|
|
39
|
+
→ 权限管线:
|
|
40
|
+
① 规则层(不调模型)
|
|
41
|
+
deny 规则(含内置 deny)、Hook deny → 拒绝
|
|
42
|
+
危险命令表 → 询问
|
|
43
|
+
[auto] 受保护路径、项目外写入、网络命令、删除类命令 → 询问
|
|
44
|
+
Hook ask → 询问
|
|
45
|
+
allow 规则、Hook allow、本会话记忆 → 放行
|
|
46
|
+
② 模式
|
|
47
|
+
plan / default / auto-edit / full-auto:同以前的模式真值表
|
|
48
|
+
allowlist:只读工具放行,其余拒绝(不在允许名单)
|
|
49
|
+
auto:静态判定(不调模型)→ 放行;未决定 → ③
|
|
50
|
+
③ [auto] 模型分类器:allow → 放行;ask / 出错 / 超时 → 询问
|
|
51
|
+
→ 询问时走审批链(宿主 broker → 界面 → 无人值守按拒绝)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- 后面的步骤不能放宽前面的结论:allow 规则越不过危险命令和 auto 的规则层,分类器只能处理①②都没决定的调用。
|
|
55
|
+
- `allowlist` 从不询问:凡是会询问的(危险命令、Hook ask)一律拒绝,拒绝说明写「不在允许名单」。
|
|
56
|
+
- 无人值守(`-p`、RPC 未接审批)时,询问一律按拒绝;auto 模式下分类器仍会先跑,判 allow 的照样执行。
|
|
57
|
+
|
|
58
|
+
## auto 的三层
|
|
59
|
+
|
|
60
|
+
### ① 规则层
|
|
61
|
+
|
|
62
|
+
不调模型,命中即询问(无人值守拒绝):
|
|
63
|
+
|
|
64
|
+
| 类别 | 内容 |
|
|
65
|
+
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
66
|
+
| 危险命令 | `dangerous.ts` 的整张表(所有模式通用) |
|
|
67
|
+
| 机密路径 | 读或写 `.env`、`.env.*`(`.env.example` / `.sample` / `.template` / `.dist` 除外)、`.ssh/`、`.gnupg/`、`.aws/`、`.kube/config`、`.docker/config.json`、`.netrc`、`.pgpass`、私钥(`id_rsa` 等、`*.pem`、`*.key`、`*.p12`、`*.pfx`、`*.jks`)、ama 的 `auth.json` |
|
|
68
|
+
| 受保护写入 | 写 `.git/` 内部、项目里的 `.ama/`(可能改 Hook 与配置)、项目目录外的任何路径(`/dev/null` 等除外) |
|
|
69
|
+
| 网络命令 | `curl`、`wget`、`ssh`、`scp`、`rsync`、`nc`、`gh`;`git push / pull / fetch / clone`;`npm / pnpm / yarn / bun install / add / ci / update / publish / dlx`、`npx`;`pip install`、`cargo install / publish`、`go get / install`、`brew / apt install`、`docker pull / push / login` 等 |
|
|
70
|
+
| 删除与回退 | `rm -r` / `rm -f`、`find -delete`、`git clean`、`git checkout -- …` / `git restore`、`git stash drop / clear`、`shred`、`truncate` |
|
|
71
|
+
| bash 里的路径 | 重定向目标(`>`、`>>`、`&>`、`tee`)、`cp` / `mv` / `mkdir` / `touch` / `ln` / `chmod` 的目标在项目外或受保护;命令参数里出现机密路径(`cat .env`) |
|
|
72
|
+
|
|
73
|
+
「项目目录」是会话 cwd。文件工具按 `path` 参数判断;bash 按 `dangerous.ts` 同一套分词与嵌套展开(`sh -c`、`eval`、`xargs`、`find -exec`)逐段判断。
|
|
74
|
+
|
|
75
|
+
### ② 静态判定
|
|
76
|
+
|
|
77
|
+
不调模型,满足即放行:
|
|
78
|
+
|
|
79
|
+
- 只读工具(read、ls、grep、glob 等,`permission: "read"`)。
|
|
80
|
+
- write / edit 目标在项目目录内且不在受保护路径(受保护的已在①询问)。
|
|
81
|
+
- bash:命令里每一段(`&&`、`||`、`;`、`|` 切开)都在**安全名单**内,且
|
|
82
|
+
- 没有命令替换 `$(…)`、反引号、`<(…)`;
|
|
83
|
+
- 参数里没有变量展开 `$X`,没有可能匹配点文件的通配(`.e*`);
|
|
84
|
+
- 没有 `sh -c` 之类的嵌套(shell、`eval`、`xargs` 都不在名单里)。
|
|
85
|
+
管道到安全命令(`cat a | grep b | wc -l`)没问题;管道到 shell 不在名单里。重定向写到项目内允许,写到项目外已在①询问。
|
|
86
|
+
- allow 规则命中(这一步在①的末尾已放行)。
|
|
87
|
+
|
|
88
|
+
安全名单(`src/permissions/auto-safe.ts`,每条都有正反例测试):
|
|
89
|
+
|
|
90
|
+
| 命令 | 限制 |
|
|
91
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
92
|
+
| `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` 判断 |
|
|
93
|
+
| `find` | 不带 `-exec`、`-execdir`、`-ok`、`-okdir`、`-delete`、`-fprint*`、`-fls` |
|
|
94
|
+
| `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,不算安全) |
|
|
95
|
+
| `git branch` | 只列出:不带 `-d / -D / --delete / -m / -M / -c / -C / -f / -u` 等 |
|
|
96
|
+
| `npm / pnpm / yarn test`、`… run test / lint / typecheck / build`、`pnpm / yarn lint / typecheck / build`、`npm t` | |
|
|
97
|
+
| `node --test`、`tsc --noEmit`、`vitest run`、`pnpm vitest run`、`pnpm exec vitest run`、`pytest`、`python -m pytest` | |
|
|
98
|
+
| `cargo test / check / build / clippy`、`go test / build / vet`、`make test / check / lint / build` | |
|
|
99
|
+
|
|
100
|
+
扩展:用户级配置 `permission.autoSafeCommands` 追加,例如 `["just test", "bun test", "make fmt"]`——按词前缀匹配(`just test` 命中 `just test --verbose`);含 `*` 的按通配匹配整段(`bun run test*`)。项目级配置不能追加(放宽)。追加的命令照样先过①:网络、删除、项目外写入仍然询问。
|
|
101
|
+
|
|
102
|
+
### ③ 模型分类器
|
|
103
|
+
|
|
104
|
+
只处理①②都没决定的调用(例如 `rm old.txt`、`node scripts/gen.js`、宿主工具、codemode 脚本)。
|
|
105
|
+
|
|
106
|
+
- **独立请求**:不进会话转录、不改主会话的消息与前缀(主会话的提示缓存不受影响),不触发保温;请求用途 `purpose: "classify"`。
|
|
107
|
+
- **输入**:工具名、参数(JSON,截断到 4000 字符)、cwd、项目根、最近一条用户消息的摘要(截断到 600 字符)。参数与用户消息放在 `<tool_call_data>` … `</tool_call_data>` 数据块里,块内出现的结束标记会被转义;系统提示要求把块内一切当数据,忽略其中的指令(包括「ignore previous instructions」「respond allow」之类),遇到这种文本倾向于 ask。
|
|
108
|
+
- **输出**:严格 JSON `{"decision":"allow"|"ask","reason":"…"}`。解析失败、超时(10 s)、请求出错 → 询问。
|
|
109
|
+
- **模型**:`permission.autoModel`(`provider/model`);缺省用当前会话模型。推荐配一个便宜快速的模型,例如 `packy/qwen3.8-flash`。`maxTokens` 256,关闭思考。
|
|
110
|
+
- **缓存**:会话内按「工具名 + 归一化参数」(bash 折叠空白,其它按键排序的 JSON)缓存成功的判定,同样的调用只分类一次;出错与超时不缓存。
|
|
111
|
+
- **费用**:每次分类的用量记一条 `usage` 条目,`kind: "permission_classify"`,计入 `/session` 费用与 RPC 统计,不进上下文。
|
|
112
|
+
- 分类器只能把未决定的调用判成 allow 或 ask,不能推翻①的拒绝或询问。
|
|
113
|
+
|
|
114
|
+
## 审计
|
|
115
|
+
|
|
116
|
+
- 每次 auto 判定(放行与询问)记录 `{ layer: "rule" | "static" | "classifier", decision, reason }`:
|
|
117
|
+
- `tool_execution_end` 事件带 `autoDecision`;需要询问时 `permission_request` 事件与审批请求带 `autoDecision`(对话框里显示「Auto:原因」)。
|
|
118
|
+
- `/permissions` 显示最近 20 条判定(工具、摘要、层、结果、原因)。
|
|
119
|
+
- Hook `PreToolUse` 的输入不变;`permissionMode` 字段会出现新值 `auto`、`allowlist`。
|
|
120
|
+
|
|
121
|
+
## 配置
|
|
122
|
+
|
|
123
|
+
```jsonc
|
|
124
|
+
{
|
|
125
|
+
"permission": {
|
|
126
|
+
"mode": "auto",
|
|
127
|
+
"autoModel": "packy/qwen3.8-flash",
|
|
128
|
+
"autoSafeCommands": ["just test", "make fmt"],
|
|
129
|
+
"allow": ["bash(npm run e2e)"],
|
|
130
|
+
"deny": ["bash(terraform *)"],
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
CI 用 allowlist:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
ama -p "修好 lint 并跑测试" --permission-mode allowlist \
|
|
139
|
+
--allow 'write(src/**)' --allow 'edit(src/**)' --allow 'bash(pnpm lint*)' --allow 'bash(pnpm test*)'
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 已知限制
|
|
143
|
+
|
|
144
|
+
- 静态判定看命令文本,不看命令真实会读什么:`grep -r token .` 会读到项目里的 `.env`,按安全名单放行。需要更严时,对 `.env` 加 deny 规则(`read(**/.env*)`、`bash(*.env*)`)。
|
|
145
|
+
- 「项目目录」就是 cwd,不跟随符号链接;在子目录启动时,上级目录算项目外。
|
|
146
|
+
- `npm test` / `make test` 之类会执行项目自己的脚本,安全名单按「在项目内跑测试与构建」放行;不信任的仓库请用 `default` 或 `plan`。
|
|
147
|
+
- `sudo` / `doas` 前缀的命令即使后面是安全命令也不算安全(`sudo` 本身在危险命令表里,询问)。
|
|
148
|
+
- 分类器是模型判断,不是安全边界。它前面的规则层不调模型、可审阅;真正不可接受的操作请写 deny 规则。
|
package/docs/providers.md
CHANGED
|
@@ -23,22 +23,22 @@
|
|
|
23
23
|
- `ama init`:建目录(0700)并补齐缺失的 `config.json` 与 `config.schema.json`,逐个打印「已创建」或
|
|
24
24
|
「已存在,未改动」;已存在的 `config.json` 一律不覆盖(`--force` 也不),`config.schema.json` 不是用户文件,
|
|
25
25
|
每次 `init` 都重写为当前版本;不创建空的 `auth.json`。
|
|
26
|
-
-
|
|
27
|
-
(`AMA_NO_INIT=1` 关闭;SDK
|
|
26
|
+
- **首次运行自动初始化**:进入对话的命令(交互、`-p`、`--mode rpc`)与 `ama providers add` 启动时若配置目录不存在,
|
|
27
|
+
静默建目录并写最小 `config.json` 与 schema(`AMA_NO_INIT=1` 关闭;SDK 与测试不触发)。只读子命令(`config show` /
|
|
28
|
+
`path`、`doctor`、`models list`、`providers list`、`auth list`、`sessions` 等)不创建也不改写配置目录。
|
|
28
29
|
- 最小 `config.json`:
|
|
29
30
|
|
|
30
31
|
```json
|
|
31
32
|
{
|
|
32
33
|
"$schema": "./config.schema.json",
|
|
33
34
|
"version": 1,
|
|
34
|
-
"thinkingLevel": "medium",
|
|
35
|
-
"permission": { "mode": "default" },
|
|
36
|
-
"tools": { "preset": "default" },
|
|
37
35
|
"providers": {}
|
|
38
36
|
}
|
|
39
37
|
```
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
不写任何缺省值(缺省值调整时老配置同样生效,`ama config show` 里来源也显示 default),也不写
|
|
40
|
+
`defaultModel`(见下文「缺省模型」)。`ama init` 结束时打印下一步(`ama auth set` / `ama providers add` /
|
|
41
|
+
`ama doctor`)。`config.schema.json` 里每个键都带说明与缺省值(运行时决定的键只写规则),编辑器悬停可见。
|
|
42
42
|
|
|
43
43
|
- `ama config path`:打印配置目录、数据目录与各文件路径(标出是否存在);`ama config edit`:用
|
|
44
44
|
`$VISUAL` / `$EDITOR` 打开 `config.json`(不存在先 `init`),没有编辑器时打印路径。
|
|
@@ -164,7 +164,8 @@ export PACKY_API_KEY=sk-...
|
|
|
164
164
|
Messages 在 baseUrl 以 `/v1` 结尾时拼 `/messages`,否则 `/v1/messages`。
|
|
165
165
|
- 不想手写 `models`:`ama models discover packy` 列出中转站的模型(`GET {baseUrl}/models`);
|
|
166
166
|
`--probe` 对每个模型依次试供应商协议、completions、responses、messages 的最小请求,记第一个成功
|
|
167
|
-
的(每模型最多 3 次,`--limit` 限制探测的模型数,缺省 30
|
|
167
|
+
的(每模型最多 3 次,`--limit` 限制探测的模型数,缺省 30,执行前打印预估;模型之间并发,探测规则同下文
|
|
168
|
+
`providers add --probe`);
|
|
168
169
|
`--write` 把结果合并进用户级 `config.json`(已有同 id 不覆盖,只写 `id` 与和供应商不同的 `api`,
|
|
169
170
|
原文件备份为 `config.json.bak`)。上下文等元数据在运行时从 models.dev 缓存补(见下文「模型元数据」),
|
|
170
171
|
匹配不到的条目没有 `contextWindow`,自动压缩随之关闭,需要时手动补。
|
|
@@ -180,6 +181,19 @@ Claude Code 的通行约定),优先级低于 config 与 auth.json 的 `baseU
|
|
|
180
181
|
`prompt_cache_key`)。`ama config show` 的「供应商」节与 `ama doctor` 标出 baseUrl 来自哪个变量;
|
|
181
182
|
零配置挑的缺省模型来自官方目录,中转站未必有,用 `--model` 或 `defaultModel` 指定。
|
|
182
183
|
|
|
184
|
+
### 缺省模型
|
|
185
|
+
|
|
186
|
+
没有 `--model`、续会话的模型与 `defaultModel` 时,按供应商顺序(内置在前,config 里的自定义供应商在后)取第一个
|
|
187
|
+
有 key(或本地服务可达)的供应商,再在它的模型里挑:
|
|
188
|
+
|
|
189
|
+
- **内置供应商**:目录首条(目录按推荐顺序整理);
|
|
190
|
+
- **自定义供应商**(中转站,模型表是上游 `/models` 的顺序):在 models.dev 有价格(输入价 > 0)、支持工具调用、
|
|
191
|
+
上下文 ≥ 64k 的模型里取**输入价最低**的;同价取上下文大的,再同取列表靠前的;一个都不满足才退回列表首条。
|
|
192
|
+
|
|
193
|
+
`ama providers add` 在还没有 `defaultModel` 时按同一规则挑一个写进 `defaultModel`(探测过只在探测通过的模型里
|
|
194
|
+
挑),摘要里写明选了谁、为什么;已有 `defaultModel` 不改。`ama config show` 与 `ama doctor` 的「模型」一行同样
|
|
195
|
+
说明原因。没有任何可用模型时,启动提示列出 key 的环境变量名、`ama auth set` 与 `ama providers add`。
|
|
196
|
+
|
|
183
197
|
```sh
|
|
184
198
|
OPENAI_BASE_URL=https://proxy.example/v1 OPENAI_API_KEY=$PACKY_API_KEY ama -p "hi" --model openai/qwen3.8-flash
|
|
185
199
|
```
|
|
@@ -239,7 +253,8 @@ ama providers add packy --base-url https://www.packyapi.com/v1 --key-env PACKY_A
|
|
|
239
253
|
```
|
|
240
254
|
ama providers add <id> --base-url <url> [--channel <name>=<api>@<baseUrl> …] [--api <api>|auto]
|
|
241
255
|
[--key-env <VAR>] [--probe] [--limit N] [--probe-models a,b,…]
|
|
242
|
-
[--max-requests N] [--
|
|
256
|
+
[--max-requests N] [--concurrency N] [--probe-timeout ms]
|
|
257
|
+
[--prefer chat,responses,messages] [--include-no-tools] [--yes]
|
|
243
258
|
ama providers list
|
|
244
259
|
ama providers channels <id>
|
|
245
260
|
ama providers remove <id>
|
|
@@ -258,7 +273,13 @@ ama providers refresh <id> [--probe …]
|
|
|
258
273
|
- **`--probe`**:对选中的模型(`--probe-models` 列出的,缺省按 id 字母序不分大小写取前 `--limit` 个,缺省 30)
|
|
259
274
|
逐个渠道发一次最小请求(有提示时只试提示里的渠道),**探测成功的渠道全部写进模型的 `channels`**,顺序按
|
|
260
275
|
`--prefer`(缺省 chat、responses、messages);全部失败的模型不写入;未探测的按提示写入并在表格里标「未探测」。
|
|
261
|
-
|
|
276
|
+
执行前打印请求数与耗时预估,超过 `--max-requests`(缺省 60)时截断模型数。
|
|
277
|
+
- **判定**:HTTP 成功且流里出现第一个内容事件(文本 / 思考 / 工具调用)即判可用并立刻断开,不等推理模型
|
|
278
|
+
想完;只收到流开头、还没有内容时再等 1 s,期间没有报错也判可用(防止中转先回 200 再在流里报错被误判)。
|
|
279
|
+
HTTP 错误、流里的错误、超时(`--probe-timeout`,缺省 15000 ms)判不可用并记原因。
|
|
280
|
+
- **并发**:「模型 × 渠道」同时在途最多 `--concurrency` 个(1–16,缺省 6);结果按模型、渠道顺序打印,
|
|
281
|
+
终端里单行刷新「探测 18/60」,最后打印总用时。
|
|
282
|
+
- **限流**:401 / 403 立即停止;429 时并发减半、2 s 后重试该请求一次,重试仍 429 则停止。
|
|
262
283
|
- **渠道收敛**:写入前删掉没有任何模型挂载的候选渠道;`defaultChannel` 取剩下的第一个(按 `--prefer`)。
|
|
263
284
|
- **写入**:用户级 `config.json` 的 `providers.<id>`(先备份为 `config.json.bak`)。模型条目只写 `id` 与
|
|
264
285
|
`channels`;上下文、输出、图像、推理、价格**不写进配置**,运行时从 models.dev 缓存补(见下节),所以
|
|
@@ -526,6 +547,8 @@ ama models cache-probe <provider/id> [--tokens 2048] [--gap-ms 3000] [--json] [-
|
|
|
526
547
|
|
|
527
548
|
## 测试用 fake 供应商
|
|
528
549
|
|
|
529
|
-
`--
|
|
550
|
+
`--model fake/echo`:回显最后一条用户消息。零配置的模型选择器、`ama doctor`、`ama models list`、
|
|
551
|
+
`ama providers list`、`ama config show` 缺省不列 fake;`AMA_SHOW_FAKE=1` 或设了 `AMA_FAKE_SCRIPT` 时照列,
|
|
552
|
+
显式 `--model fake/…` 任何时候都可用。设 `AMA_FAKE_SCRIPT=<file.json>` 后
|
|
530
553
|
按脚本第 n 次调用产出文本、思考、工具调用、429、溢出、断流、延迟,脚本格式见
|
|
531
554
|
`src/ai/fake/fake-script.ts`,示例在 `test/fixtures/scripts/`。
|
package/docs/rpc.md
CHANGED
|
@@ -101,13 +101,13 @@
|
|
|
101
101
|
|
|
102
102
|
### 工具、权限、发现
|
|
103
103
|
|
|
104
|
-
| 命令 | 参数
|
|
105
|
-
| --------------------- |
|
|
106
|
-
| `get_tools` | —
|
|
107
|
-
| `set_active_tools` | `names: string[]`
|
|
108
|
-
| `set_permission_mode` | `mode: plan \| default \| auto-edit \| full-auto` | `{ mode }`
|
|
109
|
-
| `get_commands` | —
|
|
110
|
-
| `get_skills` | —
|
|
104
|
+
| 命令 | 参数 | `data` |
|
|
105
|
+
| --------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| `get_tools` | — | `{ tools: { name, description, parameters, permission, active }[] }`(注册表全部工具,`active` 表示模型当前能看到) |
|
|
107
|
+
| `set_active_tools` | `names: string[]` | `{ names }`(生效后的活动工具名) |
|
|
108
|
+
| `set_permission_mode` | `mode: plan \| allowlist \| default \| auto-edit \| auto \| full-auto` | `{ mode }`(未知模式 → `invalid_arguments`) |
|
|
109
|
+
| `get_commands` | — | `{ commands: { name, description?, source: "builtin" \| "template" \| "skill" }[] }`;Skill 名写作 `skill:<名>` |
|
|
110
|
+
| `get_skills` | — | `{ skills: { name, description, location, … }[] }`(已发现的 Skill;`location` 是 SKILL.md 路径) |
|
|
111
111
|
|
|
112
112
|
合计 33 条命令,名字即 `RpcCommandMap` 的键。
|
|
113
113
|
|
|
@@ -115,35 +115,35 @@
|
|
|
115
115
|
|
|
116
116
|
事件就是进程内 `SessionEvent`(`src/agent/types.ts`),只有 `message_update` 在线上换成纯增量。按出现场景分组:
|
|
117
117
|
|
|
118
|
-
| 事件 | 字段
|
|
119
|
-
| ---------------------------------------------------- |
|
|
120
|
-
| `session_start` | `sessionId`、`sessionFile?`、`cwd`、`reason: startup \| resume \| new \| fork`
|
|
121
|
-
| `session_changed` | `sessionId`、`sessionFile?`
|
|
122
|
-
| `before_agent_start` | `prompt`(经 UserPromptSubmit Hook 与模板展开之后)
|
|
123
|
-
| `agent_start` / `turn_start` / `agent_before_settle` | —
|
|
124
|
-
| `turn_end` | `message`(助手消息)、`toolResults`
|
|
125
|
-
| `agent_end` | `stopReason`、`willRetry`
|
|
126
|
-
| `agent_settled` | `warning?`(运行彻底结束,含重试与 followUp)
|
|
127
|
-
| `message_start` / `message_end` | `message`(`AgentMessage`)
|
|
128
|
-
| `message_update` | `assistantMessageEvent`、`usage?`(见下)
|
|
129
|
-
| `tool_execution_start` | `toolCallId`、`toolName`、`args`、`parentToolCallId?`
|
|
130
|
-
| `tool_execution_update` | `toolCallId`、`toolName`、`partial`(运行中的输出文本)、`parentToolCallId?`
|
|
131
|
-
| `tool_execution_end` | `toolCallId`、`toolName`、`result`、`isError`、`parentToolCallId
|
|
132
|
-
| `queue_update` | `steering: string[]`、`followUp: string[]`
|
|
133
|
-
| `compaction_start` | `trigger: threshold \| overflow \| manual`
|
|
134
|
-
| `compaction_end` | `trigger`、`result?`、`aborted`、`willRetry`、`error?`
|
|
135
|
-
| `auto_retry_start` | `attempt`、`maxAttempts`、`delayMs`、`errorMessage`
|
|
136
|
-
| `auto_retry_end` | `success`、`attempt`、`finalError?`
|
|
137
|
-
| `permission_request` | `requestId`、`toolName`、`input`、`reason: mode \| dangerous \| hook`、`hookReason?`、`timeoutMs`、`preview
|
|
138
|
-
| `permission_resolved` | `requestId`、`decision`
|
|
139
|
-
| `permission_mode_changed` | `mode`
|
|
140
|
-
| `model_changed` | `model: { provider, id, channel? }`
|
|
141
|
-
| `thinking_level_changed` | `level`
|
|
142
|
-
| `entry_appended` | `entry`(刚落盘的会话条目)
|
|
143
|
-
| `hook_executed` | `event`、`command`、`exitCode`(超时或被信号杀死为 null)、`durationMs`
|
|
144
|
-
| `cache_miss` | `missedTokens`、`missedCost?`、`reason`、`detail?`、`idleMs`
|
|
145
|
-
| `cache_warm` | `phase: scheduled \| sent \| stopped`、`nextWarmAt?`、`usage?`、`cost?`、`reason?`
|
|
146
|
-
| `context_pressure` | `percent`、`threshold: 70 \| 90`、`remainingTokens?`、`estimatedTurnsLeft?`
|
|
118
|
+
| 事件 | 字段 |
|
|
119
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
120
|
+
| `session_start` | `sessionId`、`sessionFile?`、`cwd`、`reason: startup \| resume \| new \| fork` |
|
|
121
|
+
| `session_changed` | `sessionId`、`sessionFile?` |
|
|
122
|
+
| `before_agent_start` | `prompt`(经 UserPromptSubmit Hook 与模板展开之后) |
|
|
123
|
+
| `agent_start` / `turn_start` / `agent_before_settle` | — |
|
|
124
|
+
| `turn_end` | `message`(助手消息)、`toolResults` |
|
|
125
|
+
| `agent_end` | `stopReason`、`willRetry` |
|
|
126
|
+
| `agent_settled` | `warning?`(运行彻底结束,含重试与 followUp) |
|
|
127
|
+
| `message_start` / `message_end` | `message`(`AgentMessage`) |
|
|
128
|
+
| `message_update` | `assistantMessageEvent`、`usage?`(见下) |
|
|
129
|
+
| `tool_execution_start` | `toolCallId`、`toolName`、`args`、`parentToolCallId?` |
|
|
130
|
+
| `tool_execution_update` | `toolCallId`、`toolName`、`partial`(运行中的输出文本)、`parentToolCallId?` |
|
|
131
|
+
| `tool_execution_end` | `toolCallId`、`toolName`、`result`、`isError`、`parentToolCallId?`、`autoDecision?`(auto 模式:`{ layer: rule \| static \| classifier, decision, reason, cached? }`)、`denied?`(`true`:被权限 / Hook / 审批拒绝而没有执行,原因见 `result`) |
|
|
132
|
+
| `queue_update` | `steering: string[]`、`followUp: string[]` |
|
|
133
|
+
| `compaction_start` | `trigger: threshold \| overflow \| manual` |
|
|
134
|
+
| `compaction_end` | `trigger`、`result?`、`aborted`、`willRetry`、`error?` |
|
|
135
|
+
| `auto_retry_start` | `attempt`、`maxAttempts`、`delayMs`、`errorMessage` |
|
|
136
|
+
| `auto_retry_end` | `success`、`attempt`、`finalError?` |
|
|
137
|
+
| `permission_request` | `requestId`、`toolName`、`input`、`reason: mode \| dangerous \| hook`、`hookReason?`、`timeoutMs`、`preview?`、`autoDecision?`(auto 模式下为什么询问) |
|
|
138
|
+
| `permission_resolved` | `requestId`、`decision` |
|
|
139
|
+
| `permission_mode_changed` | `mode` |
|
|
140
|
+
| `model_changed` | `model: { provider, id, channel? }` |
|
|
141
|
+
| `thinking_level_changed` | `level` |
|
|
142
|
+
| `entry_appended` | `entry`(刚落盘的会话条目) |
|
|
143
|
+
| `hook_executed` | `event`、`command`、`exitCode`(超时或被信号杀死为 null)、`durationMs` |
|
|
144
|
+
| `cache_miss` | `missedTokens`、`missedCost?`、`reason`、`detail?`、`idleMs` |
|
|
145
|
+
| `cache_warm` | `phase: scheduled \| sent \| stopped`、`nextWarmAt?`、`usage?`、`cost?`、`reason?` |
|
|
146
|
+
| `context_pressure` | `percent`、`threshold: 70 \| 90`、`remainingTokens?`、`estimatedTurnsLeft?` |
|
|
147
147
|
|
|
148
148
|
另有非会话事件 `{"type":"notification","level":"info"|"warn"|"error","message":…}`:宿主 `ui.notify` 与应答之后的运行失败。
|
|
149
149
|
|
package/docs/session-format.md
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
|
|
55
55
|
### `usage` 条目
|
|
56
56
|
|
|
57
|
-
不进上下文的请求用量,计入 `/session` 费用与 RPC
|
|
57
|
+
不进上下文的请求用量,计入 `/session` 费用与 RPC 统计,投影时跳过。`kind` 目前有 `"cache_warm"`(缓存保温请求,见 [providers.md](providers.md)「缓存」)与 `"permission_classify"`(auto 权限模式的分类请求,见 [permissions.md](permissions.md)):
|
|
58
58
|
|
|
59
59
|
```json
|
|
60
60
|
{
|
|
@@ -116,4 +116,5 @@
|
|
|
116
116
|
## 读取方
|
|
117
117
|
|
|
118
118
|
- `ama sessions list|show`、交互模式 `/resume`、RPC `get_entries{since}`(以 entry id 为游标返回 `{ entries, leafId }`)、`get_tree`。
|
|
119
|
+
- 只读扫描(不加锁、不修复):`ama stats`、`ama sessions search|export`、`--from`,见 [sessions.md](sessions.md)。
|
|
119
120
|
- 嵌入方可以直接读文件:按行解析,首行为头,跳过 `type: "leaf"` 的行后按 `parentId` 建树;遇到不认识的条目类型保留但不进上下文。
|
package/docs/sessions.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# 会话统计、检索、复用与导出
|
|
2
|
+
|
|
3
|
+
这几条命令都只读会话目录(`<数据目录>/sessions`,`--session-dir` 可改;文件格式见 [session-format.md](session-format.md)):不加锁、不修复半行、不改文件,正在运行的会话也能读。范围缺省是**当前目录**的会话,`--all` 看全部。
|
|
4
|
+
|
|
5
|
+
## 统计:`ama stats`
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ama stats [--since 7d|30d|today|YYYY-MM-DD] [--until …]
|
|
9
|
+
[--by day|week|month|provider|channel|model|project]
|
|
10
|
+
[--project <目录> | --all] [--top N] [--json] [--no-cache]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
$ ama stats --by model
|
|
15
|
+
全部时间 · 项目 /home/me/proj · 2 个会话
|
|
16
|
+
请求 9(对话 7 · permission_classify 1 · cache_warm 1)
|
|
17
|
+
回合 3 · 平均耗时 23.3s
|
|
18
|
+
Token 输入 8.7k · 输出 1.7k · 缓存读 74.3k · 缓存写 0
|
|
19
|
+
缓存命中率 89.5%(报告缓存的端点 2/2,其余不进分母)
|
|
20
|
+
费用 $0.0398(另有 3 次请求无价,未计入)
|
|
21
|
+
错误 / 重试 0 / 0
|
|
22
|
+
|
|
23
|
+
会话 请求 回合 输入 输出 缓存读 缓存写 命中率 费用
|
|
24
|
+
anthropic/claude-sonnet-4-5 1 6 2 4.1k 731 72.3k 0 94.6% $0.0398
|
|
25
|
+
packy/deepseek-v4-flash 1 3 1 4.6k 980 2k 0 30.8% —
|
|
26
|
+
|
|
27
|
+
工具调用 Top 5
|
|
28
|
+
read 3
|
|
29
|
+
edit 2
|
|
30
|
+
…
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- `--since` / `--until`:`7d` 是含今天的最近 7 天,`today` 是今天,日期是本地日期;两端都含。
|
|
34
|
+
- `--by project` 没给 `--project` 时看全部项目。`--json` 输出同样的数据(另带 `files`:扫描 / 命中缓存 / 无效的文件数)。
|
|
35
|
+
|
|
36
|
+
### 口径
|
|
37
|
+
|
|
38
|
+
| 项 | 怎么算 |
|
|
39
|
+
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| 请求 | 每次模型请求一次:回合里的 assistant 消息记「对话」;`usage` 条目按 `kind` 分开(`cache_warm` 保温、`permission_classify` auto 分类…);`compaction` / `branch_summary` 带的用量记同名 kind。失败后重试的那次也算请求 |
|
|
41
|
+
| 回合 | 一条非 `steer` 的用户消息开始一个回合,到下一条为止;至少有一条 assistant 才计。耗时 = 最后一条 assistant 的落盘时间 − 用户消息的落盘时间 |
|
|
42
|
+
| token | `input` 不含缓存部分(同会话层);缓存读 / 写分开列 |
|
|
43
|
+
| 缓存命中率 | cacheRead /(input + cacheRead + cacheWrite),只算**报告缓存**的端点:同一 `provider/model@channel` 在扫描范围内出现过任何非零缓存读写才算;其余端点不进分母(与会话层三态一致,不报缓存的中转不会把命中率拉成 0) |
|
|
44
|
+
| 费用 | 只加带 `usage.cost` 的请求(模型有价格);无价请求数单独报,全部无价时只给 token |
|
|
45
|
+
| 工具调用 | assistant 里的工具调用块按名字计;codemode 脚本内的调用不展开 |
|
|
46
|
+
| 错误 / 重试 | `stopReason: "error"` 的 assistant;`context_edit{reason:"retry"}`(自动重试剔除的失败尝试) |
|
|
47
|
+
| 渠道 | 最近一条 `model_change` 与请求同 provider / model 时取它的 `channel` |
|
|
48
|
+
|
|
49
|
+
`task` 子会话是独立文件,按它自己的 cwd 计入。
|
|
50
|
+
|
|
51
|
+
### 性能与索引
|
|
52
|
+
|
|
53
|
+
- 扫描按行进行;`toolResult`、`custom`、`label` 等与统计无关的行按行首的 `{"type":…,"message":{"role":…` 直接跳过、不解析(ama 写的行 type 总是第一个键;别的程序写的行退回完整解析)。
|
|
54
|
+
- 每个文件的摘要缓存在 `<数据目录>/stats-index.json`,按文件 mtime 与大小失效;时区变化整份作废;扫描全部时顺带删掉已不存在的文件。`--no-cache` 不读也不写。
|
|
55
|
+
- 实测(本机,`src/session/stats-perf.test.ts`):1000 个会话、67 MB(每个 12 回合、24 次工具调用、2 KB 工具结果),冷扫描约 160 ms,命中索引约 15 ms。
|
|
56
|
+
|
|
57
|
+
## 检索:`ama sessions search`
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
ama sessions search <关键词|/正则/标志> [--all] [--role user|assistant|tool] [--since 7d] [--limit N] [--json]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
$ ama sessions search parser
|
|
65
|
+
3f9a1c2e#1 2026-09-29 01:00 /home/me/proj user 修复 parser 在空输入时崩溃的 bug
|
|
66
|
+
3f9a1c2e@4 2026-09-29 01:00 /home/me/proj tool src/parser.ts:42: if (input.length === 0)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- 关键词不区分大小写;`/…/` 是 JavaScript 正则(标志照写,如 `/todo|fixme/i`)。
|
|
70
|
+
- 检索用户文本、助手文本与工具调用(`名字 参数 JSON`)、工具结果;不检索思考、system 与 custom。`--role` 可逗号分隔多个。
|
|
71
|
+
- 每行:会话 id 前 8 位 + 编号(user 是 `#n`,可直接给 `--from`;其它是条目序号 `@k`,即文件里第 k 条条目)、时间、项目、角色、片段。stdout 是终端且没设 `NO_COLOR` 时命中处高亮,否则纯文本。`--json` 每条命中一行。
|
|
72
|
+
- 最新的会话在前;`--limit` 缺省 20。关键词不含引号与反斜杠时先在原始行上预筛,不命中的行不解析。
|
|
73
|
+
|
|
74
|
+
## 复用:`sessions show` 编号与 `--from`
|
|
75
|
+
|
|
76
|
+
`ama sessions show <id>` 在末尾列出用户消息,按文件顺序编号(含插话 `steer`、排队 `followUp` 与宿主注入,标出 origin 与图片数):
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
用户消息(ama --from 3f9a1c2e#<编号> 复用):
|
|
80
|
+
#1 2026-09-29 01:00:06 修复 parser 在空输入时崩溃的 bug
|
|
81
|
+
#2 2026-09-29 01:00:44 顺便把错误信息改成中文
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`--from <id>[#编号]` 用那条消息作新提示(不写编号取最后一条),开的是新会话,可以换模型:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
ama -p --from 3f9a1c2e#1 --model packy/deepseek-v4-flash # 同一个问题换个模型问
|
|
88
|
+
ama -p --from 3f9a1c2e "只改测试,不动实现" # 位置参数接在原文后面(空一行)
|
|
89
|
+
ama --from 3f9a1c2e#2 # 交互界面:作为初始提示直接发送
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- `-p` 时原消息里的图片一并发送(写进临时目录、走 `--image` 的校验,模型不收图片时退出 2;运行后删除)。交互 / 行式界面只带文本,有图片时在 stderr 提示一行。
|
|
93
|
+
- 编号越界、格式不对 → 退出 2;会话不存在 → 退出 5。`--mode rpc` 不支持。
|
|
94
|
+
|
|
95
|
+
## 导出:`ama sessions export`
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
ama sessions export <id> [--format md|json|jsonl] [--output <文件>] [--branch leaf|all]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| 格式 | 内容 |
|
|
102
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
103
|
+
| `md`(缺省) | 给人读:用户消息(带编号)、助手文本、工具调用(参数 JSON 截到 500 字符)与结果(截到 2000 字符)、压缩 / 分支摘要、切换模型、末尾用量表;思考与 system 不写,图片写占位 |
|
|
104
|
+
| `json` | `{ format: "ama.session-export", version: 1, session, branch, leafId, userMessages, usage, entries }`,`entries` 是原条目 |
|
|
105
|
+
| `jsonl` | 头 + 所选条目,与会话文件同形状,可以再被 ama 读回(不带 `leaf` 行) |
|
|
106
|
+
|
|
107
|
+
- `--branch leaf`(缺省)是根到当前叶子的分支(与 `/tree` 当前位置一致);`all` 是文件里全部条目。
|
|
108
|
+
- `--output` 写文件(权限 0600),否则写 stdout。
|
|
109
|
+
- **脱敏**:导出前把 key / token 形态的字符串换成 `[REDACTED]`——`sk-…`、`sk-ant-…`、`ghp_…`、`github_pat_…`、`xox?-…`、`AIza…`、`AKIA…`、`npm_…`、JWT、`Bearer` / `Basic` 凭据、PEM 私钥块,以及 `apiKey` / `secret` / `token` / `password` / `authorization` 之后紧跟 `:` 或 `=` 的值;json / jsonl 里键名像机密的字符串值整段遮掉。图片的 base64 保留。只认形态,不保证遮全,分享前仍请自己看一遍。
|
|
110
|
+
|
|
111
|
+
## 请求明细(设计,未实现)
|
|
112
|
+
|
|
113
|
+
计划在会话里追加 `custom{customType:"ama.request"}`(不进上下文),每次模型请求一条:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"type": "custom",
|
|
118
|
+
"customType": "ama.request",
|
|
119
|
+
"data": {
|
|
120
|
+
"purpose": "turn",
|
|
121
|
+
"provider": "packy",
|
|
122
|
+
"model": "kimi-k2.5",
|
|
123
|
+
"channel": "messages",
|
|
124
|
+
"startedAt": "…",
|
|
125
|
+
"firstByteMs": 820,
|
|
126
|
+
"durationMs": 6400,
|
|
127
|
+
"httpStatus": 200,
|
|
128
|
+
"attempt": 1,
|
|
129
|
+
"stopReason": "toolUse"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
暂不实现的原因:HTTP 状态与首字节时间只在协议层(`src/ai/http.ts`、各 `apis/*`)可见,重试在 `agent/session-run.ts`,记录点要同时碰这几处,正与超时 / 重试反馈的改动重叠。现阶段 `ama stats` 用已有数据近似:回合耗时取落盘时间差,重试取 `context_edit{reason:"retry"}`,失败取 `stopReason: "error"`。实现时在 `StreamOptions.onResponse` 旁加一个请求结束回调,由会话层把上面的字段写成 `custom` 条目;`ama stats` 读到后按请求给出耗时分布与 HTTP 状态计数。
|