thincoder 0.12.37 → 0.12.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +303 -0
- package/package.json +2 -1
- package/src/agent-tools/consult.mjs +48 -9
- package/src/agent-tools/verify.mjs +13 -8
- package/src/agent.mjs +12 -1
- package/src/config.mjs +16 -4
- package/src/context.mjs +148 -2
- package/src/prompts/discipline.md +8 -4
- package/src/prompts/main.md +4 -2
- package/src/tools/bash.md +1 -0
- package/src/tools/codemode.mjs +125 -134
- package/src/tools/exec-prelude.mjs +84 -0
- package/src/tools/execute.md +14 -2
- package/src/tools/file_ops.md +16 -0
- package/src/tools/get_current_time.md +6 -0
- package/src/tools/git.md +10 -4
- package/src/tools/git.mjs +60 -8
- package/src/tools/grep.md +3 -1
- package/src/tools/index.mjs +7 -1
- package/src/tools/ls.md +1 -0
- package/src/tools/ops.mjs +142 -0
- package/src/tools/process.md +10 -0
- package/src/tools/sleep.md +5 -0
- package/src/tools/system.mjs +28 -6
- package/src/tools/tree.md +13 -0
- package/src/tools/tree.mjs +66 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
本文件记录 ThinCoder CLI 的发布历史。格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),版本遵循[语义化版本](https://semver.org/lang/zh-CN/)。
|
|
4
|
+
|
|
5
|
+
## [0.12.39] — 2026-08-23
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **主 agent 委托策略**:`main.md` 把「广度探索 → explore 子代理(隔离上下文,逐步读/搜不进主历史)」「仅当即将立刻编辑时才自己 read」「coder 验证 = 读改动文件 + 跑测试」从建议升级为明确规则
|
|
10
|
+
- **历史卫生**:轮末 `summarizeRunExplorations` 把一轮内连续探索结果(read/grep/glob/ls/code_search/doc_search/repo_outline)LLM 蒸馏为 `[Exploration summary]`(机器线收缩、人读线全量不变);压缩 `SUMMARIZE_PROMPT` 加「已改动文件清单 + 未决点/待办」两清单
|
|
11
|
+
- **编码纪律**:`discipline.md` 工作流程 + 调试策略要求用 `task` 跟踪;「改码前读文档」「中/小改后更新文档」嵌入 Workflow 各 tier 箭头序列
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- 轮末探索摘要边界 `_runStartHistoryLen` 在压缩重建机器线后变 stale → 重置到 tail 起点
|
|
16
|
+
|
|
17
|
+
## [0.12.38] — 2026-08-23
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **对齐 thinworker 编程工具集**:新增 `file_ops`(move/copy/rename)、`process`(列进程)、`get_current_time`、`sleep` 四个内置工具,及 `tree`(递归目录树 + `depth`,对齐 thinworker `repomap`);各工具描述带「Route to X instead of bash」反向路由
|
|
22
|
+
- **内部能力对齐 thinworker**:`grep` 加 `literal`/`ignoreCase`;`ls` 加 `filter`(通配符);`bash` 加 `filter`(正则行过滤);`git` 加 `show`/`rm`/`commit`/`push` 子命令 + `filter`;`verify` 加 `filter`/`workdir`
|
|
23
|
+
- **`execute` 工具重做**:由同步 vm 沙箱改为 `node --input-type=module --eval` 子进程——支持顶层 `await`、动态 `import()`(直接加载项目 `.mjs`)、原生 `console`/`fetch`,新增 `workdir`/`filter` 参数,保留 killable 超时(无限循环可被终止);`exec-prelude.mjs` 提供 readFile/writeFile/glob/grep/log/require(路径隔离 workspace)
|
|
24
|
+
- **consult 指定子集模型**:`consult_start` 新增 `models` 选择器(`provider:model`/裸 provider/裸 model,大小写不敏感、去重保序)
|
|
25
|
+
- **模型规格更新**:新增 glm-5.3、deepseek-v4-flash-vision-exp;gpt-5.6/claude-5 收敛 `thinking=false`
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **apply_patch 多 hunk 错位**:同一文件多个 hunks 因前一段改变行数而错位应用、静默损坏——改为重扫上下文 + splice
|
|
30
|
+
- **git 写操作吞错**:`commit`/`push`/`rm` 改用 `runGitStrict` 返回 stderr + exit code,失败不再伪装成功;`show` 补 ref 校验(防 `-` 开头注入选项)
|
|
31
|
+
- **tool 补齐项加固**:`get_current_time` 补 weekday;`sleep` 防 NaN;`tree` 深度/计数校验、省略号单次、根目录报错;`log` 非法 count 回退默认
|
|
32
|
+
|
|
33
|
+
### Docs
|
|
34
|
+
|
|
35
|
+
- `TOOLS.md` 注册表计数同步(含 ops/tree)
|
|
36
|
+
- Changelog backfill 0.12.36 / 0.12.33
|
|
37
|
+
|
|
38
|
+
## [0.12.37] — 2026-08-22
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **advisor 标签还原**:状态栏徽标与 /advisor 菜单项由 "GUARD" 显示名还原为 "ADVISOR"(内部配置字段 `advisor.guard` 不变,仅显示名)
|
|
43
|
+
- **SSE 流式 tool_calls 防御性合并**(PROVIDER.md §10):跳过 null/畸形元素并计数,按 index/id/name/tail 合并、补齐缺失 id、计数丢弃
|
|
44
|
+
- **Windows 剪贴板 BOM 防御**:UTF-8 输出后剥离前导 `\uFEFF`(IK9UWM 后续)
|
|
45
|
+
- **上下文压缩空安全**:`t.function?.name` 防 tool name 缺失
|
|
46
|
+
|
|
47
|
+
### Refactor
|
|
48
|
+
|
|
49
|
+
- **session.mjs 拆分**:`migrateHashLength` 抽到 `session-migrate.mjs`,文件回到 ≤500 行硬限内
|
|
50
|
+
- **/config embedding 保存重构**:`embeddingPatch()` 抽取 + `DEFAULTS` 导出(供单测)
|
|
51
|
+
|
|
52
|
+
### Prompt
|
|
53
|
+
|
|
54
|
+
- **确认纪律 carve-out**:system.md 补 doc/code 一致性例外——"改动前确认"门禁不适用于已确认任务的既有义务(文档跟码一致、记录刚做的决策、关闭 advisor 标记的文档缺口)
|
|
55
|
+
|
|
56
|
+
## [0.12.36] — 2026-08-22
|
|
57
|
+
|
|
58
|
+
### Docs & advisor
|
|
59
|
+
|
|
60
|
+
- **文档归属纪律 + advisor 设计评审增强**(规格 AGENT-LOOP.md §12):新建 `docs/design/README.md` 文档地图(板块→文档映射表 + 存量碎片"待合并(TODO)"标注 + 归属规则);system.md 补文档归属纪律条款(写文档前先查地图定位所属板块——找到就改、不得为既有板块新建文件;确无归属才新建并登记;同一机制只在一处详述权威源、其余引用不复制);advisor-design.md 加第 7 维 **Document ownership**(与现有文档矛盾 🔴、该并入却新建/重复描述 🟡)与引用纪律(引用原文用精确 file:line、未核实标注 unverified);design 提示词 fallback 删除转硬加载(`loadPrompt` 同 round1/2/3 待遇,缺失即抛错——静默降级会丢 Approval Signal 规则致评审无法批准);messages.mjs design 分支 Instructions 补 Methodology compliance 维度、存在文档地图时注入 Document Map 段供归属检查对照。两端 prompts 保持 byte-identical、测试同步覆盖
|
|
61
|
+
|
|
62
|
+
## [0.12.35] — 2026-08-21
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- **advisor 开关语义重构**:评审能力恒启用——`advisor` 工具任何模式都可调用(删除 `advisor.enabled` gate,不再返回 "not enabled");开关语义收敛为 guard——收尾推回仅当 `advisor.guard === true`(默认 OFF,评审自愿调用,打开才强制)。工程模式行为不变(评审恒可用、guard 豁免)
|
|
67
|
+
- **`advisor.enabled` 废弃**:字段不再读写,存量配置不迁移——旧 `{ enabled: true }` 用户升级后不再强制评审(pre-release 约定,CHANGELOG 说明即可);/advisor 菜单删除 "Advisor ON/OFF" toggle,Guard 成为唯一开关
|
|
68
|
+
|
|
69
|
+
### Prompt system
|
|
70
|
+
|
|
71
|
+
- **提示词借鉴增量(kimi-code 对照)**:explore.md 新增 Thoroughness levels 三档(quick 单点定向 / medium 默认适度并行 / thorough 全面分析且报告须列出搜索过什么与没找到什么);main.md Delegate well 补委派 explore 时在 task 描述中指定彻底度(未指定走默认);system.md 确认理解句补 "including the most important acceptance criteria";subagent 工具 description 同步补彻底度说明。两端 15 个 prompt 文件保持 byte-identical(新增 CLI 侧比对测试防漂移)
|
|
72
|
+
- **开工前计划确认纪律**:system.md 追加无豁免纪律——任何写文件动作(write/edit/apply_patch/insert_after/delete/hashline_edit 及一切写文件的 bash)前必须纯文字复述理解+计划要点并等待用户明确确认(未确认/沉默/用户回复新问题或新要求 → 一律不动手;"这太明显了不用问"不是跳过理由;用户的新问题不是确认;需求变化后重新复述重新确认);engineering.md 澄清完成后、写需求/设计文档前同样须把理解+计划文字化并等待确认。两端 15 个 prompt 文件保持 byte-identical(两端测试断言关键句)
|
|
73
|
+
|
|
74
|
+
## [0.12.34] — 2026-08-18
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- **/rename 命令** — 改会话标题(renameSlot 双写,与 VS Code 共享)
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- **/config 候选池 effort picker 显示真实枚举** — 从固定 min/low/medium/high/max 改为动态读 specForModel(model).reasoningEffortEnum;无枚举的模型跳过 effort 步
|
|
83
|
+
- **question 工具 options 防御** — LLM 误传对象时取 label 字段,避免渲染 [object Object]
|
|
84
|
+
|
|
85
|
+
## [0.12.33] — 2026-08-17
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
|
|
89
|
+
- **撞轮数墙可无限继续**:subagent/飞刀/会诊统一经 continue 面板无限续(resume 保留 history,会诊继续重置墙钟;去掉 MAX_RESUMES)
|
|
90
|
+
- **MiMo 预置 provider**:按量付费(api.xiaomimimo.com/v1) + Token Plan(token-plan-cn.xiaomimimo.com/v1),模型规格 mimo-v2.5-pro/mimo-v2.5(1M 上下文 / 128K 输出 / 深度思考)
|
|
91
|
+
- **环境变量配置源彻底移除**:THINCODER_* 回退全部删除,config.json 唯一配置源;空配置不再合成 deepseek 默认 provider
|
|
92
|
+
- **effort 枚举钳制 + qwen3.8-max spec 修正**(consult/escalate)
|
|
93
|
+
|
|
94
|
+
## [0.12.32] — 2026-08-16
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
|
|
98
|
+
- **会诊触发条款重构**(两轮会诊驱动的修复):触发规则从飞刀段移入会诊段且自包含——功能请求语义("会诊一下"触发、"consult the docs"不触发)+ 用户请求覆盖自主判断;consult_start 描述补对称触发句;飞刀段补 fly-in/口语变体
|
|
99
|
+
- **飞刀三个真实断链**(会诊发现,此前"代码在但真实跑必翻车"):
|
|
100
|
+
- 删墙钟看门狗——固定墙钟误杀正常但慢的手术(实测两个 max-effort 顾问读 5 个文件即撞 10min 墙);完全依赖 turns + FETCH_TIMEOUT + 用户 Stop 直传
|
|
101
|
+
- effort 枚举钳制——池 effort 越界不再让候选"起飞即死",回退预设并标注
|
|
102
|
+
- AUTO 传导对齐 subagent——headless 嵌入下父 autoApprove 正确放行子 agent 写操作
|
|
103
|
+
- **config 加载校验**:consultModels 池 provider 名必须存在于 providers[],条目形状校验——静默运行时失败改为启动即报错
|
|
104
|
+
- **撞墙可继续(kimi-k3 飞刀)**:escalate 子 agent 撞 turn 上限后弹"继续?"(复用 onPermissionRequest,TUI 同款 y/n 面板),resume:true 续跑不重复注入任务、预算重置,上限 2 次;顺带修复 ContinueError e.turns → e.turn(原来打印 "undefined turns")
|
|
105
|
+
- consult 死代码补 precheck(无 key 时明确失败回复而非原始 401)
|
|
106
|
+
|
|
107
|
+
### Docs
|
|
108
|
+
|
|
109
|
+
- CLI CONSULTATION.md / ESCALATE.md 文档地图收录、FEATURES.md 功能全览补齐(7→11 个)、README 会诊别名、checklist T2-T6 验收
|
|
110
|
+
|
|
111
|
+
## [0.12.31] — 2026-08-16
|
|
112
|
+
|
|
113
|
+
### Fixed
|
|
114
|
+
|
|
115
|
+
- **/config consult model picker**: adding a consult model now uses pickModelForSlot (provider AND model are both OPTION pickers, reusing /model's async-fetched model list) — was free-text for the model name
|
|
116
|
+
- **Prompt adaptation**: CLI main.md was missing the consult (会诊) + escalate (飞刀) sections — the CLI main agent did not know "会诊" meant consult_start. Ported both + added the 会诊 alias to consult_start's tool description
|
|
117
|
+
|
|
118
|
+
### Prompt system
|
|
119
|
+
|
|
120
|
+
- Attention optimization + cross-end consistency: split over-long sentences, fixed an escalate-timing contradiction (up-front ability judgment), unified Review discipline + advisor rounds — all 15 prompt files byte-identical with the plugin
|
|
121
|
+
|
|
122
|
+
### Docs
|
|
123
|
+
|
|
124
|
+
- CONSULTATION.md + ESCALATE.md design docs (CLI implementation differences vs the plugin)
|
|
125
|
+
|
|
126
|
+
## [0.12.30] — 2026-08-16
|
|
127
|
+
|
|
128
|
+
### New: 会诊 (consult) + 飞刀 (escalate) — full VS Code plugin parity
|
|
129
|
+
|
|
130
|
+
- `consult_start` / `consult_check` / `consult_stop`: several configured models run as parallel independent read-only consultants — each with its own TUI activity card, `main_history` access to the failure trail, arrival-order reply queue, stopped/terminated settle states, wall-clock watchdog
|
|
131
|
+
- `escalate`: fly in a stronger model for one expert implementation run — coder role, full write path, permission gate, mutations merge into the parent's verify/advisor guards, turn-cap reads as partial work, timeout reads as timeout
|
|
132
|
+
- Config: `agent.consultModels` ([{ provider, model, effort? }], up to 5, validated), `agent.consultTurns` (40), `agent.consultTimeoutMs` (600000)
|
|
133
|
+
- **/config now manages the consult pool**: list / add / remove models; per-model reasoning-effort is an OPTION picker (none/min/low/medium/high/max); consultTurns + consultTimeoutMs (entered in minutes)
|
|
134
|
+
|
|
135
|
+
### Discipline
|
|
136
|
+
|
|
137
|
+
- UI rule added: fixed-choice values must be OPTIONS (picker/menu), never free-text — free-text only for genuinely open-ended input
|
|
138
|
+
|
|
139
|
+
## [0.12.29] — 2026-08-16
|
|
140
|
+
|
|
141
|
+
### Fixed
|
|
142
|
+
|
|
143
|
+
- **Coder sub-agents (subagent role=coder) get verify + advisor** — CLI parity with the plugin's escalate diagnosis: the system prompt names both tools but the tool table only gave them to eng-coder; a coder sub-agent hit unknown-tool and self-verified via bash
|
|
144
|
+
- **Cache-audit follow-ups**: OS/cwd reminder injected once per process (was every run); interrupt-resume now re-grounds the time (was stuck on the pre-interrupt time); skills scan sorted deterministically (filesystem-dependent readdir order could byte-change the system prompt with zero content change)
|
|
145
|
+
|
|
146
|
+
## [0.12.28] — 2026-08-16
|
|
147
|
+
|
|
148
|
+
### Cache-hit-rate fix (user-reported low hit rate on session start)
|
|
149
|
+
|
|
150
|
+
- **Machine line (contextHistory) now keeps transient messages on persist** — every CLI invocation is a new process; the previous reals-only reload plus fresh re-injections (git/OS/outline/doc/memory/time) diverged at index ~1 → whole-prefix cache miss on the first request of every session. Resume now rebuilds a byte-identical machine line; new injections append at the tail
|
|
151
|
+
- Time reminder moved to the END of the message sequence (after the user input) — aligned with the plugin fix, robust against any future machine-line disk reload
|
|
152
|
+
|
|
153
|
+
### Fixed
|
|
154
|
+
|
|
155
|
+
- normalizeToolPairing early-return hole: toolById empty must not skip placeholder filling when assistant tool_calls are declared (dangling tool_calls 400 otherwise)
|
|
156
|
+
|
|
157
|
+
## [0.12.27] — 2026-08-15
|
|
158
|
+
|
|
159
|
+
- Time injection moved OUT of the system prompt into a transient per-run user reminder — system prompts fully static again (prefix caches hit across hours, not minutes); local time + IANA timezone at second precision; now covers ALL agent depths (subagents previously had no time grounding at all)
|
|
160
|
+
|
|
161
|
+
## [0.12.26] — 2026-08-15
|
|
162
|
+
|
|
163
|
+
### ACP extensions for thincoder-desktop (proposals ①②③④, all implemented)
|
|
164
|
+
|
|
165
|
+
- **① Session persistence**: every ACP turn end (success/cancel/failure — finally semantics) writes the session archive via saveSession; session/list / load / resume now have a real data source. Save is injectable and failures never break the queue
|
|
166
|
+
- **② Checkpoints**: checkpoint/create / checkpoint/list / checkpoint/restore ACP handlers; NON-git cwds now snapshot by full-directory copy (v2 layout, nongit meta) instead of silently returning null
|
|
167
|
+
- **③ Memory**: memory/list / memory/remove ACP handlers over the shared ~/.thincoder store
|
|
168
|
+
- **④ Custom provider headers**: provider.headers object in config.json merges into every LLM request (chat + /models); Authorization cannot be overridden; non-string values sanitized out
|
|
169
|
+
|
|
170
|
+
### Fixed
|
|
171
|
+
|
|
172
|
+
- Time injection vs prefix-cache conflict: system-prompt "Current time" is now MINUTE precision — byte-identical within the same minute so DeepSeek prefix caches still hit (was: seconds precision broke the cache every run)
|
|
173
|
+
|
|
174
|
+
## [0.12.25] — 2026-08-15
|
|
175
|
+
|
|
176
|
+
- Local time + timezone injected into every system prompt (main agent, subagents, advisor) — prepareRun appends `Current time: <local> (<IANA zone>)`; sessionStart was ISO/UTC and session-scoped, subagents had nothing
|
|
177
|
+
- bash.md Windows guidance corrected: the shell is cmd.exe (NOT Git Bash) — &&/|| work, cmd built-ins, NUL not /dev/null, prefer node -e for complex logic
|
|
178
|
+
|
|
179
|
+
## [0.12.24] — 2026-08-14
|
|
180
|
+
|
|
181
|
+
### Added
|
|
182
|
+
|
|
183
|
+
- **glm-code provider preset** — the Zhipu GLM Coding Plan endpoint (`https://open.bigmodel.cn/api/coding/paas/v4`, glm-5.2, same key as GLM; server-side forced thinking).
|
|
184
|
+
|
|
185
|
+
### Fixed
|
|
186
|
+
|
|
187
|
+
- **Model specs synced with official vendor docs (verified 2026-08)** — DeepSeek v4 duals effort enum +low and cacheMode→auto; qwen3.x/max/plus maxOutput→131072 (qwen-plus was 32K).
|
|
188
|
+
- **Retired models dropped** — deepseek-chat/reasoner, kimi-k2, moonshot v1 (vendor shutdowns; unknown IDs fall back to the 128K default spec).
|
|
189
|
+
|
|
190
|
+
### Changed
|
|
191
|
+
|
|
192
|
+
- Repository URL → github.com/xinbo-tech/thincoder.
|
|
193
|
+
|
|
194
|
+
## [0.12.23] - 2026-08-13
|
|
195
|
+
|
|
196
|
+
- **修复** svg 图片毒化会话——read_image 读 svg 后以 image_url 进历史,Kimi 等视觉 API(全部仅支持位图)此后每轮请求 400 "unsupported image format",会话永久卡死;现在发送时按格式净化:非 png/jpeg/gif/webp 的 image part 替换为占位文本,净化上移至 format dispatch 之前覆盖 openai/anthropic/google 全部通路,历史本身不改写(切回支持的模型/格式可恢复)
|
|
197
|
+
- **改进** read_image 对 svg 返回文本源码(svg 本是文本标记,任何模型可读,绕过 vision gate);bmp 拒绝并提示转 PNG(无主流视觉 API 支持)
|
|
198
|
+
|
|
199
|
+
## [0.12.22] - 2026-08-13
|
|
200
|
+
|
|
201
|
+
- **修复** 缓存命中率对 Kimi 显示——usage 缓存字段归一化:Kimi/OpenAI 风格 `prompt_tokens_details.cached_tokens` 映射为 DeepSeek 风格 `prompt_cache_hit_tokens`,miss 由 prompt_tokens − hit 推导(此前 Kimi 的命中率永不显示)
|
|
202
|
+
- **安全** fetch 重定向目标做 SSRF 检查——3xx 可把公网 URL 跳进内网(重定向绕过);相对 URL 正确解析、仅 http/https、私网/元数据拦截
|
|
203
|
+
- **新增** Tavily 结构化搜索(可选)——config 配 `websearch.apiKey` 后 `websearch` 走 Tavily API(稳定 JSON,不再依赖 Bing 页面结构);无 key 回退 Bing 抓取,零门槛不破坏
|
|
204
|
+
|
|
205
|
+
## [0.12.21] - 2026-08-13
|
|
206
|
+
|
|
207
|
+
- **修复** 恢复会话大量重复 "❯ ThinCoder:" 标签——history 按每次 LLM 调用存一条 assistant 消息(一个 turn 多段),恢复时每段都渲染了标签;现在只在 turn 开始渲染一次,跨页懒加载边界状态正确保留
|
|
208
|
+
- **改进** 恢复保真度:完整工具结果(不再一行摘要)+ reasoning 思考流以 dim 行恢复(超长自动折叠)——恢复后的会话与退出前基本一致;首帧渲染实测约 50ms
|
|
209
|
+
|
|
210
|
+
## [0.12.20] - 2026-08-13
|
|
211
|
+
|
|
212
|
+
- **修复** TUI 恢复旧 display 快照导致"看不到最新消息"——display 字段彻底废弃(saveSession 不再写、loadSession 不再读),恢复永远从 history 重建;配合 VS Code 端 0.1.5 的清空,跨端会话漂移根治
|
|
213
|
+
- **新增** TUI 懒加载历史恢复:启动只物化最近 200 条消息(8000+ 条会话不再冻结启动),PgUp 到顶按 50 条/页加载更早历史,scroll 补偿保持视觉位置
|
|
214
|
+
- **新增** question 选项列表末尾追加"✍ Custom answer…"——选中切自由输入,用户可补充/修正 AI 的预设选项
|
|
215
|
+
- **重构** execute 工具移除假沙箱:require()/process 全可用(bash 本就能触达任意 Node API,拦 require 只会误导模型);移除动态 import 拦截与 SSRF 私网拒绝;保留 timeout / cwd 约束 / 输出上限等工程保护
|
|
216
|
+
- **改进** 工程模式 prompt:新增提问风格指引(默认开放式自由文本,选项仅用于有限枚举);审查修复 5 处(需求优先步骤、designToken 仅走参数、用户审批呈现 advisor 发现、澄清完成判据、advisor 重试 3 轮上限)
|
|
217
|
+
|
|
218
|
+
## [0.12.19] - 2026-08-11
|
|
219
|
+
|
|
220
|
+
- **重构** bash 工具安全模型:移除全部破坏性命令文本拦截(rm -rf / DROP TABLE 等)——文本匹配是安全剧场(恶意模型可绕过、误伤正常操作),真实防线 = 审批层(autoApprove)+ 快照(gitGuardSnapshot / checkpoint),与 env 透传、git"快照后放行永不拦截"统一
|
|
221
|
+
- **新增** 危险命令标注(detectDanger,参考 kimi-code):recursive-delete / sudo / pipe-to-shell / dd / mkfs / 裸设备 / chmod 777 / fork bomb 在 TUI 与 ACP 审批面板红色标注——只提示不拦截,帮人做审批决策;引号感知(commit message 等纯文本不误标,反引号内容保留)
|
|
222
|
+
- **文档** TOOLS.md 安全边界同步(零文本拦截 + 危险标注)
|
|
223
|
+
|
|
224
|
+
## [0.12.18] - 2026-08-10
|
|
225
|
+
|
|
226
|
+
- **修复** Qwen 路由等代理的模型 ID(如 `kimi/kimi-k3`)跳过 `reasoning_effort` 参数——路由可能误处理该参数导致空回复
|
|
227
|
+
|
|
228
|
+
## [0.12.17] - 2026-08-10
|
|
229
|
+
|
|
230
|
+
- **修复** `kimi/kimi-k3`(Qwen 路由前缀 ID)模型规格匹配 — 正确继承 kimi-k3 的 1M 上下文 / 131K 输出 / 多模态
|
|
231
|
+
|
|
232
|
+
## [0.12.16] - 2026-08-10
|
|
233
|
+
|
|
234
|
+
- **修复** 非 SSE JSON 响应被误判为错误(HTTP 200 + tool_calls 的合法 JSON 响应现在正确解析,而非报 "Response is not SSE")
|
|
235
|
+
- **改进** API 错误信息可读性:非 SSE 错误响应包含 HTTP 状态码 + 具体错误原因
|
|
236
|
+
|
|
237
|
+
## [0.12.15] - 2026-08-10
|
|
238
|
+
|
|
239
|
+
- **改进** API 错误信息可读性:非 SSE 响应预拦截,提取 HTTP 状态码 + 具体错误原因(替代原来模糊的 "Response is not SSE")
|
|
240
|
+
|
|
241
|
+
## [0.12.14] - 2026-08-10
|
|
242
|
+
|
|
243
|
+
- **修复** 小终端 permission 面板挤出输入框(layout 溢出补偿新增 permission 压缩)
|
|
244
|
+
- **修复** iTerm2 Ctrl+C 键盘协议序列泄漏(stdin 剥离未处理的 CSI u 序列)
|
|
245
|
+
- **修复** eng 模式 advisor token 正则错配(改用完整 token 构建正则,与 prompt 格式一致)
|
|
246
|
+
- **修复** 输入框 ↑ 键历史导航草稿丢失(进入/编辑历史模式时扩容草稿保护)
|
|
247
|
+
- **重构** key-handler 拆分搜索模块(key-handler-search.mjs)
|
|
248
|
+
- **文档** 架构文档计数/模块/状态同步更新
|
|
249
|
+
|
|
250
|
+
## [0.12.13] - 2026-08-08
|
|
251
|
+
|
|
252
|
+
评审机制全面重构(用户驱动的三轮决策):
|
|
253
|
+
|
|
254
|
+
- **prior 硬解析移除**:收敛轮注入上一轮评审的完整原文(模型直接理解),删除表头匹配与 all-clear 短语两类"字符串解析 LLM 输出"的脆弱机制
|
|
255
|
+
- **评审触发范围收缩**:评审只跟代码修改绑定——bash/git 等副作用工具不再触发多余评审轮(评审后读日志/清理临时文件不再要求重复评审)
|
|
256
|
+
- **AGENTS.md 文档地图**:需求基线声明(REQUIREMENTS.md + 设计文档 + 对话背景)+ docs/design/ 27 份文档分组清单,评审者按地图定位需求文档
|
|
257
|
+
- **项目根发现**:多项目工作区从评审范围定位子项目 AGENTS.md(工作区元地图不遮蔽);修复混合路径分隔符误判
|
|
258
|
+
- **收敛体共享模块**:round 2+ 消息构建单一来源;空回复/纯工具输出不再冒充评审记录
|
|
259
|
+
|
|
260
|
+
## [0.12.12] - 2026-08-07
|
|
261
|
+
|
|
262
|
+
- advisor 记录按真实时序落盘(timeline)、markdown 表格 render-before-measure 对齐修复(含 heading 多行/双重粗体)、requirements 兜底、评审结论可用性提示
|
|
263
|
+
- 双线消息历史(人读线 + 机读线)、压缩只作用于机读线、机读消息不进人读线
|
|
264
|
+
- 临时文件(tmp-*)不触发 advisor guard;config.mjs 加固(spec 预排序、providers 守卫、saveConfig 写副本)
|
|
265
|
+
- VS Code 扩展发布准备(marketplace 元数据、.vscodeignore、vscode-mock 依赖修复)
|
|
266
|
+
|
|
267
|
+
## [0.12.11] - 2026-08-05
|
|
268
|
+
|
|
269
|
+
- subagent 按类型配置模型(`/submodel` + `subagentModels`)
|
|
270
|
+
- 可配置 bash shell(`/shell platform` 切换)
|
|
271
|
+
- 其他稳定性与体验改进
|
|
272
|
+
|
|
273
|
+
## [0.12.10] - 2026-08-05
|
|
274
|
+
|
|
275
|
+
- 代码质量梳理:清理未使用的导出、advisor 计时器与静态导入修复、复评不再因旧会话数据误报
|
|
276
|
+
|
|
277
|
+
## [0.12.9] - 2026-08-04
|
|
278
|
+
|
|
279
|
+
- 提示词体系质量梳理:移除工程模式与 advisor 的冲突、交付评审语义修正
|
|
280
|
+
|
|
281
|
+
## [0.12.8] - 2026-08-04
|
|
282
|
+
|
|
283
|
+
- pending-task 推回最多触发一次(消除无界完成循环)
|
|
284
|
+
|
|
285
|
+
## [0.12.7] - 2026-08-03
|
|
286
|
+
|
|
287
|
+
- 折叠可读性修复(主输出/思考永不折叠)、窄终端宽表格裁剪
|
|
288
|
+
|
|
289
|
+
## [0.12.6] - 2026-08-02
|
|
290
|
+
|
|
291
|
+
- checkpoint v2、git 破坏性命令保护、鼠标支持、长消息折叠、bash 行为约束
|
|
292
|
+
|
|
293
|
+
## [0.12.5] - 2026-08-01
|
|
294
|
+
|
|
295
|
+
- 行内代码下划线样式
|
|
296
|
+
|
|
297
|
+
## [0.12.4] - 2026-08-01
|
|
298
|
+
|
|
299
|
+
- 压缩统一规范、Kimi For Coding、Ctrl+C 双重确认、空响应重试、markdown 渲染修复
|
|
300
|
+
|
|
301
|
+
## [0.12.3] 及更早
|
|
302
|
+
|
|
303
|
+
v0.12.x 早期版本、v0.11.x、v0.8.x、v0.7.x 与 v0.2–v0.6 系列——完整历史见 [git 提交记录](https://gitee.com/shanghai-xinbo/thincoder/commits/main)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thincoder",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.39",
|
|
4
4
|
"description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"bin/",
|
|
24
24
|
"src/",
|
|
25
25
|
"README.md",
|
|
26
|
+
"CHANGELOG.md",
|
|
26
27
|
"LICENSE"
|
|
27
28
|
],
|
|
28
29
|
"repository": {
|
|
@@ -19,6 +19,36 @@ function consultLabel(m) {
|
|
|
19
19
|
return `${m.provider}:${m.model}`
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/** Narrow the configured consultModels pool to a requested subset.
|
|
23
|
+
* Each selector is "provider:model", a bare provider name, or a bare model name
|
|
24
|
+
* (case-insensitive). Returns { models, error } — error set when a selector matches
|
|
25
|
+
* nothing (surface the typo rather than silently dropping it). Absent/empty selectors
|
|
26
|
+
* → the full pool. */
|
|
27
|
+
function selectConsultModels(pool, selectors) {
|
|
28
|
+
if (selectors == null || (Array.isArray(selectors) && selectors.length === 0)) return { models: pool, error: null }
|
|
29
|
+
const list = Array.isArray(selectors) ? selectors : [selectors] // coerce a bare string → [string]
|
|
30
|
+
const selected = []
|
|
31
|
+
const seen = new Set()
|
|
32
|
+
const unknowns = []
|
|
33
|
+
for (const raw of list) {
|
|
34
|
+
const s = String(raw).trim().toLowerCase()
|
|
35
|
+
const matches = pool.filter((m) =>
|
|
36
|
+
consultLabel(m).toLowerCase() === s ||
|
|
37
|
+
String(m.provider ?? "").toLowerCase() === s ||
|
|
38
|
+
String(m.model ?? "").toLowerCase() === s,
|
|
39
|
+
)
|
|
40
|
+
if (matches.length === 0) unknowns.push(String(raw))
|
|
41
|
+
else for (const m of matches) {
|
|
42
|
+
const key = consultLabel(m)
|
|
43
|
+
if (!seen.has(key)) { seen.add(key); selected.push(m) }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (unknowns.length > 0) {
|
|
47
|
+
return { models: null, error: `unknown consult model selector(s): ${unknowns.join(", ")} — choose from: ${pool.map(consultLabel).join(", ")}` }
|
|
48
|
+
}
|
|
49
|
+
return { models: selected, error: null }
|
|
50
|
+
}
|
|
51
|
+
|
|
22
52
|
/** Read-only tool injected into consultation children (via createAgent's tools).
|
|
23
53
|
* Lets the consultant pull the main agent's conversation history on demand —
|
|
24
54
|
* the failure trail is first-class evidence, not a retelling. */
|
|
@@ -225,31 +255,40 @@ export const consultStartTool = {
|
|
|
225
255
|
"arrives, judge/verify it yourself with your own tools, and call consult_stop(id) once a reply is good enough.\n" +
|
|
226
256
|
"Parameters:\n" +
|
|
227
257
|
"- problem (required): a brief — the symptom, what you already tried (failure trail), and entry-point files. " +
|
|
228
|
-
"Do NOT paste raw error logs; consultants pull the main session history themselves via their main_history tool
|
|
258
|
+
"Do NOT paste raw error logs; consultants pull the main session history themselves via their main_history tool.\n" +
|
|
259
|
+
"- models (optional): subset of agent.consultModels to run — an array of \"provider:model\", bare provider, or bare model names (case-insensitive). Omit to run all.",
|
|
229
260
|
parameters: {
|
|
230
261
|
type: "object",
|
|
231
|
-
properties: {
|
|
262
|
+
properties: {
|
|
263
|
+
problem: { type: "string", description: "Problem brief (symptom + failure trail + entry files)" },
|
|
264
|
+
models: { type: "array", items: { type: "string" }, description: 'Optional subset of agent.consultModels to run (default: all). Each entry is "provider:model", a bare provider name, or a bare model name (case-insensitive).' },
|
|
265
|
+
},
|
|
232
266
|
required: ["problem"],
|
|
233
267
|
},
|
|
234
|
-
async execute({ problem }, ctx) {
|
|
268
|
+
async execute({ problem, models }, ctx) {
|
|
235
269
|
if (typeof problem !== "string" || !problem.trim()) return "Error: problem is required and must be a non-empty string"
|
|
236
270
|
const agent = ctx.agent
|
|
237
271
|
if (!agent) return "Error: consult requires an agent context"
|
|
238
|
-
const
|
|
239
|
-
if (!Array.isArray(
|
|
272
|
+
const pool = agent.config?.agent?.consultModels ?? []
|
|
273
|
+
if (!Array.isArray(pool) || pool.length === 0)
|
|
240
274
|
return "Consultation is not configured — add agent.consultModels ([{ provider, model }], up to 5) to ~/.thincoder/config.json"
|
|
241
|
-
if (
|
|
275
|
+
if (pool.length > 5) return `Error: consultModels supports at most 5 models (got ${pool.length})`
|
|
276
|
+
|
|
277
|
+
// `models` (optional) narrows the pool to a subset; absent/empty → run the whole pool.
|
|
278
|
+
const picked = selectConsultModels(pool, models)
|
|
279
|
+
if (picked.error) return picked.error
|
|
280
|
+
const run = picked.models
|
|
242
281
|
|
|
243
282
|
agent._consultSessions ??= new Map()
|
|
244
283
|
const id = String((agent._consultIdCounter = (agent._consultIdCounter ?? 0) + 1))
|
|
245
284
|
const session = {
|
|
246
285
|
id, controllers: [], replies: [], pending: 0, waiters: [],
|
|
247
|
-
failed: 0, terminated: 0, stopped: false, received: 0, total:
|
|
248
|
-
models:
|
|
286
|
+
failed: 0, terminated: 0, stopped: false, received: 0, total: run.length,
|
|
287
|
+
models: run.map(consultLabel),
|
|
249
288
|
}
|
|
250
289
|
agent._consultSessions.set(id, session)
|
|
251
290
|
|
|
252
|
-
for (const m of
|
|
291
|
+
for (const m of run) {
|
|
253
292
|
session.pending++
|
|
254
293
|
const ctrl = new AbortController()
|
|
255
294
|
session.controllers.push(ctrl)
|
|
@@ -2,7 +2,7 @@ import { repairHistory, listWorkDir } from "../agent.mjs"
|
|
|
2
2
|
import { isDocFile } from "../advisor/repos.mjs"
|
|
3
3
|
import { execSync, spawn, spawnSync } from "node:child_process"
|
|
4
4
|
import { readFileSync, existsSync } from "node:fs"
|
|
5
|
-
import { join } from "node:path"
|
|
5
|
+
import { join, resolve } from "node:path"
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Source module → test file mapping. Heuristic: the FIRST path component
|
|
@@ -58,12 +58,17 @@ export const verifyTool = {
|
|
|
58
58
|
type: "object",
|
|
59
59
|
properties: {
|
|
60
60
|
full: { type: "boolean", description: "Run the full test suite (npm test) instead of just related tests. Default false — use sparingly, per the testing discipline rules." },
|
|
61
|
+
workdir: { type: "string", description: "Optional: run verify in this subdirectory (relative to cwd or absolute) — for monorepos" },
|
|
62
|
+
filter: { type: "string", description: "Optional: limit the test run to matching test names (node --test-name-pattern / npm test -- --test-name-pattern)" },
|
|
61
63
|
},
|
|
62
64
|
},
|
|
63
65
|
readonly: true,
|
|
64
66
|
outputPanel: true, // stream test output to a panel instead of inline
|
|
65
67
|
async execute(args, ctx) {
|
|
66
68
|
const cwd = ctx.agent.cwd
|
|
69
|
+
// workdir only relocates WHERE tests (and package.json) live — changed-file
|
|
70
|
+
// resolution (git diff) stays anchored to the project root.
|
|
71
|
+
const testCwd = args.workdir ? resolve(cwd, args.workdir) : cwd
|
|
67
72
|
const lines = []
|
|
68
73
|
lines.push("=== VERIFICATION REPORT ===")
|
|
69
74
|
lines.push("")
|
|
@@ -130,7 +135,7 @@ export const verifyTool = {
|
|
|
130
135
|
const relatedTests = [...new Set(modules.map((m) => MODULE_TO_TEST[m]).filter(Boolean))]
|
|
131
136
|
|
|
132
137
|
// 4. Run tests
|
|
133
|
-
const pkgPath = join(
|
|
138
|
+
const pkgPath = join(testCwd, "package.json")
|
|
134
139
|
const hasTestScript = existsSync(pkgPath) && (() => { try { return !!JSON.parse(readFileSync(pkgPath, "utf8")).scripts?.test } catch { return false } })()
|
|
135
140
|
|
|
136
141
|
if (args.full) {
|
|
@@ -138,7 +143,7 @@ export const verifyTool = {
|
|
|
138
143
|
if (hasTestScript) {
|
|
139
144
|
lines.push("")
|
|
140
145
|
lines.push("Tests (full suite):")
|
|
141
|
-
const result = await runTestSuite(
|
|
146
|
+
const result = await runTestSuite(testCwd, ctx, args.filter)
|
|
142
147
|
if (result.passed) {
|
|
143
148
|
lines.push("✓ All tests passed.")
|
|
144
149
|
ctx.agent._verifyPassed = !syntaxFailed
|
|
@@ -163,7 +168,7 @@ export const verifyTool = {
|
|
|
163
168
|
continue
|
|
164
169
|
}
|
|
165
170
|
try {
|
|
166
|
-
const result = await runTestFile(cwd, testFile, ctx)
|
|
171
|
+
const result = await runTestFile(cwd, testFile, ctx, args.filter)
|
|
167
172
|
if (result.passed) {
|
|
168
173
|
lines.push(` ✓ ${testFile}`)
|
|
169
174
|
} else {
|
|
@@ -248,9 +253,9 @@ export const verifyTool = {
|
|
|
248
253
|
* Run a single test file with node --test, no maxBuffer limit.
|
|
249
254
|
* Returns { passed: boolean, tail: string } — the last few lines of output.
|
|
250
255
|
*/
|
|
251
|
-
function runTestFile(cwd, testPath, ctx) {
|
|
256
|
+
function runTestFile(cwd, testPath, ctx, filter) {
|
|
252
257
|
return new Promise((resolve, reject) => {
|
|
253
|
-
const child = spawn("node", ["--test", testPath], {
|
|
258
|
+
const child = spawn("node", filter ? ["--test", "--test-name-pattern", filter, testPath] : ["--test", testPath], {
|
|
254
259
|
cwd, stdio: ["ignore", "pipe", "pipe"],
|
|
255
260
|
env: { ...process.env, FORCE_COLOR: "0" },
|
|
256
261
|
})
|
|
@@ -288,9 +293,9 @@ function runTestFile(cwd, testPath, ctx) {
|
|
|
288
293
|
* Test output is streamed through ctx.callbacks.onToolOutput (TUI can display progress in real time).
|
|
289
294
|
* Returns { passed: boolean, tail: string }.
|
|
290
295
|
*/
|
|
291
|
-
function runTestSuite(cwd, ctx) {
|
|
296
|
+
function runTestSuite(cwd, ctx, filter) {
|
|
292
297
|
return new Promise((resolve, reject) => {
|
|
293
|
-
const child = spawn("npm", ["test"], {
|
|
298
|
+
const child = spawn("npm", filter ? ["test", "--", `--test-name-pattern=${filter}`] : ["test"], {
|
|
294
299
|
cwd, shell: true, stdio: ["ignore", "pipe", "pipe"],
|
|
295
300
|
env: { ...process.env, FORCE_COLOR: "0" },
|
|
296
301
|
})
|
package/src/agent.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { chat } from "./provider/index.mjs"
|
|
6
6
|
import { estimateText } from "./provider/rate.mjs"
|
|
7
|
-
import { compressIfNeeded, compressFallback, COMPRESS_FAILURE_LIMIT, pushReal } from "./context.mjs"
|
|
7
|
+
import { compressIfNeeded, compressFallback, COMPRESS_FAILURE_LIMIT, pushReal, summarizeRunExplorations } from "./context.mjs"
|
|
8
8
|
import { specForModel } from "./config.mjs"
|
|
9
9
|
import { readFileSync } from "node:fs"
|
|
10
10
|
import { join, dirname } from "node:path"
|
|
@@ -95,6 +95,7 @@ export function createAgent({
|
|
|
95
95
|
_sessionStart: sessionStart,
|
|
96
96
|
_lastPromptTokens: null, _usageAtLen: null,
|
|
97
97
|
_compressFailures: 0,
|
|
98
|
+
_runStartHistoryLen: 0, // machine-line length at the start of the current run — end-of-run exploration distillation slices from here
|
|
98
99
|
_currentTurn: 0, _maxTurns: 100, // turn counter for status bar display
|
|
99
100
|
}
|
|
100
101
|
}
|
|
@@ -106,6 +107,10 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
|
|
|
106
107
|
{ depth, signal, overrideTurns, resume, systemPrompt: SYSTEM_PROMPT, disciplineRules: DISCIPLINE_RULES, mainOverlay: MAIN_OVERLAY },
|
|
107
108
|
)
|
|
108
109
|
|
|
110
|
+
// End-of-run exploration distillation boundary (CONTEXT-COMPACTION §5): prepareRun has already
|
|
111
|
+
// pushed the user input + injections, so everything appended from here is "this run's" work.
|
|
112
|
+
agent._runStartHistoryLen = agent.history.length
|
|
113
|
+
|
|
109
114
|
// Per-run bookkeeping reset. On `resume` (ContinueError continuation) these are
|
|
110
115
|
// PRESERVED: the resumed run must keep mutation tracking so the advisor/verify
|
|
111
116
|
// guards stay active (a guard pushback on the last turn must not silently vanish),
|
|
@@ -301,6 +306,12 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
|
|
|
301
306
|
honestReminderInjected = cr.honestReminderInjected
|
|
302
307
|
advisorPushbacks = cr.advisorPushbacks
|
|
303
308
|
if (cr.action === "continue") continue
|
|
309
|
+
if (depth === 0) {
|
|
310
|
+
// End-of-run exploration distillation (CONTEXT-COMPACTION §5): this run's inline
|
|
311
|
+
// exploration results become one semantic note before the final return. Silent (N3):
|
|
312
|
+
// distillation failure must never block the return or lose history.
|
|
313
|
+
try { await summarizeRunExplorations(agent, callbacks, signal) } catch { /* silent (N3) */ }
|
|
314
|
+
}
|
|
304
315
|
return cr.content
|
|
305
316
|
}
|
|
306
317
|
|
package/src/config.mjs
CHANGED
|
@@ -87,7 +87,7 @@ export const DEFAULTS = {
|
|
|
87
87
|
* multimodal: whether multimodal (image/vision input supported)
|
|
88
88
|
* cacheMode: context caching mode: "auto"=automatic / "prompt"=needs explicit / "none"=unsupported
|
|
89
89
|
* thinkApi: thinking API type: "type"=thinking.type field / "effort"=reasoning_effort field
|
|
90
|
-
*
|
|
90
|
+
* thinkEnabledValue: when thinkApi is "type", the value used to enable thinking (default "enabled"; MiniMax uses "adaptive")
|
|
91
91
|
* reasoningEcho: reasoning_content cross-turn echo strategy: "required"=must echo (error if missing) / "optional"=echo optional (default: don't echo)
|
|
92
92
|
* reasoningEffortEnum: valid reasoning_effort enum values (if undeclared, no validation — passed through as-is)
|
|
93
93
|
* tempRange: valid temperature range [min, max] (if undeclared, no clamping)
|
|
@@ -96,6 +96,8 @@ const MODEL_SPECS = [
|
|
|
96
96
|
// DeepSeek V4 series
|
|
97
97
|
["deepseek-v4-pro", { context: 1_000_000, maxOutput: 384_000, thinking: true, prefixMode: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"], tempRange: [0, 2] }],
|
|
98
98
|
["deepseek-v4-flash", { context: 1_000_000, maxOutput: 384_000, thinking: true, prefixMode: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"], tempRange: [0, 2] }],
|
|
99
|
+
// DeepSeek V4 Flash Vision (experimental) — image input on top of the full V4-Flash stack
|
|
100
|
+
["deepseek-v4-flash-vision-exp", { context: 1_000_000, maxOutput: 384_000, thinking: true, prefixMode: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"], tempRange: [0, 2], multimodal: true }],
|
|
99
101
|
// Kimi series
|
|
100
102
|
["kimi-k3", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, cacheMode: "auto", thinkApi: "effort", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"] }],
|
|
101
103
|
// Qwen router prefixes model IDs with provider namespace: kimi/kimi-k3 → kimi-k3 (IK7K4V)
|
|
@@ -103,17 +105,22 @@ const MODEL_SPECS = [
|
|
|
103
105
|
// Kimi For Coding endpoint uses the short model ID "k3" (same specs as kimi-k3) — IK5VGJ
|
|
104
106
|
["k3", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, cacheMode: "auto", thinkApi: "effort", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"] }],
|
|
105
107
|
// GLM series
|
|
108
|
+
// GLM-5.3: thinking always-on (no "disabled"); effort converges to low/high/max — NOT the
|
|
109
|
+
// 7-level glm-5.2 enum (verified vs docs.bigmodel.cn GLM-5.3 page, 2026-08)
|
|
110
|
+
["glm-5.3", { context: 1_000_000, maxOutput: 128_000, thinking: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "optional", reasoningEffortEnum: ["low", "high", "max"], tempRange: [0, 1], noUsageStream: true }],
|
|
106
111
|
["glm-5.2", { context: 1_000_000, maxOutput: 128_000, thinking: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "optional", reasoningEffortEnum: ["max", "xhigh", "high", "medium", "low", "minimal", "none"], tempRange: [0, 1], noUsageStream: true }],
|
|
107
112
|
["glm-5", { context: 1_000_000, maxOutput: 128_000, thinking: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "optional", reasoningEffortEnum: ["max", "xhigh", "high", "medium", "low", "minimal", "none"], tempRange: [0, 1], noUsageStream: true }],
|
|
108
113
|
["glm-4", { context: 128_000, maxOutput: 32_000, thinking: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "optional", tempRange: [0, 1], noUsageStream: true }],
|
|
109
114
|
// GPT series
|
|
115
|
+
["gpt-5.6-sol", { context: 1_050_000, maxOutput: 128_000, thinking: false, multimodal: true, cacheMode: "prompt" }],
|
|
116
|
+
["gpt-5.6", { context: 1_050_000, maxOutput: 128_000, thinking: false, multimodal: true, cacheMode: "prompt" }],
|
|
110
117
|
["gpt-4.1", { context: 1_000_000, maxOutput: 128_000, thinking: false, cacheMode: "prompt" }],
|
|
111
118
|
["gpt-4o", { context: 128_000, maxOutput: 16_000, thinking: false, multimodal: true, cacheMode: "prompt" }],
|
|
112
119
|
// Qwen series
|
|
113
|
-
["qwen3.8-max-preview", { context: 1_000_000, maxOutput:
|
|
120
|
+
["qwen3.8-max-preview", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", reasoningEffortEnum: ["xhigh", "medium", "low"], tempRange: [0, 2] }],
|
|
114
121
|
// qwen3.7-max rejects image parts outright (DashScope 400 "Unexpected item type in content") — text-only
|
|
115
|
-
["qwen3.7-max", { context: 1_000_000, maxOutput:
|
|
116
|
-
["qwen3.8-max", { context: 1_000_000, maxOutput:
|
|
122
|
+
["qwen3.7-max", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, cacheMode: "none", thinkApi: "effort", reasoningEffortEnum: ["xhigh", "high"], tempRange: [0, 2] }],
|
|
123
|
+
["qwen3.8-max", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", reasoningEffortEnum: ["xhigh", "medium", "low"], tempRange: [0, 2] }],
|
|
117
124
|
["qwen-max", { context: 1_000_000, maxOutput: 131_072, thinking: false, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", tempRange: [0, 2] }],
|
|
118
125
|
["qwen-plus", { context: 1_000_000, maxOutput: 131_072, thinking: false, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", tempRange: [0, 2] }],
|
|
119
126
|
["qwen", { context: 1_000_000, maxOutput: 131_072, thinking: false, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", tempRange: [0, 2] }],
|
|
@@ -127,6 +134,8 @@ const MODEL_SPECS = [
|
|
|
127
134
|
["minimax-m3", { context: 1_000_000, maxOutput: 128_000, thinking: true, multimodal: true, cacheMode: "auto", thinkApi: "type", thinkEnabledValue: "adaptive", tempRange: [0, 2], noUsageStream: true }],
|
|
128
135
|
["minimax-m1", { context: 256_000, maxOutput: 128_000, thinking: false, cacheMode: "auto", noUsageStream: true }],
|
|
129
136
|
// Grok series (xAI — OpenAI-compatible)
|
|
137
|
+
// grok-4.x: 500K context per xAI Grok 4.6 spec (corrected 2026-08; earlier entries said 1M)
|
|
138
|
+
["grok-4.6", { context: 500_000, maxOutput: 64_000, thinking: false, multimodal: true, tempRange: [0, 2] }],
|
|
130
139
|
["grok-4.5", { context: 500_000, maxOutput: 64_000, thinking: false, multimodal: true, tempRange: [0, 2] }],
|
|
131
140
|
["grok-4", { context: 500_000, maxOutput: 64_000, thinking: false, multimodal: true, tempRange: [0, 2] }],
|
|
132
141
|
["grok-4-mini", { context: 128_000, maxOutput: 16_000, thinking: false, tempRange: [0, 2] }],
|
|
@@ -134,10 +143,13 @@ const MODEL_SPECS = [
|
|
|
134
143
|
["mistral-large", { context: 128_000, maxOutput: 32_000, thinking: false, multimodal: true, tempRange: [0, 2] }],
|
|
135
144
|
["codestral", { context: 256_000, maxOutput: 32_000, thinking: false, tempRange: [0, 2] }],
|
|
136
145
|
// Claude series (Anthropic)
|
|
146
|
+
["claude-opus-5", { context: 1_000_000, maxOutput: 128_000, thinking: false, multimodal: true, cacheMode: "none", format: "anthropic" }],
|
|
147
|
+
["claude-sonnet-5", { context: 1_000_000, maxOutput: 128_000, thinking: false, multimodal: true, cacheMode: "none", format: "anthropic" }],
|
|
137
148
|
["claude-opus-4", { context: 200_000, maxOutput: 32_000, thinking: false, multimodal: true, cacheMode: "none", format: "anthropic" }],
|
|
138
149
|
["claude-sonnet-4", { context: 200_000, maxOutput: 32_000, thinking: false, multimodal: true, cacheMode: "none", format: "anthropic" }],
|
|
139
150
|
["claude-3.5-haiku", { context: 200_000, maxOutput: 8_192, thinking: false, cacheMode: "none", format: "anthropic" }],
|
|
140
151
|
// Gemini series (Google)
|
|
152
|
+
["gemini-3-pro", { context: 1_000_000, maxOutput: 64_000, thinking: false, multimodal: true, cacheMode: "none", format: "google", noUsageStream: true }],
|
|
141
153
|
["gemini-2.5-pro", { context: 2_000_000, maxOutput: 64_000, thinking: false, multimodal: true, cacheMode: "none", format: "google", noUsageStream: true }],
|
|
142
154
|
["gemini-2.5-flash", { context: 1_000_000, maxOutput: 64_000, thinking: false, multimodal: true, cacheMode: "none", format: "google", noUsageStream: true }],
|
|
143
155
|
]
|