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
@@ -1,265 +0,0 @@
1
- /**
2
- * settings tool — agent-side configuration adjustment (SETTINGS-TOOL.md, 2026-09-05).
3
- * 用户裁定:全量 config.json 任意键(点分路径)+ 单工具多动作 list/get/set + 双端同批。
4
- * 语义:set = 写盘(config.json——磁盘真相最小化:只写被设的键——默认值不固化)+
5
- * 热应用(ctx.agent.config 内存对象立即更新——回合边界键下回合生效)。
6
- * 护栏:敏感键(路径段含 apiKey/key/token/secret/password)回显/错误文本永不出现明文
7
- * (••••(masked)——防密钥泄漏进会话历史/trace);已知键类型校验——非 null 叶子类型表
8
- * 自动派生自 config.mjs DEFAULTS(不手写防漂移——F-S1.5/N-S1.5);null 默认值键与同族键
9
- * 走显式形状表(_NULL_LEAF_SHAPES 相等面 / _SIBLING_SHAPES 存在性面——按各自真实消费形态
10
- * 校验,完备性机械锁 + 一次性警告;SETTINGS-TOOL.md §8);set 侧效走审批门(dispatch 动作级分类)。
11
- */
12
- import { DEFAULTS, configPath, writeConfigAtomic } from "../config.mjs"
13
-
14
- /** 敏感键段判定(完整点分路径的段级匹配——apiKey/api_key/api-key/token/secret/password 形态) */
15
- const SENSITIVE_SEGMENT = /(^|[._-])(api[_-]?key|key|token|secret|password)($|[._-])/i
16
- const MASKED = "••••(masked)"
17
-
18
- function isSensitiveKey(path) {
19
- return SENSITIVE_SEGMENT.test(path)
20
- }
21
-
22
- /* ─── 形状层(第 8 批 2026-09-11——SETTINGS-TOOL.md §8.3;两表结构:相等面 / 存在性面) ─── */
23
-
24
- /**
25
- * null 叶子形状表——唯一「逐键手写形状」面(完备性锁的**相等面**:键集 == _nullLeafPaths(DEFAULTS))。
26
- * 表值 = 形状规格:接受集 = 该键真实消费形态 ∪ `null`(显式清除——消费面均有「未设置」态);
27
- * 不可消费形态(应用侧读不出写入值)→ 拒绝(违例抛错,磁盘/内存零变化)。
28
- */
29
- const _NULL_LEAF_SHAPES = {
30
- defaultModel: { kind: "nonEmptyString", providerModel: true, expects: 'non-empty string ("provider:model" composite)' },
31
- "agent.subagentModel": { kind: "nonEmptyString", expects: "non-empty string" },
32
- shell: { kind: "nonEmptyString", expects: "non-empty string" },
33
- "memory.team": { kind: "team", expects: "object { repo: string, name?, dir? }" },
34
- }
35
-
36
- /** 同族/跨端形状表(**存在性断言面**——不参与完备性锁的集合相等)。W3 同族:`agent.subagentModels`
37
- * 默认 `{}` ⇒ 派生表零条目 ⇒ 零约束(字符串被静默忽略——回落 subagentModel)。 */
38
- const _SIBLING_SHAPES = {
39
- "agent.subagentModels": { kind: "roleMap", expects: "object of role→non-empty string" },
40
- }
41
-
42
- /** 派生:递归遍历默认值对象——叶子(非对象/数组值)记 `路径 → typeof 默认值`(旧语义逐字保留:
43
- * null 叶子记 "object",其约束由形状表接管);数组不递归(下标元素无类型约束——全量域)。 */
44
- function _deriveTypes(obj, prefix = "", out = {}) {
45
- for (const [k, v] of Object.entries(obj)) {
46
- const p = prefix ? `${prefix}.${k}` : k
47
- if (v !== null && typeof v === "object" && !Array.isArray(v)) _deriveTypes(v, p, out)
48
- else out[p] = Array.isArray(v) ? "array" : typeof v
49
- }
50
- return out
51
- }
52
-
53
- /** 类型表 = 派生 + 可选 `patch` 覆盖(测试缝——非 null 叶子逐键与派生结果相等;T-S2.15)。 */
54
- function _buildShapeTable(obj, patch = null) {
55
- const out = _deriveTypes(obj)
56
- return patch ? Object.assign(out, patch) : out
57
- }
58
- const TYPE_MAP = _buildShapeTable(DEFAULTS)
59
-
60
- /** null 叶子路径枚举(数组不递归——与派生表同域)。 */
61
- function _nullLeafPaths(obj, prefix = "", out = []) {
62
- for (const [k, v] of Object.entries(obj)) {
63
- const p = prefix ? `${prefix}.${k}` : k
64
- if (v === null) out.push(p)
65
- else if (typeof v === "object" && !Array.isArray(v)) _nullLeafPaths(v, p, out)
66
- }
67
- return out
68
- }
69
-
70
- /** 完备性锁(N-S1.5):未声明的 null 叶子 = 形状表漂移——返回键名 + 一次性警告列出键名。 */
71
- let _shapeWarned = false
72
- function _checkShapeCompleteness(defaults, warn = console.warn) {
73
- const declared = new Set(Object.keys(_NULL_LEAF_SHAPES))
74
- const missing = _nullLeafPaths(defaults).filter((p) => !declared.has(p))
75
- if (missing.length && !_shapeWarned) {
76
- _shapeWarned = true
77
- warn(`[settings] null-default keys missing from _NULL_LEAF_SHAPES: ${missing.join(", ")} — declare their real consumption shape (SETTINGS-TOOL.md §8.3; N-S1.5 completeness lock)`)
78
- }
79
- return missing
80
- }
81
- _checkShapeCompleteness(DEFAULTS) // 装载自检——形状表必须覆盖全部 null 叶子(漂移即警告)
82
-
83
- /** 值位渲染(错误文本——敏感键命中时遮罩:N-S1.2 / D-S2.4——明文零进错误句)。 */
84
- function _shownValue(path, value) {
85
- return isSensitiveKey(path) ? MASKED : JSON.stringify(value)
86
- }
87
-
88
- function _kindOf(value) {
89
- return value === null ? "null" : Array.isArray(value) ? "array" : typeof value
90
- }
91
-
92
- function _expectsError(path, expects, value) {
93
- return new Error(`settings set: "${path}" expects ${expects} — got ${_kindOf(value)} (${_shownValue(path, value)})`)
94
- }
95
-
96
- /** 形状校验(形状表命中键——`null` 在 _checkKnownKeyValue 已放行)。 */
97
- function _checkShape(path, spec, value) {
98
- if (spec.kind === "team") {
99
- if (!value || typeof value !== "object" || Array.isArray(value)) throw _expectsError(path, spec.expects, value)
100
- if (typeof value.repo !== "string" || !value.repo.trim()) {
101
- throw new Error(`settings set: "${path}" requires a non-empty "repo" (team layer stays off without it) — got ${_shownValue(path, value)}`)
102
- }
103
- for (const k of ["name", "dir"]) {
104
- if (value[k] !== undefined && (typeof value[k] !== "string" || !value[k].trim())) throw _expectsError(path, spec.expects, value)
105
- }
106
- return
107
- }
108
- if (spec.kind === "roleMap") {
109
- if (!value || typeof value !== "object" || Array.isArray(value)) throw _expectsError(path, spec.expects, value)
110
- for (const v of Object.values(value)) {
111
- if (typeof v !== "string" || !v.trim()) throw _expectsError(path, spec.expects, value)
112
- }
113
- return
114
- }
115
- // nonEmptyString —— defaultModel 另加 provider:model 形态面(与 model-ref.mjs:25-36 同判据;
116
- // 存在性面不查——D-S2.5)
117
- if (typeof value !== "string" || !value.trim()) throw _expectsError(path, spec.expects, value)
118
- if (spec.providerModel) {
119
- const sep = value.indexOf(":")
120
- if (sep <= 0 || !value.slice(sep + 1)) throw _expectsError(path, spec.expects, value)
121
- }
122
- }
123
-
124
- /**
125
- * 已知键值校验(set 写盘前;违例抛错——磁盘/内存零变化):
126
- * ① 形状表命中键(`_NULL_LEAF_SHAPES` / `_SIBLING_SHAPES` **两表都查**——表值 = 形状规格)→ 形状校验
127
- * (`null` = 显式清除,放行);② 非 null 叶子(派生表值 = `typeof` 串)→ 既有语义逐字保留
128
- * (`want === "array"` 跳过 / `null` + `want === "object"` 放行);③ 未知键 → 原样通过(全量域)。
129
- */
130
- function _checkKnownKeyValue(path, value) {
131
- const spec = _NULL_LEAF_SHAPES[path] ?? _SIBLING_SHAPES[path]
132
- if (spec) {
133
- if (value === null) return // 显式清除 = 有效动作(消费面均有「未设置」态——D-S2.3)
134
- return _checkShape(path, spec, value)
135
- }
136
- const want = TYPE_MAP[path]
137
- if (want && want !== "array") {
138
- const got = value === null ? "null" : typeof value
139
- if (got !== want && !(value === null && want === "object")) throw _expectsError(path, want, value)
140
- }
141
- }
142
-
143
- /* ─── 既有工具面 helpers(第 8 批零改动) ─── */
144
-
145
- /** 点分路径解析(含数组数字段)——逐段下钻;返回 { ok, value } 或缺失段信息 */
146
- function resolvePath(obj, path) {
147
- const segs = String(path).split(".")
148
- let cur = obj
149
- for (let i = 0; i < segs.length; i++) {
150
- if (cur === null || typeof cur !== "object" || !(segs[i] in cur)) {
151
- return { ok: false, reached: segs.slice(0, i).join("."), missing: segs[i], depth: i }
152
- }
153
- cur = cur[segs[i]]
154
- }
155
- return { ok: true, value: cur }
156
- }
157
-
158
- /** 点分路径写入(自动建中间对象——cmd-config 数值项同款);返回旧值 */
159
- function setKeyPath(obj, path, value) {
160
- const segs = String(path).split(".")
161
- let cur = obj
162
- for (let i = 0; i < segs.length - 1; i++) {
163
- if (cur[segs[i]] === null || typeof cur[segs[i]] !== "object") cur[segs[i]] = {}
164
- cur = cur[segs[i]]
165
- }
166
- const last = segs[segs.length - 1]
167
- const old = cur[last]
168
- cur[last] = value
169
- return old
170
- }
171
-
172
- /** 递归展平 config 对象(数组下标段形态)→ 排序的 { path, value } 列表 */
173
- function flatten(obj, prefix = "", out = []) {
174
- for (const [k, v] of Object.entries(obj)) {
175
- const p = prefix ? `${prefix}.${k}` : k
176
- if (v !== null && typeof v === "object") flatten(v, p, out)
177
- else out.push({ path: p, value: v })
178
- }
179
- return out.sort((a, b) => (a.path < b.path ? -1 : 1))
180
- }
181
-
182
- /** 行格式化:`key = value (type)`——敏感键值遮罩 */
183
- function formatLine({ path, value }) {
184
- const shown = isSensitiveKey(path) ? MASKED : value
185
- return `${path} = ${shown} (${Array.isArray(value) ? "array" : typeof value})`
186
- }
187
-
188
- function parseValue(raw) {
189
- const s = String(raw)
190
- try {
191
- const v = JSON.parse(s)
192
- // JSON 顶层标量都接受;解析失败(裸字符串)→ 字符串字面
193
- if (typeof v === "number" || typeof v === "boolean" || v === null || Array.isArray(v) || typeof v === "object") return v
194
- // "abc"(带引号传的字符串)——JSON.parse 成功但返回字符串:返回**解析值**(去引号)
195
- // ——两端统一 ①(2026-09-11 用户裁定;SETTINGS-TOOL.md §9.3)。
196
- return v
197
- } catch {
198
- return s // 裸 abc → 字符串字面
199
- }
200
- }
201
-
202
- export function settingsTool(opts = {}) {
203
- const cfgPath = opts.configPath ?? configPath // 测试注入 tmp 文件;默认全局 configPath
204
-
205
- return {
206
- name: "settings",
207
- description:
208
- "Adjust ThinCoder runtime configuration — persisted to config.json AND hot-applied to the live agent config.\n" +
209
- "Actions: list (all keys + values, flattened) | get <key> | set <key> <value> — dot paths into config.json (agent.maxTurns, traces.enabled, providers.0.model, any nesting).\n" +
210
- "set persists to disk (only the set key is written — defaults are never baked in) and takes effect in the running session immediately (turn-boundary keys apply next turn); the value survives restarts.\n" +
211
- "Known keys are type-checked: scalar keys against the built-in defaults (agent.maxTurns a number, traces.enabled a boolean); keys whose default is null against their real consumption shape — defaultModel \"provider:model\", agent.subagentModel / shell non-empty string, memory.team object with a repo — so a value the app would silently drop is refused (null clears the key). Unknown keys under a known section are stored as given. Values parse as JSON first (true/false/numbers/objects/arrays), else stay strings.\n" +
212
- "SENSITIVE keys (path segment contains apiKey/key/token/secret/password) are NEVER echoed in plaintext — list/get/set replies show ••••(masked); setting a sensitive key is allowed and stored, but never echoed back.\n" +
213
- "list/get are read-only (planMode ok, no approval); set is a side effect (approval gate). The /config TUI command is the human equivalent.",
214
- parameters: {
215
- type: "object",
216
- properties: {
217
- action: { type: "string", enum: ["list", "get", "set"], description: "list — full key/value inventory; get <key> — one value; set <key> <value> — persist + hot-apply" },
218
- key: { type: "string", description: "Dot path into config.json, e.g. agent.maxTurns / traces.enabled / providers.0.model" },
219
- value: { type: "string", description: "New value for set — parsed as JSON first (true/5/{...}/[...]), otherwise kept as string" },
220
- },
221
- required: ["action"],
222
- },
223
- readonly: false, // set 是侧效——dispatch 动作级分类放行 list/get
224
- async execute(args, ctx) {
225
- const action = args?.action
226
- if (!["list", "get", "set"].includes(action)) throw new Error(`settings: action must be one of list/get/set — got ${JSON.stringify(action)}`)
227
- const config = ctx?.agent?.config ?? null
228
- if (!config) throw new Error("settings: no live agent config (ctx.agent.config missing)")
229
- if (action === "list") {
230
- const rows = flatten(config)
231
- if (rows.length === 0) return "(空配置——无已设置键)"
232
- return rows.map(formatLine).join("\n")
233
- }
234
- if (action === "get") {
235
- if (!args.key) throw new Error("settings get: key is required (dot path, e.g. agent.maxTurns)")
236
- const r = resolvePath(config, args.key)
237
- if (!r.ok) {
238
- const hint = r.depth === 0 ? "(config 顶层无此键——顶层可用键见 settings list)" : `(父键 ${r.reached || "顶层"} 存在——无 ${r.missing} 子键)`
239
- throw new Error(`settings get: no such key "${args.key}" ${hint}`)
240
- }
241
- return formatLine({ path: args.key, value: r.value })
242
- }
243
- // set
244
- if (!args.key || args.value === undefined) throw new Error("settings set: key and value are required")
245
- const value = parseValue(args.value)
246
- // 已知键校验(形状表两表都查 + 派生类型表——SETTINGS-TOOL.md §8.3;未知键 JSON 原样)
247
- _checkKnownKeyValue(String(args.key), value)
248
- // 写盘(D-F5b:磁盘真相最小化——writeConfigAtomic 磁盘新鲜读 + 只改被设键 + mtime
249
- // 门控——默认不固化;冲突/畸形抛错,内存不热应用(零虚假成功))+ 热应用(内存对象)
250
- const r = await writeConfigAtomic(cfgPath, (disk) => {
251
- setKeyPath(disk, args.key, value)
252
- })
253
- if (!r.ok) throw new Error("config changed on disk concurrently — retry (settings set not applied)")
254
- setKeyPath(config, args.key, value)
255
- const shown = isSensitiveKey(String(args.key)) ? MASKED : value
256
- return `settings set: ${args.key} = ${shown} (${Array.isArray(value) ? "array" : typeof value})${isSensitiveKey(String(args.key)) ? " — stored(值不回显)" : " — persisted + hot-applied(运行中已生效)"}`
257
- },
258
- }
259
- }
260
-
261
- /**
262
- * 测试缝导出(`_` 前缀——config.mjs `_setConfigPathForTest` 先例;SETTINGS-TOOL.md §8.3 第 6 条):
263
- * `_checkShapeCompleteness` = 完备性锁的运行时面(T-S2.14「一次性警告列出键名」的机械断言缝)。
264
- */
265
- export { _buildShapeTable, _nullLeafPaths, _NULL_LEAF_SHAPES, _SIBLING_SHAPES, _checkKnownKeyValue, _checkShapeCompleteness }
@@ -1,47 +0,0 @@
1
- import { loadSkills, readSkill } from "../skills.mjs"
2
- import { escapeXml } from "../agent.mjs"
3
-
4
- /**
5
- * skill tool: load project skill files on demand (.thincoder/skills/*.md).
6
- * After loading, skill content is injected into the conversation wrapped in <skill-loaded> for subsequent reference.
7
- * Use action="list" to see all available skills.
8
- */
9
- export const skillTool = {
10
- name: "skill",
11
- description:
12
- "Load a project skill from .thincoder/skills/. Skills contain reusable instructions, workflows, or reference material. Use this when the user references a skill by name, or when a task matches a known skill's description. Call with action='list' to see available skills; call with action='load' and name=<skill> to activate one. " +
13
- "Returns the skill list ('list'), the load confirmation ('load' — instructions arrive in the next message), or Error: ... with the available skills.",
14
- parameters: {
15
- type: "object",
16
- properties: {
17
- action: { type: "string", enum: ["list", "load"], description: "'list' to see available skills, 'load' to activate one" },
18
- name: { type: "string", description: "Skill name (for 'load' action)" },
19
- },
20
- required: ["action"],
21
- },
22
- readonly: true,
23
- async execute(args, ctx) {
24
- const skills = await loadSkills(ctx.agent.cwd)
25
- if (args.action === "list") {
26
- if (skills.length === 0) return "No project skills found in .thincoder/skills/."
27
- return skills.map((s) => `- ${s.name}: ${s.description}`).join("\n")
28
- }
29
- if (!args.name) return "Error: skill name required for 'load' action."
30
- // Dedup: skip reloading if history already contains an <skill-loaded> block with the same name
31
- // (history is the ledger; if it got compacted away we naturally won't find it here — correct behavior)
32
- if (ctx.agent.history?.some((m) => typeof m.content === "string" && m.content.includes(`<skill-loaded name="${args.name}"`))) {
33
- return `Skill "${args.name}" is already loaded in this conversation — follow the instructions in the existing <skill-loaded> block above. Do not reload it.`
34
- }
35
- const content = await readSkill(ctx.agent.cwd, args.name)
36
- if (!content) {
37
- const available = skills.map((s) => s.name).join(", ")
38
- return `Error: skill "${args.name}" not found. Available: ${available || "(none)"}`
39
- }
40
- // Inject skill content into history (will appear as the next user message)
41
- ctx.agent._pendingReminders = ctx.agent._pendingReminders ?? []
42
- ctx.agent._pendingReminders.push(
43
- `<skill-loaded name="${args.name}" source=".thincoder/skills/${args.name}.md">\n${escapeXml(content)}\n</skill-loaded>\n\nFollow the skill's instructions above for the current task.`
44
- )
45
- return `Skill "${args.name}" loaded. Instructions will appear in the next message.`
46
- },
47
- }