@armadra/agent 0.7.5 → 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.
- package/CHANGELOG.md +21 -0
- package/CHANGELOG.zh-CN.md +21 -0
- package/dist/agent/session-classifier.d.ts +2 -0
- package/dist/agent/session-classifier.js +24 -3
- package/dist/agent/session-extensions.d.ts +1 -0
- package/dist/agent/session-extensions.js +3 -0
- package/dist/agent/session-subagent.js +9 -2
- package/dist/agent/subagent-background.d.ts +5 -1
- package/dist/agent/subagent-background.js +13 -1
- package/dist/agent/subagent-fork.d.ts +10 -5
- package/dist/agent/subagent-fork.js +22 -6
- package/dist/agent/subagent-registry.d.ts +1 -1
- package/dist/agent/subagent-registry.js +1 -1
- package/dist/agents/catalog.d.ts +1 -0
- package/dist/ai/apis/max-tokens.d.ts +4 -0
- package/dist/ai/apis/max-tokens.js +12 -1
- package/dist/ai/providers/enrich.d.ts +6 -2
- package/dist/ai/providers/enrich.js +13 -2
- package/dist/ai/providers/max-tokens-cache.d.ts +22 -0
- package/dist/ai/providers/max-tokens-cache.js +95 -0
- package/dist/ai/providers/registry.js +3 -3
- package/dist/bundle/ama.cjs +296 -103
- package/dist/checkpoints/shadow-git.d.ts +5 -1
- package/dist/checkpoints/shadow-git.js +11 -2
- package/dist/cli/compose-providers.d.ts +1 -0
- package/dist/cli/compose-providers.js +3 -0
- package/dist/cli/compose-store.d.ts +6 -2
- package/dist/cli/compose-store.js +8 -4
- package/dist/cli/compose.js +2 -2
- package/dist/config/json-schema.js +1 -0
- package/dist/config/key-docs.js +1 -0
- package/dist/config/schema-w5.js +2 -0
- package/dist/config/settings-registry.js +1 -0
- package/dist/config/types-w5.d.ts +5 -0
- package/dist/i18n/catalog.d.ts +2 -0
- package/dist/i18n/messages/config-keys.d.ts +2 -0
- package/dist/i18n/messages/config-keys.js +2 -0
- package/dist/i18n/messages/config.d.ts +2 -0
- package/dist/i18n/messages/interactive.d.ts +2 -0
- package/dist/i18n/messages/interactive.js +2 -0
- package/dist/i18n/messages/session.js +2 -2
- package/dist/modes/interactive/agent-ui.js +7 -0
- package/dist/session/manager.d.ts +3 -1
- package/dist/session/manager.js +5 -0
- package/dist/session/offload.d.ts +10 -2
- package/dist/session/offload.js +27 -4
- package/dist/tools/background-jobs.d.ts +4 -4
- package/dist/tools/background-jobs.js +4 -4
- package/dist/tools/bash.d.ts +2 -1
- package/dist/tools/bash.js +4 -3
- package/dist/tools/task-ctl.js +2 -1
- package/dist/tools/task.d.ts +9 -0
- package/dist/tools/task.js +27 -1
- package/docs/agents.md +14 -3
- package/docs/en/permissions.md +1 -1
- package/docs/en/providers.md +4 -4
- package/docs/en/sessions.md +2 -1
- package/docs/permissions.md +1 -1
- package/docs/providers.md +9 -5
- package/docs/session-format.md +2 -1
- package/docs/sessions.md +2 -1
- package/package.json +1 -1
package/dist/tools/task.d.ts
CHANGED
|
@@ -20,6 +20,15 @@ import type { ModelThinkingLevel } from "../ai/types.js";
|
|
|
20
20
|
import { AgentCatalog } from "../agents/catalog.js";
|
|
21
21
|
import type { SubagentRequest, ToolContext, ToolDefinition } from "./types.js";
|
|
22
22
|
export declare const MAX_TASK_DEPTH = 1;
|
|
23
|
+
/** <task> 指令首个非空行,超长截断(#191:fork 子会话被拒时提醒模型回到子任务)。 */
|
|
24
|
+
export declare function taskLine(prompt: string): string;
|
|
25
|
+
/** fork 子会话的标记条目(custom,不进上下文):`{ taskLine }`。 */
|
|
26
|
+
export declare const FORK_CUSTOM_TYPE = "ama.fork";
|
|
27
|
+
/**
|
|
28
|
+
* 子会话里 task / task_ctl 的拒绝文案(固定英文)。#191:fork 子会话(有 `ama.fork` 条目)说明身份并
|
|
29
|
+
* 附 <task> 首行;其它情况用 `fallback`(fresh 按深度拒绝的原文案)。
|
|
30
|
+
*/
|
|
31
|
+
export declare function nestedTaskRejection(ctx: ToolContext, tool: string, fallback: string): string;
|
|
23
32
|
export declare const DEFAULT_TASK_MAX_TURNS = 30;
|
|
24
33
|
export interface TaskInput {
|
|
25
34
|
prompt: string;
|
package/dist/tools/task.js
CHANGED
|
@@ -19,6 +19,32 @@
|
|
|
19
19
|
import { AgentCatalog } from "../agents/catalog.js";
|
|
20
20
|
import { TASK_NOTIFICATION_RULE } from "../agents/result.js";
|
|
21
21
|
export const MAX_TASK_DEPTH = 1;
|
|
22
|
+
/** fork 子会话首条 custom 条目里 <task> 指令首行摘要的上限(字符)。 */
|
|
23
|
+
const TASK_LINE_MAX = 120;
|
|
24
|
+
/** <task> 指令首个非空行,超长截断(#191:fork 子会话被拒时提醒模型回到子任务)。 */
|
|
25
|
+
export function taskLine(prompt) {
|
|
26
|
+
const line = prompt
|
|
27
|
+
.split("\n")
|
|
28
|
+
.find((text) => text.trim() !== "")
|
|
29
|
+
?.trim() ?? "";
|
|
30
|
+
return line.length > TASK_LINE_MAX ? `${line.slice(0, TASK_LINE_MAX - 1)}…` : line;
|
|
31
|
+
}
|
|
32
|
+
/** fork 子会话的标记条目(custom,不进上下文):`{ taskLine }`。 */
|
|
33
|
+
export const FORK_CUSTOM_TYPE = "ama.fork";
|
|
34
|
+
/**
|
|
35
|
+
* 子会话里 task / task_ctl 的拒绝文案(固定英文)。#191:fork 子会话(有 `ama.fork` 条目)说明身份并
|
|
36
|
+
* 附 <task> 首行;其它情况用 `fallback`(fresh 按深度拒绝的原文案)。
|
|
37
|
+
*/
|
|
38
|
+
export function nestedTaskRejection(ctx, tool, fallback) {
|
|
39
|
+
const mark = ctx.session.lastCustom(FORK_CUSTOM_TYPE);
|
|
40
|
+
if (mark === undefined || mark === null)
|
|
41
|
+
return fallback;
|
|
42
|
+
const line = typeof mark.taskLine === "string" ? mark.taskLine : "";
|
|
43
|
+
return (`${tool} rejected: you are a sub-agent forked from the main conversation and cannot start ` +
|
|
44
|
+
"or manage sub-agents. Do not call task or task_ctl again; complete the sub-task in your " +
|
|
45
|
+
"<task> block yourself with your other tools, then end with your report." +
|
|
46
|
+
(line !== "" ? ` Your task begins: "${line}"` : ""));
|
|
47
|
+
}
|
|
22
48
|
export const DEFAULT_TASK_MAX_TURNS = 30;
|
|
23
49
|
const THINKING = [
|
|
24
50
|
"off",
|
|
@@ -143,7 +169,7 @@ export function createTaskTool(_options = {}) {
|
|
|
143
169
|
promptGuidelines: [TASK_NOTIFICATION_RULE],
|
|
144
170
|
async execute(input, ctx) {
|
|
145
171
|
if (ctx.depth >= MAX_TASK_DEPTH || ctx.spawnSubagent === undefined) {
|
|
146
|
-
return fail("Sub-agents are not available here (nested tasks are not allowed).");
|
|
172
|
+
return fail(nestedTaskRejection(ctx, "task", "Sub-agents are not available here (nested tasks are not allowed)."));
|
|
147
173
|
}
|
|
148
174
|
const request = buildSubagentRequest(input, ctx);
|
|
149
175
|
if (typeof request === "string")
|
package/docs/agents.md
CHANGED
|
@@ -158,12 +158,15 @@ worktree 里与父 cwd 对应的目录。结束时没有改动(工作区干净
|
|
|
158
158
|
看得到父的计划、提醒与读过的文件内容,但 `read` 记录从空开始(编辑前照旧要先读)。
|
|
159
159
|
- 类型声明了 `tools` / `disallowed-tools`(或参数给了 `tools`)时,工具表不变,不可用的工具在执行层拒绝
|
|
160
160
|
(`Tool "X" is not available in this session.`),并在 `<task>` 里列出。只读类型照旧走只读管线。
|
|
161
|
+
- `<task>` 同时列出按深度拒绝的 `task` / `task_ctl` 并要求不要调用它们(执行层仍是深度拒绝的文案)。实测仍有模型把父消息里
|
|
162
|
+
「调用 task」的指令当成自己的:调一次被拒,多一轮请求,有时随后只回报失败、不做子任务(见 [benchmarks](benchmarks/efficiency-2026-10.md)「F2」)。
|
|
161
163
|
- `isolation: "worktree"` 时子会话的 cwd 是 worktree,`<task>` 里说明上文的相对路径指父目录;cwd 节变化以尾部补丁发送,
|
|
162
164
|
不影响已缓存的前缀。
|
|
163
165
|
|
|
164
166
|
以下情况回落为 fresh(日志记一条 info,结果 `details.context` 标 `fresh`):调用参数或类型指定了与父不同的模型或思考级别;
|
|
165
|
-
父会话还没有发出过请求;父上一次请求的输入超过(窗口 − `compaction.reserveTokens
|
|
166
|
-
照原文件重开,不受影响。只在请求了 fork 时 `details.context`
|
|
167
|
+
父会话还没有发出过请求;父上一次请求的输入超过(窗口 − `compaction.reserveTokens`)× `subagents.forkMaxContextRatio`
|
|
168
|
+
(缺省 0.5)。续聊(`taskId`)与 resume 照原文件重开,不受影响。只在请求了 fork 时 `details.context` 才出现;后台任务的
|
|
169
|
+
`running` 结果也带它,任务排队或 worktree 隔离尚未就绪时该结果不带,`get_tasks` / 完成通知的 `TaskInfo.context` 仍有。
|
|
167
170
|
|
|
168
171
|
何时用 fork:子任务需要你已经读过、讨论过的内容时。fork 的首个请求把父上下文整段带上,按命中价计费;fresh 只发系统提示、
|
|
169
172
|
工具表与任务说明,但你得在 `prompt` 里写清全部背景,子 Agent 往往还要重新读文件。
|
|
@@ -175,7 +178,14 @@ worktree 里与父 cwd 对应的目录。结束时没有改动(工作区干净
|
|
|
175
178
|
| OpenAI 系(经中转,不发路由键) | 两次分别 0% 与 85%,取决于中转把请求路由到哪个上游 | 视中转而定 |
|
|
176
179
|
| Anthropic(1/10,写入 1.25×) | 未实测;前缀相同,命中应与父的下一回合相同 | 需要父上下文时 |
|
|
177
180
|
|
|
178
|
-
|
|
181
|
+
fork 的成本不止首请求:子会话**每一回合**都重读整段父前缀。按命中价折算,8k 父上下文 × 30 回合在 DeepSeek(1/50)上约
|
|
182
|
+
4.8k 全价 token,在 Kimi / Anthropic(1/4–1/10)上约 24k–60k,中转落空时全价。所以 DeepSeek 上子任务只要重读 ≥ 1 份
|
|
183
|
+
已读文件就划算;Kimi / Anthropic 上只在子任务**主要**依赖父上下文时用;经不发路由键的中转不建议。窗口很大的模型上可以调低
|
|
184
|
+
`subagents.forkMaxContextRatio`,免得几十万 token 的父上下文被每回合重读。
|
|
185
|
+
|
|
186
|
+
`explore` 这类只需要定位文件的任务建议保持 fresh;内置类型(包括 `general`)都缺省 fresh:`general` 的典型任务(按指令
|
|
187
|
+
改某处代码)通常不需要父上下文、回合又多,而且长会话里 fork 会按上面的比例静默回落,缺省 fork 会让同一类型在不同时刻拿到
|
|
188
|
+
不同的上下文。需要时在调用参数或类型定义里写 `context: fork`。
|
|
179
189
|
|
|
180
190
|
### 事件与统计
|
|
181
191
|
|
|
@@ -191,6 +201,7 @@ RPC `get_tasks` / `get_agents` 返回任务快照与可用类型(来源、定
|
|
|
191
201
|
| `subagents.maxPending` | 排队上限,缺省 16 |
|
|
192
202
|
| `subagents.retainSessions` | 保留在内存里的已结束子会话数(LRU),缺省 4,0 = 结束即释放;只认用户级 |
|
|
193
203
|
| `subagents.defaultModel` | 子 Agent 缺省模型,不设继承父会话 |
|
|
204
|
+
| `subagents.forkMaxContextRatio` | fork 回落比例(见「fork 模式」),0.05–0.95,缺省 0.5;只认用户级 |
|
|
194
205
|
| `subagents.background` | `auto`(缺省)\| `always` \| `never`,见「前台与后台」;用户 / 项目 / 宿主级都认 |
|
|
195
206
|
| `subagents.autoBackgroundAfterMs` | 前台任务运行超过该毫秒数自动转后台,缺省 0(关闭);用户 / 项目级都认 |
|
|
196
207
|
| `agents.dirs` | 追加的定义目录 |
|
package/docs/en/permissions.md
CHANGED
|
@@ -172,7 +172,7 @@ It only handles calls neither ① nor ② decided (e.g. `rm old.txt`, `node scri
|
|
|
172
172
|
- **A separate request**: it never enters the session transcript and does not change the main session's messages or prefix (the main session's prompt cache is unaffected), and does not trigger warming; the request purpose is `purpose: "classify"`.
|
|
173
173
|
- **Input**: tool name, arguments (JSON, truncated to 4000 characters), cwd, project root, and a summary of the latest user message (truncated to 600 characters). Arguments and the user message sit inside a `<tool_call_data>` … `</tool_call_data>` data block, with end markers inside the block escaped; the system prompt requires treating everything inside as data and ignoring instructions in it (including things like "ignore previous instructions" or "respond allow"), leaning to ask when such text appears.
|
|
174
174
|
- **Output**: strict JSON `{"decision":"allow"|"ask","reason":"…"}`. A parse failure, a timeout (10 s) or a request error → ask.
|
|
175
|
-
- **Model**: `permission.autoModel` (`provider/model`); when unset (or not found), the small model from the session provider's catalog (the file-level `small`: deepseek → `deepseek-flash`, anthropic → `claude-haiku-4-5`, openai → `gpt-6-luna`, google → `gemini-3.5-flash-lite`, moonshot → `kimi-k2.6` and so on; it must be found and have a key), otherwise the current session model; the chosen model is logged once at debug level.
|
|
175
|
+
- **Model**: `permission.autoModel` (`provider/model`); when unset (or not found), the small model from the session provider's catalog (the file-level `small`: deepseek → `deepseek-flash`, anthropic → `claude-haiku-4-5`, openai → `gpt-6-luna`, google → `gemini-3.5-flash-lite`, moonshot → `kimi-k2.6` and so on; it must be found and have a key), otherwise the current session model; the chosen model is logged once at debug level. For relays and custom providers: when the session model inherits an official catalog entry by id and the provider's model table (config `models[]`, the discovery cache) lists a small model of the same vendor (one that also inherits that catalog's `small` entry by id, e.g. a packy `deepseek-v4-pro` session → `deepseek-v4-flash`), that model is used, otherwise the session model; entries synthesized outside the model table are never used. Setting `permission.autoModel` is still recommended. A cheap, fast model is recommended, e.g. `packy/qwen3.8-flash`. `maxTokens` 256, thinking off.
|
|
176
176
|
- **Caching**: successful decisions are cached within the session by "tool name + normalized arguments" (bash collapses whitespace, others use JSON with sorted keys), so identical calls are classified once; errors and timeouts are not cached.
|
|
177
177
|
- **Cost**: each classification records a `usage` entry with `kind: "permission_classify"`, counted in `/session` cost and RPC stats, never in the context.
|
|
178
178
|
- The classifier can only judge undecided calls as allow or ask; it cannot overturn ①'s deny or ask.
|
package/docs/en/providers.md
CHANGED
|
@@ -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
|
|
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
|
|
package/docs/en/sessions.md
CHANGED
|
@@ -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
|
|
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
|
|
package/docs/permissions.md
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
693
|
+
中转常把目录里的 `maxTokens` 写成与窗口相同,这时第一次请求会被拒一次。学到的上限记入
|
|
694
|
+
`<dataDir>/models/max-tokens-caps.json`(只有模型引用、上限与时间),之后的进程启动时载回、首个请求直接用上限;
|
|
695
|
+
条目 30 天后过期、重新探测,删掉该文件即重测(中转放宽了上限时)。长期用的模型可以在 `models[]` /
|
|
696
|
+
`modelOverrides[]` 里把 `maxTokens` 填成端点实际上限,连第一次也省掉;写了更小的值时以它为准。
|
|
693
697
|
|
|
694
698
|
### 请求超时与重试
|
|
695
699
|
|
package/docs/session-format.md
CHANGED
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
- RPC `get_entries`(`getEntries()`)与 fork 从文件读回原文,fork 出的文件含全部 base64。
|
|
108
108
|
- 换叶子(`/tree`、rewind 回到编辑之前)后,不再被生效编辑改写的条目就地回读,之前拿到的同一消息对象也随之恢复;回到编辑之后再次卸载。打开文件时先扫一遍 `context_edit` 行,读到被改写的条目时随即剥掉图片,峰值不含这些旧图。
|
|
109
109
|
- 内存会话(`inMemory`)与尚未落盘的延迟会话不卸载。文本内容(如被 `prune` 的工具结果)照旧常驻。
|
|
110
|
-
- 会话文件被其它进程改写(如 trash
|
|
110
|
+
- 会话文件被其它进程改写(如 trash 后被清理)导致读不回时,该条目保留占位并告警,不抛错;告警经会话日志(`AMA_LOG`,TUI 显示在通知区),会话建好之前(打开文件期间)的告警先缓冲、会话建好时补出。SDK 传入的 `sessionManager` 同样接到会话的 `log`。
|
|
111
111
|
|
|
112
112
|
## custom 类型
|
|
113
113
|
|
|
@@ -117,6 +117,7 @@
|
|
|
117
117
|
| -------------------- | ---------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
118
118
|
| `ama.todo` | `custom` | `data: { items: { id, text, status: pending \| in_progress \| done, planStep? }[] }`,整表快照 | `todo` 工具 `set` / `update`、计划获批时由步骤生成(W5-F);`get` 取分支上最近一条 |
|
|
119
119
|
| `ama.task` | `custom` | `data: { parentToolCallId, description, parentSession? }`;第五波见表后说明 | `task` 子会话的首条条目;第五波起父会话也写 |
|
|
120
|
+
| `ama.fork` | `custom` | `data: { taskLine }`:`<task>` 指令首个非空行(≤ 120 字符) | fork 子会话复制分支后追加;`task` / `task_ctl` 被拒时据此说明身份并附摘要(#191) |
|
|
120
121
|
| `ama.codemode-store` | `custom` | `data: { entries }`,`store()` 的完整快照 | codemode 脚本成功结束且写过 store;读取取分支上最近一条 |
|
|
121
122
|
| `ama.aborted` | `custom_message` | `content`:告诉模型上一条回复被用户中断;`display: false` | 用户中断运行 |
|
|
122
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
|
|
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
|
|