@armadra/agent 0.7.4 → 0.7.6

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 (81) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/CHANGELOG.zh-CN.md +32 -0
  3. package/dist/agent/session-classifier.d.ts +2 -0
  4. package/dist/agent/session-classifier.js +24 -3
  5. package/dist/agent/session-extensions.d.ts +1 -0
  6. package/dist/agent/session-extensions.js +3 -0
  7. package/dist/agent/session-subagent.js +9 -2
  8. package/dist/agent/subagent-background.d.ts +5 -1
  9. package/dist/agent/subagent-background.js +13 -1
  10. package/dist/agent/subagent-fork.d.ts +10 -5
  11. package/dist/agent/subagent-fork.js +22 -6
  12. package/dist/agent/subagent-registry.d.ts +1 -1
  13. package/dist/agent/subagent-registry.js +1 -1
  14. package/dist/agents/catalog.d.ts +1 -0
  15. package/dist/ai/apis/anthropic-request.js +6 -1
  16. package/dist/ai/apis/cache-params.js +2 -1
  17. package/dist/ai/apis/google-request.js +2 -1
  18. package/dist/ai/apis/max-tokens.d.ts +4 -0
  19. package/dist/ai/apis/max-tokens.js +12 -1
  20. package/dist/ai/apis/openai-request.js +3 -1
  21. package/dist/ai/apis/openai-responses-request.js +2 -1
  22. package/dist/ai/json-body.d.ts +33 -1
  23. package/dist/ai/json-body.js +96 -18
  24. package/dist/ai/providers/enrich.d.ts +6 -2
  25. package/dist/ai/providers/enrich.js +13 -2
  26. package/dist/ai/providers/max-tokens-cache.d.ts +22 -0
  27. package/dist/ai/providers/max-tokens-cache.js +95 -0
  28. package/dist/ai/providers/registry.js +3 -3
  29. package/dist/bundle/ama.cjs +982 -442
  30. package/dist/checkpoints/shadow-git.d.ts +5 -1
  31. package/dist/checkpoints/shadow-git.js +11 -2
  32. package/dist/cli/bootstrap.js +13 -5
  33. package/dist/cli/compose-providers.d.ts +1 -0
  34. package/dist/cli/compose-providers.js +3 -0
  35. package/dist/cli/compose-store.d.ts +6 -2
  36. package/dist/cli/compose-store.js +8 -4
  37. package/dist/cli/compose.js +2 -2
  38. package/dist/cli/deps.d.ts +1 -1
  39. package/dist/config/json-schema.js +1 -0
  40. package/dist/config/key-docs.js +1 -0
  41. package/dist/config/schema-w5.js +2 -0
  42. package/dist/config/settings-registry.js +1 -0
  43. package/dist/config/types-w5.d.ts +5 -0
  44. package/dist/i18n/catalog.d.ts +2 -0
  45. package/dist/i18n/messages/config-keys.d.ts +2 -0
  46. package/dist/i18n/messages/config-keys.js +2 -0
  47. package/dist/i18n/messages/config.d.ts +2 -0
  48. package/dist/i18n/messages/interactive.d.ts +2 -0
  49. package/dist/i18n/messages/interactive.js +2 -0
  50. package/dist/i18n/messages/session.js +2 -2
  51. package/dist/modes/acp/acp-server.js +17 -15
  52. package/dist/modes/acp/acp-sessions.d.ts +14 -1
  53. package/dist/modes/acp/acp-sessions.js +44 -1
  54. package/dist/modes/interactive/agent-ui.js +7 -0
  55. package/dist/session/manager.d.ts +18 -0
  56. package/dist/session/manager.js +75 -14
  57. package/dist/session/offload.d.ts +59 -0
  58. package/dist/session/offload.js +218 -0
  59. package/dist/session/store.d.ts +12 -4
  60. package/dist/session/store.js +18 -8
  61. package/dist/tools/background-jobs.d.ts +4 -4
  62. package/dist/tools/background-jobs.js +4 -4
  63. package/dist/tools/bash.d.ts +2 -1
  64. package/dist/tools/bash.js +4 -3
  65. package/dist/tools/read-lines.d.ts +39 -0
  66. package/dist/tools/read-lines.js +163 -86
  67. package/dist/tools/read.js +3 -3
  68. package/dist/tools/task-ctl.js +2 -1
  69. package/dist/tools/task.d.ts +9 -0
  70. package/dist/tools/task.js +27 -1
  71. package/docs/acp.md +6 -3
  72. package/docs/agents.md +14 -3
  73. package/docs/en/acp.md +2 -2
  74. package/docs/en/permissions.md +1 -1
  75. package/docs/en/providers.md +4 -4
  76. package/docs/en/sessions.md +2 -1
  77. package/docs/permissions.md +1 -1
  78. package/docs/providers.md +9 -5
  79. package/docs/session-format.md +11 -0
  80. package/docs/sessions.md +2 -1
  81. package/package.json +1 -1
@@ -380,7 +380,7 @@ Models of relays and custom providers (`models[]` entries, and models a relay sy
380
380
  - Not inherited: `cost` (relay prices differ; write the relay's price yourself), `promptCache.short` / `long` (TTLs are promises of the official host), request formats such as `compat.thinkingFormat`, and cache host capabilities.
381
381
  - models.dev: on a hit, models.dev explicitly matches that catalog entry's snapshot (e.g. `deepseek/deepseek-flash` rather than a reseller's entry of the same name); window, output limit and price still come from models.dev (the price source stays `models.dev`). Without models.dev the window comes from the catalog and the output limit is capped at 64k.
382
382
  - Matching: after lowercasing and stripping one vendor prefix (like `deepseek-ai/`, `moonshotai/`) and `:latest`, the id is compared exactly with first-party catalog ids and their `aliases`; only a **unique hit** inherits (when two catalog entries claim the same key, neither is used); `vendor/model` ids in aggregator catalogs (openrouter etc.) do not take part. When the exact comparison misses, a thinking-tier suffix (`-minimal` / `-low` / `-medium` / `-high` / `-xhigh` / `-tiered`) is stripped and tried once: the relay picks the thinking tier from such a name, so only images, window and cache threshold are inherited, `reasoning` stays off and ama sends no thinking parameters. Example: `gemini-3.8-flash-low` → `google/gemini-3.8-flash`, with image input and a 1M window.
383
- - Switches: `"catalog": false` on a `models[]` entry turns it off; `"catalog": "deepseek/deepseek-v4-pro"` names the catalog entry explicitly (when the id does not line up). Catalog aliases live in the `aliases` of `catalog/*.json` entries (such as `deepseek-v4-flash` on `deepseek-flash`).
383
+ - Switches: `"catalog": false` on a `models[]` entry turns it off; `"catalog": "deepseek/deepseek-v4-pro"` names the catalog entry explicitly (when the id does not line up); when the named entry does not exist a warning is recorded (startup header count, `ama models list`, `AMA_LOG`) and the model inherits nothing, without falling back to inheriting by id. Catalog aliases live in the `aliases` of `catalog/*.json` entries (such as `deepseek-v4-flash` on `deepseek-flash`).
384
384
  - A relay that points the same id at another upstream causes a mismatch: the sources column of `ama models list` shows which entry was inherited; turn it off with `catalog: false`. Whether `requiresReasoningContentOnAssistantMessages` holds through a relay depends on the relay passing `reasoning_content` through; if not, set it to `false` in the `compat` of `modelOverrides`.
385
385
 
386
386
  ### Image input
@@ -487,16 +487,16 @@ Inference only looks at the final request's host name (`HOST_CACHE_CAPABILITIES`
487
487
 
488
488
  **Automatic stripping on 400**: when an endpoint rejects with 400 and the error body names `prompt_cache_key` / `prompt_cache_retention` / `prompt_cache_options` / `cache_control`, ama records `provider/model` in an in-process strip table, resends once without those fields (still with a single terminal event), and suggests once which switch to write; later requests in the same process omit them directly.
489
489
 
490
- The second-to-last user breakpoint: when one turn carries many parallel tool results, the last user message can sit beyond the lookback window the official docs describe (about 20 blocks) from where the previous request wrote, and without it the whole stretch is rewritten; breakpoints themselves are free. The lookback window cannot be measured through relays, so this follows the official docs.
490
+ The second-to-last user breakpoint: when one turn carries many parallel tool results, the last user message can sit beyond the lookback window the official docs describe (about 20 blocks) from where the previous request wrote, and without it the whole stretch is rewritten; breakpoints themselves are free. The lookback window cannot be measured through relays, so this follows the official docs and public reports and has not been sampled against the official endpoint.
491
491
 
492
492
  ### max_tokens
493
493
 
494
494
  Before each request is sent, its `max_tokens` (Completions' `max_completion_tokens`, Responses' `max_output_tokens`, Gemini's `maxOutputTokens`) goes through two steps:
495
495
 
496
496
  - **Proactive clamp**: when the model's `contextWindow` is known, it becomes `min(requested, max(1024, window − estimated input − 2048))`, with input estimated as request-body characters / 4 (CJK-heavy contexts are underestimated; the next step covers that). Anthropic budgeted thinking (`thinking.type: "enabled"`) and Gemini with a positive thinking budget are not clamped: the budget is derived from the limit, and changing it would invalidate the message cache.
497
- - **Reactive fix**: when an endpoint rejects with 400 and states the limit (wordings like `Range of max_tokens should be [1, N]` or `max_tokens … must be / at most / less than or equal to N`), the limit for `provider/model` is recorded in an in-process table and the request is resent once with it (like the automatic stripping above, only before the stream starts and still with a single terminal event); later requests in the same process use the limit directly. Anthropic's `input length and max_tokens exceed context limit: X + Y > Z` is resent once with `Z − X` and not recorded as a model limit; when `Z − X` is below 1024 it is handled as a context overflow (compact, then retry).
497
+ - **Reactive fix**: when an endpoint rejects with 400 and states the limit (wordings like `Range of max_tokens should be [1, N]` or `max_tokens … must be / at most / less than or equal to N`), the limit for `provider/model` is recorded in an in-process table and the request is resent once with it (like the automatic stripping above, only before the stream starts and still with a single terminal event); later requests in the same process use the limit directly. Anthropic's `input length and max_tokens exceed context limit: X + Y > Z` is resent once with `Z − X` and not recorded as a model limit; when `Z − X` is below 1024 it is handled as a context overflow (compact, then retry). This wording follows the official docs and public reports and has not been sampled against the official endpoint.
498
498
 
499
- Relays often list a catalog `maxTokens` equal to the window; the first request is then rejected once and never again in the same process. For a model you use long term, set `maxTokens` to the endpoint's real limit in `models[]` / `modelOverrides[]` to skip even that first rejection.
499
+ Relays often list a catalog `maxTokens` equal to the window; the first request is then rejected once. The learned limit is recorded in `<dataDir>/models/max-tokens-caps.json` (only the model reference, the limit and a timestamp), loaded back when later processes start, so their first request already uses the limit; entries expire after 30 days and are probed again, and deleting the file forces a re-probe (when a relay raised its limit). For a model you use long term, set `maxTokens` to the endpoint's real limit in `models[]` / `modelOverrides[]` to skip even that first rejection; a smaller value written there takes precedence.
500
500
 
501
501
  ### Request timeouts and retries
502
502
 
@@ -203,7 +203,7 @@ Rolling back code (`/rewind`, design in [rewind-plan.md](../rewind-plan.md), Chi
203
203
  - **Snapshots**: at the start of a new turn, `git add -A` + `write-tree` + `commit-tree` (parent: the previous shadow commit), with the commit id recorded as the checkpoint's `shadowCommit`. The shadow repository uses a fixed identity and empty global / system config (your signing, hooks, filters and templates are not read), `core.autocrlf=false` with line-ending conversion off, so raw disk bytes are stored; `gc.auto=0`.
204
204
  - **Ignores**: `.gitignore` in the working directory applies; when the working directory is inside a git repository, paths your repository ignores (parent `.gitignore` files, `info/exclude`, the global ignore file) stay out of the shadow repository too; `.git` is always excluded.
205
205
  - **Restore**: the current working directory is written as a tree and compared with the target commit, so only differing files are processed; conflict and safety checks are the same as in `tools` (symbolic links, hard links, non-regular files and directories on the path replaced by links are skipped). The "known" current content = what is in the latest shadow snapshot, what ama last wrote or what the latest checkpoint recorded; anything else counts as a change outside the turn and is skipped by default. Ignored files are left alone; files changed by edit / write but not in the shadow repository (outside the working directory, ignored) restore from the `tools` records. When the target checkpoint has no shadow commit (after a downgrade, or the shadow repository was deleted) the whole restore follows `tools`.
206
- - **Guards**: in these cases the session downgrades to `tools` with a one-time notice: `git` is not on PATH; the working directory (ignored files excluded) has more than 20 000 files (checked before the first snapshot); a single snapshot takes longer than 3 seconds (that commit is kept; creating the shadow repository the first time does not count). It is not enabled when the working directory is the home directory or the file-system root.
206
+ - **Guards**: in these cases the session downgrades to `tools` with a one-time notice: `git` is not on PATH; the working directory (ignored files excluded) has more than 20 000 files (checked before the first snapshot); two snapshots in a row take longer than 3 seconds (the second commit is kept; the first snapshot, which builds the index, does not count, nor does creating the shadow repository). It is not enabled when the working directory is the home directory or the file-system root.
207
207
  - **Limitations**:
208
208
  - bash changes in the latest turn enter a snapshot only when the next turn starts; rolling back before that, they cannot be told apart from manual changes and count as conflicts (overwriting is an option).
209
209
  - git only records the executable bit: restore only adjusts the executable bit and keeps other permission bits; symbolic links and submodules are not restored.
@@ -219,6 +219,7 @@ Rolling back code (`/rewind`, design in [rewind-plan.md](../rewind-plan.md), Chi
219
219
  - For conversation-only or code-only rewinds, an `ama.rewind-note` is appended at the end before the next prompt to tell the model which files disagree with the conversation; nothing is appended for conversation + code.
220
220
  - In-memory sessions and `checkpoints.mode: "off"` create no checkpoints, so only conversation rewinds are possible.
221
221
  - When Esc interrupts a run before this turn produced any reply or tool call, the turn is withdrawn and the original message put back (`ui.restoreOnCancel`, default true).
222
+ - Going back to before images were downgraded reads them back from the session file; if the file was changed externally and they cannot be read back, the images stay empty and one warning is logged (`AMA_LOG`; shown in the notice area in the TUI).
222
223
 
223
224
  ## Request details (design, not implemented)
224
225
 
@@ -174,7 +174,7 @@ default / auto-edit 下,满足下面全部条件的 bash 调用免审批(`Pe
174
174
  - **模型**:`permission.autoModel`(`provider/model`);没配(或找不到)时用会话供应商目录里的小模型(目录文件级
175
175
  `small`:deepseek → `deepseek-flash`、anthropic → `claude-haiku-4-5`、openai → `gpt-6-luna`、google →
176
176
  `gemini-3.5-flash-lite`、moonshot → `kimi-k2.6` 等,要能找到且有 key),再否则用当前会话模型;选中的模型记一条
177
- debug 日志。中转与自定义供应商没有目录小模型,仍用会话模型。推荐配一个便宜快速的模型,例如 `packy/qwen3.8-flash`。`maxTokens` 256,关闭思考。
177
+ debug 日志。中转 / 自定义供应商:会话模型按 id 继承了官方目录条目、且该供应商模型表(config `models[]`、发现缓存)里列有同厂商的小模型(同样按 id 继承到目录 `small` 条目,例如 packy 的 `deepseek-v4-pro` 会话 → `deepseek-v4-flash`)时用它,否则用会话模型;不使用模型表之外的合成条目。仍推荐配 `permission.autoModel`。推荐配一个便宜快速的模型,例如 `packy/qwen3.8-flash`。`maxTokens` 256,关闭思考。
178
178
  - **缓存**:会话内按「工具名 + 归一化参数」(bash 折叠空白,其它按键排序的 JSON)缓存成功的判定,同样的调用只分类一次;出错与超时不缓存。
179
179
  - **费用**:每次分类的用量记一条 `usage` 条目,`kind: "permission_classify"`,计入 `/session` 费用与 RPC 统计,不进上下文。
180
180
  - 分类器只能把未决定的调用判成 allow 或 ask,不能推翻①的拒绝或询问。
package/docs/providers.md CHANGED
@@ -540,7 +540,8 @@ src/ai/providers/catalog.test.ts` 自动删除并重新生成 `catalog-data.ts`
540
540
  `-xhigh` / `-tiered`)试一次:这类 id 由中转按名字决定思考档,所以只继承图片、窗口与缓存门槛,`reasoning` 不打开,
541
541
  ama 不再发思考参数。例:`gemini-3.8-flash-low` → `google/gemini-3.8-flash`,带图片输入、1M 窗口。
542
542
  - 开关:`models[]` 条目写 `"catalog": false` 关闭;`"catalog": "deepseek/deepseek-v4-pro"` 显式指定目录条目(id
543
- 对不上时)。目录别名写在 `catalog/*.json` 条目的 `aliases` 里(如 `deepseek-flash` 的 `deepseek-v4-flash`)。
543
+ 对不上时);指定的条目不存在时记一条 warning(启动头部计数、`ama models list`、`AMA_LOG`),该模型不继承、也不回落
544
+ 按 id 继承。目录别名写在 `catalog/*.json` 条目的 `aliases` 里(如 `deepseek-flash` 的 `deepseek-v4-flash`)。
544
545
  - 中转把同名 id 指到别的上游时会误配:`ama models list` 的来源列能看出继承了哪条,用 `catalog: false` 关掉。
545
546
  `requiresReasoningContentOnAssistantMessages` 经中转是否成立取决于中转是否透传 `reasoning_content`,不成立时用
546
547
  `modelOverrides` 的 `compat` 写 `false`。
@@ -672,7 +673,8 @@ Anthropic 的 `baseUrl` 以 `/v1` 结尾时请求 `{baseUrl}/messages`,不会
672
673
  (仍只有一个终止事件),提示一次建议写哪个开关;同一进程里之后的请求直接不带。
673
674
 
674
675
  倒数第二条 user 的断点:一次回合里并行工具结果很多时,最后一条 user 离上一次请求的写入点可能超出官方文档说的
675
- 回看窗口(约 20 个块),没有它就要整段重写;断点本身不计费。回看窗口没法经中转实测,这一条按官方文档实现。
676
+ 回看窗口(约 20 个块),没有它就要整段重写;断点本身不计费。回看窗口没法经中转实测,这一条按官方文档与公开报告实现,
677
+ 未经官方端点取样。
676
678
 
677
679
  ### max_tokens
678
680
 
@@ -686,10 +688,12 @@ Anthropic 的 `baseUrl` 以 `/v1` 结尾时请求 `{baseUrl}/messages`,不会
686
688
  `max_tokens … must be / at most / less than or equal to N` 一类文案),把 `provider/model` 的上限记入进程内的表、
687
689
  以上限重发一次(与上面的 400 自动剥离一样只发生在流开始之前,仍只有一个终止事件),同一进程里之后的请求直接用
688
690
  上限。Anthropic 的 `input length and max_tokens exceed context limit: X + Y > Z` 按 `Z − X` 重发一次、不记为模型
689
- 上限;`Z − X` 不足 1024 时按上下文溢出处理(压缩后重试)。
691
+ 上限;`Z − X` 不足 1024 时按上下文溢出处理(压缩后重试)。这条文案按官方文档与公开报告实现,未经官方端点取样。
690
692
 
691
- 中转常把目录里的 `maxTokens` 写成与窗口相同,这时第一次请求会被拒一次,之后同一进程里不再出现;长期用的模型可以
692
- 在 `models[]` / `modelOverrides[]` 里把 `maxTokens` 填成端点实际上限,连第一次也省掉。
693
+ 中转常把目录里的 `maxTokens` 写成与窗口相同,这时第一次请求会被拒一次。学到的上限记入
694
+ `<dataDir>/models/max-tokens-caps.json`(只有模型引用、上限与时间),之后的进程启动时载回、首个请求直接用上限;
695
+ 条目 30 天后过期、重新探测,删掉该文件即重测(中转放宽了上限时)。长期用的模型可以在 `models[]` /
696
+ `modelOverrides[]` 里把 `maxTokens` 填成端点实际上限,连第一次也省掉;写了更小的值时以它为准。
693
697
 
694
698
  ### 请求超时与重试
695
699
 
@@ -99,6 +99,16 @@
99
99
  - fork(`--fork <id>`、`/fork`、RPC `fork`):把根 → 指定条目的分支复制到新文件,头的 `parentSession` 指回原文件;不复制 `leaf` 行。
100
100
  - `task` 子会话:独立文件,头带 `parentSession`,首条条目是 `custom{customType:"ama.task"}`。fork 式子会话(`task.context: "fork"`)同样如此:父会话分支被复制到新文件,首条 `ama.task` 作为新根条目,复制的首条重挂到它下面(`data.context: "fork"`、`data.forkedFrom` = 复制到的父条目 id)。
101
101
 
102
+ ## 内存表示
103
+
104
+ 文件格式与上面完全一致;以下只是 `SessionManager` 在内存里的取舍(#170)。已落盘的会话里,**活动分支上被活动分支上的 `context_edit`(任何 reason)改写的 `message` 条目**不再进上下文,它的图片块在内存里换成 `data: ""` 的占位(`mimeType` 保留),只记该行在文件中的位置,需要原文时按位置读回一行:
105
+
106
+ - `entries()` / `branch()` / `getEntry()` 与 SDK 的 `session.entries` 返回占位版本;投影不受影响(被改写的条目本来就用替换文本)。
107
+ - RPC `get_entries`(`getEntries()`)与 fork 从文件读回原文,fork 出的文件含全部 base64。
108
+ - 换叶子(`/tree`、rewind 回到编辑之前)后,不再被生效编辑改写的条目就地回读,之前拿到的同一消息对象也随之恢复;回到编辑之后再次卸载。打开文件时先扫一遍 `context_edit` 行,读到被改写的条目时随即剥掉图片,峰值不含这些旧图。
109
+ - 内存会话(`inMemory`)与尚未落盘的延迟会话不卸载。文本内容(如被 `prune` 的工具结果)照旧常驻。
110
+ - 会话文件被其它进程改写(如 trash 后被清理)导致读不回时,该条目保留占位并告警,不抛错;告警经会话日志(`AMA_LOG`,TUI 显示在通知区),会话建好之前(打开文件期间)的告警先缓冲、会话建好时补出。SDK 传入的 `sessionManager` 同样接到会话的 `log`。
111
+
102
112
  ## custom 类型
103
113
 
104
114
  `custom` 不进上下文,`custom_message` 进上下文。ama 自己使用的 `customType`:
@@ -107,6 +117,7 @@
107
117
  | -------------------- | ---------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
108
118
  | `ama.todo` | `custom` | `data: { items: { id, text, status: pending \| in_progress \| done, planStep? }[] }`,整表快照 | `todo` 工具 `set` / `update`、计划获批时由步骤生成(W5-F);`get` 取分支上最近一条 |
109
119
  | `ama.task` | `custom` | `data: { parentToolCallId, description, parentSession? }`;第五波见表后说明 | `task` 子会话的首条条目;第五波起父会话也写 |
120
+ | `ama.fork` | `custom` | `data: { taskLine }`:`<task>` 指令首个非空行(≤ 120 字符) | fork 子会话复制分支后追加;`task` / `task_ctl` 被拒时据此说明身份并附摘要(#191) |
110
121
  | `ama.codemode-store` | `custom` | `data: { entries }`,`store()` 的完整快照 | codemode 脚本成功结束且写过 store;读取取分支上最近一条 |
111
122
  | `ama.aborted` | `custom_message` | `content`:告诉模型上一条回复被用户中断;`display: false` | 用户中断运行 |
112
123
  | `ama.hook_context` | `custom_message` | `content`:UserPromptSubmit Hook 的 `additionalContext`;`display: false` | 随用户提示进上下文 |
package/docs/sessions.md CHANGED
@@ -184,7 +184,7 @@ ama sessions trace <id|文件> [--html [文件]] [--json] [--output <文件>] [-
184
184
  - **快照**:新回合开始时 `git add -A` + `write-tree` + `commit-tree`(父为上一个影子提交),提交 id 记进检查点的 `shadowCommit`。影子仓库用固定身份、空的全局 / 系统配置(不读你的签名、钩子、过滤器与模板),`core.autocrlf=false` 且关掉换行转换,存的是磁盘原字节;`gc.auto=0`。
185
185
  - **忽略**:工作目录里的 `.gitignore` 生效;工作目录在 git 仓库里时,你的仓库判定为忽略的路径(上级目录的 `.gitignore`、`info/exclude`、全局忽略文件)同样不进影子仓库;`.git` 一律排除。
186
186
  - **恢复**:把当前工作目录写成树,与目标提交比较,只处理有差异的文件;冲突与安全检查与 `tools` 相同(符号链接、硬链接、非普通文件、路径上的目录被换成链接都跳过)。「已知」的当前内容 = 最近一个影子快照里的、ama 最后写入的或最近检查点记录的,其余视为回合外的改动,缺省跳过。被忽略的文件不碰;edit / write 改过、但不在影子仓库里的文件(工作目录外、被忽略)按 `tools` 记录恢复。目标检查点没有影子提交(降级之后、或影子仓库已删)时整次按 `tools` 恢复。
187
- - **护栏**:以下情况本会话降级为 `tools` 并提示一次——PATH 里找不到 `git`;工作目录(不含忽略的)超过 20 000 个文件(第一次快照前检查);单次快照超过 3 秒(这次的提交保留;不含第一次建影子仓库的时间)。工作目录是家目录或文件系统根目录时不启用。
187
+ - **护栏**:以下情况本会话降级为 `tools` 并提示一次——PATH 里找不到 `git`;工作目录(不含忽略的)超过 20 000 个文件(第一次快照前检查);连续两次快照超过 3 秒(第二次的提交保留;首次快照要建索引,不计入,也不含第一次建影子仓库的时间)。工作目录是家目录或文件系统根目录时不启用。
188
188
  - **限制**:
189
189
  - 最近一个回合里 bash 的改动要到下一个回合开始才进快照;在那之前回滚,这些改动与手动修改分不开,算冲突(可选择覆盖)。
190
190
  - git 只记可执行位:恢复时只调整可执行位,其它权限位保留;符号链接与子模块不恢复。
@@ -200,6 +200,7 @@ ama sessions trace <id|文件> [--html [文件]] [--json] [--output <文件>] [-
200
200
  - 仅对话或仅代码时,下一次提示前在末尾追加一条 `ama.rewind-note` 告诉模型哪些文件与对话不一致;对话 + 代码不追加。
201
201
  - 内存会话与 `checkpoints.mode: "off"` 不建检查点,只能仅对话。
202
202
  - 运行中 Esc 中断、本回合还没有任何回复或工具调用时,撤回该回合并回填原消息(`ui.restoreOnCancel`,缺省 true)。
203
+ - 回到图片被降级之前时图片从会话文件回读;文件已被外部改写、读不回时图片保留为空,并告警一次(`AMA_LOG`,TUI 显示在通知区;见 [session-format.md](session-format.md)「内存表示」)。
203
204
 
204
205
  ## 请求明细(设计,未实现)
205
206
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armadra/agent",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "description": "A coding and coordination agent that runs standalone or embedded in Armadra",
5
5
  "type": "module",
6
6
  "keywords": [