mocode-ai 0.4.8 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/agent/core.d.ts +112 -0
- package/dist/agent/core.js +56 -13
- package/dist/agent/index.d.ts +19 -0
- package/dist/agent/index.js +1 -1
- package/dist/agent/mode.d.ts +29 -0
- package/dist/agent/spawn.d.ts +36 -0
- package/dist/attachments/image.d.ts +22 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/config/file.d.ts +23 -0
- package/dist/config/index.d.ts +114 -0
- package/dist/config/index.js +61 -5
- package/dist/config/presets.d.ts +57 -0
- package/dist/context/budget.d.ts +79 -0
- package/dist/context/classifier.d.ts +14 -0
- package/dist/context/encoders/_util.d.ts +11 -0
- package/dist/context/encoders/code.d.ts +2 -0
- package/dist/context/encoders/doc.d.ts +14 -0
- package/dist/context/encoders/graph.d.ts +11 -0
- package/dist/context/encoders/index.d.ts +2 -0
- package/dist/context/encoders/log.d.ts +2 -0
- package/dist/context/encoders/memory.d.ts +2 -0
- package/dist/context/encoders/passthrough.d.ts +11 -0
- package/dist/context/encoders/search.d.ts +2 -0
- package/dist/context/encoders/summary.d.ts +12 -0
- package/dist/context/encoders/table.d.ts +2 -0
- package/dist/context/encoders/tree.d.ts +2 -0
- package/dist/context/index.d.ts +6 -0
- package/dist/context/lifecycle.d.ts +51 -0
- package/dist/context/pipeline.d.ts +9 -0
- package/dist/context/registry.d.ts +9 -0
- package/dist/context/relevance.d.ts +65 -0
- package/dist/context/types.d.ts +36 -0
- package/dist/index.d.ts +1 -0
- package/dist/llm/capabilities.d.ts +19 -0
- package/dist/llm/index.d.ts +111 -0
- package/dist/llm/index.js +22 -1
- package/dist/memory/discover.d.ts +15 -0
- package/dist/memory/index.d.ts +13 -0
- package/dist/memory/reflect.d.ts +34 -0
- package/dist/memory/store.d.ts +124 -0
- package/dist/permissions/index.js +138 -0
- package/dist/pet/bridge.d.ts +73 -0
- package/dist/pet/protocol.d.ts +83 -0
- package/dist/pet/state.d.ts +28 -0
- package/dist/plan/active.d.ts +23 -0
- package/dist/plan/index.d.ts +13 -0
- package/dist/plan/state.d.ts +2 -0
- package/dist/plan/store.d.ts +78 -0
- package/dist/project-skill/index.js +180 -0
- package/dist/project-skill/initializer.js +178 -0
- package/dist/project-snapshot/index.js +85 -0
- package/dist/project-snapshot/llm-snapshot.js +127 -0
- package/dist/project-snapshot/static-files.js +87 -0
- package/dist/repl/index.d.ts +22 -0
- package/dist/repl/index.js +268 -2
- package/dist/rollback/index.d.ts +79 -0
- package/dist/sandbox/command.d.ts +3 -0
- package/dist/sandbox/index.d.ts +4 -0
- package/dist/sandbox/jail.d.ts +17 -0
- package/dist/sandbox/policy.d.ts +21 -0
- package/dist/sandbox/root.d.ts +5 -0
- package/dist/session/compact.d.ts +92 -0
- package/dist/session/drop.d.ts +19 -0
- package/dist/session/index.d.ts +13 -0
- package/dist/session/persist.d.ts +30 -0
- package/dist/session/scheduler.d.ts +53 -0
- package/dist/skills/discover.d.ts +33 -0
- package/dist/skills/index.d.ts +13 -0
- package/dist/tools/builtins/ask-human.d.ts +9 -0
- package/dist/tools/builtins/codegraph.d.ts +2 -0
- package/dist/tools/builtins/codegraph.js +17 -4
- package/dist/tools/builtins/drop-context.d.ts +18 -0
- package/dist/tools/builtins/edit-file.d.ts +2 -0
- package/dist/tools/builtins/edit-file.js +1 -0
- package/dist/tools/builtins/glob.d.ts +2 -0
- package/dist/tools/builtins/grep.d.ts +2 -0
- package/dist/tools/builtins/index.d.ts +2 -0
- package/dist/tools/builtins/index.js +7 -0
- package/dist/tools/builtins/memory-forget.d.ts +2 -0
- package/dist/tools/builtins/memory-forget.js +1 -0
- package/dist/tools/builtins/memory-list.d.ts +2 -0
- package/dist/tools/builtins/memory-save.d.ts +2 -0
- package/dist/tools/builtins/memory-save.js +1 -0
- package/dist/tools/builtins/memory-search.d.ts +2 -0
- package/dist/tools/builtins/memory-update.d.ts +2 -0
- package/dist/tools/builtins/memory-update.js +1 -0
- package/dist/tools/builtins/project-skill-update.js +104 -0
- package/dist/tools/builtins/read-file.d.ts +2 -0
- package/dist/tools/builtins/run-command.d.ts +2 -0
- package/dist/tools/builtins/run-command.js +1 -0
- package/dist/tools/builtins/switch-mode.d.ts +2 -0
- package/dist/tools/builtins/task.d.ts +2 -0
- package/dist/tools/builtins/task.js +1 -0
- package/dist/tools/builtins/todolist.d.ts +2 -0
- package/dist/tools/builtins/todolist.js +124 -8
- package/dist/tools/builtins/use-skill.d.ts +2 -0
- package/dist/tools/builtins/web-fetch.d.ts +2 -0
- package/dist/tools/builtins/web-search.d.ts +2 -0
- package/dist/tools/builtins/web-search.js +12 -1
- package/dist/tools/builtins/write-file.d.ts +2 -0
- package/dist/tools/builtins/write-file.js +1 -0
- package/dist/tools/constants.d.ts +52 -0
- package/dist/tools/registry.d.ts +18 -0
- package/dist/tools/types.d.ts +42 -0
- package/dist/ui/batch.d.ts +67 -0
- package/dist/ui/batch.js +19 -2
- package/dist/ui/clipboard.d.ts +9 -0
- package/dist/ui/content.d.ts +55 -0
- package/dist/ui/content.js +68 -3
- package/dist/ui/diff.d.ts +34 -0
- package/dist/ui/diff.js +15 -24
- package/dist/ui/intervention.d.ts +46 -0
- package/dist/ui/layout.d.ts +225 -0
- package/dist/ui/layout.js +67 -11
- package/dist/ui/markdown.d.ts +46 -0
- package/dist/ui/mouse.d.ts +29 -0
- package/dist/ui/prompt.d.ts +61 -0
- package/dist/ui/render.d.ts +60 -0
- package/dist/ui/spinner.d.ts +26 -0
- package/dist/ui/theme.d.ts +47 -0
- package/dist/updater/index.d.ts +22 -0
- package/package.json +1 -1
package/dist/config/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import dotenv from 'dotenv';
|
|
5
|
+
import { loadSnapshot } from '../project-snapshot/index.js';
|
|
6
|
+
import { buildProjectSkillSection } from '../project-skill/index.js';
|
|
5
7
|
/**
|
|
6
8
|
* 按优先级加载配置文件并回填 process.env:
|
|
7
9
|
* 候选(后者覆盖前者,优先级升序):<cwd>/.env(兼容旧用法,最低)→ ~/.mocode/config(全局)→ <cwd>/.mocode/config(项目级覆盖,最高)。
|
|
@@ -77,6 +79,30 @@ const PLATFORM_NOTE = (() => {
|
|
|
77
79
|
* 默认关(新用户零侵入):这段 + 工具表里的 5 个 memory_* + 系统提示尾部的 Memory Index
|
|
78
80
|
* 都不出现;打开 /memory_switch 后下一次新建 system message 才注入。
|
|
79
81
|
*/
|
|
82
|
+
/**
|
|
83
|
+
* 项目快照段落:开 isProjectSnapshotEnabled() 且有快照时才拼。
|
|
84
|
+
* 告诉 LLM 项目已有哪些静态文件可 cache hit,减少无谓 read_file。
|
|
85
|
+
*/
|
|
86
|
+
function buildSnapshotSection() {
|
|
87
|
+
if (!config.projectSnapshotEnabled)
|
|
88
|
+
return '';
|
|
89
|
+
try {
|
|
90
|
+
const snap = loadSnapshot();
|
|
91
|
+
if (!snap)
|
|
92
|
+
return '';
|
|
93
|
+
// 快照内容已经是完整的 markdown,直接返回
|
|
94
|
+
return `\n${snap.content}\n`;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return '';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 项目快照总开关查询器(read-file 工具用)。
|
|
102
|
+
*/
|
|
103
|
+
export function isProjectSnapshotEnabled() {
|
|
104
|
+
return config.projectSnapshotEnabled;
|
|
105
|
+
}
|
|
80
106
|
const SYSTEM_PROMPT_MEMORY_SECTION = `
|
|
81
107
|
## Memory (cross-session long-term facts)
|
|
82
108
|
- A "memory index" (id/title/summary only) is injected into the system prompt. Retrieve full body via memory_search (pass id or keyword); use memory_list to see the entire index.
|
|
@@ -174,7 +200,7 @@ ${PLATFORM_NOTE}
|
|
|
174
200
|
- Use glob to find file paths, grep to search content. **Don't use run_command for file-level checks** (existence / listing / type) — those have no clean cmd.exe equivalent and Windows path escaping fails often. Use \`glob\` to list, and just call \`read_file\` to test existence (returns ENOENT as a clean error string).
|
|
175
201
|
- run_command has side effects on the host — state intent before invoking (delete, install, push, reset, etc.).
|
|
176
202
|
- Call ask_human when you hit a decision point requiring user input (multiple implementation approaches, unclear intent, or needing extra info to proceed) — list options for the user to pick (they can also choose "custom input" to answer freely). Don't call it frequently when the task is clear and you can decide yourself; if the user cancels, switch approach or proceed with available info — don't re-ask the same question.
|
|
177
|
-
- **Trim context when stale**: when an old tool result is dead weight (sub-goal done, no downstream consumer, or superseded by a later read), call drop_context to stub it; otherwise rely on automatic pruning.
|
|
203
|
+
- **Trim context when stale**: when an old tool result is dead weight (sub-goal done, no downstream consumer, or superseded by a later read), call drop_context to stub it; otherwise rely on automatic pruning. **When your context gets too long, don't hesitate to use drop_context proactively** — it's cheap and designed to be called, not saved for emergencies.
|
|
178
204
|
- **Batch writes and commands too, not just reads**: the executor runs ALL returned tool_calls (reads, writes, commands) before the next LLM call. Emit independent edit_file / write_file / run_command in one response when the chain is clear — don't serialize them across turns just because they have side effects. (The read-only batching note in Step Economy applies to writes the same way.)
|
|
179
205
|
- **Chain shell workflows in a single \`run_command\`**: use \`&&\`, \`;\`, \`|\`, \`>\`, heredocs to fold multi-step scripts (\`mkdir -p x && cat > x/file.ts <<'EOF' ... EOF && npm test\`) into one call. Only emit a follow-up turn when the result forces a decision (error, ambiguous output, branching logic).
|
|
180
206
|
|
|
@@ -193,11 +219,10 @@ ${PLATFORM_NOTE}
|
|
|
193
219
|
- Confirm with the user before irreversible or outward-facing operations (delete, overwrite existing files, push, request external services), unless explicitly authorized.
|
|
194
220
|
- Operate only within authorized scope; when unsure, ask — don't guess.
|
|
195
221
|
|
|
196
|
-
${memorySection}
|
|
222
|
+
${buildSnapshotSection()}${config.projectSkillEnabled ? buildProjectSkillSection() : ''}${memorySection}
|
|
197
223
|
|
|
198
|
-
## Working notepad (todolist)
|
|
199
|
-
- For tasks
|
|
200
|
-
- Plan is file-backed (\`todolist read\` to re-orient). See the tool description for the full action set.
|
|
224
|
+
## Working notepad (todolist)
|
|
225
|
+
- For genuinely complex tasks only: explore codebase → clarify with user → create plan → execute step by step. See tool description for details.
|
|
201
226
|
|
|
202
227
|
## Termination & Reporting
|
|
203
228
|
- Stop immediately when no more tools are needed; give conclusions directly.
|
|
@@ -250,6 +275,9 @@ export const config = {
|
|
|
250
275
|
theme: process.env.MOCODE_THEME || 'default',
|
|
251
276
|
themeFromShell,
|
|
252
277
|
llmKeysFromShell,
|
|
278
|
+
projectSnapshotEnabled: process.env.MOCODE_PROJECT_SNAPSHOT !== 'false',
|
|
279
|
+
permissionEnabled: process.env.MOCODE_PERMISSION !== 'false',
|
|
280
|
+
projectSkillEnabled: process.env.MOCODE_PROJECT_SKILL === 'true',
|
|
253
281
|
};
|
|
254
282
|
/**
|
|
255
283
|
* 运行时更新模型相关配置(/model 命令调)。
|
|
@@ -300,3 +328,31 @@ export function updateMemoryConfig(enabled) {
|
|
|
300
328
|
config.memoryEnabled = enabled;
|
|
301
329
|
process.env.MEMORY_ENABLED = enabled ? 'true' : 'false';
|
|
302
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* 项目专属 Skill 总开关:单一来源。/project_skill、buildBasePrompt、
|
|
333
|
+
* tools/builtins/index.ts 都从这里查。默认 false(零侵入)。
|
|
334
|
+
*/
|
|
335
|
+
export function isProjectSkillEnabled() {
|
|
336
|
+
return config.projectSkillEnabled;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* 切换项目专属 Skill 开关(/project_skill on|off 调)。
|
|
340
|
+
* - 更新 config 单例字段(其它模块下次调 isProjectSkillEnabled() 即拿新值)。
|
|
341
|
+
* - 同步 process.env.MOCODE_PROJECT_SKILL(下次启动 loadEnvFiles 不被文件回填)。
|
|
342
|
+
* 持久化(写 ~/.mocode/config 的 MOCODE_PROJECT_SKILL 键)由调用方走 writeConfigKeys。
|
|
343
|
+
*/
|
|
344
|
+
export function updateProjectSkillConfig(enabled) {
|
|
345
|
+
config.projectSkillEnabled = enabled;
|
|
346
|
+
process.env.MOCODE_PROJECT_SKILL = enabled ? 'true' : 'false';
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* 切换项目快照开关(/snapshot on|off 调)。
|
|
350
|
+
* - 更新 config 单例字段(其它模块下次读 config.projectSnapshotEnabled 即拿新值:
|
|
351
|
+
* buildSnapshotSection 现拼现读、read-file 每次 execute 现读)。
|
|
352
|
+
* - 同步 process.env.MOCODE_PROJECT_SNAPSHOT(下次启动 loadEnvFiles 不会被文件回填)。
|
|
353
|
+
* 持久化(写 ~/.mocode/config 的 MOCODE_PROJECT_SNAPSHOT 键)由调用方走 updateConfigKey。
|
|
354
|
+
*/
|
|
355
|
+
export function updateSnapshotConfig(enabled) {
|
|
356
|
+
config.projectSnapshotEnabled = enabled;
|
|
357
|
+
process.env.MOCODE_PROJECT_SNAPSHOT = enabled ? 'true' : 'false';
|
|
358
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 多模型预设(`/model save <name>` 保存的命名配置)的纯 I/O 叶子。
|
|
3
|
+
*
|
|
4
|
+
* 存储:每个预设一个独立 JSON 文件 `~/.mocode/models/<name>.json`。
|
|
5
|
+
* - 选用 per-file 而非单一 index.json:写不需要读-合并-写整本,天然原子(单文件 rename),
|
|
6
|
+
* 符合 memory/store.ts 的惯例;并发写多个预设互不打架。
|
|
7
|
+
* - 路径前缀与 CONFIG_PATH 共享 `~/.mocode/`,保证权限/位置一致。
|
|
8
|
+
*
|
|
9
|
+
* 不读 process.env、不触发 config 单例初始化——repl(/model 子命令)与 commands/config.ts
|
|
10
|
+
* 之外的脚本都能安全 import。错误一律抛(写失败属异常路径,调用方决定怎么提示用户)。
|
|
11
|
+
*
|
|
12
|
+
* 命名约束:[a-zA-Z0-9_-]{1,32}。理由:
|
|
13
|
+
* - 排除路径分隔符与 .. 防止越权写文件。
|
|
14
|
+
* - 与 dotenv key 风格一致,便于未来扩展到同名 env override。
|
|
15
|
+
* - 32 位上限防极长名撑爆文件名系统。
|
|
16
|
+
*/
|
|
17
|
+
/** 预设目录:`~/.mocode/models/`(按需创建)。 */
|
|
18
|
+
export declare const MODELS_DIR: string;
|
|
19
|
+
/** 单个预设的内容:与 updateModelConfig 的可选字段对齐。 */
|
|
20
|
+
export interface ModelPreset {
|
|
21
|
+
name: string;
|
|
22
|
+
baseURL: string;
|
|
23
|
+
apiKey: string;
|
|
24
|
+
model: string;
|
|
25
|
+
contextWindow: number;
|
|
26
|
+
}
|
|
27
|
+
/** 名字是否合法(调用方复用,避免在多处重复同一正则)。 */
|
|
28
|
+
export declare function isValidPresetName(name: string): boolean;
|
|
29
|
+
/** 读单个预设;不存在抛错。 */
|
|
30
|
+
export declare function getPreset(name: string): ModelPreset;
|
|
31
|
+
/** 读单个预设;不存在返回 null(供列表/可选切换场景)。 */
|
|
32
|
+
export declare function readPreset(name: string): ModelPreset | null;
|
|
33
|
+
/** 写/覆盖一个预设(原子:写 tmp 再 rename)。 */
|
|
34
|
+
export declare function savePreset(preset: ModelPreset): void;
|
|
35
|
+
/** 删除一个预设;不存在返回 false,成功返回 true。 */
|
|
36
|
+
export declare function deletePreset(name: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 重命名一个预设(原子:link+unlink,跨设备时退化为 copy+unlink)。
|
|
39
|
+
* 用于 /model rename <old> <new>;不存在的旧名 / 已存在的新名返回 false,具体由调用方决定提示文案。
|
|
40
|
+
*/
|
|
41
|
+
export declare function renamePreset(oldName: string, newName: string): boolean;
|
|
42
|
+
/** 列出全部预设(按 name 升序);目录不存在返回空数组。 */
|
|
43
|
+
export declare function listPresets(): ModelPreset[];
|
|
44
|
+
/**
|
|
45
|
+
* 把当前 config 的 LLM 四键(baseURL/apiKey/model/contextWindow)迁为命名预设。
|
|
46
|
+
* 用于启动时一次性兜底老用户:加 /model 之前就已经在 ~/.mocode/config 写过的配置,
|
|
47
|
+
* /model list 应该立刻能看到,而不是空。已有同名预设则不重复写。
|
|
48
|
+
*
|
|
49
|
+
* 返回新建的预设名;若未配置完整(baseURL/apiKey 缺失)或已有同名则返回 null。
|
|
50
|
+
* 设计为幂等:重启调用一次也只会生效一次。
|
|
51
|
+
*/
|
|
52
|
+
export declare function migrateCurrentToPreset(input: {
|
|
53
|
+
baseURL: string;
|
|
54
|
+
apiKey: string;
|
|
55
|
+
model: string;
|
|
56
|
+
contextWindow: number;
|
|
57
|
+
}): string | null;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { ChatMessage } from '../llm/index.js';
|
|
2
|
+
/** 五区分账(占比对齐 CONTEXT_WINDOW)。顺序固定,便于遍历。 */
|
|
3
|
+
export declare const BUDGET_LAYERS: readonly ["system", "history", "toolRecent", "toolOld", "summary", "reserve"];
|
|
4
|
+
export type BudgetLayer = (typeof BUDGET_LAYERS)[number];
|
|
5
|
+
/** 占比(总和 = 0.95,留 5% 给 Reserve)。对齐用户修正版:
|
|
6
|
+
* Recent Tool 25%(原 40% 偏大,因 Hot 区不该被压)+ Old Tool 25% 同等 +
|
|
7
|
+
* History 20% + System 15% + Summary 10%(平时 0 占用,触发后才用) */
|
|
8
|
+
export declare const BUDGET_RATIO: Record<BudgetLayer, number>;
|
|
9
|
+
/** Hot/Cold 划分:当前 step 起往前 HOT_TURN_WINDOW 个 user turn 之内的工具结果视为 Hot,
|
|
10
|
+
* 之外的视为 Cold。0 = 全 Cold(等同老路径);越短 Hot 越小,压缩越激进。 */
|
|
11
|
+
export declare const HOT_TURN_WINDOW = 4;
|
|
12
|
+
/** 工具消息推入历史后,经过的「消费者 push 次数」即 age。
|
|
13
|
+
* Cold 区内:age ≥ TOOL_OLD_AGE 的非观察类工具结果可被调度器就地 stub。
|
|
14
|
+
* 默认 2 = 跨过 2 个消费者 push 仍未被消费,等同 lifecycle 的 DEFAULT_AGE_THRESHOLD。 */
|
|
15
|
+
export declare const TOOL_OLD_AGE = 2;
|
|
16
|
+
/** 从 history 末尾向前找最后一个 user 消息的索引;无 user 返 -1。 */
|
|
17
|
+
export declare function lastUserIndex(history: ChatMessage[]): number;
|
|
18
|
+
/** 从 idx 处向前数第 N 个 user turn 的边界 index(含该 user 之后的内容)。
|
|
19
|
+
* 用于把 history 切成 Hot 区(tail 一段,endExclusive=history.length)与 Cold 区(0..endExclusive)。
|
|
20
|
+
* 若 N 个 user 不足,Hot 区 = history.length(全保护);Cold 区空,无压缩目标。 */
|
|
21
|
+
export declare function userTurnBoundary(history: ChatMessage[], window: number): number;
|
|
22
|
+
/** 单区预算分配。绝对值 = ratio * window。 */
|
|
23
|
+
export interface LayerBudget {
|
|
24
|
+
actual: number;
|
|
25
|
+
budget: number;
|
|
26
|
+
overBudget: boolean;
|
|
27
|
+
/** overBudget 比值 = max(0, actual - budget) / budget;无超 = 0。
|
|
28
|
+
* 调度器用此值做 ROI 排序——溢出越多,优先处理。 */
|
|
29
|
+
overRatio: number;
|
|
30
|
+
}
|
|
31
|
+
/** 一次评估的完整报告(供 agent/core.ts 决策)。 */
|
|
32
|
+
export interface BudgetReport {
|
|
33
|
+
step: number;
|
|
34
|
+
total: number;
|
|
35
|
+
window: number;
|
|
36
|
+
layers: Record<BudgetLayer, LayerBudget>;
|
|
37
|
+
/** 实际超预算的层(按 overRatio 降序,排前面先处理)。 */
|
|
38
|
+
triggers: BudgetLayer[];
|
|
39
|
+
/** 总占用超总阈(0.85*window)的兜底触发 — 单独字段,与 layers.triggers 分开。 */
|
|
40
|
+
totalOver: boolean;
|
|
41
|
+
/** Hot/Cold 边界:Cold 区 = [1, hotBoundary);Hot 区 = [hotBoundary, length)。
|
|
42
|
+
* agent/core.ts 拿到后可对 Cold 区做就地 stub,Hot 区只 cap。 */
|
|
43
|
+
hotBoundary: number;
|
|
44
|
+
}
|
|
45
|
+
/** 评估当前 history 的五区预算(纯函数,改不动 history)。
|
|
46
|
+
* 传入 step 是当前所在 step 编号(agent 循环 step 变量),用于日志/调试。 */
|
|
47
|
+
export declare function evaluateBudget(history: ChatMessage[], window: number, step?: number): BudgetReport;
|
|
48
|
+
/** 调度器决策动作(纯数据,不动 history)。agent/core.ts 据此调用既有闸。 */
|
|
49
|
+
export type ScheduleAction = {
|
|
50
|
+
kind: 'warn';
|
|
51
|
+
layer: BudgetLayer;
|
|
52
|
+
reason: string;
|
|
53
|
+
} | {
|
|
54
|
+
/** 对 Cold 区(hotBoundary 之前)的 tool 消息按 ROI 做 L1→L2→L3 渐进压缩。
|
|
55
|
+
* 注意:Hot 区永不发此 action——精细剔除由 lifecycle/pruner/drop_context 负责。 */
|
|
56
|
+
kind: 'shrink_cold_tools';
|
|
57
|
+
level: 1 | 2 | 3;
|
|
58
|
+
} | {
|
|
59
|
+
/** Hot 区只 cap(降单条上限),不 stub;aggressive = true 时调低阈值走更严的 cap。
|
|
60
|
+
* 精细剔除(cap 不够时)由 lifecycle age stub 与 drop_context 工具兜底。 */
|
|
61
|
+
kind: 'cap_hot_tools';
|
|
62
|
+
aggressive: boolean;
|
|
63
|
+
} | {
|
|
64
|
+
kind: 'compact_history';
|
|
65
|
+
focus?: string;
|
|
66
|
+
};
|
|
67
|
+
/** 根据 BudgetReport 生成调度动作(从轻到重,直至总占用回落到 0.85 以下)。
|
|
68
|
+
* 规则:
|
|
69
|
+
* - system 超 → warn(不压,配置问题不是内容问题)
|
|
70
|
+
* - toolOld 超 → 先 L1(中截超大)→ L2(same-path 已有 relevance)→ L3(age stub,新增)
|
|
71
|
+
* - toolRecent 超 → cap(只降低单条上限,不 stub)
|
|
72
|
+
* - history 超 或 totalOver → compact_history(调 maybeCompact / compactHistory)
|
|
73
|
+
* - summary 超 → 不动(摘要本身就压缩产物,删它等于丢历史,只能放任或扩 Recent 预算) */
|
|
74
|
+
export declare function scheduleActions(report: BudgetReport): ScheduleAction[];
|
|
75
|
+
/** 拍平成人类可读(供 /context 命令与 check-budget 脚本用)。 */
|
|
76
|
+
export declare function formatReport(report: BudgetReport): string;
|
|
77
|
+
/** 便捷:把 history 一把估成总 token 数(给 cap.js 等复用,避免重复实现)。
|
|
78
|
+
* 注意:此处是粗估(只看 content 长度),不区分五区——只用于「系统层整体还剩多少」快查。 */
|
|
79
|
+
export declare function quickEstimate(history: ChatMessage[]): number;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ContextKind } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 判定 ContextKind。
|
|
4
|
+
* - 有 BY_NAME 强先验 → 用之(内置工具确定性强)。
|
|
5
|
+
* - 否则形状启发(MCP / 未来工具)。
|
|
6
|
+
* - 都不中 → passthrough(不动)。
|
|
7
|
+
*
|
|
8
|
+
* @param toolName 工具名
|
|
9
|
+
* @param output 工具原始输出(形状启发用;有 BY_NAME 时不读)
|
|
10
|
+
* @param _args 已解析参数(预留:未来 read_file 的 offset/limit 可影响 code 编码策略;Phase 1 不用)
|
|
11
|
+
*/
|
|
12
|
+
export declare function classify(toolName: string, output: string, _args: Record<string, unknown> | null): ContextKind;
|
|
13
|
+
/** 暴露 BY_NAME 副本供调试 / 未来 /context 展示(只读视图)。 */
|
|
14
|
+
export declare function knownToolKinds(): Record<string, ContextKind>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** 去 ANSI CSI 序列(颜色 / 光标 / 清屏等)。语义无损:颜色码不含信息。 */
|
|
2
|
+
export declare function stripAnsi(s: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* 折叠连续空行(只含空白字符的行)≥ threshold → 单个空行。
|
|
5
|
+
* 用于 prose / source dump(web_fetch / use_skill / task / codegraph):HTML→文本与 markdown 常留多余空行,
|
|
6
|
+
* 多空行与单空行语义等价,折叠无损。默认 threshold=3(只动真正过量的空行,常见 ≤2 空行不动)。
|
|
7
|
+
*
|
|
8
|
+
* 注意:用 `.trim() === ''` 判空——故 read_file 的 ` 2\t`(行号前缀 + tab,trim 后剩 `2`)不会被
|
|
9
|
+
* 视作空行。read_file 的空行折叠见 code encoder(前缀感知)。
|
|
10
|
+
*/
|
|
11
|
+
export declare function collapseBlankRuns(text: string, threshold?: number): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ContextEncoder } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Doc Encoder(web_fetch / use_skill):折叠连续空行(≥3 → 1)。
|
|
4
|
+
*
|
|
5
|
+
* 输入:
|
|
6
|
+
* - web_fetch 的 `URL (HTTP ...)\n\n<body>`(HTML→文本,常留多余空行)。
|
|
7
|
+
* - use_skill 的 `# Skill: name\n\n<body>`(SKILL.md 正文,frontmatter 已剥离)。
|
|
8
|
+
* 输出:空行折叠;正文 / 指令逐字保留。
|
|
9
|
+
*
|
|
10
|
+
* 不变量:URL 前缀行 / Skill 标题行保留;正文文本逐字保留(仅折叠空行——markdown / HTML 多空行与
|
|
11
|
+
* 单空行语义等价,无损)。use_skill 走放宽 cap(MAX_SKILL_RESULT)保指令完整——本 encoder 在 cap 前先
|
|
12
|
+
* 折叠空行,减少被 cap 截断的风险。
|
|
13
|
+
*/
|
|
14
|
+
export declare const docEncoder: ContextEncoder;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContextEncoder } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Graph Encoder(codegraph):去 ANSI + 折叠连续空行(≥3 → 1)。
|
|
4
|
+
*
|
|
5
|
+
* 输入:codegraph CLI 返回的 `[退出码 N]\n<源码 + 调用路径>`(可能含 ANSI 颜色、多余空行)。
|
|
6
|
+
* 输出:去 ANSI + 空行折叠;结构(调用路径块、源码段)不动。
|
|
7
|
+
*
|
|
8
|
+
* 不变量:退出码行保留;源码与调用路径文本逐字保留(仅去颜色码 + 折叠空行);不删内容行。
|
|
9
|
+
* 保守:不重构调用路径 / 不去重源码(codegraph CLI 输出格式未稳定,需先采样真实输出定不变量,留后续)。
|
|
10
|
+
*/
|
|
11
|
+
export declare const graphEncoder: ContextEncoder;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContextEncoder } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 兜底 encoder:identity,原样返回。
|
|
4
|
+
* - classifier 未命中任何 kind(返回 'passthrough')时用。
|
|
5
|
+
* - pipeline 总开关关闭(MOCODE_CONTEXT_OPTIMIZE=false)时,所有 kind 都走它 → 行为与改造前逐字节一致。
|
|
6
|
+
* - Phase 1 阶段 registry 只注册它 → 全链路零行为变化。
|
|
7
|
+
* - 任何 encoder 报错时,pipeline catch 后回落到它(传原 output)。
|
|
8
|
+
*
|
|
9
|
+
* 永不抛错:output 可能是任意字符串(含 ANSI / 多行 / 非法 UTF-8 片段),identity 直接返回,无解析风险。
|
|
10
|
+
*/
|
|
11
|
+
export declare const passthroughEncoder: ContextEncoder;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ContextEncoder } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Summary Encoder(task):折叠连续空行(≥3 → 1)。
|
|
4
|
+
*
|
|
5
|
+
* 输入:task 工具返回的子 agent 最终摘要文本(已是摘要,可能含多余空行),可能带截断尾标
|
|
6
|
+
* `…(子 agent 摘要已截断 N 字符)`。
|
|
7
|
+
* 输出:空行折叠;摘要文本逐字保留。
|
|
8
|
+
*
|
|
9
|
+
* 不变量:摘要事实文本逐字保留(仅折叠空行);截断尾标保留。子 agent 摘要已是高密度文本,只做轻量去冗余,
|
|
10
|
+
* 不再做进一步压缩(避免丢事实)。
|
|
11
|
+
*/
|
|
12
|
+
export declare const summaryEncoder: ContextEncoder;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { optimizeToolResult } from './pipeline.js';
|
|
2
|
+
export type { ContextKind, ContextEncoder, EncoderInput, EncoderOutput, } from './types.js';
|
|
3
|
+
export { classify, knownToolKinds } from './classifier.js';
|
|
4
|
+
export { registerEncoder, registerAll, getEncoder, registeredKinds, } from './registry.js';
|
|
5
|
+
export { evaluateBudget, scheduleActions, formatReport, quickEstimate, userTurnBoundary, lastUserIndex, BUDGET_LAYERS, BUDGET_RATIO, HOT_TURN_WINDOW, TOOL_OLD_AGE, } from './budget.js';
|
|
6
|
+
export type { BudgetLayer, LayerBudget, BudgetReport, ScheduleAction, } from './budget.js';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ChatMessage } from '../llm/index.js';
|
|
2
|
+
/** 工具消息的观察者生命周期状态。 */
|
|
3
|
+
export type LifeState = 'LIVE' | 'REFERENCED' | 'OBSOLETE' | 'STUB';
|
|
4
|
+
/**
|
|
5
|
+
* Observation Lifecycle Engine。
|
|
6
|
+
* 每个 runAgentCore 实例持一个;pushToolResult 出口调 pushTool、mutation 分支调 pushMutation。
|
|
7
|
+
* 内部 try/catch 兜底,对外永不抛错。
|
|
8
|
+
*/
|
|
9
|
+
export declare class LifecycleEngine {
|
|
10
|
+
private readonly ageThreshold;
|
|
11
|
+
/** 工具消息 idx → 状态。 */
|
|
12
|
+
private readonly states;
|
|
13
|
+
/** 工具消息 idx → 被消费的次数(同一上游被多次消费也只算 REFERENCED,不计并发)。 */
|
|
14
|
+
private readonly consumerCount;
|
|
15
|
+
/** 工具消息 idx → 自 push 以来的「消费者 push」次数(用于老化判定)。
|
|
16
|
+
* 每次 pushTool 触发,所有 LIVE 工具消息 age++。 */
|
|
17
|
+
private readonly age;
|
|
18
|
+
/** producer 路径 → 生产者工具消息 idx 列表(逆查用:某个 path 被消费时,反查上游 producer)。
|
|
19
|
+
* 注意:不存 read_file,因为 read 自身就是消费者不充当 producer。 */
|
|
20
|
+
private readonly producersByPath;
|
|
21
|
+
/** 当前步序号(用于 age 老化:每次 pushTool 自增,对比 age 阈值)。 */
|
|
22
|
+
private step;
|
|
23
|
+
/** 最后 user 索引缓存(pushTool 时重算;pushMutation 时也重算,因为 mutation 可能跟 user 同行)。 */
|
|
24
|
+
private lastUser;
|
|
25
|
+
constructor(ageThreshold?: number);
|
|
26
|
+
/** 新工具结果 push 进 history 时调;idx = history.length - 1。
|
|
27
|
+
* mutation 工具(edit_file/write_file)的 push 跳过本轮的 autoStubOrphans(由调用方在
|
|
28
|
+
* pushMutation 标完 read REFERENCED 之后再触发),避免刚被 mutation 消费的 read 被提前 STUB。 */
|
|
29
|
+
pushTool(history: ChatMessage[], idx: number): void;
|
|
30
|
+
/** mutation 工具(edit_file/write_file)push 后调。语义与 pushTool 一致,但额外标记「被 mutation 消费」的 read。
|
|
31
|
+
* 注意:本层不直接 stub read(那是 Relevance Pruner 的职责);本层只更新状态图。
|
|
32
|
+
* 注意:puhToolResult 出口已经登记过 mutation 本身,这里不再调 pushTool(避免 age 翻倍)。 */
|
|
33
|
+
pushMutation(history: ChatMessage[], mutationIdx: number, path: string): void;
|
|
34
|
+
/** 老化自动 STUB:扫描所有 LIVE(且非观察类)且 age ≥ 阈值且不在保护区的工具消息 → OBSOLETE → STUB。 */
|
|
35
|
+
private autoStubOrphans;
|
|
36
|
+
/** 实际替换 content 为存根。 */
|
|
37
|
+
private stubOne;
|
|
38
|
+
/** 找某条 tool 消息对应的 assistant.tool_calls.arguments。 */
|
|
39
|
+
private findToolArgs;
|
|
40
|
+
/** 拿某 idx 的当前状态;不在图里返 null。 */
|
|
41
|
+
getState(idx: number): LifeState | null;
|
|
42
|
+
/** 拿当前各状态计数。供 /context 显示「live=N, referenced=M, obsolete=K, stubbed=S」。 */
|
|
43
|
+
stats(): {
|
|
44
|
+
live: number;
|
|
45
|
+
referenced: number;
|
|
46
|
+
obsolete: number;
|
|
47
|
+
stubbed: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** 默认单例工厂。runAgentCore 入口 new 一个,后续 pushTool / pushMutation 共享。 */
|
|
51
|
+
export declare function createLifecycleEngine(ageThreshold?: number): LifecycleEngine;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 优化工具结果为进 LLM 的 tool 消息 content。
|
|
3
|
+
*
|
|
4
|
+
* @param name 工具名
|
|
5
|
+
* @param output executeTool 的原始返回字符串
|
|
6
|
+
* @param argsRaw 工具 arguments 原始 JSON 字符串(tc.arguments,可空;未传则 args=null)
|
|
7
|
+
* @returns 进 history 的 content 字符串(永不抛错)
|
|
8
|
+
*/
|
|
9
|
+
export declare function optimizeToolResult(name: string, output: string, argsRaw?: string): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ContextEncoder, ContextKind } from './types.js';
|
|
2
|
+
/** 注册一个 encoder(后注册覆盖先注册,允许 MCP 覆盖默认)。返回 encoder 自身供链式。 */
|
|
3
|
+
export declare function registerEncoder(enc: ContextEncoder): ContextEncoder;
|
|
4
|
+
/** 批量注册(启动期 pipeline 调一次)。幂等:重复调忽略。 */
|
|
5
|
+
export declare function registerAll(list: ContextEncoder[]): void;
|
|
6
|
+
/** 取某 kind 的 encoder;未注册返 undefined(pipeline 回落 passthrough)。 */
|
|
7
|
+
export declare function getEncoder(kind: ContextKind): ContextEncoder | undefined;
|
|
8
|
+
/** 调试:列出已注册 kind。 */
|
|
9
|
+
export declare function registeredKinds(): ContextKind[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ChatMessage } from '../llm/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* 维护「path → 该 path 所有 read_file tool 消息的 history index」映射。
|
|
4
|
+
* - observePush:把刚 push 的 read_file tool 消息登记,并把同 path 的"更早" read 全部 stub。
|
|
5
|
+
* - observeMutation:把该 mutation path 的"在 mutation 之前的" read 全部 stub。
|
|
6
|
+
*
|
|
7
|
+
* 设计:每个 agent 会话(每个 runAgentCore 实例)持有一个 pruner。会话结束/换 plan 时
|
|
8
|
+
* 可新建;不持久化(history 重建时索引自然过期)。
|
|
9
|
+
*
|
|
10
|
+
* 零依赖:仅依赖 ChatMessage 形状;不 import llm / tools / agent。
|
|
11
|
+
*/
|
|
12
|
+
export declare class RelevancePruner {
|
|
13
|
+
/** path → [history index, ...] 按插入序;最新在末尾。 */
|
|
14
|
+
private readonly readByPath;
|
|
15
|
+
/** 把刚 push 的消息通知 pruner。
|
|
16
|
+
* - 只处理 tool 消息(role==='tool')。
|
|
17
|
+
* - 只关心 read_file:登记 + 反向 stub 同 path 旧 read。
|
|
18
|
+
* - 非 read_file 的 tool 消息:无操作(本层只管 read_file)。
|
|
19
|
+
* - 非 tool 消息(assistant / user / system):无操作。
|
|
20
|
+
*/
|
|
21
|
+
observePush(history: ChatMessage[], msg: ChatMessage): void;
|
|
22
|
+
/**
|
|
23
|
+
* 把该 mutation path 的"在 mutation 之前的" read 全部 stub。
|
|
24
|
+
* 通常用于 edit_file / write_file 工具:mutation 之后,之前的 read_file(p) 内容
|
|
25
|
+
* 已失效(已不再是文件当前状态),模型后续若依赖旧 read 来 edit_file 会失败,但 edit_file
|
|
26
|
+
* 的 old_string 来自模型记忆/后读,不依赖旧 read 结果文本。
|
|
27
|
+
*
|
|
28
|
+
* 调用时机:agent/core.ts 在 mutation 工具调用的 pushToolResult 之后立即调;
|
|
29
|
+
* 此时 history 末尾就是 mutation 的 tool 消息,prior reads 指 < idx。
|
|
30
|
+
*/
|
|
31
|
+
observeMutation(history: ChatMessage[], path: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* 把 history 里 "path 同 + index < beforeIdx + 不在当前轮保护区" 的所有 read_file
|
|
34
|
+
* tool 消息替换为存根(只改 .content,不动 id / 数组结构)。
|
|
35
|
+
*
|
|
36
|
+
* 实现:
|
|
37
|
+
* - 用 readByPath[path] 直接拿到所有 index(已登记过),筛 < beforeIdx 的 stub。
|
|
38
|
+
* - 同时扫一遍 [1, beforeIdx) 区间找未登记的(防御:索引可能漏登;不依赖索引也能 stub,
|
|
39
|
+
* 保证正确性。索引只用于"避免重复扫全表"的优化)。
|
|
40
|
+
* - protectedFrom = lastUserIndex(history):user 之后一律不动。
|
|
41
|
+
* - 幂等:已是 STUB_PREFIX 的跳过。
|
|
42
|
+
*/
|
|
43
|
+
private stubPriorReads;
|
|
44
|
+
}
|
|
45
|
+
/** 默认单例:每个 agent 循环一个。runAgentCore 入口 new 一个,后续 observe 共享。 */
|
|
46
|
+
export declare function createRelevancePruner(): RelevancePruner;
|
|
47
|
+
/**
|
|
48
|
+
* 扫 history,统计被相关性裁剪 stub 的 read_file tool 消息(条数 + 原字节数)。
|
|
49
|
+
* 供 /context 渲染统计行用(让用户直观看到「prune 帮了多少」)。
|
|
50
|
+
* 永不抛错(对齐本模块契约);history 为空 / 无 stub 时返零值。
|
|
51
|
+
*
|
|
52
|
+
* 注意:stub 后只剩 stub 字符串(原 content 已丢失),故只能从 stub 字符串里 parse
|
|
53
|
+
* 原字节数,误差 = stub 时记录的 content.length(精确);token 估算走 estimateTokens。
|
|
54
|
+
*/
|
|
55
|
+
export declare function computePruneStats(history: ChatMessage[]): {
|
|
56
|
+
stubbed: number;
|
|
57
|
+
/** 原 content 总字节数(仅 stubbed 的)。 */
|
|
58
|
+
originalChars: number;
|
|
59
|
+
/** 反推原 token 数(粗略:estimateTokens(originalChars 字符))。 */
|
|
60
|
+
originalTokens: number;
|
|
61
|
+
/** 当前 stub 字符串总字节数。 */
|
|
62
|
+
stubChars: number;
|
|
63
|
+
/** 估算释放的 token 数(originalTokens - 当前 stub 占的 token)。 */
|
|
64
|
+
freedTokens: number;
|
|
65
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Context 的数据类型。classifier 据工具名 + 输出形状判定,选 encoder。 */
|
|
2
|
+
export type ContextKind = 'tree' | 'search' | 'graph' | 'log' | 'code' | 'table' | 'memory' | 'doc' | 'status' | 'summary' | 'diff' | 'ast' | 'repo' | 'passthrough';
|
|
3
|
+
/** encoder 的输入。pipeline 解析 argsRaw 后传入。 */
|
|
4
|
+
export interface EncoderInput {
|
|
5
|
+
/** 工具名(强先验信号)。 */
|
|
6
|
+
toolName: string;
|
|
7
|
+
/** 工具原始输出字符串(executeTool 的返回值,未经任何编码)。 */
|
|
8
|
+
output: string;
|
|
9
|
+
/** 已解析的工具参数;非法或空返 null。encoder 可据此做上下文感知编码(如 read_file 的 offset/limit)。 */
|
|
10
|
+
args: Record<string, unknown> | null;
|
|
11
|
+
/** 软目标字符数(来自 tools/constants.ts 的 cap 常量,按工具名取)。encoder 应尽量压到 budget 内,
|
|
12
|
+
* 但最终长度裁剪仍由 pipeline 末尾的 capToolResultForHistory 兜底(保 head+标记+tail)。 */
|
|
13
|
+
budget?: number;
|
|
14
|
+
}
|
|
15
|
+
/** encoder 的输出。 */
|
|
16
|
+
export interface EncoderOutput {
|
|
17
|
+
/** 编码后文本(喂给 LLM 的 tool 消息 content)。 */
|
|
18
|
+
text: string;
|
|
19
|
+
/** 编码元数据(调试 / 未来 /context 统计用;不影响喂给 LLM 的内容)。 */
|
|
20
|
+
meta?: {
|
|
21
|
+
kind: ContextKind;
|
|
22
|
+
originalLen: number;
|
|
23
|
+
encodedLen: number;
|
|
24
|
+
/** 给调试者的备注(如"12 files · tree-encoded")。 */
|
|
25
|
+
note?: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Encoder 统一接口:一类型一 encoder,纯函数,不抛错(失败由 pipeline 兜底)。
|
|
30
|
+
* 扩展:在 encoders/ 加 xxx.ts 导出 ContextEncoder,在 encoders/index.ts 的 builtinEncoders 加一行。
|
|
31
|
+
* 无需动 agent / llm / core。
|
|
32
|
+
*/
|
|
33
|
+
export interface ContextEncoder {
|
|
34
|
+
kind: ContextKind;
|
|
35
|
+
encode(input: EncoderInput): EncoderOutput;
|
|
36
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型能力检测:仅用于 /image 等多模态输入的友好提示。
|
|
3
|
+
*
|
|
4
|
+
* 策略 — 保守 + 显式白名单/黑名单:
|
|
5
|
+
* - 命中 KNOWN_TEXT_ONLY_PREFIXES 任一前缀 → false(确定无视觉)
|
|
6
|
+
* - 命中 KNOWN_VISION_FAMILIES 任一前缀 → true(确定有视觉)
|
|
7
|
+
* - 其它(未识别 / 自定义代理 / 新发布模型)→ 默认 true(放行,让 API 拒收兜底)
|
|
8
|
+
*
|
|
9
|
+
* 理由:视觉能力在 2024 后已成新模型默认;自定义 OpenAI 代理常把上游视觉模型重命名;
|
|
10
|
+
* 过度保守会误伤合法配置。
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* 判断模型是否支持视觉(多模态 image_url)。
|
|
14
|
+
* - 模型未配置(`config.model` 为空或 default 兜底字符串)→ true(不阻断,等 API 报错)
|
|
15
|
+
* - 命中 KNOWN_TEXT_ONLY_PREFIXES 任一前缀 → false(确定无视觉,优先查,避免 gpt-4o-mini vs -search 系列名冲突)
|
|
16
|
+
* - 命中 KNOWN_VISION_FAMILIES 任一前缀 → true
|
|
17
|
+
* - 其它(未识别 / 自定义代理 / 新发布模型)→ true(保守放行)
|
|
18
|
+
*/
|
|
19
|
+
export declare function modelSupportsVision(model: string): boolean;
|