@armadra/agent 0.7.2 → 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 (178) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/CHANGELOG.zh-CN.md +41 -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 +30 -5
  12. package/dist/agent/session-compaction.js +69 -29
  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 +3142 -1805
  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/compaction/prefix-estimate.d.ts +3 -1
  86. package/dist/compaction/prefix-estimate.js +7 -3
  87. package/dist/config/json-schema.js +9 -2
  88. package/dist/config/key-docs.js +13 -4
  89. package/dist/config/schema-w5.js +9 -1
  90. package/dist/config/schema.d.ts +1 -1
  91. package/dist/config/schema.js +19 -3
  92. package/dist/config/settings-registry.js +6 -0
  93. package/dist/config/types-w5.d.ts +2 -0
  94. package/dist/config/types.d.ts +19 -1
  95. package/dist/config/types.js +4 -0
  96. package/dist/i18n/catalog.d.ts +15 -2
  97. package/dist/i18n/messages/cli.d.ts +2 -0
  98. package/dist/i18n/messages/cli.js +2 -0
  99. package/dist/i18n/messages/config-keys.d.ts +10 -0
  100. package/dist/i18n/messages/config-keys.js +12 -2
  101. package/dist/i18n/messages/config.d.ts +14 -0
  102. package/dist/i18n/messages/config.js +4 -0
  103. package/dist/i18n/messages/errors.d.ts +3 -2
  104. package/dist/i18n/messages/errors.js +15 -2
  105. package/dist/i18n/messages/report-context.d.ts +4 -0
  106. package/dist/i18n/messages/report-context.js +4 -0
  107. package/dist/i18n/messages/report.d.ts +6 -0
  108. package/dist/i18n/messages/report.js +2 -0
  109. package/dist/i18n/messages/settings.d.ts +4 -0
  110. package/dist/i18n/messages/settings.js +6 -2
  111. package/dist/i18n/messages/subcommands.d.ts +2 -2
  112. package/dist/i18n/messages/subcommands.js +4 -2
  113. package/dist/modes/acp/acp-server.js +15 -5
  114. package/dist/modes/acp/acp-sessions.d.ts +9 -0
  115. package/dist/modes/acp/acp-sessions.js +25 -0
  116. package/dist/modes/context-report.d.ts +2 -0
  117. package/dist/modes/context-report.js +25 -8
  118. package/dist/modes/print/json-event.d.ts +12 -1
  119. package/dist/modes/print/json-event.js +48 -1
  120. package/dist/modes/rpc/rpc-mode.d.ts +1 -0
  121. package/dist/modes/rpc/rpc-mode.js +9 -2
  122. package/dist/modes/session-report.d.ts +1 -1
  123. package/dist/modes/session-report.js +12 -6
  124. package/dist/rpc.d.ts +5 -2
  125. package/dist/session/line-reader.d.ts +28 -0
  126. package/dist/session/line-reader.js +82 -0
  127. package/dist/session/list.d.ts +14 -0
  128. package/dist/session/list.js +150 -0
  129. package/dist/session/manager.d.ts +7 -3
  130. package/dist/session/manager.js +21 -48
  131. package/dist/session/migrate.d.ts +2 -1
  132. package/dist/session/migrate.js +2 -1
  133. package/dist/session/projection.d.ts +16 -3
  134. package/dist/session/projection.js +96 -29
  135. package/dist/session/scan.d.ts +3 -3
  136. package/dist/session/scan.js +9 -18
  137. package/dist/session/store.d.ts +3 -0
  138. package/dist/session/store.js +37 -31
  139. package/dist/session/types.d.ts +7 -2
  140. package/dist/tools/bash.d.ts +1 -1
  141. package/dist/tools/bash.js +5 -4
  142. package/dist/tools/glob.d.ts +2 -2
  143. package/dist/tools/glob.js +2 -2
  144. package/dist/tools/grep.js +4 -6
  145. package/dist/tools/image-file.js +2 -1
  146. package/dist/tools/read-lines.d.ts +32 -0
  147. package/dist/tools/read-lines.js +147 -0
  148. package/dist/tools/read.d.ts +6 -2
  149. package/dist/tools/read.js +47 -27
  150. package/dist/tools/task.d.ts +9 -0
  151. package/dist/tools/task.js +21 -8
  152. package/dist/tools/truncate.d.ts +7 -0
  153. package/dist/tools/truncate.js +11 -0
  154. package/dist/tools/types.d.ts +13 -0
  155. package/dist/trace/build-index.js +13 -1
  156. package/dist/tui/ansi.d.ts +2 -0
  157. package/dist/tui/ansi.js +7 -3
  158. package/dist/tui/components/editor-buffer.js +2 -2
  159. package/dist/tui/components/editor.js +5 -6
  160. package/dist/tui/keys.js +2 -2
  161. package/dist/tui/stdin-buffer.js +3 -3
  162. package/docs/acp.md +5 -1
  163. package/docs/agents.md +42 -6
  164. package/docs/codemode.md +4 -2
  165. package/docs/en/acp.md +3 -2
  166. package/docs/en/host-api.md +1 -1
  167. package/docs/en/permissions.md +2 -2
  168. package/docs/en/providers.md +54 -20
  169. package/docs/en/rpc.md +23 -7
  170. package/docs/en/sessions.md +4 -2
  171. package/docs/en/tui.md +3 -3
  172. package/docs/permissions.md +4 -1
  173. package/docs/providers.md +85 -22
  174. package/docs/rpc.md +22 -6
  175. package/docs/session-format.md +3 -2
  176. package/docs/sessions.md +3 -1
  177. package/docs/tui.md +2 -2
  178. package/package.json +2 -2
@@ -56,6 +56,9 @@ export function subagentEnvironment(deps, catalog) {
56
56
  const pending = config.subagents?.maxPending;
57
57
  if (pending !== undefined)
58
58
  env.maxPending = pending;
59
+ const retain = config.subagents?.retainSessions; // [M-F] 子会话句柄 LRU 上限
60
+ if (retain !== undefined)
61
+ env.retain = retain;
59
62
  // [W7-B1] 缺省后台与自动转后台(配置键 W7-B2 登记于 config/types-w5.ts)
60
63
  const sub = config.subagents;
61
64
  env.background = resolveTaskBackground(sub?.background, deps.assembly.unattended === true);
@@ -25,6 +25,7 @@ import { createLimitsExtension } from "../agent/limits.js";
25
25
  import { createRemindersExtension, remindersSettings } from "../agent/reminders.js";
26
26
  import { planExtensionFor } from "../plan/compose.js";
27
27
  import { createSubagentsFactory } from "./compose-agents.js";
28
+ import { requestTimeoutsFactory } from "./compose-request.js";
28
29
  import { createExternalStatsExtension } from "../drivers/store.js";
29
30
  export function composeExtensions(deps) {
30
31
  return [
@@ -42,5 +43,6 @@ export function composeExtensions(deps) {
42
43
  // [W5-E] 外部 Agent 记账 → SessionStats.external(只主会话)
43
44
  ({ core }) => core.depth > 0 ? undefined : createExternalStatsExtension(() => core.manager.branch()),
44
45
  createSubagentsFactory(deps), // [W5-G] 子 Agent 任务注册表
46
+ requestTimeoutsFactory(deps), // [ME-C] request.streamIdleTimeoutMs → 每次请求的流中空闲上限
45
47
  ];
46
48
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 模型请求的流中空闲超时(docs/model-efficiency-plan.md D18)。[ME-C]
3
+ *
4
+ * 等响应头仍由 `request.idleTimeoutMs` / `AMA_IDLE_TIMEOUT_MS`(compose-session.ts `idleTimeoutFrom`)
5
+ * 决定;流开始后两块数据之间的上限读 `AMA_STREAM_IDLE_TIMEOUT_MS` > config
6
+ * `request.streamIdleTimeoutMs`,都没有时协议层取缺省 180 000(ai/http.ts)。
7
+ *
8
+ * `session.ts` 不加行:值经会话扩展的 `wrapStream` 写进每次请求的 `StreamOptions.streamIdleTimeoutMs`
9
+ * (已显式给出的不覆盖);工厂对主会话与 task 子会话都装(子会话沿用父的扩展表)。
10
+ */
11
+ import type { SessionExtensionFactory } from "../agent/session-extensions.js";
12
+ import type { AmaConfig } from "../config/types.js";
13
+ import type { ComposeExtensionDeps } from "./compose-extensions.js";
14
+ export declare const STREAM_IDLE_TIMEOUT_ENV = "AMA_STREAM_IDLE_TIMEOUT_MS";
15
+ /** 流中空闲超时:环境变量 > config;都没有 → undefined。0 关闭;非法的环境变量值忽略并 warning。 */
16
+ export declare function streamIdleTimeoutFrom(config: Pick<AmaConfig, "request">, env?: NodeJS.ProcessEnv, warn?: (message: string) => void): number | undefined;
17
+ /** 给每次请求补上 `streamIdleTimeoutMs`(调用方已给出的不覆盖)。 */
18
+ export declare function streamIdleExtension(ms: number): SessionExtensionFactory;
19
+ /** compose-extensions.ts 表里的一行:配置或环境变量给了值才装。 */
20
+ export declare function requestTimeoutsFactory(deps: ComposeExtensionDeps): SessionExtensionFactory;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 模型请求的流中空闲超时(docs/model-efficiency-plan.md D18)。[ME-C]
3
+ *
4
+ * 等响应头仍由 `request.idleTimeoutMs` / `AMA_IDLE_TIMEOUT_MS`(compose-session.ts `idleTimeoutFrom`)
5
+ * 决定;流开始后两块数据之间的上限读 `AMA_STREAM_IDLE_TIMEOUT_MS` > config
6
+ * `request.streamIdleTimeoutMs`,都没有时协议层取缺省 180 000(ai/http.ts)。
7
+ *
8
+ * `session.ts` 不加行:值经会话扩展的 `wrapStream` 写进每次请求的 `StreamOptions.streamIdleTimeoutMs`
9
+ * (已显式给出的不覆盖);工厂对主会话与 task 子会话都装(子会话沿用父的扩展表)。
10
+ */
11
+ import { msg } from "../i18n/index.js";
12
+ export const STREAM_IDLE_TIMEOUT_ENV = "AMA_STREAM_IDLE_TIMEOUT_MS";
13
+ /** 流中空闲超时:环境变量 > config;都没有 → undefined。0 关闭;非法的环境变量值忽略并 warning。 */
14
+ export function streamIdleTimeoutFrom(config, env = process.env, warn = () => undefined) {
15
+ const raw = env[STREAM_IDLE_TIMEOUT_ENV];
16
+ if (raw !== undefined && raw.trim() !== "") {
17
+ const value = Number(raw);
18
+ if (Number.isFinite(value) && value >= 0)
19
+ return value;
20
+ warn(msg().cli.composeSession.invalidStreamIdleTimeout(raw));
21
+ }
22
+ return config.request?.streamIdleTimeoutMs;
23
+ }
24
+ /** 给每次请求补上 `streamIdleTimeoutMs`(调用方已给出的不覆盖)。 */
25
+ export function streamIdleExtension(ms) {
26
+ return () => ({
27
+ id: "request-timeouts",
28
+ wrapStream: (stream) => (model, context, options) => stream(model, context, options.streamIdleTimeoutMs === undefined
29
+ ? { ...options, streamIdleTimeoutMs: ms }
30
+ : options),
31
+ });
32
+ }
33
+ /** compose-extensions.ts 表里的一行:配置或环境变量给了值才装。 */
34
+ export function requestTimeoutsFactory(deps) {
35
+ const ms = streamIdleTimeoutFrom(deps.assembly.config, deps.env, (message) => deps.log("warn", message));
36
+ return ms === undefined ? () => undefined : streamIdleExtension(ms);
37
+ }
@@ -411,8 +411,8 @@ export async function createSessionAlongside(target, request, options = {}) {
411
411
  const { assembly } = record;
412
412
  const sessionDir = assembly.paths.sessionDir;
413
413
  const fresh = (id) => assembly.overrides?.noSession === true
414
- ? SessionManager.inMemory(current.cwd)
415
- : SessionManager.create(sessionDirForCwd(sessionDir, current.cwd), current.cwd, id !== undefined ? { id } : {});
414
+ ? SessionManager.inMemory(record.current.cwd)
415
+ : SessionManager.create(sessionDirForCwd(sessionDir, record.current.cwd), record.current.cwd, id !== undefined ? { id } : {});
416
416
  let manager;
417
417
  if (request.kind === "new")
418
418
  manager = fresh();
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 模型元数据的一行描述(`ama models list`、`ama config show`、`ama providers`):上下文、输出、思考、
3
- * 图片、渠道,以及每个字段来自哪里(config / 目录 / models.dev / 缺省)与 models.dev 匹配。
3
+ * 图片、渠道,以及每个字段来自哪里(config / 目录 / 按 id 继承的目录 / models.dev / 缺省)、继承的
4
+ * 目录条目与 models.dev 匹配。
4
5
  */
5
6
  import { type ModelMetadata } from "../../ai/providers/enrich.js";
6
7
  import type { Model, ProviderRegistryApi } from "../../ai/types.js";
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 模型元数据的一行描述(`ama models list`、`ama config show`、`ama providers`):上下文、输出、思考、
3
- * 图片、渠道,以及每个字段来自哪里(config / 目录 / models.dev / 缺省)与 models.dev 匹配。
3
+ * 图片、渠道,以及每个字段来自哪里(config / 目录 / 按 id 继承的目录 / models.dev / 缺省)、继承的
4
+ * 目录条目与 models.dev 匹配。
4
5
  */
5
6
  import { sourceText } from "../../ai/providers/enrich.js";
6
7
  import { matchLabel } from "../../ai/providers/models-dev.js";
@@ -52,5 +53,5 @@ export function sourcesLine(metadata) {
52
53
  };
53
54
  const parts = Object.keys(label).map((field) => `${label[field]} ${sourceText(metadata.sources[field])}`);
54
55
  const match = metadata.looked ? matchLabel(metadata.match) : undefined;
55
- return t.sources(parts, match, metadata.toolCall === false);
56
+ return t.sources(parts, match, metadata.toolCall === false, metadata.catalog);
56
57
  }
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * codemode 父进程侧:起子进程、转发工具调用、超时杀树(设计 §5.5 沙箱第 1–4 条)。[B10]
3
3
  *
4
- * - 命令行:`<node> --permission --allow-fs-read=<入口> --disallow-code-generation-from-strings
5
- * <入口> --ama-codemode-sandbox`;Node 22.0–22.12 用 `--experimental-permission`。不授予文件写、
4
+ * - 命令行:`<node> --max-old-space-size=<MB> --permission --allow-fs-read=<入口>
5
+ * --disallow-code-generation-from-strings <入口> --ama-codemode-sandbox`;堆上限取 `codemode.maxHeapMb`
6
+ * (缺省 256,0 不加),超出时子进程 OOM 退出,映射为脚本错误(docs/memory-plan.md D10);Node 22.0–22.12 用 `--experimental-permission`。不授予文件写、
6
7
  * 子进程、worker、addon、inspector 权限;以空环境启动(拿不到密钥与会话路径);嵌入 Electron
7
8
  * 时设 `ELECTRON_RUN_AS_NODE=1` 运行同一可执行文件。
8
9
  * - 入口:bundle 里是 ama.cjs 同目录的 `ama-sandbox.cjs`(构建脚本定义 `__AMA_SANDBOX_ENTRY__`);
@@ -25,10 +26,12 @@ import { type ScriptOptions, type StoreSnapshot, type ToolDecl } from "./protoco
25
26
  export declare const MAX_COLLECTED_OUTPUT_CHARS: number;
26
27
  /** 外层 abort 后等子进程自行结束的宽限。 */
27
28
  export declare const ABORT_GRACE_MS = 500;
29
+ /** 子进程 V8 老生代堆上限(MB)缺省值;`codemode.maxHeapMb` 覆盖,0 不设上限。 */
30
+ export declare const DEFAULT_CODEMODE_HEAP_MB = 256;
28
31
  /** 子进程入口的绝对路径(realpath);找不到 → undefined。 */
29
32
  export declare function resolveSandboxEntry(moduleUrl?: string): string | undefined;
30
- /** 子进程参数(不含可执行文件)。 */
31
- export declare function sandboxArgs(entry: string, capability?: Pick<SandboxCapability, "permissionFlag">): string[];
33
+ /** 子进程参数(不含可执行文件)。V8 选项必须在 `--permission` 与脚本之前。 */
34
+ export declare function sandboxArgs(entry: string, capability?: Pick<SandboxCapability, "permissionFlag">, heapMb?: number): string[];
32
35
  /** codemode 子进程的 OS 沙箱策略:拒绝网络,任何位置都不可写。 */
33
36
  export declare const CODEMODE_OS_POLICY: OsSandboxPolicy;
34
37
  /** 完整命令行:`<node> <sandboxArgs>`,有 OS 沙箱时再经它包装。 */
@@ -53,7 +56,11 @@ export interface SandboxRunRequest {
53
56
  os?: Pick<OsSandboxStatus, "kind" | "path">;
54
57
  /** true:必须经 OS 沙箱启动(Node 22 / 24 的 strict 依赖它),包装不了直接失败。 */
55
58
  requireOsSandbox?: boolean;
59
+ /** 子进程堆上限(MB),缺省 DEFAULT_CODEMODE_HEAP_MB;0 不设上限。 */
60
+ maxHeapMb?: number;
56
61
  }
62
+ /** 子进程因堆上限 OOM 退出时给模型的文案(固定英文)。 */
63
+ export declare function heapLimitError(heapMb: number): string;
57
64
  export interface SandboxRunResult {
58
65
  ok: boolean;
59
66
  error?: string;
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * codemode 父进程侧:起子进程、转发工具调用、超时杀树(设计 §5.5 沙箱第 1–4 条)。[B10]
3
3
  *
4
- * - 命令行:`<node> --permission --allow-fs-read=<入口> --disallow-code-generation-from-strings
5
- * <入口> --ama-codemode-sandbox`;Node 22.0–22.12 用 `--experimental-permission`。不授予文件写、
4
+ * - 命令行:`<node> --max-old-space-size=<MB> --permission --allow-fs-read=<入口>
5
+ * --disallow-code-generation-from-strings <入口> --ama-codemode-sandbox`;堆上限取 `codemode.maxHeapMb`
6
+ * (缺省 256,0 不加),超出时子进程 OOM 退出,映射为脚本错误(docs/memory-plan.md D10);Node 22.0–22.12 用 `--experimental-permission`。不授予文件写、
6
7
  * 子进程、worker、addon、inspector 权限;以空环境启动(拿不到密钥与会话路径);嵌入 Electron
7
8
  * 时设 `ELECTRON_RUN_AS_NODE=1` 运行同一可执行文件。
8
9
  * - 入口:bundle 里是 ama.cjs 同目录的 `ama-sandbox.cjs`(构建脚本定义 `__AMA_SANDBOX_ENTRY__`);
@@ -29,6 +30,8 @@ import { LineSplitter, SANDBOX_MAIN_FLAG, decodeChildLine, encodeLine, } from ".
29
30
  export const MAX_COLLECTED_OUTPUT_CHARS = 16 * 1024 * 1024;
30
31
  /** 外层 abort 后等子进程自行结束的宽限。 */
31
32
  export const ABORT_GRACE_MS = 500;
33
+ /** 子进程 V8 老生代堆上限(MB)缺省值;`codemode.maxHeapMb` 覆盖,0 不设上限。 */
34
+ export const DEFAULT_CODEMODE_HEAP_MB = 256;
32
35
  /** 子进程入口的绝对路径(realpath);找不到 → undefined。 */
33
36
  export function resolveSandboxEntry(moduleUrl = import.meta.url) {
34
37
  const here = dirname(fileURLToPath(moduleUrl));
@@ -44,9 +47,11 @@ export function resolveSandboxEntry(moduleUrl = import.meta.url) {
44
47
  }
45
48
  return undefined;
46
49
  }
47
- /** 子进程参数(不含可执行文件)。 */
48
- export function sandboxArgs(entry, capability = detectSandboxCapability()) {
50
+ /** 子进程参数(不含可执行文件)。V8 选项必须在 `--permission` 与脚本之前。 */
51
+ export function sandboxArgs(entry, capability = detectSandboxCapability(), heapMb = DEFAULT_CODEMODE_HEAP_MB) {
52
+ const heap = Math.floor(heapMb);
49
53
  return [
54
+ ...(heap > 0 ? [`--max-old-space-size=${heap}`] : []),
50
55
  capability.permissionFlag,
51
56
  `--allow-fs-read=${entry}`,
52
57
  "--disallow-code-generation-from-strings",
@@ -75,9 +80,14 @@ export function sandboxEnv(versions = process.versions, platform = process.platf
75
80
  }
76
81
  return env;
77
82
  }
83
+ /** 子进程因堆上限 OOM 退出时给模型的文案(固定英文)。 */
84
+ export function heapLimitError(heapMb) {
85
+ return `Script exceeded the codemode memory limit (${heapMb} MB); process the data in smaller pieces`;
86
+ }
78
87
  export async function runSandbox(request) {
79
88
  const started = Date.now();
80
89
  const entry = request.entry ?? resolveSandboxEntry();
90
+ const heapMb = Math.floor(request.maxHeapMb ?? DEFAULT_CODEMODE_HEAP_MB);
81
91
  const base = {
82
92
  outputs: [],
83
93
  timedOut: false,
@@ -96,7 +106,7 @@ export async function runSandbox(request) {
96
106
  if (request.signal.aborted) {
97
107
  return { ...base, ok: false, error: "Script aborted", aborted: true, elapsedMs: 0 };
98
108
  }
99
- const command = sandboxCommand(request.nodePath ?? process.execPath, sandboxArgs(entry, request.capability), request.os ?? osSandboxStatus());
109
+ const command = sandboxCommand(request.nodePath ?? process.execPath, sandboxArgs(entry, request.capability, heapMb), request.os ?? osSandboxStatus());
100
110
  if (request.requireOsSandbox === true && !command.networkDenied) {
101
111
  return {
102
112
  ...base,
@@ -121,6 +131,7 @@ export async function runSandbox(request) {
121
131
  result.pid = pid;
122
132
  let collected = 0;
123
133
  let stderr = "";
134
+ let outOfMemory = false;
124
135
  let settled = false;
125
136
  let finish;
126
137
  const finished = new Promise((resolve) => (finish = resolve));
@@ -166,7 +177,11 @@ export async function runSandbox(request) {
166
177
  child.stdin.on("error", () => { });
167
178
  child.stderr.setEncoding("utf8");
168
179
  child.stderr.on("data", (chunk) => {
169
- stderr = (stderr + chunk).slice(-4000);
180
+ stderr = stderr + chunk;
181
+ // 原生栈可能把 OOM 那行挤出 4000 字符的尾部:在截断前判定
182
+ if (heapMb > 0 && /heap out of memory/i.test(stderr))
183
+ outOfMemory = true;
184
+ stderr = stderr.slice(-4000);
170
185
  });
171
186
  const splitter = new LineSplitter();
172
187
  const onLine = (line) => {
@@ -247,6 +262,10 @@ export async function runSandbox(request) {
247
262
  onLine(line);
248
263
  if (pid !== undefined && !isWindows)
249
264
  untrackProcessGroup(pid);
265
+ if (outOfMemory) {
266
+ settle({ ok: false, error: heapLimitError(heapMb) });
267
+ return;
268
+ }
250
269
  const detail = stderr.trim().split("\n").slice(-3).join(" | ");
251
270
  settle({
252
271
  ok: false,
@@ -48,6 +48,8 @@ export interface CodemodeToolOptions {
48
48
  /** 子进程入口(测试 / 嵌入方覆盖)。 */
49
49
  entry?: string;
50
50
  nodePath?: string;
51
+ /** 子进程堆上限(MB,`codemode.maxHeapMb`);缺省 256,0 不设上限。 */
52
+ maxHeapMb?: number;
51
53
  }
52
54
  export interface CodemodeDetails {
53
55
  ok: boolean;
@@ -222,6 +222,7 @@ export function createCodemodeTool(options) {
222
222
  requireOsSandbox: strictNeedsOsSandbox(capability),
223
223
  ...(options.entry !== undefined ? { entry: options.entry } : {}),
224
224
  ...(options.nodePath !== undefined ? { nodePath: options.nodePath } : {}),
225
+ ...(options.maxHeapMb !== undefined ? { maxHeapMb: options.maxHeapMb } : {}),
225
226
  callTool: async (name, args, signal) => {
226
227
  if (!callable.has(name))
227
228
  throw new Error(`Tool ${name} is not available in codemode`);
@@ -316,6 +317,9 @@ export function codemodeToolFactory(overrides = {}) {
316
317
  const budget = ctx.config.codemode?.inlineBudget;
317
318
  if (budget !== undefined)
318
319
  options.inlineBudget = budget;
320
+ const heapMb = ctx.config.codemode?.maxHeapMb;
321
+ if (heapMb !== undefined)
322
+ options.maxHeapMb = heapMb;
319
323
  const maxChars = ctx.config.tools?.maxToolResultChars;
320
324
  if (maxChars !== undefined)
321
325
  options.maxResultChars = maxChars;
@@ -7,8 +7,10 @@
7
7
  *
8
8
  * 只用于统计:不写分支、不改请求体,前缀缓存不受影响;压缩阈值也不看它(摘要压不掉这部分)。
9
9
  */
10
- import type { ToolDecl } from "../ai/types.js";
10
+ import type { SystemMessage, ToolDecl } from "../ai/types.js";
11
11
  import type { ContextEstimate } from "./estimate.js";
12
+ /** 将要作为首条 `system` 消息发送的内容(只用于统计,不落盘)。 */
13
+ export declare function prefixSystemMessage(sections: Readonly<Record<string, string>>, tools: readonly ToolDecl[]): SystemMessage;
12
14
  /** 系统提示(已装配的命名节)+ 工具声明的估算。 */
13
15
  export declare function estimatePrefixTokens(sections: Readonly<Record<string, string>>, tools: readonly ToolDecl[]): number;
14
16
  /** 上下文估算的来源(`SessionStats.context.source`)。 */
@@ -8,12 +8,16 @@
8
8
  * 只用于统计:不写分支、不改请求体,前缀缓存不受影响;压缩阈值也不看它(摘要压不掉这部分)。
9
9
  */
10
10
  import { estimateMessageTokens } from "./estimate.js";
11
- /** 系统提示(已装配的命名节)+ 工具声明的估算。 */
12
- export function estimatePrefixTokens(sections, tools) {
11
+ /** 将要作为首条 `system` 消息发送的内容(只用于统计,不落盘)。 */
12
+ export function prefixSystemMessage(sections, tools) {
13
13
  const message = { role: "system", sections: { ...sections }, timestamp: 0 };
14
14
  if (tools.length > 0)
15
15
  message.toolsAdded = [...tools];
16
- return estimateMessageTokens(message);
16
+ return message;
17
+ }
18
+ /** 系统提示(已装配的命名节)+ 工具声明的估算。 */
19
+ export function estimatePrefixTokens(sections, tools) {
20
+ return estimateMessageTokens(prefixSystemMessage(sections, tools));
17
21
  }
18
22
  /**
19
23
  * 给投影估算标来源;投影里还没有系统消息时把前缀基线加进去(`prefix` 懒算,只在需要时调用)。
@@ -11,7 +11,7 @@ import { PERMISSION_MODES_STRICT_FIRST } from "../permissions/types.js";
11
11
  import { msg } from "../i18n/index.js";
12
12
  import { defaultFor, isDynamicDefault, keyDoc } from "./key-docs.js";
13
13
  import { HOOK_TIMEOUT_MAX_MS, THINKING_LEVELS } from "./schema.js";
14
- import { CACHE_RETENTIONS, CHANNEL_NAME_PATTERN, CHECKPOINT_MODES, SANDBOX_ENABLED_MODES, SANDBOX_NETWORK_MODES, CODEMODE_MODES, IMAGE_RESIZE_MODES, PLAN_BASH_MODES_STRICT_FIRST, PLAN_UNATTENDED_MODES, STATUS_LINE_MODES, TOOLS_PRESET_INPUTS, LANGUAGE_SETTINGS, AGENT_BAR_MODES, CHATGPT_FLAVORS, MEMORY_SCOPES, MEMORY_SUBAGENT_MODES, } from "./types.js";
14
+ import { CACHE_RETENTIONS, CHANNEL_NAME_PATTERN, CHECKPOINT_MODES, SANDBOX_ENABLED_MODES, SANDBOX_NETWORK_MODES, CODEMODE_MODES, IMAGE_RESIZE_MODES, PLAN_BASH_MODES_STRICT_FIRST, PLAN_UNATTENDED_MODES, STATUS_LINE_MODES, TOOLS_PRESET_INPUTS, LANGUAGE_SETTINGS, AGENT_BAR_MODES, CHATGPT_FLAVORS, MEMORY_SCOPES, MEMORY_SUBAGENT_MODES, MIN_CONTEXT_BUDGET, } from "./types.js";
15
15
  import { AGENT_ID_PATTERN } from "./schema-w5.js";
16
16
  import { AGENTS_RESERVED_KEYS, SUBAGENT_BACKGROUND_MODES } from "./types-w5.js";
17
17
  export const CONFIG_SCHEMA_FILE = "config.schema.json";
@@ -89,6 +89,10 @@ function providerSchema() {
89
89
  description: m.modelsDev,
90
90
  anyOf: [{ type: "string", pattern: "^[^/]+/.+$" }, { const: false }],
91
91
  },
92
+ catalog: {
93
+ description: m.modelCatalog,
94
+ anyOf: [{ type: "string", pattern: "^[^/]+/.+$" }, { const: false }],
95
+ },
92
96
  headers: stringRecord,
93
97
  compat,
94
98
  promptCache: object({ short: num(), long: num(), minTokens: num() }),
@@ -156,6 +160,7 @@ function w5Sections() {
156
160
  defaultModel: str(),
157
161
  background: oneOf(SUBAGENT_BACKGROUND_MODES),
158
162
  autoBackgroundAfterMs: num(0),
163
+ retainSessions: num(0),
159
164
  }),
160
165
  models: object({ aliases: object({ fast: str(), strong: str() }), enabled: strings }),
161
166
  fallbackModel: str(),
@@ -244,6 +249,7 @@ function buildBaseSchema() {
244
249
  enabled: bool(),
245
250
  reserveTokens: num(0),
246
251
  keepRecentTokens: num(0),
252
+ contextBudget: num(MIN_CONTEXT_BUDGET),
247
253
  prune: object({
248
254
  keepResults: num(0),
249
255
  clearAtLeast: { anyOf: [{ const: "auto" }, { type: "number", minimum: 0 }] },
@@ -267,6 +273,7 @@ function buildBaseSchema() {
267
273
  mode: oneOf(CODEMODE_MODES),
268
274
  inlineBudget: num(0),
269
275
  requireStrict: bool(),
276
+ maxHeapMb: num(0),
270
277
  }),
271
278
  hooks: object({ timeoutMs: num(1, HOOK_TIMEOUT_MAX_MS) }),
272
279
  ui: object({
@@ -295,7 +302,7 @@ function buildBaseSchema() {
295
302
  missNotices: bool(),
296
303
  warmSubagents: bool(),
297
304
  }),
298
- request: object({ idleTimeoutMs: num(0) }),
305
+ request: object({ idleTimeoutMs: num(0), streamIdleTimeoutMs: num(0) }),
299
306
  checkpoints: object({
300
307
  mode: oneOf(CHECKPOINT_MODES),
301
308
  maxFileBytes: num(0),
@@ -14,15 +14,18 @@ import { DEFAULT_CODEX_CLIENT_VERSION } from "../auth/chatgpt/presets.js";
14
14
  import { messagesFor, msg } from "../i18n/index.js";
15
15
  import { DEFAULT_INLINE_BUDGET } from "../codemode/declarations.js";
16
16
  import { DEFAULT_CONFIG, mergeConfig } from "./merge.js";
17
- import { DEFAULT_CACHE_CONFIG, DEFAULT_CHECKPOINTS_CONFIG } from "./types.js";
17
+ import { DEFAULT_CACHE_CONFIG, DEFAULT_CHECKPOINTS_CONFIG, DEFAULT_STREAM_IDLE_TIMEOUT_MS, } from "./types.js";
18
18
  /** 代码里生效、但 DEFAULT_CONFIG 不写的缺省(写进去会改变合并结果或 `config.codemode` 的有无)。 */
19
19
  const IMPLICIT_DEFAULTS = {
20
20
  providers: {},
21
21
  permission: { builtinDeny: true, autoSafeCommands: [] },
22
22
  tools: { default: [] },
23
- codemode: { inlineBudget: DEFAULT_INLINE_BUDGET, requireStrict: false },
23
+ codemode: { inlineBudget: DEFAULT_INLINE_BUDGET, requireStrict: false, maxHeapMb: 256 },
24
24
  cache: { ...DEFAULT_CACHE_CONFIG },
25
- request: { idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS },
25
+ request: {
26
+ idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
27
+ streamIdleTimeoutMs: DEFAULT_STREAM_IDLE_TIMEOUT_MS,
28
+ },
26
29
  ui: {
27
30
  compact: false,
28
31
  logo: "auto",
@@ -56,7 +59,13 @@ const IMPLICIT_DEFAULTS = {
56
59
  images: { resize: "auto" },
57
60
  plan: { bash: "readonly", unattended: "stop" },
58
61
  agents: { maxConcurrent: 3, dirs: [] },
59
- subagents: { maxConcurrent: 4, maxPending: 16, background: "auto", autoBackgroundAfterMs: 0 },
62
+ subagents: {
63
+ maxConcurrent: 4,
64
+ maxPending: 16,
65
+ background: "auto",
66
+ autoBackgroundAfterMs: 0,
67
+ retainSessions: 4,
68
+ },
60
69
  reminders: { todo: true, fileChanges: true, contextPressure: true, budget: true },
61
70
  todo: { reminder: 10 },
62
71
  };
@@ -83,12 +83,20 @@ export function validateConfigW5(c, config) {
83
83
  c.oneOf(s, "thinkingLevel", p, THINKING_LEVELS);
84
84
  });
85
85
  checkAgents(c, config);
86
- checkSection(c, config, "subagents", ["maxConcurrent", "maxPending", "defaultModel", "background", "autoBackgroundAfterMs"], (s, p) => {
86
+ checkSection(c, config, "subagents", [
87
+ "maxConcurrent",
88
+ "maxPending",
89
+ "defaultModel",
90
+ "background",
91
+ "autoBackgroundAfterMs",
92
+ "retainSessions",
93
+ ], (s, p) => {
87
94
  c.number(s, "maxConcurrent", p, 1, 64);
88
95
  c.number(s, "maxPending", p, 0, 1024);
89
96
  c.string(s, "defaultModel", p);
90
97
  c.oneOf(s, "background", p, SUBAGENT_BACKGROUND_MODES);
91
98
  c.number(s, "autoBackgroundAfterMs", p, 0);
99
+ c.number(s, "retainSessions", p, 0);
92
100
  });
93
101
  checkSection(c, config, "models", ["aliases", "enabled"], (s, p) => {
94
102
  c.stringArray(s, "enabled", p);
@@ -5,7 +5,7 @@
5
5
  * 校验结果是诊断列表:`severity: "error"` 拒绝该文件(启动退出码 3),`"warning"` 只提示
6
6
  * (未知字段等)。`path` 是 JSON 指针风格的字段路径(`permission.mode`、`hooks.PreToolUse[0]`)。
7
7
  */
8
- import type { AmaConfig, AuthFile, ProfileFile, TrustFile } from "./types.js";
8
+ import { type AmaConfig, type AuthFile, type ProfileFile, type TrustFile } from "./types.js";
9
9
  import { type Diagnostic } from "./checker.js";
10
10
  import type { HookConfig } from "../hooks/types.js";
11
11
  export type { AmaConfig, AuthFile, ProfileFile, TrustFile, TrustEntry, ProviderConfig, PermissionConfig, CompactionConfig, RetryConfig, ToolsConfig, ToolsPreset, ToolsPresetInput, CodemodeConfig, CodemodeMode, HooksSettings, UiConfig, SkillsConfig, CacheConfig, CheckpointsConfig, ModelConfig, ModelOverride, ChannelConfig, } from "./types.js";
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { isAbsolute } from "node:path";
9
9
  import { msg } from "../i18n/index.js";
10
+ import { MIN_CONTEXT_BUDGET, } from "./types.js";
10
11
  import { Checker, THINKING_LEVELS, checkSection, join, } from "./checker.js";
11
12
  import { W5_COMPACTION_KEYS, W5_CONFIG_KEYS, W5_UI_KEYS, checkCompactionW5, checkUiW5, validateConfigW5, } from "./schema-w5.js";
12
13
  import { W6_CONFIG_KEYS, W6_UI_KEYS, checkOAuthEntry, checkProfileMemory, checkUiW6, validateConfigW6, } from "./schema-w6.js";
@@ -93,6 +94,12 @@ function checkModels(c, value, path, channels) {
93
94
  (typeof modelsDev !== "string" || !/^[^/]+\/.+$/.test(modelsDev))) {
94
95
  c.error(join(p, "modelsDev"), msg().config.schema.modelsDev);
95
96
  }
97
+ const catalog = item["catalog"];
98
+ if (catalog !== undefined &&
99
+ catalog !== false &&
100
+ (typeof catalog !== "string" || !/^[^/]+\/.+$/.test(catalog))) {
101
+ c.error(join(p, "catalog"), msg().config.schema.catalog);
102
+ }
96
103
  c.stringArray(item, "channels", p);
97
104
  const used = item["channels"];
98
105
  if (Array.isArray(used)) {
@@ -221,11 +228,18 @@ export function validateConfig(value) {
221
228
  c.error(join(p, "builtinDeny"), msg().config.schema.builtinDeny);
222
229
  }
223
230
  });
224
- const compactionKeys = ["enabled", "reserveTokens", "keepRecentTokens", ...W5_COMPACTION_KEYS];
231
+ const compactionKeys = [
232
+ "enabled",
233
+ "reserveTokens",
234
+ "keepRecentTokens",
235
+ "contextBudget",
236
+ ...W5_COMPACTION_KEYS,
237
+ ];
225
238
  checkSection(c, value, "compaction", compactionKeys, (s, p) => {
226
239
  c.boolean(s, "enabled", p);
227
240
  c.number(s, "reserveTokens", p);
228
241
  c.number(s, "keepRecentTokens", p);
242
+ c.number(s, "contextBudget", p, MIN_CONTEXT_BUDGET);
229
243
  checkCompactionW5(c, s, p);
230
244
  });
231
245
  checkSection(c, value, "retry", ["enabled", "maxRetries", "baseDelayMs", "maxDelayMs"], (s, p) => {
@@ -241,10 +255,11 @@ export function validateConfig(value) {
241
255
  c.number(s, "bashTimeoutMs", p, 1);
242
256
  c.stringArray(s, "disabled", p);
243
257
  });
244
- checkSection(c, value, "codemode", ["mode", "inlineBudget", "requireStrict"], (s, p) => {
258
+ checkSection(c, value, "codemode", ["mode", "inlineBudget", "requireStrict", "maxHeapMb"], (s, p) => {
245
259
  c.oneOf(s, "mode", p, CODEMODE_MODES);
246
260
  c.number(s, "inlineBudget", p, 0);
247
261
  c.boolean(s, "requireStrict", p);
262
+ c.number(s, "maxHeapMb", p, 0);
248
263
  });
249
264
  checkSection(c, value, "hooks", ["timeoutMs"], (s, p) => {
250
265
  c.number(s, "timeoutMs", p, 1, HOOK_TIMEOUT_MAX_MS);
@@ -281,8 +296,9 @@ export function validateConfig(value) {
281
296
  checkUiW6(c, s, p);
282
297
  });
283
298
  checkSection(c, value, "skills", ["dirs"], (s, p) => c.stringArray(s, "dirs", p));
284
- checkSection(c, value, "request", ["idleTimeoutMs"], (s, p) => {
299
+ checkSection(c, value, "request", ["idleTimeoutMs", "streamIdleTimeoutMs"], (s, p) => {
285
300
  c.number(s, "idleTimeoutMs", p, 0);
301
+ c.number(s, "streamIdleTimeoutMs", p, 0);
286
302
  });
287
303
  checkSection(c, value, "checkpoints", ["mode", "maxFileBytes", "keep"], (s, p) => {
288
304
  c.oneOf(s, "mode", p, CHECKPOINT_MODES);
@@ -57,6 +57,7 @@ const ROWS = [
57
57
  row("codemode.mode", "tools", "restart", "tighten", { prefix: true }),
58
58
  row("codemode.inlineBudget", "tools", "restart", "deny", { prefix: true }),
59
59
  row("codemode.requireStrict", "tools", "restart", "deny"),
60
+ row("codemode.maxHeapMb", "tools", "restart", "deny"),
60
61
  row("tools.maxToolResultChars", "tools", "nextSession", "deny"),
61
62
  row("tools.bashTimeoutMs", "tools", "restart", "deny"),
62
63
  row("images.resize", "tools", "restart", "deny"),
@@ -64,6 +65,7 @@ const ROWS = [
64
65
  row("compaction.enabled", "context", "now", "any"),
65
66
  row("compaction.reserveTokens", "context", "nextSession", "any"),
66
67
  row("compaction.keepRecentTokens", "context", "nextSession", "any"),
68
+ row("compaction.contextBudget", "context", "nextSession", "any", { kind: "optionalNumber" }),
67
69
  row("compaction.prune.keepResults", "context", "nextSession", "deny"),
68
70
  row("compaction.prune.clearAtLeast", "context", "nextSession", "deny"),
69
71
  row("cache.warming", "context", "now", "deny", { envOverride: "AMA_CACHE_WARMING" }),
@@ -74,6 +76,9 @@ const ROWS = [
74
76
  row("request.idleTimeoutMs", "context", "nextSession", "deny", {
75
77
  envOverride: "AMA_IDLE_TIMEOUT_MS",
76
78
  }),
79
+ row("request.streamIdleTimeoutMs", "context", "nextSession", "deny", {
80
+ envOverride: "AMA_STREAM_IDLE_TIMEOUT_MS",
81
+ }),
77
82
  row("retry.enabled", "context", "now", "deny"),
78
83
  row("retry.maxRetries", "context", "nextSession", "deny"),
79
84
  row("retry.baseDelayMs", "context", "nextSession", "deny"),
@@ -95,6 +100,7 @@ const ROWS = [
95
100
  row("agents.sessionBudgetUsd", "agents", "nextSession", "deny", { kind: "optionalNumber" }),
96
101
  row("subagents.maxConcurrent", "agents", "nextSession", "deny"),
97
102
  row("subagents.maxPending", "agents", "nextSession", "deny"),
103
+ row("subagents.retainSessions", "agents", "nextSession", "deny"),
98
104
  // [W7-B2] 改 task 工具描述(缺省后台 / 前台两种文案),故 prefix
99
105
  row("subagents.background", "agents", "restart", "any", { prefix: true }),
100
106
  row("subagents.autoBackgroundAfterMs", "agents", "restart", "any"),
@@ -77,6 +77,8 @@ export interface SubagentsConfig {
77
77
  background?: SubagentBackgroundMode;
78
78
  /** [W7-B2] 前台任务运行超过该毫秒数自动转后台;缺省 0 关闭。用户 / 项目级都认。 */
79
79
  autoBackgroundAfterMs?: number;
80
+ /** [M-F] 已结束子会话保留在内存的句柄数(LRU),缺省 4;超出的续聊时从会话文件重开。只认用户级。 */
81
+ retainSessions?: number;
80
82
  }
81
83
  /** `models.enabled` 的一项:`provider/model[@channel]` 或 `provider/*`。 */
82
84
  export declare const MODELS_ENABLED_REF: RegExp;
@@ -31,6 +31,11 @@ export type ModelConfig = Partial<Omit<Model, "id" | "provider" | "channel" | "c
31
31
  channels?: string[];
32
32
  /** models.dev 条目 `provider/model`(显式匹配);false 关闭 models.dev 补全。 */
33
33
  modelsDev?: string | false;
34
+ /**
35
+ * [ME-C0] 从内置目录继承模型固有属性:缺省按 id 别名自动匹配;`"provider/id"` 显式指定;false 关闭
36
+ * (docs/model-efficiency-plan.md D10,实现归 ME-D)。
37
+ */
38
+ catalog?: string | false;
34
39
  };
35
40
  /** 只改元数据的覆盖项(含模型级 `api` 与 `channels`)。 */
36
41
  export type ModelOverride = ModelConfig;
@@ -83,6 +88,8 @@ export interface CompactionConfig {
83
88
  enabled?: boolean;
84
89
  reserveTokens?: number;
85
90
  keepRecentTokens?: number;
91
+ /** [ME-C0] 软窗口(≥ 32768):档一 / 档二 / context_pressure 的窗口取 min(模型窗口, 它);缺省 = 模型窗口。 */
92
+ contextBudget?: number;
86
93
  /** [W5-C0] 档一裁剪参数(W5-H1);只认用户级。 */
87
94
  prune?: PruneConfig;
88
95
  /** [W5-C0] 不被档一裁剪的工具名(W5-H1);只认用户级。 */
@@ -129,6 +136,8 @@ export interface CodemodeConfig {
129
136
  inlineBudget?: number;
130
137
  /** true:运行时 Node 的权限模型不隔离网络(Node 22 / 24)时直接禁用 codemode。 */
131
138
  requireStrict?: boolean;
139
+ /** [M-F] 脚本子进程的 V8 堆上限(MB,`--max-old-space-size`),缺省 256,0 不加;只认用户级。 */
140
+ maxHeapMb?: number;
132
141
  }
133
142
  export interface HooksSettings {
134
143
  timeoutMs?: number;
@@ -183,9 +192,18 @@ export declare const DEFAULT_CACHE_CONFIG: Readonly<Required<CacheConfig>>;
183
192
  * 模型请求(W4-C)。只认用户级 / profile;环境变量 `AMA_IDLE_TIMEOUT_MS` 覆盖 `idleTimeoutMs`。
184
193
  */
185
194
  export interface RequestConfig {
186
- /** 等响应头与流中两块数据之间的最长间隔(毫秒),收到任何字节即重新计时;缺省 300 000,0 关闭。 */
195
+ /** 等响应头的最长时间(毫秒);缺省 300 000,0 关闭。流中的间隔见 `streamIdleTimeoutMs`。 */
187
196
  idleTimeoutMs?: number;
197
+ /**
198
+ * [ME-C0] 流中两块数据之间的最长间隔(毫秒);缺省 180 000,0 关闭;环境变量
199
+ * `AMA_STREAM_IDLE_TIMEOUT_MS` 覆盖(cli/compose-request.ts)。
200
+ */
201
+ streamIdleTimeoutMs?: number;
188
202
  }
203
+ /** [ME-C0] `compaction.contextBudget` 的下限。 */
204
+ export declare const MIN_CONTEXT_BUDGET = 32768;
205
+ /** [ME-C0] `request.streamIdleTimeoutMs` 的缺省(毫秒)。 */
206
+ export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 180000;
189
207
  export declare const CHECKPOINT_MODES: readonly CheckpointMode[];
190
208
  /**
191
209
  * 检查点(docs/rewind-plan.md §5)。环境变量 `AMA_CHECKPOINTS` 覆盖 `mode`;项目级只接受
@@ -47,6 +47,10 @@ export const DEFAULT_CACHE_CONFIG = Object.freeze({
47
47
  missNotices: true,
48
48
  warmSubagents: false,
49
49
  });
50
+ /** [ME-C0] `compaction.contextBudget` 的下限。 */
51
+ export const MIN_CONTEXT_BUDGET = 32_768;
52
+ /** [ME-C0] `request.streamIdleTimeoutMs` 的缺省(毫秒)。 */
53
+ export const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 180_000;
50
54
  export const CHECKPOINT_MODES = ["tools", "shadow-git", "off"];
51
55
  export const DEFAULT_CHECKPOINTS_CONFIG = Object.freeze({
52
56
  mode: "tools",