@armadra/agent 0.2.1 → 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 +99 -0
- package/README.md +142 -49
- package/dist/agent/retry.d.ts +1 -1
- package/dist/agent/retry.js +2 -1
- package/dist/agent/session-cache.js +6 -3
- 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-state.js +2 -1
- package/dist/agent/session-sync.js +9 -2
- package/dist/agent/session-tools.js +24 -3
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.js +20 -10
- 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 +5 -3
- package/dist/ai/cache/fingerprint.d.ts +1 -1
- package/dist/ai/cache/fingerprint.js +1 -1
- package/dist/ai/cache/reporting.js +2 -1
- package/dist/ai/http.d.ts +28 -6
- package/dist/ai/http.js +41 -8
- package/dist/ai/providers/channels.d.ts +51 -0
- package/dist/ai/providers/channels.js +96 -0
- package/dist/ai/providers/enrich.d.ts +34 -0
- package/dist/ai/providers/enrich.js +86 -0
- package/dist/ai/providers/models-dev-cache.d.ts +53 -0
- package/dist/ai/providers/models-dev-cache.js +147 -0
- package/dist/ai/providers/models-dev.d.ts +99 -0
- package/dist/ai/providers/models-dev.js +315 -0
- package/dist/ai/providers/registry.d.ts +42 -4
- package/dist/ai/providers/registry.js +195 -34
- 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 +30 -3
- package/dist/bundle/ama.cjs +17893 -10698
- package/dist/cli/args.d.ts +23 -5
- package/dist/cli/args.js +113 -13
- package/dist/cli/bootstrap.js +28 -3
- package/dist/cli/codemode-notice.d.ts +20 -0
- package/dist/cli/codemode-notice.js +55 -0
- package/dist/cli/compose-providers.js +3 -0
- package/dist/cli/compose-session.d.ts +8 -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 +17 -5
- package/dist/cli/compose.js +53 -19
- package/dist/cli/default-model.d.ts +38 -1
- package/dist/cli/default-model.js +96 -9
- package/dist/cli/deps.d.ts +38 -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 +10 -2
- package/dist/cli/main.js +88 -2
- 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 +20 -13
- package/dist/cli/subcommands/config.d.ts +29 -4
- package/dist/cli/subcommands/config.js +188 -25
- package/dist/cli/subcommands/context.js +5 -1
- package/dist/cli/subcommands/doctor.js +12 -1
- package/dist/cli/subcommands/init.d.ts +6 -0
- package/dist/cli/subcommands/init.js +23 -0
- package/dist/cli/subcommands/model-meta.d.ts +16 -0
- package/dist/cli/subcommands/model-meta.js +54 -0
- package/dist/cli/subcommands/models-cache-probe.js +1 -1
- package/dist/cli/subcommands/models-discover.d.ts +18 -6
- package/dist/cli/subcommands/models-discover.js +87 -52
- package/dist/cli/subcommands/models.js +26 -18
- package/dist/cli/subcommands/probe-runner.d.ts +96 -0
- package/dist/cli/subcommands/probe-runner.js +264 -0
- package/dist/cli/subcommands/providers-list.d.ts +8 -0
- package/dist/cli/subcommands/providers-list.js +97 -0
- package/dist/cli/subcommands/providers-plan.d.ts +79 -0
- package/dist/cli/subcommands/providers-plan.js +215 -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 +28 -0
- package/dist/cli/subcommands/providers.js +436 -0
- 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 +34 -0
- package/dist/config/init.js +99 -0
- package/dist/config/json-schema.d.ts +15 -0
- package/dist/config/json-schema.js +215 -0
- 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 +97 -15
- package/dist/config/types.d.ts +57 -11
- package/dist/config/types.js +19 -1
- package/dist/modes/commands-core.js +6 -5
- package/dist/modes/image-input.d.ts +27 -0
- package/dist/modes/image-input.js +78 -0
- 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 +58 -19
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +70 -82
- 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 +9 -5
- 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/startup-ui.d.ts +7 -2
- package/dist/modes/interactive/startup-ui.js +41 -11
- 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 +33 -4
- package/dist/modes/print/print-mode.js +128 -8
- package/dist/modes/rpc/commands.js +12 -2
- 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/rpc.d.ts +2 -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/projection.js +5 -1
- 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/session/types.d.ts +2 -0
- package/dist/tools/image-file.d.ts +31 -0
- package/dist/tools/image-file.js +114 -0
- package/dist/tools/presets.d.ts +35 -8
- package/dist/tools/presets.js +56 -17
- package/dist/tools/read.d.ts +4 -8
- package/dist/tools/read.js +15 -64
- 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 +241 -3
- package/docs/rpc.md +38 -38
- package/docs/session-format.md +3 -2
- package/docs/sessions.md +134 -0
- package/docs/tui.md +140 -61
- package/package.json +3 -1
package/docs/providers.md
CHANGED
|
@@ -2,6 +2,77 @@
|
|
|
2
2
|
|
|
3
3
|
内置供应商、模型引用、API Key、自定义供应商与中转站、各协议的 compat 开关,以及缓存。设计依据见 [design.md](design.md) §3、§9.1。
|
|
4
4
|
|
|
5
|
+
## 配置目录
|
|
6
|
+
|
|
7
|
+
缺省 `~/.config/ama/`(Windows `%APPDATA%\ama\`;`AMA_CONFIG_DIR` 优先,其次 `XDG_CONFIG_HOME/ama`)。数据
|
|
8
|
+
(会话、models.dev 缓存)在另一个目录:`~/.local/share/ama/`(`AMA_DATA_DIR` / `XDG_DATA_HOME`)。
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
~/.config/ama/ 0700
|
|
12
|
+
├── config.json 用户级配置:供应商、渠道、模型、权限、工具、缓存……(直接编辑)
|
|
13
|
+
├── config.schema.json config.json 的 JSON Schema(编辑器补全与校验;由 ama 生成,会被重写)
|
|
14
|
+
├── auth.json API key(0600;只在 ama auth set / ama providers add 时创建)
|
|
15
|
+
├── config.json.bak ama 改写 config.json 前的备份
|
|
16
|
+
├── hooks.json / trust.json / keybindings.json (按需)
|
|
17
|
+
└── skills/ prompts/ (按需)
|
|
18
|
+
~/.local/share/ama/
|
|
19
|
+
├── sessions/ 会话
|
|
20
|
+
└── models-dev.json models.dev 元数据缓存
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `ama init`:建目录(0700)并补齐缺失的 `config.json` 与 `config.schema.json`,逐个打印「已创建」或
|
|
24
|
+
「已存在,未改动」;已存在的 `config.json` 一律不覆盖(`--force` 也不),`config.schema.json` 不是用户文件,
|
|
25
|
+
每次 `init` 都重写为当前版本;不创建空的 `auth.json`。
|
|
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` 等)不创建也不改写配置目录。
|
|
29
|
+
- 最小 `config.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"$schema": "./config.schema.json",
|
|
34
|
+
"version": 1,
|
|
35
|
+
"providers": {}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
不写任何缺省值(缺省值调整时老配置同样生效,`ama config show` 里来源也显示 default),也不写
|
|
40
|
+
`defaultModel`(见下文「缺省模型」)。`ama init` 结束时打印下一步(`ama auth set` / `ama providers add` /
|
|
41
|
+
`ama doctor`)。`config.schema.json` 里每个键都带说明与缺省值(运行时决定的键只写规则),编辑器悬停可见。
|
|
42
|
+
|
|
43
|
+
- `ama config path`:打印配置目录、数据目录与各文件路径(标出是否存在);`ama config edit`:用
|
|
44
|
+
`$VISUAL` / `$EDITOR` 打开 `config.json`(不存在先 `init`),没有编辑器时打印路径。
|
|
45
|
+
|
|
46
|
+
示例:一个三渠道中转 + 一个图像模型 + 内置供应商的覆盖。
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"$schema": "./config.schema.json",
|
|
51
|
+
"version": 1,
|
|
52
|
+
"defaultModel": "packy/kimi-k2.5",
|
|
53
|
+
"providers": {
|
|
54
|
+
"packy": {
|
|
55
|
+
"apiKey": "$PACKY_API_KEY",
|
|
56
|
+
"channels": {
|
|
57
|
+
"chat": { "api": "openai-completions", "baseUrl": "https://www.packyapi.com/v1" },
|
|
58
|
+
"responses": { "api": "openai-responses", "baseUrl": "https://www.packyapi.com/v1" },
|
|
59
|
+
"messages": { "api": "anthropic-messages", "baseUrl": "https://www.packyapi.com" }
|
|
60
|
+
},
|
|
61
|
+
"models": [
|
|
62
|
+
{ "id": "kimi-k2.5", "channels": ["chat", "messages"] },
|
|
63
|
+
{ "id": "grok-4.7", "channels": ["responses"] },
|
|
64
|
+
{
|
|
65
|
+
"id": "qwen3-vl-flash",
|
|
66
|
+
"input": ["text", "image"],
|
|
67
|
+
"modelsDev": "llmgateway/qwen3-vl-flash"
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"deepseek": { "modelOverrides": [{ "id": "deepseek-flash", "contextWindow": 131072 }] }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
5
76
|
## 内置供应商
|
|
6
77
|
|
|
7
78
|
| id | 协议 | baseUrl | API Key 环境变量(顺序) |
|
|
@@ -93,9 +164,11 @@ export PACKY_API_KEY=sk-...
|
|
|
93
164
|
Messages 在 baseUrl 以 `/v1` 结尾时拼 `/messages`,否则 `/v1/messages`。
|
|
94
165
|
- 不想手写 `models`:`ama models discover packy` 列出中转站的模型(`GET {baseUrl}/models`);
|
|
95
166
|
`--probe` 对每个模型依次试供应商协议、completions、responses、messages 的最小请求,记第一个成功
|
|
96
|
-
的(每模型最多 3 次,`--limit` 限制探测的模型数,缺省 30
|
|
167
|
+
的(每模型最多 3 次,`--limit` 限制探测的模型数,缺省 30,执行前打印预估;模型之间并发,探测规则同下文
|
|
168
|
+
`providers add --probe`);
|
|
97
169
|
`--write` 把结果合并进用户级 `config.json`(已有同 id 不覆盖,只写 `id` 与和供应商不同的 `api`,
|
|
98
|
-
原文件备份为 `config.json.bak
|
|
170
|
+
原文件备份为 `config.json.bak`)。上下文等元数据在运行时从 models.dev 缓存补(见下文「模型元数据」),
|
|
171
|
+
匹配不到的条目没有 `contextWindow`,自动压缩随之关闭,需要时手动补。
|
|
99
172
|
|
|
100
173
|
```sh
|
|
101
174
|
ama models discover packy --probe --write --limit 8
|
|
@@ -108,10 +181,173 @@ Claude Code 的通行约定),优先级低于 config 与 auth.json 的 `baseU
|
|
|
108
181
|
`prompt_cache_key`)。`ama config show` 的「供应商」节与 `ama doctor` 标出 baseUrl 来自哪个变量;
|
|
109
182
|
零配置挑的缺省模型来自官方目录,中转站未必有,用 `--model` 或 `defaultModel` 指定。
|
|
110
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
|
+
|
|
111
197
|
```sh
|
|
112
198
|
OPENAI_BASE_URL=https://proxy.example/v1 OPENAI_API_KEY=$PACKY_API_KEY ama -p "hi" --model openai/qwen3.8-flash
|
|
113
199
|
```
|
|
114
200
|
|
|
201
|
+
### 渠道(channels):一个供应商、多种接口
|
|
202
|
+
|
|
203
|
+
同一个中转常常同时开放 Chat Completions(`/v1/chat/completions`)、Responses(`/v1/responses`)与
|
|
204
|
+
Anthropic Messages(`/v1/messages`),且每个模型只在其中一部分接口上可用。**渠道**是「协议 + 地址(+ 可选
|
|
205
|
+
的 key / headers / compat)」,一个供应商可以有多个渠道,模型声明自己挂在哪些渠道上:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"providers": {
|
|
210
|
+
"packy": {
|
|
211
|
+
"name": "Packy",
|
|
212
|
+
"apiKey": "$PACKY_API_KEY",
|
|
213
|
+
"channels": {
|
|
214
|
+
"chat": { "api": "openai-completions", "baseUrl": "https://www.packyapi.com/v1" },
|
|
215
|
+
"responses": { "api": "openai-responses", "baseUrl": "https://www.packyapi.com/v1" },
|
|
216
|
+
"messages": { "api": "anthropic-messages", "baseUrl": "https://www.packyapi.com" }
|
|
217
|
+
},
|
|
218
|
+
"defaultChannel": "chat",
|
|
219
|
+
"models": [
|
|
220
|
+
{ "id": "kimi-k2.5", "channels": ["chat", "messages"] },
|
|
221
|
+
{ "id": "grok-4.7", "channels": ["responses"] },
|
|
222
|
+
{ "id": "deepseek-v4-flash", "channels": ["chat", "responses", "messages"] },
|
|
223
|
+
{ "id": "glm-5" }
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
- **渠道字段**:`api`、`baseUrl` 必填;`apiKey`(同供应商级写法,`$ENV` / `!command` / 字面量;auth.json 里
|
|
231
|
+
`"<provider>@<channel>"` 条目同样生效)、`headers`、`compat`、`authHeader` 可选,缺省继承供应商级。渠道名
|
|
232
|
+
`[A-Za-z0-9][A-Za-z0-9_-]*`,不含 `/` 与 `@`。
|
|
233
|
+
- **模型挂载**:`models[].channels` 列出可用渠道,第一个是首选;不写 → `defaultChannel`(缺省为 `channels`
|
|
234
|
+
的第一个键)。引用了不存在的渠道 → 配置校验报带路径的错误。
|
|
235
|
+
- **模型引用**:`provider/model` 走首选渠道;`provider/model@channel` 显式指定(`--model`、`defaultModel`、
|
|
236
|
+
`/model`、SDK、RPC `set_model` 一致)。指定的渠道不在该模型的 `channels` 里 → 报错并列出可用的
|
|
237
|
+
`provider/model@channel`。`@` 之后不是该供应商的渠道名时整串仍按模型 id 处理(兼容 id 里本来带 `@` 的模型)。
|
|
238
|
+
- **向后兼容**:没有 `channels` 的供应商(含全部内置供应商)按单渠道处理——供应商级 `api` + `baseUrl`
|
|
239
|
+
就是隐式的 `default` 渠道;模型级 `api` / `baseUrl` 仍然有效,覆盖在所选渠道之上(等价于一个匿名渠道)。
|
|
240
|
+
已有配置不用改。写了 `channels` 时供应商级 `api` / `baseUrl` 不再单独成渠道。
|
|
241
|
+
- **运行时**:选中的模型带上该渠道的协议、地址、key、headers 与 compat;会话记录(`model_change`)与
|
|
242
|
+
`ModelRef` 带上 `channel`;缓存的端点键(三态、未命中、粒度推断)是 `供应商|主机|模型@渠道`,同一模型的
|
|
243
|
+
不同渠道分开统计。价格与 models.dev 元数据按模型共享。
|
|
244
|
+
|
|
245
|
+
### 一键接入:`ama providers`
|
|
246
|
+
|
|
247
|
+
只有 baseUrl 与 key 时,一条命令建好供应商、列出模型、补齐元数据:
|
|
248
|
+
|
|
249
|
+
```sh
|
|
250
|
+
ama providers add packy --base-url https://www.packyapi.com/v1 --key-env PACKY_API_KEY --probe --limit 8 --yes
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
ama providers add <id> --base-url <url> [--channel <name>=<api>@<baseUrl> …] [--api <api>|auto]
|
|
255
|
+
[--key-env <VAR>] [--probe] [--limit N] [--probe-models a,b,…]
|
|
256
|
+
[--max-requests N] [--concurrency N] [--probe-timeout ms]
|
|
257
|
+
[--prefer chat,responses,messages] [--include-no-tools] [--yes]
|
|
258
|
+
ama providers list
|
|
259
|
+
ama providers channels <id>
|
|
260
|
+
ama providers remove <id>
|
|
261
|
+
ama providers refresh <id> [--probe …]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- **key**:缺省从 stdin 读(终端下不回显,不进命令行与 shell 历史),存 `auth.json`(0600);给
|
|
265
|
+
`--key-env VAR` 时不读 stdin,`config.json` 里写 `"apiKey": "$VAR"`。供应商已有 key 时不再询问。
|
|
266
|
+
- **候选渠道**:给了 `--channel`(可重复)就只用这些;否则从 `--base-url` 推出三个——`chat`
|
|
267
|
+
(openai-completions,baseUrl 原样)、`responses`(openai-responses,同上)、`messages`
|
|
268
|
+
(anthropic-messages,去掉末尾 `/v1` 的主机根);`--api <api>` 只留对应的一个。
|
|
269
|
+
- **模型列表**:`GET {baseUrl}/models`(第一个 OpenAI 系渠道的地址;只有 Messages 渠道时是 `/v1/models`,先带
|
|
270
|
+
`Authorization: Bearer`,401 / 403 再试 `x-api-key`——实测中转只认前者)。new-api 一类中转在条目上给
|
|
271
|
+
`supported_endpoint_types`(`openai` / `openai-response` / `anthropic`),据此把模型挂到对应渠道;没有提示
|
|
272
|
+
时挂到全部候选渠道里的第一个;有提示但候选渠道都不支持(如只配了 messages 渠道时的 grok)→ 不写入。
|
|
273
|
+
- **`--probe`**:对选中的模型(`--probe-models` 列出的,缺省按 id 字母序不分大小写取前 `--limit` 个,缺省 30)
|
|
274
|
+
逐个渠道发一次最小请求(有提示时只试提示里的渠道),**探测成功的渠道全部写进模型的 `channels`**,顺序按
|
|
275
|
+
`--prefer`(缺省 chat、responses、messages);全部失败的模型不写入;未探测的按提示写入并在表格里标「未探测」。
|
|
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 则停止。
|
|
283
|
+
- **渠道收敛**:写入前删掉没有任何模型挂载的候选渠道;`defaultChannel` 取剩下的第一个(按 `--prefer`)。
|
|
284
|
+
- **写入**:用户级 `config.json` 的 `providers.<id>`(先备份为 `config.json.bak`)。模型条目只写 `id` 与
|
|
285
|
+
`channels`;上下文、输出、图像、推理、价格**不写进配置**,运行时从 models.dev 缓存补(见下节),所以
|
|
286
|
+
`refresh` 不会覆盖手改的字段,手写的值永远优先。models.dev 标明不支持工具调用的模型缺省不写入(Agent
|
|
287
|
+
离不开工具调用),`--include-no-tools` 照写。对已存在的供应商再执行 `add`:只追加新渠道与新模型,已有
|
|
288
|
+
渠道定义与模型条目一字不改。
|
|
289
|
+
- **确认**:写配置前打印摘要,终端里问一次 y/N;非 TTY 必须带 `--yes`(否则退出 2)。
|
|
290
|
+
- 打印表格:id、渠道、上下文、输出、图像、推理、工具调用、价格(models.dev 的原厂价,$/M 输入 / 输出,
|
|
291
|
+
中转实际价格可能不同)、匹配方式。
|
|
292
|
+
- `list`:全部供应商(config.json 里的与有 key 的内置供应商)→ 渠道(协议、地址、key 来源:auth.json /
|
|
293
|
+
`$VAR` / 字面量 / 无,从不显示 key)→ 模型数。`channels <id>`:每个渠道的协议、地址与挂载的模型数。
|
|
294
|
+
`remove`:删 `providers.<id>`(备份)与 auth.json 里该供应商的条目。`refresh`:重拉 `/models` 与
|
|
295
|
+
models.dev,只追加新模型(带 `--probe` 时同 add 的探测),已有条目不改;上游已下架的 id 只提示、不删。
|
|
296
|
+
|
|
297
|
+
实测(2026-10-02,一家同时提供三种接口的中转,22 个模型,共 29 次请求):
|
|
298
|
+
|
|
299
|
+
- `providers add --probe --probe-models kimi-k2.5,grok-4.7,deepseek-v4-flash` 共 8 次请求(1 次列表 + 7 次探测):
|
|
300
|
+
kimi-k2.5 → chat、messages(Responses 不通);grok-4.7 → responses;deepseek-v4-flash 在 Responses 上回
|
|
301
|
+
`incomplete_details.reason: "length"`(中转转发 DeepSeek 时不写 `max_output_tokens`),已按输出截断处理,三种接口都通。
|
|
302
|
+
- models.dev 匹配 22 / 22:原厂条目 19 个(其中 `qwen3.8-max-0902` 去日期后缀匹配到 `alibaba/qwen3.8-max`),多数一致 2 个
|
|
303
|
+
(kimi-k2.5:原厂 `moonshotai/kimi-k2.5` 不在库里,同 canonical 的 11 条取多数 262k / 图像;qwen3-coder-next),
|
|
304
|
+
唯一条目 1 个(qwen3-vl-flash)。
|
|
305
|
+
- `-p` 经 chat 与 `@messages` 两条渠道都正常;`--image` 四色方块图在 kimi-k2.5(chat、messages)与 qwen3-vl-flash 上都答对
|
|
306
|
+
红 / 绿 / 蓝 / 黄;对 glm-5(models.dev 标纯文本)直接退出 2、不发请求。
|
|
307
|
+
- 第二个供应商只配 messages 渠道:19 个模型挂上,3 个只支持 Responses 的 grok 不写入;同名模型不加前缀时报歧义并列出两家。
|
|
308
|
+
|
|
309
|
+
### 模型元数据:models.dev
|
|
310
|
+
|
|
311
|
+
[models.dev](https://models.dev) 汇总了两百多家供应商的模型参数(`https://models.dev/api.json`,约 5 MB)。
|
|
312
|
+
ama 用它给**没写元数据**的自定义模型补上下文、输出上限、输入模态、推理、价格与工具调用能力。
|
|
313
|
+
|
|
314
|
+
- **何时联网**:只有 `ama providers add|refresh`、`ama models discover`、`ama models refresh-catalog`
|
|
315
|
+
会拉取;**启动不联网**,只读缓存。缓存在数据目录 `models-dev.json`(缺省 `~/.local/share/ama/`,只留用到的
|
|
316
|
+
字段,约 2.3 MB),记获取时间与 ETag,24 小时内不重拉(`refresh-catalog` 强制,带 `If-None-Match`)。离线或
|
|
317
|
+
失败时用旧缓存并 warning。`AMA_MODELS_DEV_URL` 换数据源(镜像或本地文件服务)。
|
|
318
|
+
- **优先级**:用户配置(`models[]` / `modelOverrides[]` 里写了的字段)> 内置目录 > models.dev > 自定义缺省
|
|
319
|
+
(`maxTokens: 8192`、`input: ["text"]`、`reasoning: false`、不猜 `contextWindow`)。`ama models list` 与
|
|
320
|
+
`ama config show` 标出每个字段来自哪里(`config` / `目录` / `models.dev` / `缺省`)。
|
|
321
|
+
- **字段映射**:`contextWindow = limit.context`;`maxTokens = min(limit.output, 65536, contextWindow)`——
|
|
322
|
+
`maxTokens` 每次请求都作为 `max_tokens` 发出,models.dev 给的是原厂上限(不少模型写的是与上下文相同的
|
|
323
|
+
1M),中转换了上游后常拒收超大值,Anthropic 协议的思考预算也从它推导,64k 对编码 Agent 的单轮输出足够,
|
|
324
|
+
需要更大时在配置里写;`input` 由 `modalities.input` 含不含 `image` 定为 `["text","image"]` 或 `["text"]`;
|
|
325
|
+
`reasoning`;`cost` 取 `input` / `output` / `cache_read` / `cache_write`($/M),缺缓存价时按输入价算
|
|
326
|
+
(不假设有折扣,保温的经济性判断因此偏保守)。
|
|
327
|
+
- **匹配规则**(同一个 id 常在几十家转售商下重复出现,取值不一):
|
|
328
|
+
1. 模型上写了 `"modelsDev": "provider/model"` → 直接用该条目(写 `false` 关闭补全);
|
|
329
|
+
2. id 形如 `vendor/model` 且 models.dev 正好有这个 `provider/model` → 用它;
|
|
330
|
+
3. 按 id 不分大小写找全部同名条目;有 `canonical_model_id` 的,取指向与 id 同名的那个(否则取票数最多的),
|
|
331
|
+
它若能在原厂供应商下找到 → 用原厂条目;
|
|
332
|
+
4. 否则在(同一 canonical 的)条目里优先原厂供应商:anthropic、openai、google、deepseek、moonshotai(-cn)、
|
|
333
|
+
zhipuai、zai、alibaba(-cn)、xai、mistral、minimax(-cn)、llama(Meta)、cohere、xiaomi、stepfun 等
|
|
334
|
+
(models.dev 里没有 `qwen` / `meta` 这样的供应商 id,通义在 `alibaba`,Llama 在 `llama`);
|
|
335
|
+
5. 仍有多条 → 按 (上下文, 输出, 图像) 取多数,取值不一时记 warning;只有一条就用它;
|
|
336
|
+
6. 同名找不到时依次试归一化后的 id:去 `vendor/` 前缀、去 `:free` 一类后缀、去 `-latest`、去日期后缀
|
|
337
|
+
(`-0902`、`-20250514`、`-2025-05-14`);
|
|
338
|
+
7. 都没有 → 「未匹配」,保持自定义缺省(不猜 `contextWindow`,自动压缩关闭)。
|
|
339
|
+
|
|
340
|
+
### 图像输入
|
|
341
|
+
|
|
342
|
+
- 四条协议都把图片放进用户消息:Chat Completions `image_url`(data URL)、Responses `input_image`、
|
|
343
|
+
Anthropic `image`(base64 source)、Gemini `inlineData`;工具结果里的图片同样映射。
|
|
344
|
+
- 入口:`ama -p "描述这张图" --image a.png --image b.jpg`;交互界面与行式界面里写 `@图片路径`,或粘贴 /
|
|
345
|
+
拖入一个图片文件路径(整段输入里以 `.png` / `.jpg` / `.jpeg` / `.gif` / `.webp` 结尾且文件存在的词)。
|
|
346
|
+
- 与 `read` 工具共用 MIME 检测(按文件头识别 PNG / JPEG / GIF / WebP,扩展名不符时以文件头为准)与大小上限
|
|
347
|
+
(单张 5 MB,取各家上限中最小的 Anthropic)。
|
|
348
|
+
- 模型 `input` 不含 `image` 时直接拒绝并提示换模型(`-p` 退出 2,界面里给错误提示,不发请求);`read`
|
|
349
|
+
工具读图时只返回路径、尺寸与「当前模型不接受图片」。
|
|
350
|
+
|
|
115
351
|
接好之后:`ama models check packy/<id>` 发一次最小请求确认连通;`ama models cache-probe packy/<id>` 看这个端点报不报缓存(见下节「缓存」),中转上不报缓存的模型按建议设 `compat.cacheReporting: "silent"`,状态栏就显示「未报告」而不是 0%。
|
|
116
352
|
|
|
117
353
|
## OpenAI 兼容线的 compat
|
|
@@ -311,6 +547,8 @@ ama models cache-probe <provider/id> [--tokens 2048] [--gap-ms 3000] [--json] [-
|
|
|
311
547
|
|
|
312
548
|
## 测试用 fake 供应商
|
|
313
549
|
|
|
314
|
-
`--
|
|
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>` 后
|
|
315
553
|
按脚本第 n 次调用产出文本、思考、工具调用、429、溢出、断流、延迟,脚本格式见
|
|
316
554
|
`src/ai/fake/fake-script.ts`,示例在 `test/fixtures/scripts/`。
|
package/docs/rpc.md
CHANGED
|
@@ -54,13 +54,13 @@
|
|
|
54
54
|
| `get_last_assistant_text` | — | `{ text: string \| null }` |
|
|
55
55
|
| `get_session_stats` | — | `SessionStats`(见「会话统计」) |
|
|
56
56
|
|
|
57
|
-
`SessionState`:`isStreaming`、`isCompacting`、`isRetrying`、`model`(`{ provider, id }`
|
|
57
|
+
`SessionState`:`isStreaming`、`isCompacting`、`isRetrying`、`model`(`{ provider, id, channel? }` 或缺省;`channel` 只在多渠道供应商上出现)、`thinkingLevel`、`permissionMode`、`sessionId`、`sessionFile`、`cwd`、`sessionName`、`messageCount`、`pendingMessageCount`、`steeringMode`、`followUpMode`、`autoCompaction`、`autoRetry`。
|
|
58
58
|
|
|
59
59
|
### 模型
|
|
60
60
|
|
|
61
61
|
| 命令 | 参数 | `data` |
|
|
62
62
|
| ------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
63
|
-
| `set_model` | `provider: string`、`modelId: string`
|
|
63
|
+
| `set_model` | `provider: string`、`modelId: string`、`channel?: string` | `{ model: { provider, id, channel? } }` |
|
|
64
64
|
| `get_available_models` | — | `{ models: RpcModelInfo[] }` |
|
|
65
65
|
| `set_thinking_level` | `level: off \| minimal \| low \| medium \| high \| xhigh` | `{ level }` |
|
|
66
66
|
| `get_available_thinking_levels` | — | `{ levels: string[] }`(当前模型支持的级别;无模型时 `["off"]`) |
|
|
@@ -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 }`
|
|
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
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
| `compaction` | `summary`、`firstKeptEntryId`、`tokensBefore`、`usage?`、`details?`(`readFiles` / `modifiedFiles`) | 是,作为摘要消息 |
|
|
38
38
|
| `branch_summary` | `fromId`、`summary`、`usage?`、`details?` | 是,作为分支摘要消息 |
|
|
39
39
|
| `context_edit` | `targetId`、`replacement: string \| null`、`reason: prune \| abort \| retry \| overflow \| manual` | 改写目标条目 |
|
|
40
|
-
| `model_change` | `provider`、`modelId`
|
|
40
|
+
| `model_change` | `provider`、`modelId`、可选 `channel`(多渠道供应商的渠道名) | 否(决定续会话时的模型) |
|
|
41
41
|
| `thinking_level_change` | `thinkingLevel` | 否(决定续会话时的思考级别) |
|
|
42
42
|
| `custom` | `customType`、`data` | 否 |
|
|
43
43
|
| `custom_message` | `customType`、`content`(字符串或内容块)、`display`、`details?` | 是,作为 custom 消息 |
|
|
@@ -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 状态计数。
|