@armadra/agent 0.7.3 → 0.7.5

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 (179) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/CHANGELOG.zh-CN.md +48 -0
  3. package/dist/agent/retry.d.ts +14 -5
  4. package/dist/agent/retry.js +30 -8
  5. package/dist/agent/session-cache-key.d.ts +8 -0
  6. package/dist/agent/session-cache-key.js +46 -0
  7. package/dist/agent/session-cache.d.ts +2 -3
  8. package/dist/agent/session-cache.js +19 -37
  9. package/dist/agent/session-classifier.d.ts +3 -2
  10. package/dist/agent/session-classifier.js +36 -10
  11. package/dist/agent/session-compaction.d.ts +24 -5
  12. package/dist/agent/session-compaction.js +54 -20
  13. package/dist/agent/session-core.d.ts +2 -0
  14. package/dist/agent/session-run.d.ts +12 -0
  15. package/dist/agent/session-run.js +29 -10
  16. package/dist/agent/session-subagent.d.ts +8 -1
  17. package/dist/agent/session-subagent.js +37 -14
  18. package/dist/agent/session-sync.d.ts +8 -2
  19. package/dist/agent/session-sync.js +7 -10
  20. package/dist/agent/session-tools.js +3 -2
  21. package/dist/agent/subagent-fork.d.ts +67 -0
  22. package/dist/agent/subagent-fork.js +87 -0
  23. package/dist/agent/subagent-registry.d.ts +2 -2
  24. package/dist/agent/subagent-registry.js +2 -2
  25. package/dist/agent/tool-availability.d.ts +8 -0
  26. package/dist/agent/tool-availability.js +8 -0
  27. package/dist/agent/tool-runner.js +13 -4
  28. package/dist/agent/worktree.d.ts +8 -0
  29. package/dist/agent/worktree.js +51 -4
  30. package/dist/agents/parse.js +6 -1
  31. package/dist/agents/task-record.js +3 -0
  32. package/dist/agents/types.d.ts +2 -0
  33. package/dist/ai/apis/anthropic-messages.js +10 -5
  34. package/dist/ai/apis/anthropic-request.d.ts +2 -1
  35. package/dist/ai/apis/anthropic-request.js +15 -4
  36. package/dist/ai/apis/cache-params.js +2 -1
  37. package/dist/ai/apis/google-generative-ai.js +4 -2
  38. package/dist/ai/apis/google-request.js +2 -1
  39. package/dist/ai/apis/max-tokens.d.ts +42 -0
  40. package/dist/ai/apis/max-tokens.js +121 -0
  41. package/dist/ai/apis/openai-completions.js +10 -5
  42. package/dist/ai/apis/openai-request.js +4 -2
  43. package/dist/ai/apis/openai-responses-request.js +3 -2
  44. package/dist/ai/apis/openai-responses.js +9 -5
  45. package/dist/ai/apis/shared.d.ts +3 -1
  46. package/dist/ai/apis/shared.js +21 -3
  47. package/dist/ai/cache/fingerprint.d.ts +4 -1
  48. package/dist/ai/cache/fingerprint.js +18 -1
  49. package/dist/ai/cache/miss.d.ts +2 -1
  50. package/dist/ai/cache/miss.js +9 -3
  51. package/dist/ai/cache/reporting.js +5 -1
  52. package/dist/ai/cache/types.d.ts +10 -2
  53. package/dist/ai/cache/warmer.d.ts +6 -1
  54. package/dist/ai/cache/warmer.js +7 -0
  55. package/dist/ai/context.d.ts +12 -4
  56. package/dist/ai/context.js +43 -24
  57. package/dist/ai/fake/fake-provider.d.ts +8 -1
  58. package/dist/ai/fake/fake-provider.js +19 -7
  59. package/dist/ai/fake/fake-script.d.ts +2 -0
  60. package/dist/ai/http.d.ts +17 -9
  61. package/dist/ai/http.js +33 -13
  62. package/dist/ai/image-intern.d.ts +29 -0
  63. package/dist/ai/image-intern.js +58 -0
  64. package/dist/ai/json-body.d.ts +63 -0
  65. package/dist/ai/json-body.js +286 -0
  66. package/dist/ai/overflow.js +5 -0
  67. package/dist/ai/providers/catalog-data.js +13 -13
  68. package/dist/ai/providers/catalog.d.ts +33 -0
  69. package/dist/ai/providers/catalog.js +105 -6
  70. package/dist/ai/providers/enrich.d.ts +13 -3
  71. package/dist/ai/providers/enrich.js +87 -31
  72. package/dist/ai/sse.d.ts +3 -2
  73. package/dist/ai/sse.js +3 -2
  74. package/dist/ai/types.d.ts +9 -2
  75. package/dist/bundle/ama.cjs +3637 -1973
  76. package/dist/cli/bootstrap.js +13 -5
  77. package/dist/cli/compose-agents.js +3 -0
  78. package/dist/cli/compose-extensions.js +2 -0
  79. package/dist/cli/compose-request.d.ts +20 -0
  80. package/dist/cli/compose-request.js +37 -0
  81. package/dist/cli/compose-session.js +2 -2
  82. package/dist/cli/deps.d.ts +1 -1
  83. package/dist/cli/subcommands/model-meta.d.ts +2 -1
  84. package/dist/cli/subcommands/model-meta.js +3 -2
  85. package/dist/codemode/host-side.d.ts +11 -4
  86. package/dist/codemode/host-side.js +25 -6
  87. package/dist/codemode/tool.d.ts +2 -0
  88. package/dist/codemode/tool.js +4 -0
  89. package/dist/config/json-schema.js +9 -2
  90. package/dist/config/key-docs.js +13 -4
  91. package/dist/config/schema-w5.js +9 -1
  92. package/dist/config/schema.d.ts +1 -1
  93. package/dist/config/schema.js +19 -3
  94. package/dist/config/settings-registry.js +6 -0
  95. package/dist/config/types-w5.d.ts +2 -0
  96. package/dist/config/types.d.ts +19 -1
  97. package/dist/config/types.js +4 -0
  98. package/dist/i18n/catalog.d.ts +13 -2
  99. package/dist/i18n/messages/cli.d.ts +2 -0
  100. package/dist/i18n/messages/cli.js +2 -0
  101. package/dist/i18n/messages/config-keys.d.ts +10 -0
  102. package/dist/i18n/messages/config-keys.js +12 -2
  103. package/dist/i18n/messages/config.d.ts +14 -0
  104. package/dist/i18n/messages/config.js +4 -0
  105. package/dist/i18n/messages/errors.d.ts +3 -2
  106. package/dist/i18n/messages/errors.js +15 -2
  107. package/dist/i18n/messages/report.d.ts +2 -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 +30 -18
  114. package/dist/modes/acp/acp-sessions.d.ts +22 -0
  115. package/dist/modes/acp/acp-sessions.js +68 -0
  116. package/dist/modes/context-report.js +3 -2
  117. package/dist/modes/print/json-event.d.ts +12 -1
  118. package/dist/modes/print/json-event.js +48 -1
  119. package/dist/modes/rpc/rpc-mode.d.ts +1 -0
  120. package/dist/modes/rpc/rpc-mode.js +9 -2
  121. package/dist/modes/session-report.d.ts +1 -1
  122. package/dist/modes/session-report.js +12 -6
  123. package/dist/rpc.d.ts +5 -2
  124. package/dist/session/line-reader.d.ts +28 -0
  125. package/dist/session/line-reader.js +82 -0
  126. package/dist/session/list.d.ts +14 -0
  127. package/dist/session/list.js +150 -0
  128. package/dist/session/manager.d.ts +23 -3
  129. package/dist/session/manager.js +91 -62
  130. package/dist/session/migrate.d.ts +2 -1
  131. package/dist/session/migrate.js +2 -1
  132. package/dist/session/offload.d.ts +51 -0
  133. package/dist/session/offload.js +195 -0
  134. package/dist/session/projection.d.ts +16 -3
  135. package/dist/session/projection.js +96 -29
  136. package/dist/session/scan.d.ts +3 -3
  137. package/dist/session/scan.js +9 -18
  138. package/dist/session/store.d.ts +15 -4
  139. package/dist/session/store.js +54 -38
  140. package/dist/session/types.d.ts +7 -2
  141. package/dist/tools/bash.d.ts +1 -1
  142. package/dist/tools/bash.js +5 -4
  143. package/dist/tools/glob.d.ts +2 -2
  144. package/dist/tools/glob.js +2 -2
  145. package/dist/tools/grep.js +4 -6
  146. package/dist/tools/image-file.js +2 -1
  147. package/dist/tools/read-lines.d.ts +71 -0
  148. package/dist/tools/read-lines.js +224 -0
  149. package/dist/tools/read.d.ts +6 -2
  150. package/dist/tools/read.js +47 -27
  151. package/dist/tools/task.d.ts +9 -0
  152. package/dist/tools/task.js +21 -8
  153. package/dist/tools/truncate.d.ts +7 -0
  154. package/dist/tools/truncate.js +11 -0
  155. package/dist/tools/types.d.ts +13 -0
  156. package/dist/trace/build-index.js +13 -1
  157. package/dist/tui/ansi.d.ts +2 -0
  158. package/dist/tui/ansi.js +7 -3
  159. package/dist/tui/components/editor-buffer.js +2 -2
  160. package/dist/tui/components/editor.js +5 -6
  161. package/dist/tui/keys.js +2 -2
  162. package/dist/tui/stdin-buffer.js +3 -3
  163. package/docs/acp.md +8 -1
  164. package/docs/agents.md +42 -6
  165. package/docs/codemode.md +4 -2
  166. package/docs/en/acp.md +3 -2
  167. package/docs/en/host-api.md +1 -1
  168. package/docs/en/permissions.md +2 -2
  169. package/docs/en/providers.md +54 -20
  170. package/docs/en/rpc.md +23 -7
  171. package/docs/en/sessions.md +4 -2
  172. package/docs/en/tui.md +2 -2
  173. package/docs/permissions.md +4 -1
  174. package/docs/providers.md +85 -22
  175. package/docs/rpc.md +22 -6
  176. package/docs/session-format.md +13 -2
  177. package/docs/sessions.md +3 -1
  178. package/docs/tui.md +1 -1
  179. package/package.json +2 -2
@@ -0,0 +1,286 @@
1
+ /**
2
+ * 请求体序列化(docs/memory-plan.md D3、§2.3)。
3
+ *
4
+ * 带图请求体里 base64 占绝大部分字节;`JSON.stringify(body)` 会先拼出整份中间字符串,再由 fetch
5
+ * 编码成 UTF-8。这里先廉价扫一遍:没有 ≥ {@link LARGE_STRING_BYTES} 的字符串就照旧走原生;有就只沿
6
+ * 「普通对象 / 数组」结构拼片段——小子树仍交给原生 `JSON.stringify`,大字符串经检查无需转义时原样
7
+ * 作为一个片段。片段两种用法:`serializeJsonBody` 一次分配结果 Buffer 依次写入;`jsonFetchBody`
8
+ * (`postJson` 用)逐片段编码成流、声明总字节数,不在内存里留整份请求体。
9
+ *
10
+ * 结果与 `Buffer.from(JSON.stringify(body), "utf8")` 逐字节相同:自写部分只有结构(`{`、`,`、
11
+ * 键、`[`)与已验证无需转义的大字符串(需要转义的大字符串整个交给原生);`toJSON`、`undefined` /
12
+ * 函数 / symbol 的跳过与 `null`、数字格式、转义全部由原生处理(带 `toJSON` 的值经单键容器序列化,
13
+ * 保证 `toJSON(key)` 收到与原生相同的键)。
14
+ *
15
+ * 图片由转换器包成 {@link LargeString}(`prefix + text`):片段路径直接写转录里的原字符串,不经模板串
16
+ * 拼接后的扁平化;无需转义的结论与字节数按 ImageBlock 缓存,同一张图每进程只扫一次。流式发送时大片段
17
+ * 按 {@link STREAM_CHUNK_CHARS} 切块编码。
18
+ */
19
+ /** 达到这个长度(UTF-16 码元)的字符串才走片段路径。 */
20
+ export const LARGE_STRING_BYTES = 64 * 1024;
21
+ /** 检查是否需要转义时每次交给原生的长度:结果是新生代里的小字符串,随即可回收。 */
22
+ const CHECK_CHUNK = 16 * 1024;
23
+ /**
24
+ * 大字符串无需转义、可原样写入:逐段让原生 `JSON.stringify` 判断(长度恰好多出两个引号即没有
25
+ * 任何转义)。引号、反斜杠、控制字符、孤立代理项都会让长度变化;成对代理项若恰被切在段界上
26
+ * 也会被判为需要转义——只会多走原生,不会错。实测比同义的正则快一倍多(原生的转义扫描是向量化的)。
27
+ * 每个大字符串每次序列化只查这一次。
28
+ */
29
+ function isPlain(value) {
30
+ plainScans.count++;
31
+ for (let i = 0; i < value.length; i += CHECK_CHUNK) {
32
+ const chunk = value.slice(i, i + CHECK_CHUNK);
33
+ if (JSON.stringify(chunk).length !== chunk.length + 2)
34
+ return false;
35
+ }
36
+ return true;
37
+ }
38
+ /** 短字符串无需转义(`LargeString.prefix` 用)。 */
39
+ const isShortPlain = (value) => JSON.stringify(value).length === value.length + 2;
40
+ /** `isPlain` 的调用次数(测试用:同一块图片第二次序列化不应再扫)。 */
41
+ export const plainScans = { count: 0 };
42
+ /**
43
+ * 请求体里的大字符串叶子 `prefix + text`(data URL 或裸 base64),用来代替模板串拼接:拼接得到的
44
+ * cons string 在每次序列化时都会被扁平化成一份新的整图字符串。片段路径直接写 `prefix` 与 `text`
45
+ * (`text` 是转录里的原字符串,不拷贝);`text` 无需转义的结论按 `key`(转录里身份稳定的 ImageBlock)
46
+ * 缓存,同一块每进程只扫一次。约定:同一 `key` 的 `text` 不变。其它遍历者看到的是一个带 `toJSON`
47
+ * 的对象,原生 `JSON.stringify` 得到相同字符串。由 {@link largeString} 构造。
48
+ */
49
+ export class LargeString {
50
+ prefix;
51
+ key;
52
+ text;
53
+ constructor(prefix, key, text) {
54
+ this.prefix = prefix;
55
+ this.key = key;
56
+ this.text = text;
57
+ }
58
+ toJSON() {
59
+ return this.prefix + this.text;
60
+ }
61
+ }
62
+ /**
63
+ * `prefix + text` 作为请求体的值:`text` 够大且 `prefix` 自身无需转义时给 {@link LargeString},
64
+ * 否则就是拼好的字符串(小图与以前完全相同)。
65
+ */
66
+ export function largeString(prefix, key, text) {
67
+ if (text.length < LARGE_STRING_BYTES || !isShortPlain(prefix))
68
+ return prefix + text;
69
+ return new LargeString(prefix, key, text);
70
+ }
71
+ /** 已确认无需转义的 `LargeString.text`:键是 `key`,值是当时的 UTF-8 字节数(同时省掉再数一遍)。 */
72
+ const plainBytes = new WeakMap();
73
+ /** `LargeString.text` 可原样写入时返回其 UTF-8 字节数,否则 undefined。 */
74
+ function plainLargeBytes(value) {
75
+ if (!isShortPlain(value.prefix))
76
+ return undefined;
77
+ const known = plainBytes.get(value.key);
78
+ if (known !== undefined && known.length === value.text.length)
79
+ return known.bytes;
80
+ if (!isPlain(value.text))
81
+ return undefined;
82
+ const bytes = Buffer.byteLength(value.text, "utf8");
83
+ plainBytes.set(value.key, { length: value.text.length, bytes });
84
+ return bytes;
85
+ }
86
+ /** 结构遍历的深度上限:更深(或有环)时整棵子树交给原生,环由原生照常抛 TypeError。 */
87
+ const MAX_DEPTH = 64;
88
+ const isLargeString = (value) => typeof value === "string" && value.length >= LARGE_STRING_BYTES;
89
+ /** 只遍历原型为 Object.prototype / null 的普通对象和数组,且自身与原型链上都没有 `toJSON`。 */
90
+ function isContainer(value) {
91
+ if (typeof value !== "object" || value === null)
92
+ return false;
93
+ if (typeof value.toJSON === "function")
94
+ return false;
95
+ if (Array.isArray(value))
96
+ return true;
97
+ const proto = Object.getPrototypeOf(value);
98
+ return proto === Object.prototype || proto === null;
99
+ }
100
+ /** 收集含大字符串的容器(片段路径只深入这些容器);返回 `value` 本身是否含大字符串。 */
101
+ function collectLarge(value, marked, depth) {
102
+ if (isLargeString(value))
103
+ return true;
104
+ if (value instanceof LargeString)
105
+ return value.text.length >= LARGE_STRING_BYTES;
106
+ if (depth >= MAX_DEPTH || !isContainer(value))
107
+ return false;
108
+ let found = false;
109
+ if (Array.isArray(value)) {
110
+ for (let i = 0; i < value.length; i++) {
111
+ if (collectLarge(value[i], marked, depth + 1))
112
+ found = true;
113
+ }
114
+ }
115
+ else {
116
+ for (const key of Object.keys(value)) {
117
+ if (collectLarge(value[key], marked, depth + 1))
118
+ found = true;
119
+ }
120
+ }
121
+ if (found)
122
+ marked.add(value);
123
+ return found;
124
+ }
125
+ /** 原生序列化 `holder[key] = value` 这一项的值;被原生跳过(undefined / 函数 / symbol)时 undefined。 */
126
+ function nativeValue(key, value) {
127
+ if (typeof value !== "object" && typeof value !== "function" && typeof value !== "bigint") {
128
+ return JSON.stringify(value);
129
+ }
130
+ // 对象、函数与 bigint 可能有 toJSON(key):放进单键容器,让原生以同一个键调用
131
+ const wrapped = JSON.stringify({ [key]: value });
132
+ if (wrapped === "{}")
133
+ return undefined;
134
+ return wrapped.slice(JSON.stringify(key).length + 2, -1);
135
+ }
136
+ /** 片段:已拼好的 JSON 文本与大字符串交替;连续的小文本先在字符串里累积。 */
137
+ class Pieces {
138
+ parts = [];
139
+ /** 已推入 `parts` 的 UTF-8 字节数。 */
140
+ bytes = 0;
141
+ pending = "";
142
+ text(chunk) {
143
+ this.pending += chunk;
144
+ }
145
+ /** 大字符串原样写入(引号与 `head` 进前一个小片段);`bytes` 已知时不再数一遍。 */
146
+ large(value, head = "", bytes = Buffer.byteLength(value, "utf8")) {
147
+ const before = `${this.pending}"${head}`;
148
+ this.parts.push(before, value);
149
+ this.bytes += Buffer.byteLength(before, "utf8") + bytes;
150
+ this.pending = '"';
151
+ }
152
+ finish() {
153
+ if (this.pending.length > 0) {
154
+ this.parts.push(this.pending);
155
+ this.bytes += Buffer.byteLength(this.pending, "utf8");
156
+ }
157
+ this.pending = "";
158
+ return this.parts;
159
+ }
160
+ }
161
+ /** 写一项的值;返回是否写了(对象里被原生跳过的属性不写)。数组里跳过的值写 `null`。 */
162
+ function writeEntry(key, value, marked, out, prefix, depth) {
163
+ if (isLargeString(value)) {
164
+ if (isPlain(value)) {
165
+ out.text(prefix);
166
+ out.large(value);
167
+ }
168
+ else {
169
+ out.text(prefix + JSON.stringify(value)); // 需要转义:只有这一个字符串交给原生
170
+ }
171
+ return true;
172
+ }
173
+ if (value instanceof LargeString && value.text.length >= LARGE_STRING_BYTES) {
174
+ const bytes = plainLargeBytes(value);
175
+ out.text(prefix);
176
+ if (bytes !== undefined)
177
+ out.large(value.text, value.prefix, bytes);
178
+ else
179
+ out.text(JSON.stringify(value.toJSON())); // 需要转义:交给原生
180
+ return true;
181
+ }
182
+ // 深度与 collectLarge 一致;有环时越过上限交给原生,由它抛 TypeError
183
+ if (depth < MAX_DEPTH && isContainer(value) && marked.has(value)) {
184
+ out.text(prefix);
185
+ writeContainer(value, marked, out, depth);
186
+ return true;
187
+ }
188
+ const text = nativeValue(key, value);
189
+ if (text === undefined)
190
+ return false;
191
+ out.text(prefix + text);
192
+ return true;
193
+ }
194
+ function writeContainer(value, marked, out, depth) {
195
+ if (Array.isArray(value)) {
196
+ out.text("[");
197
+ for (let i = 0; i < value.length; i++) {
198
+ const prefix = i > 0 ? "," : "";
199
+ if (!writeEntry(String(i), value[i], marked, out, prefix, depth + 1))
200
+ out.text(`${prefix}null`);
201
+ }
202
+ out.text("]");
203
+ return;
204
+ }
205
+ out.text("{");
206
+ let first = true;
207
+ for (const key of Object.keys(value)) {
208
+ const prefix = `${first ? "" : ","}${JSON.stringify(key)}:`;
209
+ if (writeEntry(key, value[key], marked, out, prefix, depth + 1))
210
+ first = false;
211
+ }
212
+ out.text("}");
213
+ }
214
+ /**
215
+ * 含大字符串时按顺序给出各片段(拼起来就是 `JSON.stringify(body)`)与 UTF-8 总字节数;
216
+ * 不含时返回 undefined(调用方照旧走原生)。
217
+ */
218
+ function jsonParts(body) {
219
+ const marked = new Set();
220
+ if (!collectLarge(body, marked, 0))
221
+ return undefined;
222
+ const out = new Pieces();
223
+ writeEntry("", body, marked, out, "", 0); // 顶层:键为 "",与原生调用 toJSON 的键相同
224
+ const parts = out.finish();
225
+ return { parts, length: out.bytes };
226
+ }
227
+ /** 与 `Buffer.from(JSON.stringify(body), "utf8")` 逐字节相同;大字符串不经中间字符串。 */
228
+ export function serializeJsonBody(body) {
229
+ const split = jsonParts(body);
230
+ if (split === undefined)
231
+ return Buffer.from(JSON.stringify(body), "utf8");
232
+ // 一次分配、依次写入:不再有整份中间字符串,也不需要 Buffer.concat 的第二份拷贝
233
+ const buf = Buffer.allocUnsafe(split.length);
234
+ let offset = 0;
235
+ for (const part of split.parts)
236
+ offset += buf.write(part, offset, "utf8");
237
+ if (offset !== split.length) {
238
+ throw new Error(`serializeJsonBody: wrote ${offset} of ${split.length} bytes`);
239
+ }
240
+ return buf;
241
+ }
242
+ /**
243
+ * 流式请求体每块最多编码的码元数:大片段(一整张图)按游标切片(V8 切片串不拷贝)逐块编码,
244
+ * 发送期间只多出一块的 UTF-8 字节,而不是一整张图。
245
+ */
246
+ export const STREAM_CHUNK_CHARS = 256 * 1024;
247
+ /**
248
+ * 给 fetch 的请求体。不含大字符串时就是 `JSON.stringify(body)`(与以前完全相同);含大字符串时是
249
+ * 逐片段编码的流,并给出总字节数。不交给 fetch 一个 Buffer:Node 的 fetch 会把 BufferSource
250
+ * 请求体再复制两份(实测 mock 300 步带图峰值反而从约 1.0 GB 升到 1.85 GB),流则按片段边读边发,
251
+ * 同一时刻只多出一块(≤ {@link STREAM_CHUNK_CHARS} 码元)的 UTF-8 字节。字节序列与 `serializeJsonBody` 相同。
252
+ */
253
+ export function jsonFetchBody(body) {
254
+ const split = jsonParts(body);
255
+ if (split === undefined)
256
+ return { body: JSON.stringify(body) };
257
+ const { parts } = split;
258
+ let next = 0;
259
+ let offset = 0; // 当前片段已发出的码元数
260
+ const stream = new ReadableStream({
261
+ pull(controller) {
262
+ const part = parts[next];
263
+ if (part === undefined) {
264
+ controller.close();
265
+ return;
266
+ }
267
+ let end = Math.min(part.length, offset + STREAM_CHUNK_CHARS);
268
+ // 不把成对代理项切开(片段里只有成对的:孤立代理项已被原生转义)
269
+ const last = part.charCodeAt(end - 1);
270
+ if (end < part.length && last >= 0xd800 && last <= 0xdbff)
271
+ end--;
272
+ const piece = offset === 0 && end === part.length ? part : part.slice(offset, end);
273
+ if (end === part.length) {
274
+ parts[next++] = ""; // 发完即放手,片段(拼出来的小文本)可尽早回收
275
+ offset = 0;
276
+ }
277
+ else
278
+ offset = end;
279
+ controller.enqueue(Buffer.from(piece, "utf8"));
280
+ },
281
+ cancel() {
282
+ parts.length = 0;
283
+ },
284
+ });
285
+ return { body: stream, contentLength: split.length };
286
+ }
@@ -13,6 +13,11 @@
13
13
  export const OVERFLOW_PATTERNS = [
14
14
  [/prompt is too long/i, "Anthropic: prompt is too long: N tokens > M maximum"],
15
15
  [/request_too_large/i, "Anthropic: 413 request_too_large"],
16
+ // [ME-C] 只有 max-tokens.ts 判定「可用输出 < 1024、不能收紧重发」时这条错误才会冒到会话层
17
+ [
18
+ /input length and `?max_tokens`? exceed context limit/i,
19
+ "Anthropic: input length and max_tokens exceed context limit: X + Y > Z",
20
+ ],
16
21
  [/exceeds the context window/i, "OpenAI: Your input exceeds the context window of this model"],
17
22
  [/maximum context length is \d+ tokens/i, "OpenAI / OpenRouter / DeepSeek"],
18
23
  [
@@ -3,22 +3,22 @@
3
3
  * UPDATE_CATALOG=1 pnpm vitest run src/ai/providers/catalog.test.ts
4
4
  */
5
5
  export const CATALOG_SOURCES = {
6
- anthropic: '{"version":1,"provider":"anthropic","modelsDev":"anthropic","models":[{"id":"claude-fable-5-1","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-fable-5","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-5-5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-5","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-8","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-7","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":2048},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-6","promptCache":{"short":300,"long":3600,"minTokens":4096},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-5-5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-5","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-4-6","promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-4-5","promptCache":{"short":300,"long":3600,"minTokens":1024}},{"id":"claude-haiku-4-5","promptCache":{"short":300,"long":3600,"minTokens":4096}},{"id":"claude-opus-4-5","promptCache":{"short":300,"long":3600,"minTokens":4096}}]}',
6
+ anthropic: '{"version":1,"provider":"anthropic","modelsDev":"anthropic","small":"claude-haiku-4-5","models":[{"id":"claude-fable-5-1","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-fable-5","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-5-5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-5","thinkingLevelMap":{"off":null,"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-8","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-7","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":2048},"compat":{"adaptiveThinking":true}},{"id":"claude-opus-4-6","promptCache":{"short":300,"long":3600,"minTokens":4096},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-5-5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":512},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-5","thinkingLevelMap":{"xhigh":"xhigh"},"promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-4-6","promptCache":{"short":300,"long":3600,"minTokens":1024},"compat":{"adaptiveThinking":true}},{"id":"claude-sonnet-4-5","promptCache":{"short":300,"long":3600,"minTokens":1024}},{"id":"claude-haiku-4-5","promptCache":{"short":300,"long":3600,"minTokens":4096}},{"id":"claude-opus-4-5","promptCache":{"short":300,"long":3600,"minTokens":4096}}]}',
7
7
  chatgpt: '{"version":1,"provider":"chatgpt","modelsDev":false,"models":[]}',
8
- dashscope: '{"version":1,"provider":"dashscope","modelsDev":"alibaba-cn","models":[{"id":"qwen3.8-max","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":null,"xhigh":"xhigh"},"compat":{"supportsReasoningEffort":true}},{"id":"qwen3.8-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":null,"xhigh":"xhigh"},"compat":{"supportsReasoningEffort":true}},{"id":"qwen3.7-max","maxTokens":131072,"compat":{"supportsReasoningEffort":false}},{"id":"qwen3.7-plus","maxTokens":65536,"compat":{"supportsReasoningEffort":false}},{"id":"qwen3.6-plus","compat":{"supportsReasoningEffort":false}},{"id":"qwen3.6-flash","compat":{"supportsReasoningEffort":false}}]}',
9
- deepseek: '{"version":1,"provider":"deepseek","modelsDev":"deepseek","models":[{"id":"deepseek-v4-pro","maxTokens":384000,"cost":{"input":1.32,"output":3.96,"cacheRead":0.044},"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high"},"_reason":"按官方价格页的高峰价(UTC 工作日 01–04、06–10 时)计,作上界;平峰为半价。快照价是平峰价或旧价","promptCache":{"short":3600,"minTokens":2048}},{"id":"deepseek-flash","maxTokens":384000,"cost":{"input":0.3,"output":1.2,"cacheRead":0.006},"thinkingLevelMap":{"minimal":null,"low":"low","medium":null,"high":"high"},"_reason":"按官方价格页的高峰价(UTC 工作日 01–04、06–10 时)计,作上界;平峰为半价。快照价是平峰价或旧价","promptCache":{"short":3600,"minTokens":2048}}]}',
10
- google: '{"version":1,"provider":"google","modelsDev":"google","models":[{"id":"gemini-3.8-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.7-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.5-flash","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.5-flash-lite","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.1-pro-preview","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3-flash-preview","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-2.5-pro","thinkingLevelMap":{"off":null,"minimal":128,"low":2048,"medium":8192,"high":32768}},{"id":"gemini-2.5-flash","thinkingLevelMap":{"minimal":128,"low":2048,"medium":8192,"high":24576}},{"id":"gemini-2.5-flash-lite","thinkingLevelMap":{"minimal":512,"low":2048,"medium":8192,"high":24576}}]}',
11
- groq: '{"version":1,"provider":"groq","modelsDev":"groq","models":[{"id":"openai/gpt-oss-120b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"openai/gpt-oss-20b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"openai/gpt-oss-safeguard-20b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"qwen/qwen3.8-27b","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"qwen/qwen3.6-27b","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"default","xhigh":null}},{"id":"llama-3.3-70b-versatile"},{"id":"llama-3.1-8b-instant"}]}',
8
+ dashscope: '{"version":1,"provider":"dashscope","modelsDev":"alibaba-cn","small":"qwen3.8-flash","models":[{"id":"qwen3.8-max","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":null,"xhigh":"xhigh"},"compat":{"supportsReasoningEffort":true}},{"id":"qwen3.8-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":null,"xhigh":"xhigh"},"compat":{"supportsReasoningEffort":true}},{"id":"qwen3.7-max","maxTokens":131072,"compat":{"supportsReasoningEffort":false}},{"id":"qwen3.7-plus","maxTokens":65536,"compat":{"supportsReasoningEffort":false}},{"id":"qwen3.6-plus","compat":{"supportsReasoningEffort":false}},{"id":"qwen3.6-flash","compat":{"supportsReasoningEffort":false}}]}',
9
+ deepseek: '{"version":1,"provider":"deepseek","modelsDev":"deepseek","small":"deepseek-flash","models":[{"id":"deepseek-v4-pro","maxTokens":384000,"cost":{"input":1.32,"output":3.96,"cacheRead":0.044},"thinkingLevelMap":{"minimal":null,"low":null,"medium":null,"high":"high"},"_reason":"按官方价格页的高峰价(UTC 工作日 01–04、06–10 时)计,作上界;平峰为半价。快照价是平峰价或旧价","promptCache":{"short":3600,"minTokens":2048},"compat":{"requiresReasoningContentOnAssistantMessages":true}},{"id":"deepseek-flash","aliases":["deepseek-v4-flash"],"maxTokens":384000,"cost":{"input":0.3,"output":1.2,"cacheRead":0.006},"thinkingLevelMap":{"minimal":null,"low":"low","medium":null,"high":"high"},"_reason":"按官方价格页的高峰价(UTC 工作日 01–04、06–10 时)计,作上界;平峰为半价。快照价是平峰价或旧价","promptCache":{"short":3600,"minTokens":2048},"compat":{"requiresReasoningContentOnAssistantMessages":true}}]}',
10
+ google: '{"version":1,"provider":"google","modelsDev":"google","small":"gemini-3.5-flash-lite","models":[{"id":"gemini-3.8-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.7-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.5-flash","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.5-flash-lite","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3.1-pro-preview","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-3-flash-preview","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"gemini-2.5-pro","thinkingLevelMap":{"off":null,"minimal":128,"low":2048,"medium":8192,"high":32768}},{"id":"gemini-2.5-flash","thinkingLevelMap":{"minimal":128,"low":2048,"medium":8192,"high":24576}},{"id":"gemini-2.5-flash-lite","thinkingLevelMap":{"minimal":512,"low":2048,"medium":8192,"high":24576}}]}',
11
+ groq: '{"version":1,"provider":"groq","modelsDev":"groq","small":"openai/gpt-oss-20b","models":[{"id":"openai/gpt-oss-120b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"openai/gpt-oss-20b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"openai/gpt-oss-safeguard-20b","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"qwen/qwen3.8-27b","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"qwen/qwen3.6-27b","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"default","xhigh":null}},{"id":"llama-3.3-70b-versatile"},{"id":"llama-3.1-8b-instant"}]}',
12
12
  lmstudio: '{"version":1,"provider":"lmstudio","modelsDev":false,"models":[]}',
13
- minimax: '{"version":1,"provider":"minimax","modelsDev":"minimax-cn","models":[{"id":"MiniMax-M3"},{"id":"MiniMax-M2.7","channels":["messages","chat","messages-intl","chat-intl"],"thinkingLevelMap":{"off":null},"promptCache":{"short":300}},{"id":"MiniMax-M2.7-highspeed","channels":["messages","chat","messages-intl","chat-intl"],"thinkingLevelMap":{"off":null},"promptCache":{"short":300}},{"id":"MiniMax-M2.5","channels":["messages","chat","messages-intl","chat-intl"],"thinkingLevelMap":{"off":null},"promptCache":{"short":300}}]}',
14
- mistral: '{"version":1,"provider":"mistral","modelsDev":"mistral","models":[{"id":"mistral-large-latest"},{"id":"mistral-medium-latest","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"high","xhigh":null}},{"id":"mistral-small-latest","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"high","xhigh":null}},{"id":"magistral-medium-latest","cost":{"cacheRead":0.2}},{"id":"codestral-latest"},{"id":"devstral-latest","name":"Devstral 2","contextWindow":262144,"maxTokens":262144,"reasoning":false,"input":["text"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0}},{"id":"devstral-medium-latest","name":"Devstral 2 (latest)","contextWindow":262144,"maxTokens":262144,"reasoning":false,"input":["text"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0}},{"id":"ministral-8b-latest","cost":{"cacheRead":0.01}},{"id":"pixtral-large-latest","cost":{"cacheRead":0.2}}]}',
15
- moonshot: '{"version":1,"provider":"moonshot","modelsDev":"moonshotai-cn","models":[{"id":"kimi-k3","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null},"promptCache":{"short":300},"compat":{"supportsReasoningEffort":true}},{"id":"kimi-k2.7-code","thinkingLevelMap":{"off":null},"promptCache":{"short":300},"compat":{"supportsReasoningEffort":false}},{"id":"kimi-k2.7-code-highspeed","thinkingLevelMap":{"off":null},"promptCache":{"short":300},"compat":{"supportsReasoningEffort":false}},{"id":"kimi-k2.6","promptCache":{"short":300},"compat":{"supportsReasoningEffort":false}}]}',
13
+ minimax: '{"version":1,"provider":"minimax","modelsDev":"minimax-cn","small":"MiniMax-M2.5","models":[{"id":"MiniMax-M3"},{"id":"MiniMax-M2.7","channels":["messages","chat","messages-intl","chat-intl"],"thinkingLevelMap":{"off":null},"promptCache":{"short":300}},{"id":"MiniMax-M2.7-highspeed","channels":["messages","chat","messages-intl","chat-intl"],"thinkingLevelMap":{"off":null},"promptCache":{"short":300}},{"id":"MiniMax-M2.5","channels":["messages","chat","messages-intl","chat-intl"],"thinkingLevelMap":{"off":null},"promptCache":{"short":300}}]}',
14
+ mistral: '{"version":1,"provider":"mistral","modelsDev":"mistral","small":"mistral-small-latest","models":[{"id":"mistral-large-latest"},{"id":"mistral-medium-latest","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"high","xhigh":null}},{"id":"mistral-small-latest","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"high","xhigh":null}},{"id":"magistral-medium-latest","cost":{"cacheRead":0.2}},{"id":"codestral-latest"},{"id":"devstral-latest","name":"Devstral 2","contextWindow":262144,"maxTokens":262144,"reasoning":false,"input":["text"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0}},{"id":"devstral-medium-latest","name":"Devstral 2 (latest)","contextWindow":262144,"maxTokens":262144,"reasoning":false,"input":["text"],"cost":{"input":0.4,"output":2,"cacheRead":0.04,"cacheWrite":0}},{"id":"ministral-8b-latest","cost":{"cacheRead":0.01}},{"id":"pixtral-large-latest","cost":{"cacheRead":0.2}}]}',
15
+ moonshot: '{"version":1,"provider":"moonshot","modelsDev":"moonshotai-cn","small":"kimi-k2.6","models":[{"id":"kimi-k3","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null},"promptCache":{"short":300},"compat":{"supportsReasoningEffort":true}},{"id":"kimi-k2.7-code","thinkingLevelMap":{"off":null},"promptCache":{"short":300},"compat":{"supportsReasoningEffort":false}},{"id":"kimi-k2.7-code-highspeed","thinkingLevelMap":{"off":null},"promptCache":{"short":300},"compat":{"supportsReasoningEffort":false}},{"id":"kimi-k2.6","promptCache":{"short":300},"compat":{"supportsReasoningEffort":false}}]}',
16
16
  ollama: '{"version":1,"provider":"ollama","modelsDev":false,"models":[]}',
17
- openai: '{"version":1,"provider":"openai","modelsDev":"openai","models":[{"id":"gpt-6.1-sol","contextWindow":272000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-6-sol","contextWindow":272000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-6-luna","contextWindow":272000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-6-astra","contextWindow":272000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5.5","contextWindow":272000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5.4","contextWindow":272000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5.4-mini","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5.4-nano","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5-mini","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-4.1","promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-4o","promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-4o-mini","promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"o4-mini","name":"o4-mini","contextWindow":200000,"maxTokens":100000,"reasoning":true,"input":["text","image"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.275,"cacheWrite":0},"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"o3","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null},"promptCache":{"short":300,"long":86400,"minTokens":1024}}]}',
18
- openrouter: '{"version":1,"provider":"openrouter","modelsDev":"openrouter","models":[{"id":"anthropic/claude-opus-5.5","name":"Anthropic: Claude Opus 5.5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"}},{"id":"anthropic/claude-sonnet-5.5","name":"Anthropic: Claude Sonnet 5.5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"}},{"id":"anthropic/claude-haiku-4.5","name":"Anthropic: Claude Haiku 4.5"},{"id":"openai/gpt-6-sol","name":"OpenAI: GPT-6 Sol","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"compat":{"supportsMidConvoSystemMessages":true}},{"id":"openai/gpt-5.5","name":"OpenAI: GPT-5.5","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"compat":{"supportsMidConvoSystemMessages":true}},{"id":"openai/gpt-5.4-mini","name":"OpenAI: GPT-5.4 Mini","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"compat":{"supportsMidConvoSystemMessages":true}},{"id":"google/gemini-3.8-flash","name":"Google: Gemini 3.8 Flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"google/gemini-3.1-pro-preview","name":"Google: Gemini 3.1 Pro Preview","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"deepseek/deepseek-v4-pro","name":"DeepSeek: DeepSeek V4 Pro 0423","contextWindow":1024000,"thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"high","xhigh":"xhigh"},"compat":{"requiresReasoningContentOnAssistantMessages":true}},{"id":"moonshotai/kimi-k3","name":"MoonshotAI: Kimi K3","maxTokens":131072,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":null,"high":"high","xhigh":null}},{"id":"qwen/qwen3.7-max","name":"Qwen: Qwen3.7 Max"},{"id":"z-ai/glm-5.3","name":"Z.ai: GLM 5.3","maxTokens":943718,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null}},{"id":"x-ai/grok-4.7","name":"SpaceXAI: Grok 4.7","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"}}]}',
19
- stepfun: '{"version":1,"provider":"stepfun","modelsDev":"stepfun","models":[{"id":"step-5-preview"},{"id":"step-3.7-flash","channels":["messages","chat","messages-intl","chat-intl"]},{"id":"step-3.5-flash","channels":["messages","chat","messages-intl","chat-intl"]}]}',
17
+ openai: '{"version":1,"provider":"openai","modelsDev":"openai","small":"gpt-6-luna","models":[{"id":"gpt-6.1-sol","contextWindow":272000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-6-sol","contextWindow":272000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-6-luna","contextWindow":272000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-6-astra","contextWindow":272000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5.5","contextWindow":272000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5.4","contextWindow":272000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5.4-mini","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5.4-nano","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-5-mini","thinkingLevelMap":{"off":null,"minimal":"minimal","low":"low","medium":"medium","high":"high","xhigh":null},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-4.1","promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-4o","promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"gpt-4o-mini","promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"o4-mini","name":"o4-mini","contextWindow":200000,"maxTokens":100000,"reasoning":true,"input":["text","image"],"cost":{"input":1.1,"output":4.4,"cacheRead":0.275,"cacheWrite":0},"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null},"promptCache":{"short":300,"long":86400,"minTokens":1024}},{"id":"o3","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null},"promptCache":{"short":300,"long":86400,"minTokens":1024}}]}',
18
+ openrouter: '{"version":1,"provider":"openrouter","modelsDev":"openrouter","small":"openai/gpt-5.4-mini","models":[{"id":"anthropic/claude-opus-5.5","name":"Anthropic: Claude Opus 5.5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"}},{"id":"anthropic/claude-sonnet-5.5","name":"Anthropic: Claude Sonnet 5.5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"}},{"id":"anthropic/claude-haiku-4.5","name":"Anthropic: Claude Haiku 4.5"},{"id":"openai/gpt-6-sol","name":"OpenAI: GPT-6 Sol","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"compat":{"supportsMidConvoSystemMessages":true}},{"id":"openai/gpt-5.5","name":"OpenAI: GPT-5.5","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"compat":{"supportsMidConvoSystemMessages":true}},{"id":"openai/gpt-5.4-mini","name":"OpenAI: GPT-5.4 Mini","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"},"compat":{"supportsMidConvoSystemMessages":true}},{"id":"google/gemini-3.8-flash","name":"Google: Gemini 3.8 Flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"google/gemini-3.1-pro-preview","name":"Google: Gemini 3.1 Pro Preview","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"deepseek/deepseek-v4-pro","name":"DeepSeek: DeepSeek V4 Pro 0423","contextWindow":1024000,"thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"high","xhigh":"xhigh"},"compat":{"requiresReasoningContentOnAssistantMessages":true}},{"id":"moonshotai/kimi-k3","name":"MoonshotAI: Kimi K3","maxTokens":131072,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":null,"high":"high","xhigh":null}},{"id":"qwen/qwen3.7-max","name":"Qwen: Qwen3.7 Max"},{"id":"z-ai/glm-5.3","name":"Z.ai: GLM 5.3","maxTokens":943718,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null}},{"id":"x-ai/grok-4.7","name":"SpaceXAI: Grok 4.7","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"}}]}',
19
+ stepfun: '{"version":1,"provider":"stepfun","modelsDev":"stepfun","small":"step-3.5-flash","models":[{"id":"step-5-preview"},{"id":"step-3.7-flash","channels":["messages","chat","messages-intl","chat-intl"]},{"id":"step-3.5-flash","channels":["messages","chat","messages-intl","chat-intl"]}]}',
20
20
  tencent: '{"version":1,"provider":"tencent","modelsDev":"tencent-tokenhub","models":[{"id":"hy4-preview","promptCache":{"short":300,"long":3600}},{"id":"hy3","cost":{"input":0.139,"output":0.556,"cacheRead":0.0348,"cacheWrite":0},"_reason":"快照价为 0;官方价格页(2026-10)1 / 4 / 0.25 元每百万 token,按快照同一汇率(约 0.139)折算","promptCache":{"short":300,"long":3600}}]}',
21
- volcengine: '{"version":1,"provider":"volcengine","modelsDev":"volcengine","models":[{"id":"doubao-seed-2-1-pro-260628"},{"id":"doubao-seed-2-1-turbo-260628"},{"id":"doubao-seed-2-0-code-preview-260215"},{"id":"doubao-seed-2-0-lite-260428"},{"id":"deepseek-v4-pro-ga-260813"},{"id":"glm-5-3-flash-260828"}]}',
21
+ volcengine: '{"version":1,"provider":"volcengine","modelsDev":"volcengine","small":"doubao-seed-2-0-lite-260428","models":[{"id":"doubao-seed-2-1-pro-260628"},{"id":"doubao-seed-2-1-turbo-260628"},{"id":"doubao-seed-2-0-code-preview-260215"},{"id":"doubao-seed-2-0-lite-260428"},{"id":"deepseek-v4-pro-ga-260813"},{"id":"glm-5-3-flash-260828"}]}',
22
22
  xai: '{"version":1,"provider":"xai","modelsDev":"xai","models":[{"id":"grok-4.7","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"}},{"id":"grok-4.6","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh"}},{"id":"grok-4.5","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}},{"id":"grok-4.3","thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":null}}]}',
23
- zhipu: '{"version":1,"provider":"zhipu","modelsDev":"zhipuai","models":[{"id":"glm-5.3","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null},"compat":{"supportsReasoningEffort":true}},{"id":"glm-5.3-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null},"compat":{"supportsReasoningEffort":true}},{"id":"glm-5.2","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"high","xhigh":null},"compat":{"supportsReasoningEffort":true}},{"id":"glm-5-turbo","name":"GLM-5-Turbo","contextWindow":200000,"maxTokens":131072,"reasoning":true,"input":["text"],"cost":{"input":1.2,"output":4,"cacheRead":0.24,"cacheWrite":0},"compat":{"supportsReasoningEffort":false}},{"id":"glm-4.7","compat":{"supportsReasoningEffort":false}}]}',
23
+ zhipu: '{"version":1,"provider":"zhipu","modelsDev":"zhipuai","small":"glm-5.3-flash","models":[{"id":"glm-5.3","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null},"compat":{"supportsReasoningEffort":true}},{"id":"glm-5.3-flash","thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":null,"high":"high","xhigh":null},"compat":{"supportsReasoningEffort":true}},{"id":"glm-5.2","thinkingLevelMap":{"off":"none","minimal":null,"low":null,"medium":null,"high":"high","xhigh":null},"compat":{"supportsReasoningEffort":true}},{"id":"glm-5-turbo","name":"GLM-5-Turbo","contextWindow":200000,"maxTokens":131072,"reasoning":true,"input":["text"],"cost":{"input":1.2,"output":4,"cacheRead":0.24,"cacheWrite":0},"compat":{"supportsReasoningEffort":false}},{"id":"glm-4.7","compat":{"supportsReasoningEffort":false}}]}',
24
24
  };
@@ -29,6 +29,8 @@ export type CatalogEntry = Partial<Omit<CatalogModel, "cost">> & {
29
29
  _reason?: string;
30
30
  /** 模型级协议(OpenAI 推理模型走 responses 等)。 */
31
31
  api?: Api;
32
+ /** [ME-C0] 中转 / 自定义模型按 id 匹配到本条目时使用的别名(小写、去厂商前缀后比较);不进 Model。 */
33
+ aliases?: string[];
32
34
  };
33
35
  /** `catalog/*.json` 的文件形状(覆盖格式)。 */
34
36
  export interface CatalogSourceFile {
@@ -36,6 +38,8 @@ export interface CatalogSourceFile {
36
38
  provider: string;
37
39
  /** 快照里对应的 models.dev 供应商 id;false = 没有(本地服务)。 */
38
40
  modelsDev?: string | false;
41
+ /** [ME-C0] 本供应商用于 auto 模式分类的小模型 id。 */
42
+ small?: string;
39
43
  models: CatalogEntry[];
40
44
  }
41
45
  /** 从快照继承的字段(顺序即冗余检查与来源展示的顺序)。 */
@@ -69,6 +73,35 @@ export declare function parseCatalogFile(value: unknown, source: string, index?:
69
73
  export declare function loadBuiltinCatalog(index?: ModelsDevIndex): Map<string, CatalogModel[]>;
70
74
  /** 最近一次 `loadBuiltinCatalog()` 里,该目录模型从快照继承的字段(来源展示用)。 */
71
75
  export declare function catalogInherited(provider: string, modelId: string): readonly string[];
76
+ /**
77
+ * [ME-D] 中转 / 自定义模型按 id 匹配内置目录(docs/model-efficiency-plan.md D10):小写、去一层
78
+ * `vendor/` 前缀与 `:latest` 后精确比较;键是第一方条目的 id(含 `/` 的聚合商条目不进索引)与各条
79
+ * `aliases`。同一个键落在两条上即 ambiguous,不命中。精确不中时再去掉思考档后缀(`-low` 等)试一次。
80
+ */
81
+ export interface CatalogAliasHit {
82
+ /** 目录条目 `provider/id`。 */
83
+ ref: string;
84
+ /** 合并后的目录模型(深拷贝;内置快照口径)。 */
85
+ model: CatalogModel;
86
+ /** 目录条目对应的 models.dev 快照条目。 */
87
+ snapshotRef?: string;
88
+ /** 去掉思考档后缀才命中:思考档由中转按 id 决定,调用方不继承思考设置。 */
89
+ tier?: string;
90
+ }
91
+ export declare function normalizeModelId(id: string): string;
92
+ export type AliasTarget = {
93
+ provider: string;
94
+ id: string;
95
+ snapshotRef?: string;
96
+ } | "ambiguous";
97
+ /** 别名索引(测试可传入自造的目录源)。 */
98
+ export declare function buildAliasIndex(sources?: Readonly<Record<string, string>>): Map<string, AliasTarget>;
99
+ /** 按 id 唯一命中的目录条目(没有或 ambiguous 返回 undefined)。 */
100
+ export declare function catalogByAlias(id: string): CatalogAliasHit | undefined;
101
+ /** 显式指定的目录条目 `provider/id`(`models[].catalog`)。 */
102
+ export declare function catalogByRef(ref: string): CatalogAliasHit | undefined;
103
+ /** [ME-D] 供应商目录里给 auto 模式分类用的小模型 id(D12)。 */
104
+ export declare function catalogSmall(provider: string): string | undefined;
72
105
  /** 目录条目 → Model(补 provider / api 与缺省 input)。 */
73
106
  export declare function toModel(entry: CatalogModel, provider: string, api: Model["api"]): Model;
74
107
  /** 自定义模型缺省:maxTokens 8192、reasoning false、input ["text"]、name = id;不猜 contextWindow。 */