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.
- package/CHANGELOG.md +27 -0
- package/README.md +46 -49
- package/bin/thincoder.mjs +68 -34
- package/package.json +7 -6
- package/src/acp/bridge.mjs +35 -15
- package/src/acp/client-caps.mjs +86 -0
- package/src/acp/ext.mjs +86 -0
- package/src/acp/handlers-session.mjs +257 -0
- package/src/acp/handlers-slots.mjs +196 -0
- package/src/acp/login.mjs +48 -0
- package/src/acp/session.mjs +6 -4
- package/src/acp.mjs +67 -379
- package/src/cli/distill-command.mjs +3 -3
- package/src/cli/make-agent.mjs +60 -17
- package/src/cli/memory-command.mjs +3 -3
- package/src/cli/permission.mjs +4 -48
- package/src/cli/setup-wizard.mjs +1 -1
- package/src/completions.mjs +3 -1
- package/src/crash-reports.mjs +1 -1
- package/src/distill.mjs +4 -4
- package/src/heap-watch.mjs +1 -1
- package/src/prompt-injections.mjs +20 -0
- package/src/tui/agent-turn.mjs +40 -9
- package/src/tui/cmd-advisor.mjs +5 -5
- package/src/tui/cmd-config.mjs +8 -8
- package/src/tui/cmd-eng.mjs +35 -9
- package/src/tui/cmd-mcp.mjs +9 -8
- package/src/tui/cmd-model.mjs +1 -1
- package/src/tui/cmd-new.mjs +6 -5
- package/src/tui/cmd-plan.mjs +9 -0
- package/src/tui/cmd-reindex.mjs +1 -1
- package/src/tui/cmd-restore.mjs +2 -2
- package/src/tui/cmd-session.mjs +24 -1
- package/src/tui/cmd-skills.mjs +1 -1
- package/src/tui/cmd-think.mjs +22 -9
- package/src/tui/config-helpers.mjs +1 -1
- package/src/tui/display-budget.mjs +33 -11
- package/src/tui/index.mjs +18 -10
- package/src/tui/interaction.mjs +16 -7
- package/src/tui/key-modes.mjs +9 -4
- package/src/tui/ledger-surface.mjs +22 -59
- package/src/tui/model-catalog.mjs +4 -4
- package/src/tui/model-picker.mjs +8 -7
- package/src/tui/mouse.mjs +11 -6
- package/src/tui/pickers.mjs +15 -2
- package/src/tui/render-conversation.mjs +1 -1
- package/src/tui/render-frame.mjs +15 -6
- package/src/tui/render-loop.mjs +1 -1
- package/src/tui/render-segments.mjs +3 -1
- package/src/tui/slash-commands.mjs +1 -1
- package/src/tui/startup.mjs +14 -14
- package/src/tui/subagent-blocks.mjs +20 -3
- package/src/tui/subagent-freeze.mjs +73 -2
- package/src/tui/suspension-drive.mjs +57 -23
- package/src/tui/tool-events.mjs +11 -8
- package/src/tui/tui-lifecycle.mjs +9 -0
- package/src/tui/wizard.mjs +3 -3
- package/src/tui/wrapped-spawn.mjs +6 -2
- package/src/abort-provenance.mjs +0 -116
- package/src/advisor/citations.mjs +0 -139
- package/src/advisor/compaction.mjs +0 -174
- package/src/advisor/convergence.mjs +0 -80
- package/src/advisor/history.mjs +0 -77
- package/src/advisor/loop.mjs +0 -293
- package/src/advisor/messages.mjs +0 -299
- package/src/advisor/project-context.mjs +0 -194
- package/src/advisor/repos.mjs +0 -150
- package/src/advisor/run.mjs +0 -293
- package/src/advisor/truncate.mjs +0 -57
- package/src/advisor.mjs +0 -290
- package/src/agent/completion.mjs +0 -146
- package/src/agent/dispatch.mjs +0 -489
- package/src/agent/helpers.mjs +0 -384
- package/src/agent/post-turn.mjs +0 -70
- package/src/agent/record-results.mjs +0 -174
- package/src/agent/relay-prefix.mjs +0 -39
- package/src/agent/run-stages.mjs +0 -244
- package/src/agent/setup-reminders.mjs +0 -69
- package/src/agent/setup.mjs +0 -354
- package/src/agent/spawn-child.mjs +0 -243
- package/src/agent-tools/advisor-async.mjs +0 -346
- package/src/agent-tools/advisor-settle.mjs +0 -231
- package/src/agent-tools/advisor.mjs +0 -260
- package/src/agent-tools/async-settle.mjs +0 -204
- package/src/agent-tools/batch-segment.mjs +0 -195
- package/src/agent-tools/consult.mjs +0 -473
- package/src/agent-tools/design-token.mjs +0 -117
- package/src/agent-tools/digest-budget.mjs +0 -76
- package/src/agent-tools/eng.mjs +0 -67
- package/src/agent-tools/escalate-async.mjs +0 -295
- package/src/agent-tools/goal.mjs +0 -119
- package/src/agent-tools/plan.mjs +0 -81
- package/src/agent-tools/read-history.mjs +0 -309
- package/src/agent-tools/recent-changes.mjs +0 -24
- package/src/agent-tools/review-streak.mjs +0 -93
- package/src/agent-tools/settings.mjs +0 -265
- package/src/agent-tools/skill.mjs +0 -47
- package/src/agent-tools/subagent-actions.mjs +0 -482
- package/src/agent-tools/subagent-async.mjs +0 -434
- package/src/agent-tools/subagent-panel.mjs +0 -160
- package/src/agent-tools/subagent-run.mjs +0 -205
- package/src/agent-tools/subagent-scheduler.mjs +0 -392
- package/src/agent-tools/subagent-spawn.mjs +0 -459
- package/src/agent-tools/subagent.mjs +0 -404
- package/src/agent-tools/task.mjs +0 -87
- package/src/agent-tools/timer.mjs +0 -46
- package/src/agent-tools/verify.mjs +0 -271
- package/src/agent-tools.mjs +0 -17
- package/src/agent.mjs +0 -417
- package/src/auto-think.mjs +0 -115
- package/src/config-migrate.mjs +0 -70
- package/src/config.mjs +0 -496
- package/src/context.mjs +0 -392
- package/src/conventions.mjs +0 -223
- package/src/embedding.mjs +0 -120
- package/src/escape.mjs +0 -152
- package/src/expand-home.mjs +0 -16
- package/src/explore-distill.mjs +0 -155
- package/src/generate-title.mjs +0 -88
- package/src/git/checkpoint.mjs +0 -448
- package/src/git/gitmem.mjs +0 -100
- package/src/hooks.mjs +0 -97
- package/src/ledger.mjs +0 -227
- package/src/log.mjs +0 -195
- package/src/markdown.mjs +0 -106
- package/src/mcp/helpers.mjs +0 -51
- package/src/mcp/transport-http.mjs +0 -248
- package/src/mcp/transport-stdio.mjs +0 -140
- package/src/mcp/transport-ws.mjs +0 -122
- package/src/mcp.mjs +0 -295
- package/src/memory/code-index.mjs +0 -219
- package/src/memory/code-sync.mjs +0 -415
- package/src/memory/core.mjs +0 -299
- package/src/memory/delete.mjs +0 -236
- package/src/memory/docs.mjs +0 -419
- package/src/memory/file-walk.mjs +0 -109
- package/src/memory/scan.mjs +0 -95
- package/src/memory/schema.mjs +0 -452
- package/src/memory.mjs +0 -21
- package/src/model-ref.mjs +0 -66
- package/src/model-specs.mjs +0 -179
- package/src/peer-domains.mjs +0 -265
- package/src/peer-instances.mjs +0 -231
- package/src/prompt-overlays.mjs +0 -82
- package/src/prompts/advisor-design.md +0 -41
- package/src/prompts/advisor-round1.md +0 -41
- package/src/prompts/advisor-round2.md +0 -46
- package/src/prompts/advisor-round3.md +0 -42
- package/src/prompts/common.md +0 -115
- package/src/prompts/consult-base.md +0 -19
- package/src/prompts/discipline-engineering.md +0 -258
- package/src/prompts/discipline-normal.md +0 -185
- package/src/prompts/persona-coder.md +0 -21
- package/src/prompts/persona-eng-coder.md +0 -37
- package/src/prompts/persona-eng-designer.md +0 -60
- package/src/prompts/persona-engineering.md +0 -55
- package/src/prompts/persona-explore.md +0 -15
- package/src/prompts/persona-normal.md +0 -27
- package/src/prompts/persona-plan.md +0 -26
- package/src/provider/anthropic.mjs +0 -225
- package/src/provider/core.mjs +0 -476
- package/src/provider/errors.mjs +0 -101
- package/src/provider/google.mjs +0 -257
- package/src/provider/index.mjs +0 -7
- package/src/provider/list-models.mjs +0 -93
- package/src/provider/normalize.mjs +0 -81
- package/src/provider/rate.mjs +0 -108
- package/src/provider/responses.mjs +0 -495
- package/src/provider/retry.mjs +0 -88
- package/src/provider/sse.mjs +0 -264
- package/src/proxy.mjs +0 -261
- package/src/rules.mjs +0 -53
- package/src/session-gc.mjs +0 -221
- package/src/session-guard.mjs +0 -59
- package/src/session-migrate.mjs +0 -48
- package/src/session-rename.mjs +0 -38
- package/src/session-segments.mjs +0 -100
- package/src/session-slots.mjs +0 -492
- package/src/session-store.mjs +0 -441
- package/src/session.mjs +0 -492
- package/src/skills.mjs +0 -153
- package/src/text-budget.mjs +0 -46
- package/src/token-ttl.mjs +0 -274
- package/src/tools/apply_patch.md +0 -15
- package/src/tools/bash.md +0 -37
- package/src/tools/bash.mjs +0 -268
- package/src/tools/checklist-sync.mjs +0 -181
- package/src/tools/checklist.md +0 -13
- package/src/tools/checklist.mjs +0 -299
- package/src/tools/delete.md +0 -13
- package/src/tools/edit-batch.mjs +0 -191
- package/src/tools/edit-diff.mjs +0 -348
- package/src/tools/edit.md +0 -30
- package/src/tools/execute.md +0 -21
- package/src/tools/execute.mjs +0 -228
- package/src/tools/fetch.md +0 -12
- package/src/tools/file.mjs +0 -469
- package/src/tools/file_ops.md +0 -17
- package/src/tools/get_current_time.md +0 -8
- package/src/tools/git-checkpoint.mjs +0 -143
- package/src/tools/git-ext.mjs +0 -173
- package/src/tools/git.md +0 -54
- package/src/tools/git.mjs +0 -356
- package/src/tools/glob-dialect.mjs +0 -130
- package/src/tools/glob.md +0 -11
- package/src/tools/grep.md +0 -19
- package/src/tools/hashline_edit.md +0 -14
- package/src/tools/index.mjs +0 -36
- package/src/tools/insert_after.md +0 -15
- package/src/tools/lint.md +0 -10
- package/src/tools/linter.mjs +0 -128
- package/src/tools/ls.md +0 -12
- package/src/tools/lsp.md +0 -10
- package/src/tools/lsp.mjs +0 -316
- package/src/tools/ops.mjs +0 -299
- package/src/tools/patch.mjs +0 -282
- package/src/tools/process.md +0 -10
- package/src/tools/question.md +0 -16
- package/src/tools/question.mjs +0 -26
- package/src/tools/read.md +0 -20
- package/src/tools/read_image.md +0 -8
- package/src/tools/repomap.mjs +0 -314
- package/src/tools/search.mjs +0 -236
- package/src/tools/shared.mjs +0 -446
- package/src/tools/tree.md +0 -14
- package/src/tools/tree.mjs +0 -66
- package/src/tools/wait_for.md +0 -22
- package/src/tools/web.mjs +0 -224
- package/src/tools/websearch.md +0 -16
- package/src/tools/write.md +0 -11
- 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
|
|
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`
|
|
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**:
|
|
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: [
|
|
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 `/
|
|
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), `/
|
|
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
|
-
```
|
|
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
|
|
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
|
-
"
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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/
|
|
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` / `
|
|
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 "
|
|
19
|
-
import { loadConfig, configPath } from "
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
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/
|
|
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 /
|
|
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
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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("
|
|
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: (
|
|
183
|
-
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
-
|
|
296
|
-
//
|
|
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
|
|
330
|
+
// TUI-OOM-ROOTCAUSE(SESSION.md §6.14):传 slot → 绑定记录存储(身份核验 + 对账)
|
|
308
331
|
// ——人读线 = 尾窗(磁盘为准);未传 = 模式 F(全量数组)
|
|
309
332
|
applySession(agent, data, { slot })
|
|
310
|
-
// SESSION.md §11
|
|
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
|
|
392
|
-
|
|
393
|
-
|
|
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.
|
|
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
|
|
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"
|
package/src/acp/bridge.mjs
CHANGED
|
@@ -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 "
|
|
23
|
-
import { computeEditEntry, validateEditEntry, assertEditArgsExclusive, hasLineParams } from "
|
|
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 "
|
|
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
|
|
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
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
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⟧
|
|
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: (
|
|
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 }
|