thincoder 0.12.62 → 0.12.64

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 (231) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +46 -49
  3. package/bin/thincoder.mjs +68 -34
  4. package/package.json +7 -6
  5. package/src/acp/bridge.mjs +35 -15
  6. package/src/acp/client-caps.mjs +86 -0
  7. package/src/acp/ext.mjs +86 -0
  8. package/src/acp/handlers-session.mjs +257 -0
  9. package/src/acp/handlers-slots.mjs +196 -0
  10. package/src/acp/login.mjs +48 -0
  11. package/src/acp/session.mjs +6 -4
  12. package/src/acp.mjs +67 -379
  13. package/src/cli/distill-command.mjs +3 -3
  14. package/src/cli/make-agent.mjs +60 -17
  15. package/src/cli/memory-command.mjs +3 -3
  16. package/src/cli/permission.mjs +4 -48
  17. package/src/cli/setup-wizard.mjs +1 -1
  18. package/src/completions.mjs +3 -1
  19. package/src/crash-reports.mjs +1 -1
  20. package/src/distill.mjs +4 -4
  21. package/src/heap-watch.mjs +1 -1
  22. package/src/prompt-injections.mjs +20 -0
  23. package/src/tui/agent-turn.mjs +40 -9
  24. package/src/tui/cmd-advisor.mjs +5 -5
  25. package/src/tui/cmd-config.mjs +8 -8
  26. package/src/tui/cmd-eng.mjs +35 -9
  27. package/src/tui/cmd-mcp.mjs +9 -8
  28. package/src/tui/cmd-model.mjs +1 -1
  29. package/src/tui/cmd-new.mjs +6 -5
  30. package/src/tui/cmd-plan.mjs +9 -0
  31. package/src/tui/cmd-reindex.mjs +1 -1
  32. package/src/tui/cmd-restore.mjs +2 -2
  33. package/src/tui/cmd-session.mjs +24 -1
  34. package/src/tui/cmd-skills.mjs +1 -1
  35. package/src/tui/cmd-think.mjs +22 -9
  36. package/src/tui/config-helpers.mjs +1 -1
  37. package/src/tui/display-budget.mjs +33 -11
  38. package/src/tui/index.mjs +18 -10
  39. package/src/tui/interaction.mjs +16 -7
  40. package/src/tui/key-modes.mjs +9 -4
  41. package/src/tui/ledger-surface.mjs +22 -59
  42. package/src/tui/model-catalog.mjs +4 -4
  43. package/src/tui/model-picker.mjs +8 -7
  44. package/src/tui/mouse.mjs +11 -6
  45. package/src/tui/pickers.mjs +15 -2
  46. package/src/tui/render-conversation.mjs +1 -1
  47. package/src/tui/render-frame.mjs +15 -6
  48. package/src/tui/render-loop.mjs +1 -1
  49. package/src/tui/render-segments.mjs +3 -1
  50. package/src/tui/slash-commands.mjs +1 -1
  51. package/src/tui/startup.mjs +14 -14
  52. package/src/tui/subagent-blocks.mjs +20 -3
  53. package/src/tui/subagent-freeze.mjs +73 -2
  54. package/src/tui/suspension-drive.mjs +57 -23
  55. package/src/tui/tool-events.mjs +11 -8
  56. package/src/tui/tui-lifecycle.mjs +9 -0
  57. package/src/tui/wizard.mjs +3 -3
  58. package/src/tui/wrapped-spawn.mjs +6 -2
  59. package/src/abort-provenance.mjs +0 -116
  60. package/src/advisor/citations.mjs +0 -139
  61. package/src/advisor/compaction.mjs +0 -174
  62. package/src/advisor/convergence.mjs +0 -80
  63. package/src/advisor/history.mjs +0 -77
  64. package/src/advisor/loop.mjs +0 -293
  65. package/src/advisor/messages.mjs +0 -299
  66. package/src/advisor/project-context.mjs +0 -194
  67. package/src/advisor/repos.mjs +0 -150
  68. package/src/advisor/run.mjs +0 -293
  69. package/src/advisor/truncate.mjs +0 -57
  70. package/src/advisor.mjs +0 -290
  71. package/src/agent/completion.mjs +0 -146
  72. package/src/agent/dispatch.mjs +0 -489
  73. package/src/agent/helpers.mjs +0 -384
  74. package/src/agent/post-turn.mjs +0 -70
  75. package/src/agent/record-results.mjs +0 -174
  76. package/src/agent/relay-prefix.mjs +0 -39
  77. package/src/agent/run-stages.mjs +0 -244
  78. package/src/agent/setup-reminders.mjs +0 -69
  79. package/src/agent/setup.mjs +0 -354
  80. package/src/agent/spawn-child.mjs +0 -243
  81. package/src/agent-tools/advisor-async.mjs +0 -346
  82. package/src/agent-tools/advisor-settle.mjs +0 -231
  83. package/src/agent-tools/advisor.mjs +0 -260
  84. package/src/agent-tools/async-settle.mjs +0 -204
  85. package/src/agent-tools/batch-segment.mjs +0 -195
  86. package/src/agent-tools/consult.mjs +0 -473
  87. package/src/agent-tools/design-token.mjs +0 -117
  88. package/src/agent-tools/digest-budget.mjs +0 -76
  89. package/src/agent-tools/eng.mjs +0 -67
  90. package/src/agent-tools/escalate-async.mjs +0 -295
  91. package/src/agent-tools/goal.mjs +0 -119
  92. package/src/agent-tools/plan.mjs +0 -81
  93. package/src/agent-tools/read-history.mjs +0 -309
  94. package/src/agent-tools/recent-changes.mjs +0 -24
  95. package/src/agent-tools/review-streak.mjs +0 -93
  96. package/src/agent-tools/settings.mjs +0 -265
  97. package/src/agent-tools/skill.mjs +0 -47
  98. package/src/agent-tools/subagent-actions.mjs +0 -482
  99. package/src/agent-tools/subagent-async.mjs +0 -434
  100. package/src/agent-tools/subagent-panel.mjs +0 -160
  101. package/src/agent-tools/subagent-run.mjs +0 -205
  102. package/src/agent-tools/subagent-scheduler.mjs +0 -392
  103. package/src/agent-tools/subagent-spawn.mjs +0 -459
  104. package/src/agent-tools/subagent.mjs +0 -404
  105. package/src/agent-tools/task.mjs +0 -87
  106. package/src/agent-tools/timer.mjs +0 -46
  107. package/src/agent-tools/verify.mjs +0 -271
  108. package/src/agent-tools.mjs +0 -17
  109. package/src/agent.mjs +0 -417
  110. package/src/auto-think.mjs +0 -115
  111. package/src/config-migrate.mjs +0 -70
  112. package/src/config.mjs +0 -496
  113. package/src/context.mjs +0 -392
  114. package/src/conventions.mjs +0 -223
  115. package/src/embedding.mjs +0 -120
  116. package/src/escape.mjs +0 -152
  117. package/src/expand-home.mjs +0 -16
  118. package/src/explore-distill.mjs +0 -155
  119. package/src/generate-title.mjs +0 -88
  120. package/src/git/checkpoint.mjs +0 -448
  121. package/src/git/gitmem.mjs +0 -100
  122. package/src/hooks.mjs +0 -97
  123. package/src/ledger.mjs +0 -227
  124. package/src/log.mjs +0 -195
  125. package/src/markdown.mjs +0 -106
  126. package/src/mcp/helpers.mjs +0 -51
  127. package/src/mcp/transport-http.mjs +0 -248
  128. package/src/mcp/transport-stdio.mjs +0 -140
  129. package/src/mcp/transport-ws.mjs +0 -122
  130. package/src/mcp.mjs +0 -295
  131. package/src/memory/code-index.mjs +0 -219
  132. package/src/memory/code-sync.mjs +0 -415
  133. package/src/memory/core.mjs +0 -299
  134. package/src/memory/delete.mjs +0 -236
  135. package/src/memory/docs.mjs +0 -419
  136. package/src/memory/file-walk.mjs +0 -109
  137. package/src/memory/scan.mjs +0 -95
  138. package/src/memory/schema.mjs +0 -452
  139. package/src/memory.mjs +0 -21
  140. package/src/model-ref.mjs +0 -66
  141. package/src/model-specs.mjs +0 -179
  142. package/src/peer-domains.mjs +0 -265
  143. package/src/peer-instances.mjs +0 -231
  144. package/src/prompt-overlays.mjs +0 -82
  145. package/src/prompts/advisor-design.md +0 -41
  146. package/src/prompts/advisor-round1.md +0 -41
  147. package/src/prompts/advisor-round2.md +0 -46
  148. package/src/prompts/advisor-round3.md +0 -42
  149. package/src/prompts/common.md +0 -115
  150. package/src/prompts/consult-base.md +0 -19
  151. package/src/prompts/discipline-engineering.md +0 -258
  152. package/src/prompts/discipline-normal.md +0 -185
  153. package/src/prompts/persona-coder.md +0 -21
  154. package/src/prompts/persona-eng-coder.md +0 -37
  155. package/src/prompts/persona-eng-designer.md +0 -60
  156. package/src/prompts/persona-engineering.md +0 -55
  157. package/src/prompts/persona-explore.md +0 -15
  158. package/src/prompts/persona-normal.md +0 -27
  159. package/src/prompts/persona-plan.md +0 -26
  160. package/src/provider/anthropic.mjs +0 -225
  161. package/src/provider/core.mjs +0 -476
  162. package/src/provider/errors.mjs +0 -101
  163. package/src/provider/google.mjs +0 -257
  164. package/src/provider/index.mjs +0 -7
  165. package/src/provider/list-models.mjs +0 -93
  166. package/src/provider/normalize.mjs +0 -81
  167. package/src/provider/rate.mjs +0 -108
  168. package/src/provider/responses.mjs +0 -495
  169. package/src/provider/retry.mjs +0 -88
  170. package/src/provider/sse.mjs +0 -264
  171. package/src/proxy.mjs +0 -261
  172. package/src/rules.mjs +0 -53
  173. package/src/session-gc.mjs +0 -221
  174. package/src/session-guard.mjs +0 -59
  175. package/src/session-migrate.mjs +0 -48
  176. package/src/session-rename.mjs +0 -38
  177. package/src/session-segments.mjs +0 -100
  178. package/src/session-slots.mjs +0 -492
  179. package/src/session-store.mjs +0 -441
  180. package/src/session.mjs +0 -492
  181. package/src/skills.mjs +0 -153
  182. package/src/text-budget.mjs +0 -46
  183. package/src/token-ttl.mjs +0 -274
  184. package/src/tools/apply_patch.md +0 -15
  185. package/src/tools/bash.md +0 -37
  186. package/src/tools/bash.mjs +0 -268
  187. package/src/tools/checklist-sync.mjs +0 -181
  188. package/src/tools/checklist.md +0 -13
  189. package/src/tools/checklist.mjs +0 -299
  190. package/src/tools/delete.md +0 -13
  191. package/src/tools/edit-batch.mjs +0 -191
  192. package/src/tools/edit-diff.mjs +0 -348
  193. package/src/tools/edit.md +0 -30
  194. package/src/tools/execute.md +0 -21
  195. package/src/tools/execute.mjs +0 -228
  196. package/src/tools/fetch.md +0 -12
  197. package/src/tools/file.mjs +0 -469
  198. package/src/tools/file_ops.md +0 -17
  199. package/src/tools/get_current_time.md +0 -8
  200. package/src/tools/git-checkpoint.mjs +0 -143
  201. package/src/tools/git-ext.mjs +0 -173
  202. package/src/tools/git.md +0 -54
  203. package/src/tools/git.mjs +0 -356
  204. package/src/tools/glob-dialect.mjs +0 -130
  205. package/src/tools/glob.md +0 -11
  206. package/src/tools/grep.md +0 -19
  207. package/src/tools/hashline_edit.md +0 -14
  208. package/src/tools/index.mjs +0 -36
  209. package/src/tools/insert_after.md +0 -15
  210. package/src/tools/lint.md +0 -10
  211. package/src/tools/linter.mjs +0 -128
  212. package/src/tools/ls.md +0 -12
  213. package/src/tools/lsp.md +0 -10
  214. package/src/tools/lsp.mjs +0 -316
  215. package/src/tools/ops.mjs +0 -299
  216. package/src/tools/patch.mjs +0 -282
  217. package/src/tools/process.md +0 -10
  218. package/src/tools/question.md +0 -16
  219. package/src/tools/question.mjs +0 -26
  220. package/src/tools/read.md +0 -20
  221. package/src/tools/read_image.md +0 -8
  222. package/src/tools/repomap.mjs +0 -314
  223. package/src/tools/search.mjs +0 -236
  224. package/src/tools/shared.mjs +0 -446
  225. package/src/tools/tree.md +0 -14
  226. package/src/tools/tree.mjs +0 -66
  227. package/src/tools/wait_for.md +0 -22
  228. package/src/tools/web.mjs +0 -224
  229. package/src/tools/websearch.md +0 -16
  230. package/src/tools/write.md +0 -11
  231. package/src/traces/trace-store.mjs +0 -355
package/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
1
+ ## [0.12.64] — 2026-09-21
2
+
3
+ > 0.12.63 → 0.12.64(连续号——发布时定号)
4
+
5
+ ### Added
6
+
7
+ - **PROJECT-MANIFEST 模型(按用点解析)**:owning-ancestor 归属 + 五级梯发现;整档缺失非致命(启动降级 + 建档流提示)。
8
+ - **批档生命周期工具** `batch`(create / append / status / close)· **台账执行人列**(F-LX1)· **信号行两档标签**(digest/ask)+ 起始行 · **会话认领释放**(F-CR1/CR2,拒绝零写入)。
9
+ - **启动非阻塞**:会话 GC 异步化 + 陈旧清扫 + traces 清理;**启动加载行**(包裹父进程 + TUI 首帧,含修复轮收正)。
10
+
11
+ ### Changed
12
+
13
+ - **工程模式排除 `plan` 角色**(FR31)。
14
+
15
+ ### Fixed
16
+
17
+ - 批别名残留(断言 / 提示词文档引用 / 错误串)· VSC 子代理块标题对齐(CLI 标题段补全)· README 依赖声明口径收正(零三方运行时依赖)。
18
+
19
+ ## [0.12.63] — 2026-09-21
20
+
21
+ > 0.12.62 → 0.12.63(连续号——发布时定号;本版随核首发联动)
22
+
23
+ ### Fixed
24
+
25
+ - **发布依赖收正**:`@thincoder/core` `^0.1.0` → `^0.9.1`(核首发 `@thincoder/core@0.9.1` 同日落地——声明面与 registry 实发对齐;0.12.62 及以前发布形态为合并前自带拷贝、无核依赖)。
26
+ - **smoke 探针 import 修复**:`isBailianHost` 实居 `config.mjs`——原从 `provider/normalize.mjs` 静默容错导入(`catch(() => ({}))`)恒 undefined ⇒ smoke 恒 skip(假绿)✗ 修正后真跑三连测(OFF 无思考 / XHIGH / DEFAULT 有思考——dashscope 百炼 qwen 实测过 ✓)。
27
+
1
28
  ## [0.12.62] — 2026-09-13
2
29
 
3
30
  > 0.12.61 → 0.12.62(§4.3 连续号——发布时定号)
package/README.md CHANGED
@@ -2,25 +2,25 @@
2
2
 
3
3
  **Sharp Code, Zero Bloat.**
4
4
 
5
- **A "thin" AI coding agent: pure `.mjs`, no build step, zero npm dependencies, native Node.js.**
5
+ **A "thin" AI coding agent: pure `.mjs`, no build step, zero third-party dependencies, native Node.js.**
6
6
 
7
7
  The "Thin" in ThinCoder doesn't mean "feature-poor" — it means **sharp thinking, straight to the point** — like a blade.
8
8
  While every AI agent races to be "all-powerful", ThinCoder plays the opposite card: **restraint, precision, no filler**.
9
9
  Its persona is a geek engineer of few words who cuts to the bone: give it a complex requirement, get back a clean implementation.
10
10
 
11
- Design philosophy (the entire meaning of the name): if the Node standard library can do it, no dependency is allowed. The project's `node_modules` is empty.
11
+ Design philosophy (the entire meaning of the name): if the Node standard library can do it, no third-party dependency is allowed. The project's `node_modules` contains no third-party packages — only the first-party `@thincoder/core` shared core (a local link in development, resolved from the npm registry for releases).
12
12
 
13
13
  ## Features
14
14
 
15
15
  - **Fix-verify loop**: file changes without `verify` get pushed back — syntax check + tests must pass before the agent can claim completion (auto-repair up to 3 rounds)
16
16
  - **Checkpoint system**: auto-snapshot before every user task, `list`/`create`/`rewind` tools for the model, single-file restore — rewinding itself is reversible (pre-rewind state auto-saved)
17
17
  - **Codebase understanding** ⭐0.5.0: `repo_outline` (dependency outline, auto-injected at startup), `code_search` (source FTS5 + vectors + JSDoc extraction), `doc_search` (docs chunked by ## headings) — background indexing, auto-incremental updates on file writes, three tools guided by "structure → intent → details"
18
- - **Model adaptation** ⭐: top-tier only, latest only. Built-in flagship models from twenty providers — DeepSeek / Kimi / Kimi For Coding / GLM / GLM Coding Plan (智谱 Coding) / Qwen / Qwen Token Plan / MiMo (小米) / MiMo Token Plan (小米套餐) / MiniMax / OpenAI / Claude / Gemini / Grok / Mistral / Volcengine Ark (豆包) / Hunyuan (腾讯混元) / SiliconFlow (硅基流动) / OpenRouter / Groq. No legacy model compatibility. Auto-matched context windows, truncation-resume protocols (prefix/partial), thinking-mode APIs (thinking.type / reasoning_effort), reasoning_content echo strategies (reasoningEcho), output limits, temperature range clamping — all deeply adapted.
18
+ - **Model adaptation** ⭐: top-tier only, latest only. Built-in flagship models from twenty-one providers — DeepSeek / Kimi / Kimi For Coding / GLM / GLM Coding Plan (智谱 Coding) / Qwen / Qwen Token Plan / MiMo (小米) / MiMo Token Plan (小米套餐) / MiniMax / OpenAI / Claude / Gemini / Grok / Mistral / Volcengine Ark (豆包) / Hunyuan (腾讯混元) / Tencent TokenHub (腾讯混元网关) / SiliconFlow (硅基流动) / OpenRouter / Groq. No legacy model compatibility. Auto-matched context windows, truncation-resume protocols (prefix/partial), thinking-mode APIs (thinking.type / reasoning_effort), reasoning_content echo strategies (reasoningEcho), output limits, temperature range clamping — all deeply adapted.
19
19
  - **Toolset**: `read` / `write` / `edit` / `bash` / `glob` (supports `**`) / `grep` / `websearch` / `ls` / `fetch` + `read_image` (image/video paste) + three retrieval tools + MCP — all zero-dependency, file tools confined to the working directory
20
20
  - **Memory system**: three layers (personal/project/team), FTS5 + vector RRF hybrid retrieval, git-friendly markdown format
21
21
  - **Two-phase tool scheduling**: permission prompts serialized, read-only tools parallelized, side-effect tools serialized
22
22
  - **Session persistence** ⭐0.5.0: unlimited archive slots, `/session` to switch anytime, tool results visible after restore. Process-level isolation — multiple instances in the same directory each get their own session slot
23
- - **Concurrent subagents**: three roles — `explore`/`plan`/`coder` — dispatched in parallel, streaming output visible, reports land in the conversation; per-subagent model override (`subagent` tool `model` arg or `agent.subagentModel` config — e.g. discuss with `glm-5.2`, let `deepseek-v4-flash` implement)
23
+ - **Concurrent subagents**: five roles — `explore`/`plan`/`coder`/`eng-coder`/`eng-designer` — dispatched in parallel, streaming output visible, reports land in the conversation; per-subagent model override (`subagent` tool `model` arg or `agent.subagentModel` config — e.g. discuss with `glm-5.2`, let `deepseek-v4-flash` implement)
24
24
  - **Multi-model consultation 会诊 + 飞刀 (escalate)** ⭐0.12.30: `consult_start`/`consult_stop` run several configured models in parallel as independent read-only consultants (each with its own TUI activity card, `main_history` access to the failure trail); when every model settles, the full verdict digest is delivered automatically (R17 — consult_check retired). `escalate` flies in a stronger model for a single expert implementation run with full write access (default-async since R17 — the post-op report arrives automatically with its mutations merged). Candidate pool = `agent.consultModels` ([{ provider, model, effort? }], up to 5); budgets via `agent.consultTurns` / `agent.consultTimeoutMs`
25
25
  - **Plan Mode**: read-only exploration + design, implement after user approval
26
26
  - **AUTO mode**: `/auto` full authorization, no confirmations on long tasks
@@ -49,7 +49,8 @@ Three layers, all "query if present, skip if absent", unified hybrid retrieval:
49
49
  - IDE-native diffs — `write`/`edit` route through the editor buffer
50
50
  - Persisted sessions: list / load (history replay) / resume / delete
51
51
  - Per-session config: model / thinking / mode
52
- - Setup: [docs/guides/ides.md](docs/guides/ides.md)
52
+ - Setup: [ACP 接入设计](../docs/cli/design/ACP-CLIENT.md)
53
+ - Login: `thincoder acp --login` — one interactive terminal login; ACP clients and orchestrators then drive sessions headlessly
53
54
 
54
55
 
55
56
  ## Requirements
@@ -68,7 +69,7 @@ npm install -g thincoder
68
69
  thincoder
69
70
  ```
70
71
 
71
- First launch walks you through a setup wizard: arrow keys to pick a provider (built-in presets or a custom endpoint) → enter API key → optionally enter an embedding key (SiliconFlow, enables vector memory search, skippable) → arrow keys to pick a model — no hand-editing config files. Adjust anytime with `/provider`, `/model`, `/config embedkey`. `chat`/`distill` also offer in-place interactive setup when no key is configured in a terminal (in pipes/CI they exit with an error and instructions).
72
+ First launch walks you through a setup wizard: arrow keys to pick a provider (built-in presets or a custom endpoint) → enter API key → optionally enter an embedding key (SiliconFlow, enables vector memory search, skippable) → arrow keys to pick a model — no hand-editing config files. Adjust anytime with `/model` (add/remove providers, set keys, custom endpoints) or `/config embedkey`. `chat`/`distill` also offer in-place interactive setup when no key is configured in a terminal (in pipes/CI they exit with an error and instructions).
72
73
 
73
74
  You can also hand-write `~/.thincoder/config.json` (see "Configuration" below), then:
74
75
 
@@ -94,7 +95,7 @@ thincoder upgrade
94
95
 
95
96
  Running from source: replace `thincoder` above with `node bin/thincoder.mjs`.
96
97
 
97
- Slash commands in the TUI: `/help`, `/model` (two-level picker: first select provider, then model; `/model <provider>:<name>` switches directly), `/submodel` (subagent models per type — picker over global + explore/plan/coder/eng-coder slots, or `/submodel <type> <provider:model>` directly), `/shell` (platform-aware picker of available shells — e.g. `/shell` → pick Git Bash/pwsh, or `/shell "C:\Program Files\Git\bin\bash.exe"`, `/shell reset`; fixes win11 cmd encoding/command issues), `/provider` (add/remove providers, set keys, custom endpoints), `/think` (thinking mode toggle and reasoning effort), `/config` (view config, `/config embedkey` for the embedding key, `/config set` for parameters), `/session` (list/switch archived sessions), `/reindex` (rebuild the index), `/extract` (extract knowledge from the current session), `/restore` (restore checkpoint), `/clear`, `/exit`. High-frequency commands support abbreviations: `/h` `/x` `/m` `/p` `/t` `/c` `/n`. Typing `/` shows live matching hints in the status bar. Model picker supports search/filter — type to narrow down results.
98
+ Slash commands in the TUI: `/help`, `/model` (two-level picker: first select provider, then model; also add/remove providers, set keys, custom endpoints; `/model <provider>:<name>` switches directly), `/submodel` (subagent models per type — picker over global + explore/plan/coder/eng-coder/eng-designer slots, or `/submodel <type> <provider:model>` directly), `/shell` (platform-aware picker of available shells — e.g. `/shell` → pick Git Bash/pwsh, or `/shell "C:\Program Files\Git\bin\bash.exe"`, `/shell reset`; fixes win11 cmd encoding/command issues), `/think` (thinking mode toggle and reasoning effort), `/config` (view config; no-argument form opens the interactive menu — e.g. default model, embedding key, proxy; `/config embedkey` sets the embedding key directly), `/session` (list/switch archived sessions), `/reindex` (rebuild the index), `/extract` (extract knowledge from the current session), `/restore` (restore checkpoint), `/clear`, `/exit`. High-frequency commands support abbreviations: `/h` `/x` `/m` `/p` `/t` `/c` `/n`. Typing `/` shows live matching hints in the status bar. Model picker supports search/filter — type to narrow down results.
98
99
 
99
100
  Configuration comes exclusively from `~/.thincoder/config.json` — no environment-variable configuration is supported.
100
101
 
@@ -102,16 +103,16 @@ Configuration comes exclusively from `~/.thincoder/config.json` — no environme
102
103
 
103
104
  ## Configuration
104
105
 
105
- `~/.thincoder/config.json`:
106
+ `~/.thincoder/config.json` — strictly parsed (`JSON.parse`); the `//` annotations below are illustrative only and are not valid in the actual file:
106
107
 
107
- ```jsonc
108
+ ```json
108
109
  {
109
110
  "providers": [
110
111
  // multiple allowed; switch with /model <name>
111
112
  {
112
113
  "name": "deepseek",
113
114
  "baseURL": "https://api.deepseek.com/v1", // any OpenAI-compatible endpoint
114
- "apiKey": "sk-...", // or leave empty to use env vars
115
+ "apiKey": "sk-...", // or leave empty — the TUI prompts for a key when this provider is selected
115
116
  "model": "deepseek-chat",
116
117
  // optional: proactive throttling budget (match your account's rate-limit tier;
117
118
  // without it the gate is off, 429 backoff still applies).
@@ -120,7 +121,7 @@ Configuration comes exclusively from `~/.thincoder/config.json` — no environme
120
121
  // "rpm": 50, // requests/minute
121
122
  },
122
123
  ],
123
- "activeProvider": "deepseek", // currently active provider name
124
+ "defaultModel": "deepseek:deepseek-chat", // new-session starting model — "provider:model" (set via /config → 默认模型; /model switches the current session)
124
125
  "shell": null, // bash tool shell (win11: e.g. "C:\\Program Files\\Git\\bin\\bash.exe" or "pwsh"); null = system default — cmd on Windows (UTF-8 forced per command), /bin/sh elsewhere. TUI: /shell; a leading ~ in the path expands to the home directory
125
126
  "embedding": {
126
127
  // optional: without it, retrieval is pure FTS
@@ -131,7 +132,7 @@ Configuration comes exclusively from `~/.thincoder/config.json` — no environme
131
132
  "agent": {
132
133
  "maxTurns": 100, // tool-loop cap
133
134
  "subagentModel": null, // default subagent provider/model override: "provider:model" | provider name | model name; null = inherit parent provider. Per-call: subagent tool `model` arg
134
- "subagentModels": {}, // per-type override: { "explore": "...", "plan": "...", "coder": "...", "eng-coder": "..." }; priority: tool model arg > this > subagentModel > parent provider
135
+ "subagentModels": {}, // per-type override: { "explore": "...", "plan": "...", "coder": "...", "eng-coder": "...", "eng-designer": "..." }; priority: tool model arg > this > subagentModel > parent provider
135
136
  "compactThreshold": 100000, // context compaction threshold (approx. tokens)
136
137
  },
137
138
  "memory": {
@@ -165,43 +166,25 @@ Configuration comes exclusively from `~/.thincoder/config.json` — no environme
165
166
  ## Architecture
166
167
 
167
168
  ```
168
- bin/thincoder.mjs command entry (tui / chat / memory / sync / distill)
169
+ bin/thincoder.mjs command entry (tui / chat / memory / sync / distill / reindex / completion / upgrade / acp / session gc)
170
+ bin/thincoder.cjs CommonJS shim → bin/thincoder.mjs
169
171
  src/
170
- provider/ LLM calls — core.mjs (fetch, SSE streaming, reasoning_content, usage, retries),
171
- rate.mjs (TPM/RPM proactive rate gate), index.mjs (entry)
172
- embedding.mjs vector embeddings (OpenAI-compatible /v1/embeddings)
173
- tools/ 16 builtin tools + MCP wrapping + readonly scheduling flags
174
- index.mjs (registry), file/git/patch/system/web.mjs (groups), shared.mjs (schema utils),
175
- repomap.mjs (repo dependency outline: import/export regex parsing, on-demand via tool)
176
- tools.mjs re-export shim → src/tools/index.mjs
177
- mcp/ MCP client — helpers.mjs, transport-stdio/http/ws.mjs (JSON-RPC, zero-dependency)
178
- mcp.mjs MCP client entry (connectMcpServer), delegates to src/mcp/
179
- agent.mjs main loop + two-phase tool execution + reminder injection + completion guard + fix-verify loop
180
- + incremental indexing (auto reindexFile after write/edit/delete)
181
- agent/ agent loop helpers — dispatch.mjs (two-phase execution), setup.mjs (system prompt assembly), helpers.mjs
182
- agent-tools/ self-discipline tools (task/plan/goal/verify/subagent/skill/recent_changes)
183
- context.mjs rough token estimation + history compaction + task re-injection
184
- memory/ three-layer memory — schema.mjs (DDL/constants), core.mjs (CRUD + retrieval),
185
- code-index.mjs + code-sync.mjs (code_chunks), docs.mjs (doc_chunks)
186
- memory.mjs re-export shim → src/memory/*
187
- session.mjs session persistence (unlimited archive slots, isolated by project cwd, process-level isolation via sessionId + slotSessions)
188
- skills.mjs skill discovery/loading (.thincoder/skills/*.md)
189
- markdown.mjs entry format (frontmatter parse/serialize)
190
- git/ checkpoint.mjs (git patch snapshots / rewind), gitmem.mjs (Team layer git sync)
191
- distill.mjs session knowledge extraction (candidates + human confirmation)
192
- config.mjs config loading
193
- tui/ bare-ANSI terminal UI — index.mjs (startTUI), render.mjs (drawing primitives),
194
- render-frame.mjs (frame layout), render-conversation.mjs (conversation panel),
195
- markdown.mjs (lightweight inline markdown → ANSI), mouse.mjs (SGR clicks),
196
- clipboard.mjs (paste/copy), ansi.mjs
197
- tui.mjs re-export shim → src/tui/index.mjs
198
- tui-render.mjs re-export shim → src/tui/render.mjs
199
- prompts/ prompt texts — slot-based (PROMPT-SYSTEM): persona-engineering / persona-normal /
200
- persona-{eng-coder,explore,coder,plan} + common + discipline-engineering / discipline-normal
201
- + special modules (consult-base / advisor-design / advisor-round{1,2,3});
202
- assembly = assemblePrompt (prompt-overlays.mjs): persona → common → discipline → [4] AGENTS+skills
172
+ acp/ Agent Client Protocol bridge — bridge, client caps, session/slot handlers, login, transport
173
+ acp.mjs thincoder acp entry → src/acp/
174
+ cli/ top-level command implementations — setup-wizard, memory-command, distill-command,
175
+ make-agent (assembly), permission
176
+ completions.mjs shell completion scripts (thincoder completion <shell>)
177
+ crash-reports.mjs crash capture and forensics — fatal records, stderr capture, heap snapshots
178
+ distill.mjs session knowledge extraction (candidates + human confirmation)
179
+ heap-watch.mjs heap telemetry / watchdog
180
+ prompt-injections.mjs prompt-anchor values for this end — tool face (bash / question)
181
+ tui/ bare-ANSI terminal UI — index.mjs (startTUI), frame/conversation rendering,
182
+ key handling, mouse/clipboard, the cmd-* command family, subagent panels
183
+ tui.mjs re-export shim → src/tui/index.mjs
184
+ upgrade.mjs version check and self-upgrade
203
185
  test/ node:test offline unit tests (npm test)
204
- scripts/ real-environment verification scripts (compaction, team sync)
186
+ scripts/ repo-side scripts — release check, doc-impact, syntax/endpoint probes,
187
+ real-environment verification (compaction, team sync)
205
188
  ```
206
189
 
207
190
  Key design decisions:
@@ -221,7 +204,7 @@ node scripts/verify-compress.mjs # real-API verification of context compaction
221
204
  node scripts/verify-team.mjs # team memory A->git->B full-chain verification (local git, offline)
222
205
  ```
223
206
 
224
- Code conventions: pure `.mjs`, no semicolons, no npm dependencies allowed (including devDependencies).
207
+ Code conventions: pure `.mjs`, no semicolons, no third-party npm dependencies allowed (including devDependencies) — the first-party `@thincoder/core` shared core is the only npm dependency.
225
208
 
226
209
  ## Roadmap
227
210
 
@@ -229,6 +212,20 @@ Code conventions: pure `.mjs`, no semicolons, no npm dependencies allowed (inclu
229
212
 
230
213
  ## Changelog
231
214
 
215
+ ### 0.12.64 (2026-09)
216
+ - **PROJECT-MANIFEST model** — per-use project resolution (owning-ancestor discovery + a five-step ladder); a missing manifest is no longer fatal (degrade at startup + a clear prompt to build one).
217
+ - **Batch record lifecycle tool** (`batch`: create / append / status / close), **ledger executor column**, **two-tier signal lines** (digest/ask) with the start line, and **session claim release** (rejections write nothing).
218
+ - **Non-blocking startup** — async session GC, stale sweep, traces cleanup; the CLI start line shares the wrapped-parent + TUI first-paint path.
219
+ - **Engineering mode**: the `plan` role is excluded from the engineering tool set (FR31).
220
+ - **Fixes** — batch-alias residue across assertions, prompt doc-refs and error strings; subagent block-title parity; README dependency claims corrected to zero third-party runtime deps.
221
+
222
+ ### 0.12.63 (2026-09)
223
+ - **Core dependency live** — now depends on `@thincoder/core@^0.9.1` (the shared-mechanism core's first npm release); installed packages resolve the core from the registry instead of shipping duplicated copies.
224
+ - **Fix: smoke probe always skipped** — `isBailianHost` was imported from the wrong module behind a silent catch, so the pre-release real-endpoint smoke test never actually ran. Fixed; smoke now performs the real three-call thinking-mapping check.
225
+
226
+ ### 0.12.62 (2026-09)
227
+ - Last release in the pre-core shape (self-contained copies); 0.12.63 onward resolves the core from `@thincoder/core`.
228
+
232
229
  ### 0.12.11 (2026-08)
233
230
  - **Subagent model per type** — `subagent` tool `model` arg, `config.agent.subagentModels` (per explore/plan/coder/eng-coder) and `config.agent.subagentModel` (global fallback); priority: tool arg > type > global > parent provider. `/submodel` TUI command: picker over 5 slots (global + 4 roles) with provider→model selection, or direct args (`/submodel coder deepseek:deepseek-v4-flash`).
234
231
  - **Configurable bash shell** — `config.shell` or `/shell` TUI command: platform-aware picker (Windows: pwsh/Git Bash/WSL bash; POSIX: bash/zsh/fish — availability-detected, custom path supported). Windows default cmd now forces UTF-8 per command (`chcp 65001`) — fixes garbled Chinese output on win11.
@@ -303,7 +300,7 @@ Code conventions: pure `.mjs`, no semicolons, no npm dependencies allowed (inclu
303
300
 
304
301
  ### 0.9.0 (2026-07)
305
302
  - **Checkpoint v2** — snapshot-before-destructive with full workspace copies, per-file restore (`cat` preview), auto-recover on failed apply, and escape-hatch hints when git checks fail
306
- - **Lifecycle Hooks** — `PreToolUse` / `PostToolUse` / `PostToolUseFailure` / `Notification` events. User-defined shell commands in config, with per-tool regex matching, timeout control, and `block`/`allow`/`notify` actions. Implemented in `src/hooks.mjs`, integrated into tool dispatch.
303
+ - **Lifecycle Hooks** — `PreToolUse` / `PostToolUse` / `PostToolUseFailure` / `Stop` events. User-defined shell commands in config, with per-tool regex matching, timeout control, and `block`/`allow`/`notify` actions. Implemented in `thincoder-core/hooks.mjs`, integrated into tool dispatch.
307
304
  - **Built-in Skills (5)** — `pdf-create`, `xlsx-create`, `frontend-design`, `code-review`, `api-design` ship with the installation. Each is a standalone markdown instruction file using zero-dependency approaches (Chrome headless for PDF, PowerShell for Excel, etc.).
308
305
  - **Conversation message folding** — Long tool result blocks (>8 consecutive dim lines) auto-collapse to first 2 lines + "… N more lines — Enter to expand". `/fold on|off` toggles globally.
309
306
  - **Tree-shaped tasks** — `checklist` tool now supports hierarchical task IDs (`T1`, `T1.1`, `T1.2.1`) with auto-assigned numbering. `add` accepts `parent` parameter for subtree positioning. Indentation-based persistence in `checklist.md`.
package/bin/thincoder.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  * thincoder memory <sub> Memory management: list / search / put / remove
8
8
  * thincoder upgrade Update to the latest version from npm
9
9
  * thincoder completion <sh> Shell completion: bash / zsh / fish
10
- * thincoder session gc Session dir GC: --dry-run report / --confirm delete cold projects (SESSION.md §12)
10
+ * thincoder session gc Session dir GC: --dry-run report / --confirm delete cold projects (SESSION.md §6.12)
11
11
  * thincoder acp Agent Client Protocol server (stdio, for Zed/JetBrains/Paseo)
12
12
  * thincoder -v Print version
13
13
  * thincoder --help Print help
@@ -15,11 +15,14 @@
15
15
 
16
16
  import { readFileSync } from "node:fs"
17
17
  import { isAbsolute, join } from "node:path"
18
- import { runAgent } from "../src/agent.mjs"
19
- import { loadConfig, configPath } from "../src/config.mjs"
20
- import { cleanupTraces } from "../src/traces/trace-store.mjs"
21
- import { createMemory, syncDir } from "../src/memory.mjs"
22
- import { assembleAgent, teamConfig, gitAuthor, validateProvider } from "../src/cli/make-agent.mjs"
18
+ import { runAgent } from "@thincoder/core/agent.mjs"
19
+ import { loadConfig, configPath } from "@thincoder/core/config.mjs"
20
+ import { tracesRoot } from "@thincoder/core/traces/trace-store.mjs"
21
+ import { scheduleTraceCleanup } from "@thincoder/core/traces/trace-cleanup.mjs"
22
+ import { createMemory, syncDir } from "@thincoder/core/memory.mjs"
23
+ // 批 1 CORE-DEFECT-FIXES B3:onWait 相位值域 + 文案单源(消费面禁各自枚举——PROVIDER.md §6.20)
24
+ import { waitStatusText } from "@thincoder/core/provider/wait-status.mjs"
25
+ import { assembleAgent, attachManifest, teamConfig, gitAuthor, validateProvider } from "../src/cli/make-agent.mjs"
23
26
  import { memoryCommand } from "../src/cli/memory-command.mjs"
24
27
  import { setupWizard } from "../src/cli/setup-wizard.mjs"
25
28
  import { summarize, askPermission } from "../src/cli/permission.mjs"
@@ -27,6 +30,12 @@ import { distillCommand } from "../src/cli/distill-command.mjs"
27
30
  import { prepareCrashReporting, recentCrashHint, writeCrashRecord } from "../src/crash-reports.mjs"
28
31
  import { setTuiActive, restoreTerminalAfterCrash } from "../src/tui/tui-lifecycle.mjs"
29
32
  import { spawnTuiWrapped } from "../src/tui/wrapped-spawn.mjs"
33
+ import { configurePromptInjections } from "@thincoder/core/prompt-files.mjs"
34
+ import { CLI_PROMPT_INJECTIONS } from "../src/prompt-injections.mjs"
35
+
36
+ // U2(CORE-UNIFICATION §2.6.3 专项补⑦ / §2.13.8(六)):CLI 端锚取值表——**进程入口、任何装配之前**注册一次(调用期应用 ⇒ 无导入序要求)。
37
+ // 漏配 = 锚字面静默进模型 ⇒ 入口面用例(test/integration/cli-prompt-entry.test.mjs)显式覆盖本调用路径。
38
+ configurePromptInjections(CLI_PROMPT_INJECTIONS)
30
39
 
31
40
  const [command, ...args] = process.argv.slice(2)
32
41
  // TUI-STDERR-CAPTURE(F-1):TUI 启动(tui/无命令)默认包装——父 spawn 子 tee stderr 落盘(外部
@@ -93,8 +102,10 @@ const USAGE = `thincoder - thin coding agent
93
102
 
94
103
  Usage:
95
104
  thincoder Launch the interactive TUI
105
+ thincoder tui Launch the interactive TUI (explicit form of the default)
96
106
  thincoder chat [--auto] <prompt> One-shot agent run (tools enabled), streams reply to stdout; --auto approves all tool calls
97
107
  thincoder acp Agent Client Protocol server (stdio — Zed/JetBrains/Paseo drive sessions)
108
+ thincoder acp --login Authenticate this machine for ACP clients (terminal auth flow), then exit
98
109
  thincoder memory list [--type=<t>] List memory entries
99
110
  thincoder memory search <query> Search memory
100
111
  thincoder memory put --type=<t> --title=<t> --content=<c> [--tags=<t>]
@@ -105,7 +116,8 @@ Usage:
105
116
  Extract knowledge candidates from a session
106
117
  transcript file; confirm each before saving
107
118
  thincoder session gc --dry-run | --confirm <hash|--all>
108
- Session dir GC: report/delete cold project data (cold = manifest idle >90d, no live slots)
119
+ Session dir GC: report/recycle cold & stale project data into a 7-day recycle bin
120
+ (cold = manifest idle >90d; stale = no live owner + unreachable/empty cwd + 7-day window)
109
121
  thincoder upgrade Update to the latest version from npm
110
122
  thincoder completion <sh> Generate shell completion script (bash / zsh / fish)
111
123
  thincoder -v, --version Print version
@@ -115,7 +127,7 @@ Config: ~/.thincoder/config.json — providers[] (one default model per channel)
115
127
 
116
128
  /** Unified message when no API key is configured */
117
129
  function noKeyMessage() {
118
- return `No API key configured yet. Run "thincoder" to enter TUI, use /provider add and /provider key; or edit ${configPath} directly`
130
+ return `No API key configured yet. Run "thincoder" to enter TUI, use /model to add a provider and set its key; or edit ${configPath} directly`
119
131
  }
120
132
 
121
133
  /** Delay exit: process.exit right after fetch triggers libuv assertion on Windows/Node 24; let handles drain first */
@@ -123,12 +135,18 @@ function exitSoon(code) {
123
135
  setTimeout(() => process.exit(code), 100)
124
136
  }
125
137
 
126
- // D-TR9(2026-09-05):启动轨迹清理——删除超过 traces.retentionHours(默认 24h)的
127
- // 轨迹文件(fire-and-forget——不阻塞启动——失败静默——与轨迹写盘同纪律)。
128
- try {
129
- const startupCfg = loadConfig()
130
- cleanupTraces({ retentionHours: startupCfg.traces?.retentionHours ?? 24 }).catch(() => {})
131
- } catch { /* 配置缺失/损坏 → 跳过清理(零风险) */ }
138
+ // D-TR9(2026-09-05):启动轨迹清理——删除超过 traces.retentionHours(默认 24h)的轨迹文件
139
+ // (fire-and-forget——不阻塞启动——失败静默——与轨迹写盘同纪律)。
140
+ // D-TR13(2026-09-21 · STARTUP-LATENCY 批 · TRACES.md §6.4):触发面 = **会话型命令白名单**
141
+ // (`tui` 含无参默认路径 `command === undefined` / `chat` / `acp`);白名单外命令(`--version` /
142
+ // `--help` / `completion` / `memory` / `sync` / `reindex` / `distill` / `upgrade` / `session`)
143
+ // 零启动清理。**启动清理 = 启动窗外延迟拍**——核侧 `scheduleTraceCleanup`(`TRACE_CLEANUP_DELAY_MS` = 3s,自调度点起;失败静默 / 不 unref 保后台排空)。
144
+ if (command === undefined || command === "tui" || command === "chat" || command === "acp") {
145
+ try {
146
+ const startupCfg = loadConfig()
147
+ scheduleTraceCleanup({ dir: tracesRoot(), retentionHours: startupCfg.traces?.retentionHours ?? 24 })
148
+ } catch { /* 配置缺失/损坏 → 跳过清理(零风险) */ }
149
+ }
132
150
 
133
151
  switch (command) {
134
152
  case "chat": {
@@ -145,7 +163,7 @@ switch (command) {
145
163
  if (crashNotice) console.error(crashNotice)
146
164
 
147
165
  const agent = await assembleAgent()
148
- // SESSION.md §8 D-S4(F4)+ MODEL-MERGE-SESSION:headless 无 TUI —— 可读错误 + 退出码 1,
166
+ // SESSION.md §6.8 D-S4(F4)+ MODEL-MERGE-SESSION:headless 无 TUI —— 可读错误 + 退出码 1,
149
167
  // 不弹 UI、不崩溃;文案改引 defaultModel(F-6 引导 A headless 面——/config → 默认模型)
150
168
  if (agent._providerInvalid) {
151
169
  const dm = agent.config?.defaultModel
@@ -169,7 +187,7 @@ switch (command) {
169
187
  // Wizard may have configured an embedding key: attach vector search
170
188
  const fresh = loadConfig()
171
189
  if (fresh.embedding?.apiKey && agent.memory && !agent.memory.embedder) {
172
- const { createEmbedder } = await import("../src/embedding.mjs")
190
+ const { createEmbedder } = await import("@thincoder/core/embedding.mjs")
173
191
  agent.memory.embedder = createEmbedder(fresh.embedding)
174
192
  }
175
193
  }
@@ -179,8 +197,9 @@ switch (command) {
179
197
  try {
180
198
  await runAgent(agent, prompt, {
181
199
  onToken: (text) => process.stdout.write(text),
182
- onWait: ({ phase, seconds }) => {
183
- console.error(phase === "gate" ? `[rate-limit] TPM throttle waiting ~${seconds}s` : `[rate-limit] 429 response, retrying in ${seconds}s`)
200
+ onWait: (ev) => {
201
+ const s = waitStatusText(ev)
202
+ if (s) console.error(`[rate-limit] ${s}`)
184
203
  },
185
204
  onToolCall: (name, toolArgs) => {
186
205
  console.error(`\n[tool] ${name} ${summarize(toolArgs)}`)
@@ -227,7 +246,7 @@ switch (command) {
227
246
  const config = loadConfig()
228
247
  const memory = createMemory({ dbPath: config.memory.dbPath })
229
248
  if (config.embedding?.apiKey) {
230
- const { createEmbedder } = await import("../src/embedding.mjs")
249
+ const { createEmbedder } = await import("@thincoder/core/embedding.mjs")
231
250
  memory.embedder = createEmbedder(config.embedding)
232
251
  }
233
252
  if (config.memory.projectDir) {
@@ -248,7 +267,7 @@ switch (command) {
248
267
  break
249
268
  }
250
269
  const memory = createMemory({ dbPath: config.memory.dbPath })
251
- const { ensureClone, pullTeam } = await import("../src/git/gitmem.mjs")
270
+ const { ensureClone, pullTeam } = await import("@thincoder/core/git/gitmem.mjs")
252
271
  try {
253
272
  const cloned = await ensureClone(team)
254
273
  if (cloned) console.log(`Cloned team repo to ${team.dir}`)
@@ -281,7 +300,7 @@ switch (command) {
281
300
  }
282
301
  const team = teamConfig(config)
283
302
  if (team) {
284
- const { ensureClone } = await import("../src/git/gitmem.mjs")
303
+ const { ensureClone } = await import("@thincoder/core/git/gitmem.mjs")
285
304
  await ensureClone(team)
286
305
  const s = await syncDir(memory, { layer: "team", dir: team.dir })
287
306
  total.added += s.added
@@ -292,22 +311,26 @@ switch (command) {
292
311
 
293
312
  case "tui":
294
313
  case undefined: {
295
- const agent = await assembleAgent()
296
- // SESSION.md §8 D-S1:TUI 路径在 startTUI 前清空无效 provider——空 provider 不流入 runAgent
314
+ // 恢复上次的会话(同一项目目录);provider 按保存的名字切回(用户上次可能换过模型)
315
+ // 2026-09-05 §6.10(R4):恢复决策按本端记录 resumeSlot(D-2 ①②③)——manifest active
316
+ // 只作"无记录端"的一次性继承源,不再作本端恢复第一依据(D-6)。
317
+ // 2026-09-18(装配门禁批 #30 · KD-M1-13 / KD-M1-15):resumeSlot **前移至装配之前**——
318
+ // 装配期模式门判据 = 会话权威值(槽优先 + config 回退);恢复记录经 slotData 形参进
319
+ // 装配钩子(同进程同一调用前移 ⇒ 零新增写动作;钩子内不调 resumeSlot——非纯读)。
320
+ const { resumeSlot, applySession, sessionDescriptor } = await import("@thincoder/core/session.mjs")
321
+ // F-MI7:resumeSlot = async(探测束不阻塞事件循环)——此处必须 await
322
+ const { slot, data } = await resumeSlot(process.cwd())
323
+ const agent = await assembleAgent({ slotData: data })
324
+ // SESSION.md §6.8 D-S1:TUI 路径在 startTUI 前清空无效 provider——空 provider 不流入 runAgent
297
325
  // (崩溃源:chat() 缺 model → 网关 400 或 fetch("undefined/...") TypeError)
298
326
  if (agent._providerInvalid) agent.provider = null
299
327
  const config = loadConfig()
300
- // 恢复上次的会话(同一项目目录);provider 按保存的名字切回(用户上次可能换过模型)
301
- // 2026-09-05 §10(R4):恢复决策按本端记录 resumeSlot(D-2 ①②③)——manifest active
302
- // 只作"无记录端"的一次性继承源,不再作本端恢复第一依据(D-6)。
303
- const { resumeSlot, applySession, sessionDescriptor } = await import("../src/session.mjs")
304
- const { slot, data } = resumeSlot(process.cwd())
305
328
  if (data) {
306
329
  // applySession 内部已按槽复合重算 compactThreshold(auto 时)——不再需要 switched 分支
307
- // TUI-OOM-ROOTCAUSE(SESSION.md §14.3.4):传 slot → 绑定记录存储(身份核验 + 对账)
330
+ // TUI-OOM-ROOTCAUSE(SESSION.md §6.14):传 slot → 绑定记录存储(身份核验 + 对账)
308
331
  // ——人读线 = 尾窗(磁盘为准);未传 = 模式 F(全量数组)
309
332
  applySession(agent, data, { slot })
310
- // SESSION.md §11.2(2026-09-08——N6 评审 🔴 修复):process restarted 句 = 进程级
333
+ // SESSION.md §6.11(2026-09-08——N6 评审 🔴 修复):process restarted 句 = 进程级
311
334
  // 信号——仅本启动 resume 路径设一次(真进程重启恢复盘上会话);/session 切换与 ACP
312
335
  // 加载走同一 applySession 收敛但不设(不误报进程重启)。prepareRun 发句即清——进程
313
336
  // 内一次。resumed:yes 独立由 applySession 的恢复事件武装(data.history 非空)。
@@ -317,6 +340,10 @@ switch (command) {
317
340
  // "load → 首回合保存"窗口内并发方翻 active 导致的静默迁移);/new 经 resetSessionState
318
341
  // 清 _slot(newSession 已认领 + 写记录,语义保留)
319
342
  agent._slot = slot
343
+ // M1 重估点(会话起点②——applySession 之后 · KD-M1-13 取值点②):CLI 的工程模式权威值 =
344
+ // 会话槽(/eng 只写槽不写 config.json);此时 agent.config.agent.engineering 已由
345
+ // applySession 按槽订正 ⇒ 与装配期判据同值 ⇒ 幂等重估(值同 ⇒ 无副作用)。
346
+ attachManifest(agent)
320
347
  // D-S3 优先级补全:applySession 可能已用会话中的有效 provider 修复(config 无效 + 会话有效)——
321
348
  // 修复后复验清除标记,仅当两者都无效才弹重选(validateProvider 幂等)
322
349
  if (agent._providerInvalid) validateProvider(agent)
@@ -382,15 +409,22 @@ switch (command) {
382
409
  }
383
410
 
384
411
  case "acp": {
385
- const { runAcpServer } = await import("../src/acp.mjs")
412
+ const { runAcpServer, runAcpLogin } = await import("../src/acp.mjs")
413
+ // §11.2 改法 4:`args` 语义 = 追加到已配置的 agent 调用——`--login` = 终端认证流程,
414
+ // 不进 stdio 服务模式(不带该旗标时行为零变);退出码语义 = 0 成功 / 非 0 失败。
415
+ if (args.includes("--login")) {
416
+ exitSoon(await runAcpLogin())
417
+ break
418
+ }
386
419
  await runAcpServer()
387
420
  break
388
421
  }
389
422
 
390
423
  case "session": {
391
- // SESSION.md §12:会话目录 GC 手动面(F2 冷 cwd 报告/删除——VS Code 端无 shell 通道,仅 CLI)
392
- const { runSessionGc } = await import("../src/session-gc.mjs")
393
- process.exitCode = runSessionGc(args)
424
+ // SESSION.md §6.12 / §6.17:会话目录 GC 显式面(冷 cwd 90 天面 + 三合取存量组全量面)——
425
+ // 双端同面(2026-09-21 端差注销:VS Code 端命令 `thincoder.sessionGc` 走同一核数据面)。
426
+ const { runSessionGc } = await import("@thincoder/core/session-gc.mjs")
427
+ process.exitCode = await runSessionGc(args)
394
428
  break
395
429
  }
396
430
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thincoder",
3
- "version": "0.12.62",
4
- "description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
3
+ "version": "0.12.64",
4
+ "description": "Thin coding agent - zero third-party dependencies, no build step, Node.js native. Sharp code, zero bloat.",
5
5
  "keywords": [
6
6
  "ai",
7
7
  "agent",
@@ -19,6 +19,9 @@
19
19
  "engines": {
20
20
  "node": ">=24"
21
21
  },
22
+ "dependencies": {
23
+ "@thincoder/core": "^0.9.1"
24
+ },
22
25
  "files": [
23
26
  "bin/",
24
27
  "src/",
@@ -31,12 +34,10 @@
31
34
  "url": "git+https://github.com/xinbo-tech/thincoder.git"
32
35
  },
33
36
  "scripts": {
34
- "test": "node test/run-fast.mjs",
37
+ "test": "node test/run.mjs",
35
38
  "prepublishOnly": "npm run release:check",
36
39
  "lint": "node scripts/check-syntax.mjs",
37
- "release:check": "node scripts/release-check.mjs",
38
- "test:full": "node test/run-full.mjs",
39
- "test:integration": "node test/run-integration.mjs"
40
+ "release:check": "node scripts/release-check.mjs"
40
41
  },
41
42
  "author": "liwei <liwei@51marine.com> (上海新舶)",
42
43
  "license": "MIT"
@@ -19,10 +19,12 @@
19
19
  * End-of-turn is NOT a notification: `session/prompt` resolves with
20
20
  * `{ stopReason: "end_turn" }` (kimi session.ts parity).
21
21
  */
22
- import { detectDanger, normalizeEOL, joinWithEol } from "../tools/shared.mjs"
23
- import { computeEditEntry, validateEditEntry, assertEditArgsExclusive, hasLineParams } from "../tools/edit-diff.mjs"
22
+ import { detectDanger, normalizeEOL, joinWithEol } from "@thincoder/core/tools/shared.mjs"
23
+ import { computeEditEntry, validateEditEntry, assertEditArgsExclusive, hasLineParams } from "@thincoder/core/tools/edit-diff.mjs"
24
24
  // 第 27 批 §12.3①/③:relay 前缀文法单一权威(模块直连——不自持正则副本)。
25
- import { parseRelayPath } from "../agent/relay-prefix.mjs"
25
+ import { parseRelayPath } from "@thincoder/core/agent/relay-prefix.mjs"
26
+ // 批 1 CORE-DEFECT-FIXES B3:onWait 相位值域 + 文案单源(本面仅日志——PROVIDER.md §6.20)
27
+ import { waitStatusText } from "@thincoder/core/provider/wait-status.mjs"
26
28
 
27
29
  /** ACP ToolKind inference (schema v1 enum) — best-effort, clients render by kind. */
28
30
  function inferToolKind(name) {
@@ -52,11 +54,18 @@ function permissionToBoolean(response) {
52
54
 
53
55
  /**
54
56
  * Build the runAgent callbacks for an ACP session.
55
- * @param {{ sessionId: string, notify: (m, p) => void, request: (m, p, o?) => Promise<any>, log?: (s) => void }} deps
57
+ * @param {{ sessionId: string, notify: (m, p) => void, request: (m, p, o?) => Promise<any>, log?: (s) => void,
58
+ * clientCaps?: { fs?: { readTextFile?: boolean, writeTextFile?: boolean } } }} deps
59
+ * `clientCaps` = the §3.4 snapshot (taken at `session/new`); capabilities omitted by the
60
+ * client are UNSUPPORTED ⇒ the fs reverse-RPC face stays local (§11.4 — never 30s 干等).
56
61
  */
57
- export function buildAcpCallbacks({ sessionId, notify, request, log = () => {} }) {
62
+ export function buildAcpCallbacks({ sessionId, notify, request, log = () => {}, clientCaps = {} }) {
58
63
  const update = (sessionUpdate, extra = {}) =>
59
64
  notify("session/update", { sessionId, update: { sessionUpdate, ...extra } })
65
+ // §11.4 fs 能力位(默认 false——文档「MUST treat all capabilities omitted … as UNSUPPORTED」);
66
+ // 判据同构 = kimi server.ts:626-636。edit 桥需读回 + 写回 ⇒ 两位皆真才路由。
67
+ const canReadFile = clientCaps?.fs?.readTextFile === true
68
+ const canWriteFile = clientCaps?.fs?.writeTextFile === true
60
69
  let toolSeq = 0
61
70
  // D15.8(TOOLS.md §15.1):tool id FIFO 队列——并行同名工具按 call 序配对(dispatch B1
62
71
  // 已测:并行结果回调顺序 = call 顺序——T-TS8/T-TS9)。取代旧 Map 按名覆盖(后写覆盖先写
@@ -99,7 +108,10 @@ export function buildAcpCallbacks({ sessionId, notify, request, log = () => {} }
99
108
  // ——与本地通道逐字一致(NF15.6b / AC15.10:not found / occurrences / 空 old / 空 new)。
100
109
  const EDIT_ABORT_PREFIX = "edit aborted (atomic — no files written): "
101
110
 
111
+ // 不变量(§11.7-2):**任何** fs/* 反向 RPC 发出前必过客户端能力位——守卫住在本函数内
112
+ // (防御面:路由分支先判,守卫兜底,漏判不发静默超时)。
102
113
  const readBuffer = async (p) => {
114
+ if (!canReadFile) throw new Error("client does not advertise fs.readTextFile")
103
115
  try {
104
116
  const read = await request("fs/read_text_file", { sessionId, path: p }, { timeoutMs: 30000 })
105
117
  return read?.text ?? read?.content ?? ""
@@ -108,6 +120,7 @@ export function buildAcpCallbacks({ sessionId, notify, request, log = () => {} }
108
120
  }
109
121
  }
110
122
  const writeBuffer = async (p, content) => {
123
+ if (!canWriteFile) throw new Error("client does not advertise fs.writeTextFile")
111
124
  try {
112
125
  await request("fs/write_text_file", { sessionId, path: p, content }, { timeoutMs: 30000 })
113
126
  } catch (e) {
@@ -176,12 +189,14 @@ export function buildAcpCallbacks({ sessionId, notify, request, log = () => {} }
176
189
  // Strip the subagent `[model]` metadata token ([model]<name>) — it's a
177
190
  // TUI/webview display signal, not conversation content, and must not reach ACP clients.
178
191
  if (/^\[model\]/.test(payload)) return
179
- // D7 (AGENT-LOOP.md §7.2 + §19.5 round2 #6 + D-M7b): strip ⟦ev⟧ event tokens (turn/
180
- // approval/done/settled/stopped/async — async = §19.5 D-M7b zero-field spawn marker)
181
- // — they carry RS control characters and are a TUI display signal; structured ACP
182
- // mapping (tool_call_update) is tracked separately in docs/TODO.md.
192
+ // D7 + 批 1 B2/D13(ACP-CLIENT.md §7.2):剥离判据 = **形态**(`⟦ev⟧<小写名>` + RS
193
+ // 终止符同现),不再枚举事件名——枚举是漏项发生器(`queued` / `cancelled` 发射点在位
194
+ // 而白名单未跟 ⇒ 随 agent_message_chunk 泄漏给客户端)。终止符约束 load-bearing:
195
+ // 放宽为无终止符形态会吞真实正文(先例教训见 src/tui/render.mjs:250-252)。
196
+ // 事件 token 是 TUI 显示信号;结构化 ACP 映射(tool_call_update)另行跟踪
197
+ // (docs/TODO.md)。
183
198
  // 有意为之:控制字符协议/转义序列剥离正则(ANSI/⟦ev⟧/SGR/history 双线分隔)
184
- if (/^⟦ev⟧(?:turn|approval|done|settled|stopped|async)\x1e/.test(payload)) return
199
+ if (/^⟦ev⟧[a-z]+\x1e/.test(payload)) return
185
200
  if (!payload) return // D6:剥后空载荷不发通知(防噪声空 chunk)
186
201
  update("agent_message_chunk", { content: { type: "text", text: payload } })
187
202
  },
@@ -193,7 +208,10 @@ export function buildAcpCallbacks({ sessionId, notify, request, log = () => {} }
193
208
  update("agent_thought_chunk", { content: { type: "text", text: payload } })
194
209
  },
195
210
  onUsage: (usage) => update("usage_update", { usage }),
196
- onWait: ({ phase, seconds }) => log(`[rate-limit] ${phase} waiting ~${seconds}s`),
211
+ onWait: (ev) => {
212
+ const s = waitStatusText(ev)
213
+ if (s) log(`[rate-limit] ${s}`)
214
+ },
197
215
  onCompress: () => log("[context] auto-compacted"),
198
216
 
199
217
  onToolCall: (name, args, toolId) => {
@@ -265,17 +283,19 @@ export function buildAcpCallbacks({ sessionId, notify, request, log = () => {} }
265
283
  },
266
284
 
267
285
  /**
268
- * fs reverse-RPC router (dispatch.mjs toolRouter, M2):
269
- * - write → fs/write_text_file (full content, no read-back)
286
+ * fs reverse-RPC router (dispatch.mjs toolRouter, M2) — 能力位 + 工具形态双判(§11.4):
287
+ * - write → fs/write_text_file (full content, no read-back) — 需 clientCaps.fs.writeTextFile
270
288
  * - edit → fs/read_text_file → computeEditEntry(本地权威——单/数组形态)→ fs/write_text_file
271
289
  * (§15.1 D15.7 委派——双通道同语义;数组=原子批量——逐条目串行累积)
290
+ * — 需 readTextFile ∧ writeTextFile(读回是 edit 桥的必要前提)
272
291
  * - apply_patch → local (unified-diff application is not routed in M2)
273
292
  * - delete, reads → local
293
+ * 未宣告 ⇒ `{ handled: false }` 回落本地(零反向 RPC、零超时等待)。
274
294
  */
275
295
  toolRouter: async (name, args) => {
276
296
  const base = name.includes("/") ? name.split("/").pop() : name
277
297
  const path = pathOf(args)
278
- if (base === "write" && path) {
298
+ if (base === "write" && path && canWriteFile) {
279
299
  if (typeof args?.content !== "string") {
280
300
  return { handled: true, result: `Error: write content must be a string (got ${typeof args?.content})` }
281
301
  }
@@ -290,7 +310,7 @@ export function buildAcpCallbacks({ sessionId, notify, request, log = () => {} }
290
310
  // 2026-09-08 D1:单形态按行号改(path + line/startLine/endLine + new_string——无
291
311
  // old_string)同样走 IDE 缓冲通道(hasLineParams 判定)——否则回落本地写盘会与
292
312
  // IDE 缓冲脱敏;editSingle → computeEditEntry 行号语义自动继承。
293
- if (base === "edit" && (Array.isArray(args?.edits) || (path && typeof args?.new_string === "string" && (typeof args?.old_string === "string" || hasLineParams(args))))) {
313
+ if (base === "edit" && canReadFile && canWriteFile && (Array.isArray(args?.edits) || (path && typeof args?.new_string === "string" && (typeof args?.old_string === "string" || hasLineParams(args))))) {
294
314
  try {
295
315
  const text = Array.isArray(args?.edits) ? await editBatch(args) : await editSingle(path, args)
296
316
  return { handled: true, result: text }