@armadra/agent 0.7.3 → 0.7.4

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 (173) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/CHANGELOG.zh-CN.md +37 -0
  3. package/dist/agent/retry.d.ts +14 -5
  4. package/dist/agent/retry.js +30 -8
  5. package/dist/agent/session-cache-key.d.ts +8 -0
  6. package/dist/agent/session-cache-key.js +46 -0
  7. package/dist/agent/session-cache.d.ts +2 -3
  8. package/dist/agent/session-cache.js +19 -37
  9. package/dist/agent/session-classifier.d.ts +3 -2
  10. package/dist/agent/session-classifier.js +36 -10
  11. package/dist/agent/session-compaction.d.ts +24 -5
  12. package/dist/agent/session-compaction.js +54 -20
  13. package/dist/agent/session-core.d.ts +2 -0
  14. package/dist/agent/session-run.d.ts +12 -0
  15. package/dist/agent/session-run.js +29 -10
  16. package/dist/agent/session-subagent.d.ts +8 -1
  17. package/dist/agent/session-subagent.js +37 -14
  18. package/dist/agent/session-sync.d.ts +8 -2
  19. package/dist/agent/session-sync.js +7 -10
  20. package/dist/agent/session-tools.js +3 -2
  21. package/dist/agent/subagent-fork.d.ts +67 -0
  22. package/dist/agent/subagent-fork.js +87 -0
  23. package/dist/agent/subagent-registry.d.ts +2 -2
  24. package/dist/agent/subagent-registry.js +2 -2
  25. package/dist/agent/tool-availability.d.ts +8 -0
  26. package/dist/agent/tool-availability.js +8 -0
  27. package/dist/agent/tool-runner.js +13 -4
  28. package/dist/agent/worktree.d.ts +8 -0
  29. package/dist/agent/worktree.js +51 -4
  30. package/dist/agents/parse.js +6 -1
  31. package/dist/agents/task-record.js +3 -0
  32. package/dist/agents/types.d.ts +2 -0
  33. package/dist/ai/apis/anthropic-messages.js +10 -5
  34. package/dist/ai/apis/anthropic-request.d.ts +2 -1
  35. package/dist/ai/apis/anthropic-request.js +9 -3
  36. package/dist/ai/apis/google-generative-ai.js +4 -2
  37. package/dist/ai/apis/max-tokens.d.ts +42 -0
  38. package/dist/ai/apis/max-tokens.js +121 -0
  39. package/dist/ai/apis/openai-completions.js +10 -5
  40. package/dist/ai/apis/openai-request.js +1 -1
  41. package/dist/ai/apis/openai-responses-request.js +1 -1
  42. package/dist/ai/apis/openai-responses.js +9 -5
  43. package/dist/ai/apis/shared.d.ts +3 -1
  44. package/dist/ai/apis/shared.js +21 -3
  45. package/dist/ai/cache/fingerprint.d.ts +4 -1
  46. package/dist/ai/cache/fingerprint.js +18 -1
  47. package/dist/ai/cache/miss.d.ts +2 -1
  48. package/dist/ai/cache/miss.js +9 -3
  49. package/dist/ai/cache/reporting.js +5 -1
  50. package/dist/ai/cache/types.d.ts +10 -2
  51. package/dist/ai/cache/warmer.d.ts +6 -1
  52. package/dist/ai/cache/warmer.js +7 -0
  53. package/dist/ai/context.d.ts +12 -4
  54. package/dist/ai/context.js +43 -24
  55. package/dist/ai/fake/fake-provider.d.ts +8 -1
  56. package/dist/ai/fake/fake-provider.js +19 -7
  57. package/dist/ai/fake/fake-script.d.ts +2 -0
  58. package/dist/ai/http.d.ts +17 -9
  59. package/dist/ai/http.js +33 -13
  60. package/dist/ai/image-intern.d.ts +29 -0
  61. package/dist/ai/image-intern.js +58 -0
  62. package/dist/ai/json-body.d.ts +31 -0
  63. package/dist/ai/json-body.js +208 -0
  64. package/dist/ai/overflow.js +5 -0
  65. package/dist/ai/providers/catalog-data.js +13 -13
  66. package/dist/ai/providers/catalog.d.ts +33 -0
  67. package/dist/ai/providers/catalog.js +105 -6
  68. package/dist/ai/providers/enrich.d.ts +13 -3
  69. package/dist/ai/providers/enrich.js +87 -31
  70. package/dist/ai/sse.d.ts +3 -2
  71. package/dist/ai/sse.js +3 -2
  72. package/dist/ai/types.d.ts +9 -2
  73. package/dist/bundle/ama.cjs +3110 -1793
  74. package/dist/cli/compose-agents.js +3 -0
  75. package/dist/cli/compose-extensions.js +2 -0
  76. package/dist/cli/compose-request.d.ts +20 -0
  77. package/dist/cli/compose-request.js +37 -0
  78. package/dist/cli/compose-session.js +2 -2
  79. package/dist/cli/subcommands/model-meta.d.ts +2 -1
  80. package/dist/cli/subcommands/model-meta.js +3 -2
  81. package/dist/codemode/host-side.d.ts +11 -4
  82. package/dist/codemode/host-side.js +25 -6
  83. package/dist/codemode/tool.d.ts +2 -0
  84. package/dist/codemode/tool.js +4 -0
  85. package/dist/config/json-schema.js +9 -2
  86. package/dist/config/key-docs.js +13 -4
  87. package/dist/config/schema-w5.js +9 -1
  88. package/dist/config/schema.d.ts +1 -1
  89. package/dist/config/schema.js +19 -3
  90. package/dist/config/settings-registry.js +6 -0
  91. package/dist/config/types-w5.d.ts +2 -0
  92. package/dist/config/types.d.ts +19 -1
  93. package/dist/config/types.js +4 -0
  94. package/dist/i18n/catalog.d.ts +13 -2
  95. package/dist/i18n/messages/cli.d.ts +2 -0
  96. package/dist/i18n/messages/cli.js +2 -0
  97. package/dist/i18n/messages/config-keys.d.ts +10 -0
  98. package/dist/i18n/messages/config-keys.js +12 -2
  99. package/dist/i18n/messages/config.d.ts +14 -0
  100. package/dist/i18n/messages/config.js +4 -0
  101. package/dist/i18n/messages/errors.d.ts +3 -2
  102. package/dist/i18n/messages/errors.js +15 -2
  103. package/dist/i18n/messages/report.d.ts +2 -0
  104. package/dist/i18n/messages/report.js +2 -0
  105. package/dist/i18n/messages/settings.d.ts +4 -0
  106. package/dist/i18n/messages/settings.js +6 -2
  107. package/dist/i18n/messages/subcommands.d.ts +2 -2
  108. package/dist/i18n/messages/subcommands.js +4 -2
  109. package/dist/modes/acp/acp-server.js +15 -5
  110. package/dist/modes/acp/acp-sessions.d.ts +9 -0
  111. package/dist/modes/acp/acp-sessions.js +25 -0
  112. package/dist/modes/context-report.js +3 -2
  113. package/dist/modes/print/json-event.d.ts +12 -1
  114. package/dist/modes/print/json-event.js +48 -1
  115. package/dist/modes/rpc/rpc-mode.d.ts +1 -0
  116. package/dist/modes/rpc/rpc-mode.js +9 -2
  117. package/dist/modes/session-report.d.ts +1 -1
  118. package/dist/modes/session-report.js +12 -6
  119. package/dist/rpc.d.ts +5 -2
  120. package/dist/session/line-reader.d.ts +28 -0
  121. package/dist/session/line-reader.js +82 -0
  122. package/dist/session/list.d.ts +14 -0
  123. package/dist/session/list.js +150 -0
  124. package/dist/session/manager.d.ts +7 -3
  125. package/dist/session/manager.js +21 -48
  126. package/dist/session/migrate.d.ts +2 -1
  127. package/dist/session/migrate.js +2 -1
  128. package/dist/session/projection.d.ts +16 -3
  129. package/dist/session/projection.js +96 -29
  130. package/dist/session/scan.d.ts +3 -3
  131. package/dist/session/scan.js +9 -18
  132. package/dist/session/store.d.ts +3 -0
  133. package/dist/session/store.js +37 -31
  134. package/dist/session/types.d.ts +7 -2
  135. package/dist/tools/bash.d.ts +1 -1
  136. package/dist/tools/bash.js +5 -4
  137. package/dist/tools/glob.d.ts +2 -2
  138. package/dist/tools/glob.js +2 -2
  139. package/dist/tools/grep.js +4 -6
  140. package/dist/tools/image-file.js +2 -1
  141. package/dist/tools/read-lines.d.ts +32 -0
  142. package/dist/tools/read-lines.js +147 -0
  143. package/dist/tools/read.d.ts +6 -2
  144. package/dist/tools/read.js +47 -27
  145. package/dist/tools/task.d.ts +9 -0
  146. package/dist/tools/task.js +21 -8
  147. package/dist/tools/truncate.d.ts +7 -0
  148. package/dist/tools/truncate.js +11 -0
  149. package/dist/tools/types.d.ts +13 -0
  150. package/dist/trace/build-index.js +13 -1
  151. package/dist/tui/ansi.d.ts +2 -0
  152. package/dist/tui/ansi.js +7 -3
  153. package/dist/tui/components/editor-buffer.js +2 -2
  154. package/dist/tui/components/editor.js +5 -6
  155. package/dist/tui/keys.js +2 -2
  156. package/dist/tui/stdin-buffer.js +3 -3
  157. package/docs/acp.md +5 -1
  158. package/docs/agents.md +42 -6
  159. package/docs/codemode.md +4 -2
  160. package/docs/en/acp.md +3 -2
  161. package/docs/en/host-api.md +1 -1
  162. package/docs/en/permissions.md +2 -2
  163. package/docs/en/providers.md +54 -20
  164. package/docs/en/rpc.md +23 -7
  165. package/docs/en/sessions.md +4 -2
  166. package/docs/en/tui.md +2 -2
  167. package/docs/permissions.md +4 -1
  168. package/docs/providers.md +85 -22
  169. package/docs/rpc.md +22 -6
  170. package/docs/session-format.md +3 -2
  171. package/docs/sessions.md +3 -1
  172. package/docs/tui.md +1 -1
  173. package/package.json +2 -2
package/docs/en/tui.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English · [简体中文](../tui.md)
4
4
 
5
- > Translated from the Chinese [docs/tui.md](../tui.md) as of commit `6a7b5eb`. When the two differ, the Chinese version is
5
+ > Translated from the Chinese [docs/tui.md](../tui.md) as of commit `4b85f89`. When the two differ, the Chinese version is
6
6
  > authoritative. Screens below are illustrative; the exact interface wording follows the interface language
7
7
  > (`ui.language`, `--lang`, `AMA_LANG`).
8
8
 
@@ -109,7 +109,7 @@ The message area (stderr with an `ama: ` prefix in line mode) shows one line in
109
109
  The misses line is broken down by cause (`3, re-billed 61k tokens ≈ $0.18 (idle timeout 2 · prefix change 1)`); while the warming timer runs, the warming line shows `streaming · next in 2m 10s · expected saving $0.18 ≥ $0.05`, and when stopped it gives the reason; with task sub-sessions there is an extra "Sub-tasks" line.
110
110
 
111
111
  - `/cache warm off|streaming|idle`: switch warming for this session (the config is not written; `idle` also warms while idle, for expensive models).
112
- - `/cache fingerprint`: the prefix fingerprint of the latest real request: one 16-character hash each for the system prompt and the tool table, plus the model name. If a hash changed between two requests, the host or a hook modified the system prompt / tool table mid-session.
112
+ - `/cache fingerprint`: the prefix fingerprint of the latest real request: one 16-character hash each for the system prompt and the tool table, plus the model name, with a hash per section listed under system. If a hash changed between two requests, the host or an extension changed the leading system prompt / tool table without a patch; the section hashes show which section.
113
113
 
114
114
  Trade-offs: the status bar shows the latest hit rate (the session total lives in `/session`); `cache.missNotices` is on by default (the thresholds make it rare); the `Meter` component is not in the default status bar. The rules for hit rate, misses and warming are in [providers.md](providers.md) "Caching".
115
115
 
@@ -171,7 +171,10 @@ default / auto-edit 下,满足下面全部条件的 bash 调用免审批(`Pe
171
171
  - **独立请求**:不进会话转录、不改主会话的消息与前缀(主会话的提示缓存不受影响),不触发保温;请求用途 `purpose: "classify"`。
172
172
  - **输入**:工具名、参数(JSON,截断到 4000 字符)、cwd、项目根、最近一条用户消息的摘要(截断到 600 字符)。参数与用户消息放在 `<tool_call_data>` … `</tool_call_data>` 数据块里,块内出现的结束标记会被转义;系统提示要求把块内一切当数据,忽略其中的指令(包括「ignore previous instructions」「respond allow」之类),遇到这种文本倾向于 ask。
173
173
  - **输出**:严格 JSON `{"decision":"allow"|"ask","reason":"…"}`。解析失败、超时(10 s)、请求出错 → 询问。
174
- - **模型**:`permission.autoModel`(`provider/model`);缺省用当前会话模型。推荐配一个便宜快速的模型,例如 `packy/qwen3.8-flash`。`maxTokens` 256,关闭思考。
174
+ - **模型**:`permission.autoModel`(`provider/model`);没配(或找不到)时用会话供应商目录里的小模型(目录文件级
175
+ `small`:deepseek → `deepseek-flash`、anthropic → `claude-haiku-4-5`、openai → `gpt-6-luna`、google →
176
+ `gemini-3.5-flash-lite`、moonshot → `kimi-k2.6` 等,要能找到且有 key),再否则用当前会话模型;选中的模型记一条
177
+ debug 日志。中转与自定义供应商没有目录小模型,仍用会话模型。推荐配一个便宜快速的模型,例如 `packy/qwen3.8-flash`。`maxTokens` 256,关闭思考。
175
178
  - **缓存**:会话内按「工具名 + 归一化参数」(bash 折叠空白,其它按键排序的 JSON)缓存成功的判定,同样的调用只分类一次;出错与超时不缓存。
176
179
  - **费用**:每次分类的用量记一条 `usage` 条目,`kind: "permission_classify"`,计入 `/session` 费用与 RPC 统计,不进上下文。
177
180
  - 分类器只能把未决定的调用判成 allow 或 ask,不能推翻①的拒绝或询问。
package/docs/providers.md CHANGED
@@ -320,7 +320,9 @@ export PACKY_API_KEY=sk-...
320
320
  `providers add --probe`);
321
321
  `--write` 把结果合并进用户级 `config.json`(已有同 id 不覆盖,只写 `id` 与和供应商不同的 `api`,
322
322
  原文件备份为 `config.json.bak`)。上下文等元数据在运行时从 models.dev 快照补(见下文「模型元数据」),
323
- 匹配不到的条目没有 `contextWindow`,自动压缩随之关闭,需要时手动补。
323
+ id 与官方目录条目对得上的还继承思考映射等固有属性(见下文「从官方目录继承」);匹配不到的条目没有
324
+ `contextWindow`,自动压缩随之关闭,需要时手动补。`--probe` 只验证协议,`thinkingFormat` 这类请求格式仍需
325
+ 实测后写进 `modelOverrides`。
324
326
 
325
327
  ```sh
326
328
  ama models discover packy --probe --write --limit 8
@@ -493,10 +495,11 @@ ama providers refresh <id> [--probe …]
493
495
  src/ai/providers/catalog.test.ts` 自动删除并重新生成 `catalog-data.ts`(之后跑 prettier);确实要钉住与快照相同的
494
496
  值时在条目上写 `"_reason": "…"`(注释,运行时忽略,该条目不做冗余检查)。目录继承的映射与自定义模型略有不同:
495
497
  `maxTokens` 不封顶(取 `min(limit.output, contextWindow)`),缺的缓存价记 0。
496
- - **优先级**:用户配置(`models[]` / `modelOverrides[]` 里写了的字段)> 内置目录(快照 ⊕ 目录覆盖)> models.dev
497
- 索引 > 自定义缺省(`maxTokens: 8192`、`input: ["text"]`、`reasoning: false`、不猜 `contextWindow`)。
498
- `ama models list` 与 `ama config show` 标出每个字段来自哪里(`config` / `目录` / `models.dev` / `缺省`;内置目录
499
- 从快照继承的字段标 `models.dev`)。
498
+ - **优先级**:用户配置(`models[]` / `modelOverrides[]` 里写了的字段)> 内置目录(快照 ⊕ 目录覆盖)> 按 id 继承的
499
+ 官方目录固有属性(下节)> models.dev 索引 > 自定义缺省(`maxTokens: 8192`、`input: ["text"]`、
500
+ `reasoning: false`、不猜 `contextWindow`)。`ama models list` 与 `ama config show` 标出每个字段来自哪里
501
+ (`config` / `目录` / `目录(按 id 匹配)` / `models.dev` / `缺省`;内置目录从快照继承的字段标 `models.dev`),
502
+ 按 id 继承时另列出目录条目(`目录 deepseek/deepseek-flash`)。
500
503
  - **字段映射**(自定义模型):`contextWindow = limit.context`;`maxTokens = min(limit.output, 65536, contextWindow)`——
501
504
  `maxTokens` 每次请求都作为 `max_tokens` 发出,models.dev 给的是原厂上限(不少模型写的是与上下文相同的
502
505
  1M),中转换了上游后常拒收超大值,Anthropic 协议的思考预算也从它推导,64k 对编码 Agent 的单轮输出足够,
@@ -519,6 +522,29 @@ src/ai/providers/catalog.test.ts` 自动删除并重新生成 `catalog-data.ts`
519
522
  7. 都没有 → 「未匹配」,保持自定义缺省(不猜 `contextWindow`,自动压缩关闭)。快照只收主流厂商,转售商专有的
520
523
  id 可能匹配不到,可在模型上写 `modelsDev` 指向收录的条目。
521
524
 
525
+ ### 从官方目录继承
526
+
527
+ 中转站与自定义供应商的模型(`models[]` 条目,以及中转上按 id 合成的模型)若 id 能对上内置目录的条目,就继承
528
+ 该模型的**固有属性**——它们跟着模型走,换了转售商也不变:
529
+
530
+ - 继承:`reasoning`、`input`、`thinkingLevelMap`、`promptCache.minTokens`、
531
+ `compat.requiresReasoningContentOnAssistantMessages`;用户写了的字段不覆盖,`modelOverrides` 仍最高。
532
+ - 不继承:`cost`(中转价不同,要用中转价请自填)、`promptCache.short` / `long`(TTL 是官方主机的承诺)、
533
+ `compat.thinkingFormat` 等请求格式与缓存主机能力。
534
+ - models.dev:命中时 models.dev 改为显式匹配该目录条目的快照(如 `deepseek/deepseek-flash`,而不是别家转售的同名
535
+ 条目),窗口、输出上限与价格照旧来自 models.dev(价格来源仍标 `models.dev`);models.dev 不可用时窗口取目录值、
536
+ 输出上限按 64k 封顶。
537
+ - 匹配:小写、去一层厂商前缀(`deepseek-ai/`、`moonshotai/` 一类)与 `:latest` 后,与第一方目录条目的 id 或
538
+ `aliases` 精确比较,**唯一命中**才继承(两条目录条目撞同一个键时都不用);聚合商目录(openrouter 等)里
539
+ `vendor/model` 形式的 id 不参与。精确比较不中时再去掉思考档后缀(`-minimal` / `-low` / `-medium` / `-high` /
540
+ `-xhigh` / `-tiered`)试一次:这类 id 由中转按名字决定思考档,所以只继承图片、窗口与缓存门槛,`reasoning` 不打开,
541
+ ama 不再发思考参数。例:`gemini-3.8-flash-low` → `google/gemini-3.8-flash`,带图片输入、1M 窗口。
542
+ - 开关:`models[]` 条目写 `"catalog": false` 关闭;`"catalog": "deepseek/deepseek-v4-pro"` 显式指定目录条目(id
543
+ 对不上时)。目录别名写在 `catalog/*.json` 条目的 `aliases` 里(如 `deepseek-flash` 的 `deepseek-v4-flash`)。
544
+ - 中转把同名 id 指到别的上游时会误配:`ama models list` 的来源列能看出继承了哪条,用 `catalog: false` 关掉。
545
+ `requiresReasoningContentOnAssistantMessages` 经中转是否成立取决于中转是否透传 `reasoning_content`,不成立时用
546
+ `modelOverrides` 的 `compat` 写 `false`。
547
+
522
548
  ### 图像输入
523
549
 
524
550
  - 四条协议都把图片放进用户消息:Chat Completions `image_url`(data URL)、Responses `input_image`、
@@ -603,22 +629,26 @@ OpenRouter 的 Messages 接口只在 `message_delta` 里给缓存 usage,解析
603
629
 
604
630
  前缀稳定由组装保证:系统提示节顺序固定、不含时间戳,工具按名排序,会话中途的变化只以 system 补丁追加在末尾([session-format.md](session-format.md)「消息」)。
605
631
 
606
- 对话开始之后的节补丁(resume 时 AGENTS.md、Skills 或 SessionStart Hook 输出变了,宿主 instructions 刷新,压缩后记忆节重新渲染)**不改写开头的 system**:没有打开 `supportsMidConvoSystemMessages` 的协议把它渲染成 `<system-reminder>` 包裹的 user 消息,插在补丁所在的位置,之前的请求仍是逐字节前缀。只有移除工具的补丁例外——工具表本身已经变了,前缀必然失效,这时全部补丁照旧折回开头。实测 DeepSeek 接受对话中途的 system 消息、前缀缓存也保持,但模型仍按开头那条回答,所以 DeepSeek 不打开这个开关([cache-midconvo-2026-10-09](benchmarks/cache-midconvo-2026-10-09.md))。下一次压缩时,这些补丁随 system 检查点并回开头。
632
+ **开头的 system 与工具声明在会话内只写一次。** 对话开始之后的节补丁(resume 时 AGENTS.md、Skills 或 SessionStart Hook 输出变了,宿主 instructions 刷新,压缩后记忆节重新渲染)不改写开头:没有打开 `supportsMidConvoSystemMessages` 的协议把它渲染成 `<system-reminder>` 包裹的 user 消息,插在补丁所在的位置,之前的请求仍是逐字节前缀。实测 DeepSeek 接受对话中途的 system 消息、前缀缓存也保持,但模型仍按开头那条回答,所以 DeepSeek 不打开这个开关([cache-midconvo-2026-10-09](benchmarks/cache-midconvo-2026-10-09.md))。
633
+
634
+ 工具表同理:对话开始后新加的工具追加在工具表末尾;**移除工具不删声明**,提醒里写一句 `Tool "X" is no longer available in this session; calls to it are rejected.`,模型仍去调用时执行层拒绝(`Tool "X" is not available in this session.`);再加回只提醒 `Tool "X" is available again.`,声明沿用第一次发送的版本。系统提示的 `tools` / `rules` 两节在对话开始后也冻结为已发送的文本。
635
+
636
+ 压缩不重写开头:压缩后的 system 检查点只重放对话开始前的 system 消息,之后的全部补丁合并成一条放在摘要后面(同样渲染为提醒),工具表按第一次出现的顺序与版本重建。所以压缩前后的首个请求 system + tools 逐字节相同,多次压缩也一样,只有摘要之后的消息需要按全价写入。
607
637
 
608
638
  ### 请求字段
609
639
 
610
- | 协议 | 字段 | 条件 |
611
- | ---------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
612
- | `anthropic-messages` | 三断点 `cache_control`(最后一条 user、system 末、最后一个工具) | `cacheRetention` 不是 `none` |
613
- | `anthropic-messages` | `ttl: "1h"` | `long` 且 `supportsLongCacheRetention`;否则按 5m |
614
- | `openai-completions` | `prompt_cache_key = sessionId`(截 64 字符) | `sendPromptCacheKey` 且不是 `none` |
615
- | `openai-completions` | `prompt_cache_retention: "24h"` | `long` 且 `supportsLongCacheRetention` |
616
- | `openai-completions`(`anthropic/*` 等) | `cache_control`(`cacheControlFormat: "anthropic"`),`long` 时带 `ttl: "1h"` | 同 Anthropic |
617
- | `openai-responses` | `prompt_cache_key` | 同 Completions |
618
- | `openai-responses` | `prompt_cache_options: { ttl: "30m" }`,否则 `prompt_cache_retention: "24h"` | `long` 且 `supportsExplicitPromptCacheMode`;否则 `long` 且 `supportsLongCacheRetention` |
619
- | OpenAI 两条 | 亲和头 `x-session-affinity` + 每请求 `x-client-request-id`(OpenRouter:`x-session-id`) | `sendSessionAffinityHeaders` 且有 sessionId |
620
- | `google-generative-ai` | 无(隐式缓存) | — |
621
- | 全部 | `toolChoice: "none"` → 各家的「禁止调用工具」写法 | 请求带工具时(摘要续写**不用**,见「压缩摘要续写」) |
640
+ | 协议 | 字段 | 条件 |
641
+ | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
642
+ | `anthropic-messages` | 四断点 `cache_control`,按优先级取前 `maxCacheBreakpoints` 个:最后一条 user、system 末、倒数第二条 user(上一次请求的写入点)、最后一个工具 | `cacheRetention` 不是 `none` |
643
+ | `anthropic-messages` | `ttl: "1h"` | `long` 且 `supportsLongCacheRetention`;否则按 5m |
644
+ | `openai-completions` | `prompt_cache_key = sessionId`(截 64 字符) | `sendPromptCacheKey` 且不是 `none` |
645
+ | `openai-completions` | `prompt_cache_retention: "24h"` | `long` 且 `supportsLongCacheRetention` |
646
+ | `openai-completions`(`anthropic/*` 等) | `cache_control`(`cacheControlFormat: "anthropic"`),`long` 时带 `ttl: "1h"` | 同 Anthropic |
647
+ | `openai-responses` | `prompt_cache_key` | 同 Completions |
648
+ | `openai-responses` | `prompt_cache_options: { ttl: "30m" }`,否则 `prompt_cache_retention: "24h"` | `long` 且 `supportsExplicitPromptCacheMode`;否则 `long` 且 `supportsLongCacheRetention` |
649
+ | OpenAI 两条 | 亲和头 `x-session-affinity` + 每请求 `x-client-request-id`(OpenRouter:`x-session-id`) | `sendSessionAffinityHeaders` 且有 sessionId |
650
+ | `google-generative-ai` | 无(隐式缓存) | — |
651
+ | 全部 | `toolChoice: "none"` → 各家的「禁止调用工具」写法 | 请求带工具时(摘要续写**不用**,见「压缩摘要续写」) |
622
652
 
623
653
  保留层级:`StreamOptions.cacheRetention` 优先;未指定时读 `AMA_CACHE_RETENTION=none|short|long`;都没有为
624
654
  `short`。Anthropic 请求体最后做 TTL 顺序校验(tools → system → messages 里 5m 之后出现 1h 则全部降为 5m)。
@@ -641,6 +671,37 @@ Anthropic 的 `baseUrl` 以 `/v1` 结尾时请求 `{baseUrl}/messages`,不会
641
671
  `prompt_cache_options` / `cache_control` 时,ama 把 `provider/model` 记入进程内的剥离表,去掉这些字段重发一次
642
672
  (仍只有一个终止事件),提示一次建议写哪个开关;同一进程里之后的请求直接不带。
643
673
 
674
+ 倒数第二条 user 的断点:一次回合里并行工具结果很多时,最后一条 user 离上一次请求的写入点可能超出官方文档说的
675
+ 回看窗口(约 20 个块),没有它就要整段重写;断点本身不计费。回看窗口没法经中转实测,这一条按官方文档实现。
676
+
677
+ ### max_tokens
678
+
679
+ 每次请求的 `max_tokens`(Completions 的 `max_completion_tokens`、Responses 的 `max_output_tokens`、Gemini 的
680
+ `maxOutputTokens`)在发出前做两件事:
681
+
682
+ - **主动收紧**:模型的 `contextWindow` 已知时取 `min(请求值, max(1024, 窗口 − 估算输入 − 2048))`,输入按请求体
683
+ 字符数 / 4 估算(中文多的上下文会估低,由下一条兜底)。Anthropic 预算型思考(`thinking.type: "enabled"`)与带
684
+ 正思考预算的 Gemini 不收紧:预算由上限推导,改它会让消息缓存失效。
685
+ - **被动修正**:端点以 400 拒收并说出上限时(`Range of max_tokens should be [1, N]`、
686
+ `max_tokens … must be / at most / less than or equal to N` 一类文案),把 `provider/model` 的上限记入进程内的表、
687
+ 以上限重发一次(与上面的 400 自动剥离一样只发生在流开始之前,仍只有一个终止事件),同一进程里之后的请求直接用
688
+ 上限。Anthropic 的 `input length and max_tokens exceed context limit: X + Y > Z` 按 `Z − X` 重发一次、不记为模型
689
+ 上限;`Z − X` 不足 1024 时按上下文溢出处理(压缩后重试)。
690
+
691
+ 中转常把目录里的 `maxTokens` 写成与窗口相同,这时第一次请求会被拒一次,之后同一进程里不再出现;长期用的模型可以
692
+ 在 `models[]` / `modelOverrides[]` 里把 `maxTokens` 填成端点实际上限,连第一次也省掉。
693
+
694
+ ### 请求超时与重试
695
+
696
+ - **超时分两段**:`request.idleTimeoutMs`(缺省 300 000,`AMA_IDLE_TIMEOUT_MS` 覆盖)只管等响应头;流开始后两块数据之间
697
+ 的上限是 `request.streamIdleTimeoutMs`(缺省 180 000,`AMA_STREAM_IDLE_TIMEOUT_MS` 覆盖)。两者都是 0 关闭,超时按
698
+ 可重试错误处理。思考不外露的端点推理阶段可能很久没有字节,误判断流会让整段输出重新计费,所以流中上限不设得更短;
699
+ 以前为慢端点调大 `idleTimeoutMs` 的,现在要调的是 `streamIdleTimeoutMs`。
700
+ - **重试**(`retry.*`):等待 = `min(maxDelayMs, max(baseDelayMs × 2^(n−1), Retry-After)) × 0.8–1.2 随机`,端点给了
701
+ `Retry-After` 就至少等那么久(失败消息上记为 `retryAfterMs`)。429 / 529 / rate limit 文案算限流,最多重试
702
+ `maxRetries + 2` 次;5xx 只认文案开头或 `status` / `HTTP` 之后的状态码(文案中间出现的「500 tokens」不算)。
703
+ - **回退模型**:配了 `fallbackModel` 时,overloaded 先快速重试一次(约 1 s),仍失败才切到回退模型。
704
+
644
705
  ### usage 与 `cacheReported`
645
706
 
646
707
  原始 usage 里出现任一缓存字段(即使为 0)→ `Usage.cacheReported = true`,都没有 → `false`:Completions 认
@@ -659,10 +720,10 @@ xAI、Mistral、OpenRouter、Google 没有承诺的 TTL,留空:不保温、
659
720
 
660
721
  ### 会话层:指纹、未命中与三态
661
722
 
662
- 每次真实请求在内存里记一条记录:前缀指纹(system 与工具表各取 sha256 前 16 位 hex,加 `provider/model`)、`promptTokens`(input + cacheRead + cacheWrite)、用量与发出时刻。下一次请求与上一条比对:
723
+ 每次真实请求在内存里记一条记录:前缀指纹(system 与工具表各取 sha256 前 16 位 hex,加 `provider/model`,另记每个 system 节的哈希,`/cache fingerprint` 逐节列出)、`promptTokens`(input + cacheRead + cacheWrite)、用量与发出时刻。下一次请求与上一条比对:
663
724
 
664
725
  - **未命中**:`missed = min(上次前缀, 本次前缀) − 本次 cacheRead`,低于噪声下限(`max(1024, promptCache.minTokens, 端点缓存粒度)`)不计——有的端点按块报缓存读(DeepSeek 经中转是 2048 一块),粒度取同一端点(供应商 + 主机 + 模型)观察到的非零 cacheRead 的最大公约数,至少 2 个样本且在 128–8192 之间才采信,只在内存、进程内跨会话复用;相对比例超过随规模自适应的门槛(约 `0.10 × √(100k / 前缀)`,夹在 2%–30%),或绝对值 ≥ 20 000 才记一次。重计费金额按本条实付单价与读价之差估算,模型无价格时只有 token。
665
- - **原因**(按顺序判定):system / 工具表指纹变了 → `prefix_changed`(`detail` 说明哪段,多半是宿主中途注册工具或 Hook 上下文变化);模型变了 → `model_changed`;间隔超过 TTL → `idle`(目录没有 TTL 的隐式缓存按 10 分钟估);两次请求之间 `task` 子任务占了间隔的 80% 以上 → `subtask`;其余 → `evicted`(服务端淘汰)。
726
+ - **原因**(按顺序判定):system / 工具表指纹变了 → `prefix_changed`(`detail` 说明哪段:`tools`,或 `system:<节名,…>`,例如 `system:hooks,memory`;开头只写一次之后,这通常意味着宿主或扩展绕过补丁直接改了首条 system);模型变了 → `model_changed`;间隔超过 TTL → `idle`(目录没有 TTL 的隐式缓存按 10 分钟估);两次请求之间 `task` 子任务占了间隔的 80% 以上 → `subtask`;其余 → `evicted`(服务端淘汰)。
666
727
  - **不算未命中**:压缩、分支摘要、档一裁剪之后的首个请求(上下文合法地变了);前缀低于最小可缓存长度。切换模型**不**豁免。
667
728
  - **三态**:按 `(provider, baseUrl 主机名, model)` 在进程内维护。`unknown`:还没有足够长的可比请求;`reported`:出现过 cacheRead 或 cacheWrite > 0;`silent`:连续 3 个可比请求(前缀 ≥ minTokens、指纹未变、间隔 < TTL)读写都是 0,或 `compat.cacheReporting: "silent"`。只有 `reported` 时显示命中率、检测未命中并保温;`unknown` / `silent` 的请求不进命中率分母,界面显示 `—` / `未报告` 而不是 0%。
668
729
 
@@ -670,7 +731,7 @@ xAI、Mistral、OpenRouter、Google 没有承诺的 TTL,留空:不保温、
670
731
 
671
732
  ### 保温
672
733
 
673
- 工具长时间运行(长测试、`task` 子任务、codemode 脚本)时,前缀可能在下一次请求前过期。保温在 TTL 到期前重放上一次真实请求(同模型、同上下文,`maxTokens: 1`),只买一次读价,把缓存续上。
734
+ 工具长时间运行(长测试、`task` 子任务、codemode 脚本)时,前缀可能在下一次请求前过期。保温在 TTL 到期前重放上一次真实请求(同模型、同上下文,`maxTokens` 取协议允许的最小值:`openai-responses` 为 16,其余为 1),只买一次读价,把缓存续上。
674
735
 
675
736
  | 项 | 规则 |
676
737
  | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -686,7 +747,9 @@ xAI、Mistral、OpenRouter、Google 没有承诺的 TTL,留空:不保温、
686
747
 
687
748
  ### 压缩摘要续写
688
749
 
689
- 档二压缩的摘要请求不再另起一段新对话,而是在与上一次真实请求逐字节相同的前缀后面追加一条摘要指令(`cacheRetention: "short"`),所以整段历史按读价计费。续写请求**不发 `tool_choice`**:实测中转与 Kimi 在 `tool_choice: "none"` 时渲染的提示不带工具定义,前缀在工具段断开、读不到缓存;Anthropic 也写明改动 tool_choice 会让消息缓存失效。工具表照常发,「不要调用工具、只输出摘要」写在末尾指令里。响应为空、被截断、含工具调用或请求出错时,回落为独立的摘要请求(`cacheRetention: "none"`)并记 warning。
750
+ 档二压缩的摘要请求不再另起一段新对话,而是在与上一次真实请求逐字节相同的前缀后面追加一条摘要指令(`cacheRetention: "short"`),所以整段历史按读价计费。续写请求**不发 `tool_choice`**:实测中转与 Kimi 在 `tool_choice: "none"` 时渲染的提示不带工具定义,前缀在工具段断开、读不到缓存;Anthropic 也写明改动 tool_choice 会让消息缓存失效。工具表照常发,「不要调用工具、只输出摘要」写在末尾指令里。响应为空、被截断、含工具调用或请求出错时,回落为独立的摘要请求(`cacheRetention: "none"`)并记 warning。开了思考的模型(预算随 `max_tokens` 推导的 Anthropic 除外),续写的输出上限为摘要上限 4096 加上该级别的思考预算(不超过模型上限),免得思考用完额度、摘要被截断。
751
+
752
+ 档一 / 档二的先后:阈值检查先算档一(裁剪工具结果)能省多少。裁完就回到预算内 → 只裁;裁完仍超预算、且可以续写(同一模型、上一次请求仍是逐字节前缀、缓存没冷)→ **不裁**,直接续写摘要——被裁的内容本来就要进摘要,而裁剪会改写历史、让续写的前缀断开,变成一次全价的独立请求;缓存已经冷了(超过目录承诺的 TTL)→ 照旧先裁,摘要走独立请求,因为前缀反正要全价重写,裁剪后的请求更小。
690
753
 
691
754
  ### 配置
692
755
 
package/docs/rpc.md CHANGED
@@ -13,7 +13,7 @@
13
13
  启动后先发 `hello`,再发当前会话的 `session_start`:
14
14
 
15
15
  ```json
16
- {"type":"hello","protocolVersion":1,"agent":"ama","version":"0.1.0","capabilities":["approvals","images","hooks","plans"]}
16
+ {"type":"hello","protocolVersion":1,"agent":"ama","version":"0.1.0","capabilities":["approvals","images","hooks","plans","compact_events"]}
17
17
  {"type":"session_start","sessionId":"…","cwd":"/work","reason":"startup"}
18
18
  ```
19
19
 
@@ -110,10 +110,12 @@
110
110
 
111
111
  ### 审批
112
112
 
113
- | 命令 | 参数 | `data` |
114
- | ------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
115
- | `set_client_capabilities` | `capabilities: ("approvals" \| "images" \| "hooks" \| "plans")[]` | `{ capabilities }` |
116
- | `permission_response` | `requestId: string`、`decision: "allow" \| "deny" \| "allow_session"` | `{ accepted: boolean }`(false = 当前没在等这个 id,已暂存,稍后被问到时生效) |
113
+ | 命令 | 参数 | `data` |
114
+ | ------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
115
+ | `set_client_capabilities` | `capabilities: ("approvals" \| "images" \| "hooks" \| "plans" \| "compact_events")[]` | `{ capabilities }` |
116
+ | `permission_response` | `requestId: string`、`decision: "allow" \| "deny" \| "allow_session"` | `{ accepted: boolean }`(false = 当前没在等这个 id,已暂存,稍后被问到时生效) |
117
+
118
+ `compact_events`(docs/memory-plan.md D9):声明后,`turn_end.toolResults`、`message_start` 与 `entry_appended` 不再重复携带工具结果与带图用户消息的正文(以 `contentOmitted: true` 标记),正文只在 `message_end` 与 `tool_execution_end` 里发;不声明时事件形状逐字节不变。形状见「[精简事件](#精简事件compact_events)」。`stream-json` 没有这个开关,输出始终是全量形状。
117
119
 
118
120
  ### 工具、权限、发现
119
121
 
@@ -212,6 +214,20 @@
212
214
 
213
215
  `parentToolCallId` 只出现在 codemode 脚本里经 `tools.*` 发起的内层调用上,值是外层 `codemode` 调用的 id;客户端据此折叠显示。内层调用不进转录。
214
216
 
217
+ `message_end`、`turn_end`、`done` / `error` 与回放(`get_messages`、`get_entries`)里的助手消息可能带两个可选字段(模型调用效率批次,docs/model-efficiency-plan.md §1.10):失败消息的 `retryAfterMs`(`Retry-After`,毫秒),工具调用块的 `rawArguments`(模型输出的原始参数字符串)。`subagent_*` 事件不变;`get_tasks` 的 `TaskInfo` 可带 `context?: "fork" | "fresh"`(子会话的实际上下文模式)。都是新增的可选字段,`RPC_PROTOCOL_VERSION` 不变,客户端忽略不认识的字段即可。
218
+
219
+ ### 精简事件(`compact_events`)
220
+
221
+ 一次工具调用的结果在全量形状下会随 `message_start`、`message_end`、`tool_execution_end`、`turn_end`、`entry_appended` 各出现一次。客户端用 `set_client_capabilities` 声明 `compact_events` 之后,其中三类事件改为:
222
+
223
+ | 事件 | 全量(未声明) | 声明 `compact_events` 时 |
224
+ | ---------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
225
+ | `turn_end` | `toolResults`:完整的 toolResult 消息 | `toolResults[]` 每项只有 `{ toolCallId, toolName, isError, timestamp, contentOmitted: true }`(不带 `content` 与 `details`) |
226
+ | `message_start` | `message` 原样 | `message` 是 toolResult 或带图片的 user 消息时 `content: ""`、`contentOmitted: true`,其余字段不变;其它消息原样 |
227
+ | `entry_appended` | `entry` 原样 | `message` 条目同上(toolResult、带图片的 user);其它条目原样 |
228
+
229
+ `message_end` 与 `tool_execution_end` 始终全量:前者是客户端替换整条消息的依据,后者带 `details`。回放命令(`get_messages`、`get_entries`)不受影响。会话切换后仍按当前声明生效;重新声明不含 `compact_events` 的能力列表即恢复全量。`RPC_PROTOCOL_VERSION` 不变。
230
+
215
231
  ### 子 Agent 事件(第五波)
216
232
 
217
233
  `task` / `task_ctl` 起的子 Agent(ama 子会话与外部 Agent 同一组事件,见 [agents.md](agents.md)「子 Agent」):
@@ -340,7 +356,7 @@
340
356
  {"type":"context_pressure","percent":71,"threshold":70,"remainingTokens":57990,"estimatedTurnsLeft":6}
341
357
  ```
342
358
 
343
- `cache_miss` 的 `detail` 只在 `prefix_changed` 时给出(`system` / `tools`)。`cache_warm{stopped}` 的 `reason` 取值与含义见 [tui.md](tui.md)「缓存与上下文」。`ama -p --output-format json` 的结果对象另有 `cache` 字段,形状同上。
359
+ `cache_miss` 的 `detail` 只在 `prefix_changed` 时给出(`tools`、`system`,或带变化节名的 `system:<节名,…>`,如 `system:hooks,memory`)。`cache_warm{stopped}` 的 `reason` 取值与含义见 [tui.md](tui.md)「缓存与上下文」。`ama -p --output-format json` 的结果对象另有 `cache` 字段,形状同上。
344
360
 
345
361
  ## 审批
346
362
 
@@ -49,7 +49,7 @@
49
49
 
50
50
  - `system`:`{ role: "system", sections, toolsAdded?, toolsRemoved?, timestamp }`。首条是全量:系统提示的命名节(固定顺序 `preamble → tools → rules → project_context → skills → memory → hooks → cwd → host → role`;`memory` 是第六波的记忆索引节,未开启记忆时不出现)与工具声明表;之后只落**补丁**——节名级替换、值为 `null` 表示删除该节,`toolsAdded` / `toolsRemoved` 增删工具。依次重放得到当前系统提示;请求时由协议层重装:首条非 system 消息之前的部分折进开头,之后的节补丁作为尾部上下文送达、不改写开头(移除工具的补丁除外,见 [providers.md](providers.md)「缓存」)。首条 system 消息在首次请求前落盘,也就是建文件的时刻。
51
51
  - `user`:`content`、`origin?`(缺省 = 普通用户输入;`steer` / `followUp` 是运行中插话与排队,`host` 是宿主 `sendUser` 注入,`interrupt` 是打断并立即发送开的新回合(TUI `Ctrl+X`、line 模式 `/interrupt`、RPC / SDK 的 `interrupt: true`;投影与普通 user 消息相同),`direct` 是用户在子 Agent 视图里直接发给该子 Agent 的消息(第六波 W6-A,写在子会话里;投影与普通 user 消息相同),其它字符串原样记录)。
52
- - `assistant`:`content`(文本 / 思考 / 工具调用块)、`api`、`provider`、`model`、`usage`、`stopReason`,以及可选的 `responseId`、`thinkingLevel`、`providerThinkingLevel`、`rawStopReason`、`errorMessage`。`usage` 是 `{ input, output, cacheRead, cacheWrite, cacheWrite1h?, reasoning?, totalTokens, cost?, cacheReported? }`:`input` 不含缓存部分,`cost` 是 `{ input, output, cacheRead, cacheWrite, total }`(美元,模型无价格时缺省),`cacheReported` 表示原始响应里出现过缓存字段。
52
+ - `assistant`:`content`(文本 / 思考 / 工具调用块)、`api`、`provider`、`model`、`usage`、`stopReason`,以及可选的 `responseId`、`thinkingLevel`、`providerThinkingLevel`、`rawStopReason`、`errorMessage`、`retryAfterMs`(失败响应的 `Retry-After`,毫秒;会话层退避取它与指数退避中的较大值)。工具调用块是 `{ type: "toolCall", id, name, arguments, thoughtSignature?, rawArguments? }`:`rawArguments` 是模型输出的原始参数字符串(能严格解析为 JSON 时才记),同协议回放时原样发回,没有时回落 `JSON.stringify(arguments)`。两者都是可选字段,格式版本不变,旧文件照常读取。`usage` 是 `{ input, output, cacheRead, cacheWrite, cacheWrite1h?, reasoning?, totalTokens, cost?, cacheReported? }`:`input` 不含缓存部分,`cost` 是 `{ input, output, cacheRead, cacheWrite, total }`(美元,模型无价格时缺省),`cacheReported` 表示原始响应里出现过缓存字段。
53
53
  - `toolResult`:对应工具调用的结果(`toolCallId`、`toolName`、`content`、`isError`、`details?`)。
54
54
 
55
55
  ### `usage` 条目
@@ -97,7 +97,7 @@
97
97
 
98
98
  - `/tree`:同一文件换叶子(落 `leaf` 行);从旧位置再发消息就形成新分支。离开的分支可以写 `branch_summary` 带进新分支。
99
99
  - fork(`--fork <id>`、`/fork`、RPC `fork`):把根 → 指定条目的分支复制到新文件,头的 `parentSession` 指回原文件;不复制 `leaf` 行。
100
- - `task` 子会话:独立文件,头带 `parentSession`,首条条目是 `custom{customType:"ama.task"}`。
100
+ - `task` 子会话:独立文件,头带 `parentSession`,首条条目是 `custom{customType:"ama.task"}`。fork 式子会话(`task.context: "fork"`)同样如此:父会话分支被复制到新文件,首条 `ama.task` 作为新根条目,复制的首条重挂到它下面(`data.context: "fork"`、`data.forkedFrom` = 复制到的父条目 id)。
101
101
 
102
102
  ## custom 类型
103
103
 
@@ -116,6 +116,7 @@
116
116
  `data` = `TaskInfo`(`taskId, agent, runner, description, background, status, startedAt, endedAt?, turns?, usage?, costUsd?,
117
117
  contextTokens?, contextWindow?, outputFile?, sessionRef?`;`contextTokens / contextWindow` 只有外部 Agent 报告时才有)+ `parentToolCallId`、`cwd`。同一 `taskId` 取分支上最后一条;resume 时据此重建任务注册表,
118
118
  `status: "running"` 视为 `interrupted`(`taskId` 续聊重开 `sessionRef.sessionFile`)。没有 `status` 的是子会话自己的首条。
119
+ 模型调用效率批次(docs/model-efficiency-plan.md)起,两处的 `data` 可带 `context?: "fork" | "fresh"`(子会话的实际上下文模式,fork 回落为 fresh 时记 `fresh`;缺省即 `fresh`),子会话首条另可带 `forkedFrom?`(fork 点的父条目 id)。都是可选字段,格式版本不变。
119
120
 
120
121
  第五波登记的类型(docs/wave5-plan.md;括号里是开始写入的批次,之前的版本不会产生,读到未知 `customType` 一律忽略)。`custom_message` 类都是 `display: false`,经扩展点 `beforePrompts` 追加在末尾,不改缓存前缀:
121
122
 
package/docs/sessions.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 会话统计、检索、复用、导出与检查点
2
2
 
3
- 这几条命令都只读会话目录(`<数据目录>/sessions`,`--session-dir` 可改;文件格式见 [session-format.md](session-format.md)):不加锁、不修复半行、不改文件,正在运行的会话也能读。范围缺省是**当前目录**的会话,`--all` 看全部。
3
+ 这几条命令都只读会话目录(`<数据目录>/sessions`,`--session-dir` 可改;文件格式见 [session-format.md](session-format.md)):不加锁、不修复半行、不改文件,正在运行的会话也能读。会话文件按块逐行读取,不整份读进内存:`ama sessions list` 只解析每个文件的头、首条条目、改名与首条提示,其余行只看类型,几十 MB 的会话也不会让内存随文件大小上涨;`--resume` 打开会话同样逐行解析。范围缺省是**当前目录**的会话,`--all` 看全部。
4
4
 
5
5
  ## 统计:`ama stats`
6
6
 
@@ -71,6 +71,8 @@ RPC `get_session_stats` 原样输出,状态栏与 `/session` 也读它们)
71
71
  | `context.autoCompactAt` | 自动摘要(档二)的阈值 = 窗口 − `compaction.reserveTokens`;自动压缩关闭、连续失败熔断或窗口缺省时**缺省** |
72
72
  | `context.pruneAt` | 工具结果裁剪(档一)的阈值 = 0.7 ×(窗口 − `reserveTokens`);缺省条件同上 |
73
73
 
74
+ 上表的「窗口」在设了 `compaction.contextBudget`(≥ 32768)时取 min(模型窗口, 它):档一、档二、熔断与 `context_pressure` 都按这个软窗口算,`contextWindow` 与百分比仍显示模型的真实窗口。窗口很大、单价按上下文长度计的模型适合设它——例如 1M 窗口的模型设 `128000`,长会话按 128k 的节奏压缩,每回合重读的前缀就不会涨到几十万 token;窗口本身不大或按缓存读价计费已经很便宜时不必设。
75
+
74
76
  - **估算口径**:CJK 表意文字 / 假名 / 谚文每字 1 token,其余字符 / 4,图片每张 1 600;usage 取 `totalTokens`(缺则输入 + 输出 + 缓存读写,含输出)。
75
77
  - **第一次请求前**(`prefix`):系统提示与工具表要到第一次请求时才写进会话,在此之前按「将要发送的那一份」(命名节文本 + 工具声明 JSON)估算,所以新会话一打开就不是 0。只用于统计,不写会话、不改请求体。
76
78
  - **压缩 / 上下文编辑之后**(`estimate`):最后那条 usage 不再代表当前上下文,改为全量估算,直到下一次请求带回新的 usage;界面在数字前加 `≈`。
package/docs/tui.md CHANGED
@@ -103,7 +103,7 @@ Accept edits | shift+tab 切换 claude-opus-5-5 medium | Ctx 3.0% 8.2k/272k a
103
103
  未命中行按原因分列(`3 次,重计费 61k token ≈ $0.18(空闲超时 2 · 前缀变化 1)`);保温行在计时中显示 `streaming · 下次 2m 10s · 期望节省 $0.18 ≥ $0.05`,停止时给原因;有 task 子会话时另有「子任务」行。
104
104
 
105
105
  - `/cache warm off|streaming|idle`:本会话内切换保温(不写配置;`idle` 在空闲时也保温,适合贵模型)。
106
- - `/cache fingerprint`:最近一次真实请求的前缀指纹——system 与工具表各一个 16 位哈希加模型名。两次之间哈希变了,就是宿主或 Hook 中途改了系统提示 / 工具表。
106
+ - `/cache fingerprint`:最近一次真实请求的前缀指纹——system 与工具表各一个 16 位哈希加模型名,system 下逐节列出各节的哈希。两次之间哈希变了,就是宿主或扩展绕过补丁改了开头的系统提示 / 工具表,节哈希指出是哪一节。
107
107
 
108
108
  取舍:状态栏显示最近一次命中率(会话累计放 `/session`);`cache.missNotices` 缺省开(门槛下很少触发);`Meter` 组件不进缺省状态栏。命中率、未命中与保温的判定规则见 [providers.md](providers.md)「缓存」。
109
109
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armadra/agent",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "A coding and coordination agent that runs standalone or embedded in Armadra",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -30,7 +30,7 @@
30
30
  "url": "git+https://github.com/Owlbay/armadra-agent.git"
31
31
  },
32
32
  "bin": {
33
- "ama": "dist/cli/main.js"
33
+ "ama": "dist/bundle/ama.cjs"
34
34
  },
35
35
  "exports": {
36
36
  ".": {