@thincoder/core 0.9.1

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 (203) hide show
  1. package/abort-provenance.mjs +116 -0
  2. package/advisor/citations.mjs +139 -0
  3. package/advisor/compaction.mjs +174 -0
  4. package/advisor/convergence.mjs +80 -0
  5. package/advisor/history.mjs +77 -0
  6. package/advisor/loop.mjs +288 -0
  7. package/advisor/messages.mjs +299 -0
  8. package/advisor/notice.mjs +141 -0
  9. package/advisor/project-context.mjs +197 -0
  10. package/advisor/repos.mjs +150 -0
  11. package/advisor/run.mjs +190 -0
  12. package/advisor/truncate.mjs +57 -0
  13. package/advisor.mjs +281 -0
  14. package/agent/child-marks.mjs +24 -0
  15. package/agent/completion.mjs +145 -0
  16. package/agent/dispatch.mjs +493 -0
  17. package/agent/family-tools.mjs +174 -0
  18. package/agent/helpers.mjs +412 -0
  19. package/agent/post-turn.mjs +70 -0
  20. package/agent/record-results.mjs +174 -0
  21. package/agent/relay-prefix.mjs +39 -0
  22. package/agent/run-stages.mjs +244 -0
  23. package/agent/setup-reminders.mjs +199 -0
  24. package/agent/setup.mjs +234 -0
  25. package/agent/spawn-child.mjs +258 -0
  26. package/agent/suspension.mjs +240 -0
  27. package/agent/write-gate.mjs +87 -0
  28. package/agent-tools/advisor-async.mjs +481 -0
  29. package/agent-tools/advisor-settle.mjs +240 -0
  30. package/agent-tools/advisor.mjs +280 -0
  31. package/agent-tools/async-discard.mjs +143 -0
  32. package/agent-tools/async-settle.mjs +299 -0
  33. package/agent-tools/batch-segment.mjs +265 -0
  34. package/agent-tools/child-permission.mjs +45 -0
  35. package/agent-tools/consult.mjs +471 -0
  36. package/agent-tools/design-token.mjs +117 -0
  37. package/agent-tools/digest-budget.mjs +76 -0
  38. package/agent-tools/eng.mjs +102 -0
  39. package/agent-tools/escalate-async.mjs +302 -0
  40. package/agent-tools/goal.mjs +119 -0
  41. package/agent-tools/panel-blocks.mjs +24 -0
  42. package/agent-tools/parent-channel.mjs +231 -0
  43. package/agent-tools/plan.mjs +86 -0
  44. package/agent-tools/read-history.mjs +309 -0
  45. package/agent-tools/recent-changes.mjs +24 -0
  46. package/agent-tools/review-facts.mjs +31 -0
  47. package/agent-tools/settings.mjs +268 -0
  48. package/agent-tools/skill.mjs +63 -0
  49. package/agent-tools/spawn-gates.mjs +109 -0
  50. package/agent-tools/subagent-actions.mjs +496 -0
  51. package/agent-tools/subagent-async.mjs +456 -0
  52. package/agent-tools/subagent-panel.mjs +160 -0
  53. package/agent-tools/subagent-run.mjs +208 -0
  54. package/agent-tools/subagent-scheduler.mjs +446 -0
  55. package/agent-tools/subagent-spawn.mjs +478 -0
  56. package/agent-tools/subagent.mjs +419 -0
  57. package/agent-tools/task.mjs +87 -0
  58. package/agent-tools/timer.mjs +46 -0
  59. package/agent-tools/verify.mjs +295 -0
  60. package/agent-tools.mjs +23 -0
  61. package/agent.mjs +430 -0
  62. package/auto-think.mjs +115 -0
  63. package/compress-form.mjs +24 -0
  64. package/config-io.mjs +277 -0
  65. package/config-migrate.mjs +178 -0
  66. package/config-presets.mjs +49 -0
  67. package/config.mjs +419 -0
  68. package/context.mjs +495 -0
  69. package/conventions.mjs +223 -0
  70. package/embedding.mjs +120 -0
  71. package/escape.mjs +152 -0
  72. package/expand-home.mjs +16 -0
  73. package/explore-distill.mjs +152 -0
  74. package/generate-title.mjs +123 -0
  75. package/git/checkpoint.mjs +448 -0
  76. package/git/gitmem.mjs +100 -0
  77. package/history-window.mjs +179 -0
  78. package/hooks.mjs +108 -0
  79. package/i18n.mjs +106 -0
  80. package/index-bin.mjs +48 -0
  81. package/index-discover.mjs +176 -0
  82. package/ledger-cmd.mjs +209 -0
  83. package/ledger-db.mjs +86 -0
  84. package/ledger-surface.mjs +76 -0
  85. package/ledger.mjs +202 -0
  86. package/log.mjs +195 -0
  87. package/manifest.mjs +338 -0
  88. package/markdown.mjs +106 -0
  89. package/mcp/helpers.mjs +51 -0
  90. package/mcp/transport-http.mjs +248 -0
  91. package/mcp/transport-stdio.mjs +140 -0
  92. package/mcp/transport-ws.mjs +122 -0
  93. package/mcp.mjs +295 -0
  94. package/memory/code-index.mjs +219 -0
  95. package/memory/code-sync.mjs +427 -0
  96. package/memory/core.mjs +318 -0
  97. package/memory/delete.mjs +242 -0
  98. package/memory/docs.mjs +431 -0
  99. package/memory/file-walk.mjs +109 -0
  100. package/memory/origin.mjs +24 -0
  101. package/memory/scan.mjs +177 -0
  102. package/memory/schema.mjs +460 -0
  103. package/memory.mjs +21 -0
  104. package/model-ref.mjs +66 -0
  105. package/model-specs.mjs +277 -0
  106. package/package.json +32 -0
  107. package/peer-domains.mjs +265 -0
  108. package/peer-instances.mjs +178 -0
  109. package/permission.mjs +79 -0
  110. package/process-probe.mjs +315 -0
  111. package/prompt-files.mjs +113 -0
  112. package/prompt-overlays.mjs +78 -0
  113. package/prompts/advisor-design.md +43 -0
  114. package/prompts/advisor-round1.md +41 -0
  115. package/prompts/advisor-round2.md +46 -0
  116. package/prompts/advisor-round3.md +42 -0
  117. package/prompts/common.md +158 -0
  118. package/prompts/consult-base.md +19 -0
  119. package/prompts/discipline-engineering.md +123 -0
  120. package/prompts/discipline-normal.md +206 -0
  121. package/prompts/persona-coder.md +21 -0
  122. package/prompts/persona-eng-coder.md +41 -0
  123. package/prompts/persona-eng-designer.md +80 -0
  124. package/prompts/persona-engineering.md +160 -0
  125. package/prompts/persona-explore.md +15 -0
  126. package/prompts/persona-normal.md +35 -0
  127. package/prompts/persona-plan.md +27 -0
  128. package/provider/anthropic.mjs +225 -0
  129. package/provider/core.mjs +491 -0
  130. package/provider/errors.mjs +101 -0
  131. package/provider/google.mjs +257 -0
  132. package/provider/index.mjs +7 -0
  133. package/provider/list-models.mjs +163 -0
  134. package/provider/normalize.mjs +81 -0
  135. package/provider/rate.mjs +168 -0
  136. package/provider/responses.mjs +495 -0
  137. package/provider/retry.mjs +88 -0
  138. package/provider/sse.mjs +264 -0
  139. package/provider/wait-status.mjs +59 -0
  140. package/proxy.mjs +274 -0
  141. package/rules.mjs +53 -0
  142. package/session-gc.mjs +248 -0
  143. package/session-guard.mjs +59 -0
  144. package/session-lifecycle.mjs +305 -0
  145. package/session-migrate.mjs +48 -0
  146. package/session-rename.mjs +38 -0
  147. package/session-segments.mjs +99 -0
  148. package/session-slot-write.mjs +168 -0
  149. package/session-slots-manifest.mjs +264 -0
  150. package/session-slots.mjs +298 -0
  151. package/session-store.mjs +441 -0
  152. package/session.mjs +244 -0
  153. package/skills.mjs +234 -0
  154. package/text-budget.mjs +79 -0
  155. package/token-ttl.mjs +285 -0
  156. package/tool-docs/apply_patch.md +15 -0
  157. package/tool-docs/bash.md +38 -0
  158. package/tool-docs/delete.md +13 -0
  159. package/tool-docs/edit.md +30 -0
  160. package/tool-docs/execute.md +21 -0
  161. package/tool-docs/fetch.md +12 -0
  162. package/tool-docs/file_ops.md +17 -0
  163. package/tool-docs/get_current_time.md +8 -0
  164. package/tool-docs/git.md +54 -0
  165. package/tool-docs/glob.md +11 -0
  166. package/tool-docs/grep.md +19 -0
  167. package/tool-docs/hashline_edit.md +14 -0
  168. package/tool-docs/insert_after.md +15 -0
  169. package/tool-docs/lint.md +10 -0
  170. package/tool-docs/ls.md +12 -0
  171. package/tool-docs/lsp.md +10 -0
  172. package/tool-docs/process.md +10 -0
  173. package/tool-docs/question.md +16 -0
  174. package/tool-docs/read.md +20 -0
  175. package/tool-docs/read_image.md +8 -0
  176. package/tool-docs/tree.md +14 -0
  177. package/tool-docs/wait_for.md +22 -0
  178. package/tool-docs/websearch.md +16 -0
  179. package/tool-docs/write.md +11 -0
  180. package/tools/bash.mjs +276 -0
  181. package/tools/edit-batch.mjs +204 -0
  182. package/tools/edit-diff.mjs +388 -0
  183. package/tools/exec-run.mjs +43 -0
  184. package/tools/execute.mjs +243 -0
  185. package/tools/file.mjs +464 -0
  186. package/tools/git-checkpoint.mjs +143 -0
  187. package/tools/git-ext.mjs +173 -0
  188. package/tools/git.mjs +415 -0
  189. package/tools/glob-dialect.mjs +130 -0
  190. package/tools/index.mjs +76 -0
  191. package/tools/linter.mjs +120 -0
  192. package/tools/lsp.mjs +335 -0
  193. package/tools/ops.mjs +293 -0
  194. package/tools/patch.mjs +290 -0
  195. package/tools/question.mjs +26 -0
  196. package/tools/repomap.mjs +314 -0
  197. package/tools/search.mjs +248 -0
  198. package/tools/shared.mjs +467 -0
  199. package/tools/tree.mjs +81 -0
  200. package/tools/web.mjs +224 -0
  201. package/tools/write-path.mjs +191 -0
  202. package/traces/trace-store.mjs +303 -0
  203. package/undo-stack.mjs +47 -0
@@ -0,0 +1,243 @@
1
+ /**
2
+ * tools/execute.mjs — JavaScript execution tool
3
+ *
4
+ * Gives the model an `execute` tool that runs JS in a child `node
5
+ * --input-type=module --eval` process — NOT the in-process vm sandbox it used
6
+ * to be. The vm route could not support dynamic `import()` (needs the
7
+ * --experimental-vm-modules flag) or await it, which pushed every real JS run
8
+ * back to `bash node -e`. A child node process gives top-level await, dynamic
9
+ * `import()` of the project's own .mjs modules, native `console`/`fetch`, AND a
10
+ * killable timeout (an in-process infinite loop would freeze the CLI; a child
11
+ * process is killed like bash).
12
+ *
13
+ * The child runs PURE node ESM — no helpers are injected (exec-prelude.mjs
14
+ * retired 2026-09-03, TOOLS.md §12: preloaded readFile/writeFile/glob/grep/log
15
+ * helpers made execute look like a file tool, bypassing the dedicated
16
+ * read/ls/glob/grep/write/edit tools). Scripts that need fs/path import the
17
+ * node: modules themselves — same boundary as bash, no fake sandbox.
18
+ *
19
+ * Parameters:
20
+ * code — JS to run inline (top-level await and import() supported). Use this OR scriptFile.
21
+ * scriptFile — run a .mjs/.js file with node (self-contained, imports what it needs). Use this OR code.
22
+ * nodeArgs — (scriptFile) extra node flags before the script (e.g. --test, --check); eval-like flags rejected
23
+ * workdir — run in this sub-directory (no directory restriction)
24
+ * filter — return only output lines matching this regex (case-insensitive)
25
+ * timeoutMs — timeout (default 30s, max 600000ms)
26
+ */
27
+ import { spawn, execFileSync } from "node:child_process"
28
+ import { resolve } from "node:path"
29
+ import { DESC } from "./shared.mjs"
30
+
31
+ const MAX_SCRIPT = 50_000
32
+ const MAX_OUTPUT = 50_000
33
+ const DEFAULT_TIMEOUT = 30_000
34
+
35
+ /**
36
+ * 超时错误文本——带重试引导(TOOLS.md §14.1 D14.1.2——"下一跳"):数字 = 实际生效的
37
+ * timeoutMs(Math.min(t, 600_000) 或默认 30s)——上限 600000 与 schema/头注/execute.md 一致。
38
+ */
39
+ const timeoutErrorText = (timeoutMs) =>
40
+ `Error: script timed out after ${timeoutMs}ms — retry with a larger timeoutMs (up to 600000) for long scripts, or use bash (default 120s) for shell commands`
41
+
42
+ /** Resolve workdir relative to cwd — no boundary assertion
43
+ * (§10.1 2026-09-02: workspace confinement removed; the child node process is
44
+ * not directory-limited — same boundary as bash). */
45
+ function resolveBaseDir(cwd, workdir) {
46
+ if (!workdir || typeof workdir !== "string") return cwd
47
+ return resolve(cwd, workdir)
48
+ }
49
+
50
+ /** Keep only output lines matching a regex (execute filter, case-insensitive). */
51
+ function applyFilter(output, filter) {
52
+ try {
53
+ const re = new RegExp(filter, "i")
54
+ const lines = output.split("\n").filter((l) => re.test(l))
55
+ return lines.length ? lines.join("\n") : `(no output lines matched filter "${filter}")`
56
+ } catch (e) {
57
+ return `Error: filter regex invalid: ${e.message}`
58
+ }
59
+ }
60
+
61
+ /** Platform-aware process tree kill — mirror of system.mjs/verify.mjs killProcessTree.
62
+ * Timeout/abort must reach grandchildren: a script that spawned children keeps the
63
+ * pipes open otherwise — "close" never fires and the tool stalls until the 3s kick
64
+ * while the orphan keeps running (2026-09-05 advisor 🟡#4). */
65
+ function killProcessTree(child) {
66
+ if (process.platform === "win32") {
67
+ try { execFileSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" }) } catch {}
68
+ } else {
69
+ try { process.kill(-child.pid, "SIGKILL") } catch {}
70
+ try { child.kill("SIGKILL") } catch {}
71
+ }
72
+ }
73
+ export { killProcessTree }
74
+
75
+ // ─── 树杀注入缝(#57——「树杀实现按端注入」,形态参 §2.13.5 注入缝)───────────────
76
+ /**
77
+ * 超时 / abort 树杀注入位(**缺省不覆盖** = 核内 `killProcessTree`——CLI 语义,零行为变;
78
+ * 端装配层可覆盖为本端树杀实现)。核内零端名分支(契约 5——本档只认 `killTree` 函数名)。
79
+ * 契约:`killTree(child) → void`(尽力而为——调用点已包 try/catch,抛错不阻断收尾)。
80
+ */
81
+ let injectedKill = null
82
+ export function configureProcessTreeKill(impl) {
83
+ injectedKill = impl && typeof impl.killTree === "function" ? impl.killTree : null
84
+ }
85
+ /** 撤销注入(测试与端装配生命周期用——缺省态 = 核内 killProcessTree)。 */
86
+ export function resetProcessTreeKill() { injectedKill = null }
87
+
88
+ /** Spawn node with the given args, capture stdout/stderr, enforce timeout/abort.
89
+ * Resolves { text, ok } — ok=false on non-zero exit / timeout / abort. */
90
+ function runNode(childArgs, baseDir, timeoutMs, signal) {
91
+ return new Promise((resolvePromise) => {
92
+ const child = spawn(process.execPath, childArgs, {
93
+ cwd: baseDir,
94
+ stdio: ["ignore", "pipe", "pipe"],
95
+ windowsHide: true,
96
+ // POSIX detached → 子进程为组首——killProcessTree 的 -pid 组杀可达孙进程(win 用
97
+ // taskkill /T 不需 detached)——2026-09-05 advisor 🟡#4
98
+ detached: process.platform !== "win32",
99
+ // 双保险(2026-09-05——裸 spawn 无 signal 教训——system.mjs 同款):abort 时
100
+ // Node 自动杀直接子进程(第一道)——onAbort 手动 kill 兜底(SIGKILL 防信号陷阱
101
+ // 脚本——见 kill 注释)——AbortError 在 error 分支让路(close 必随——走 mode 收尾)
102
+ ...(signal ? { signal } : {}),
103
+ })
104
+
105
+ let outBuf = "", errBuf = "", truncated = false, settled = false, mode = null
106
+ let timer = null, kickTimer = null
107
+
108
+ const settle = (text, ok) => {
109
+ if (settled) return
110
+ settled = true
111
+ clearTimeout(timer)
112
+ clearTimeout(kickTimer)
113
+ if (signal) signal.removeEventListener("abort", onAbort)
114
+ resolvePromise({ text, ok })
115
+ }
116
+ // Tree kill(SIGKILL/taskkill /T——signal-trapping 脚本躲不开 watchdog;孙进程持管道
117
+ // 时直接 kill 不达——close 不触发拖到 kick——2026-09-05 advisor 🟡#4 对齐 bash/verify)。
118
+ // #57:树杀实现按端注入(缺省 = 核内 killProcessTree);注入实现抛错不阻断收尾(尽力而为)。
119
+ const kill = () => { try { (injectedKill ?? killProcessTree)(child) } catch { /* kill is best-effort */ } }
120
+ // After kill, wait for "close" (child fully reaped) before settling — settling
121
+ // early races the caller deleting the cwd dir while the child still holds it.
122
+ const armKick = () => { kickTimer = setTimeout(() => settle(mode === "abort" ? "(stopped)" : timeoutErrorText(timeoutMs), false), 3000) }
123
+ const onAbort = () => { if (mode) return; mode = "abort"; kill(); armKick() }
124
+
125
+ timer = setTimeout(() => { if (!mode) { mode = "timeout"; kill(); armKick() } }, timeoutMs)
126
+
127
+ if (signal) {
128
+ if (signal.aborted) onAbort()
129
+ else signal.addEventListener("abort", onAbort, { once: true })
130
+ }
131
+
132
+ const cap = (buf, d) => {
133
+ if (buf.length < MAX_OUTPUT) return buf + d
134
+ if (!truncated) { truncated = true; return buf + "\n...[output truncated]" }
135
+ return buf
136
+ }
137
+ child.stdout.on("data", (d) => { outBuf = cap(outBuf, d.toString()) })
138
+ child.stderr.on("data", (d) => { errBuf = cap(errBuf, d.toString()) })
139
+ child.on("error", (e) => {
140
+ // signal 双保险(2026-09-05):abort 时 Node signal option 杀子进程 → 本事件以
141
+ // AbortError 先触发——让路(close 必随——close 分支 mode==="abort" →
142
+ // settle("(stopped)"))——不把中止误报为启动失败
143
+ if (e.name === "AbortError") return
144
+ settle(`Error: failed to start node: ${e.message}`, false)
145
+ })
146
+ child.on("close", (code) => {
147
+ if (mode === "abort") return settle("(stopped)", false)
148
+ if (mode === "timeout") return settle(timeoutErrorText(timeoutMs), false)
149
+ const out = outBuf.trimEnd()
150
+ const err = errBuf.trim()
151
+ if (code === 0) {
152
+ settle(out || "(no output)", true)
153
+ } else {
154
+ settle(err ? (out ? `${out}\n\n[stderr]:\n${err}` : err) : `${out}\n(exit code ${code})`.trim(), false)
155
+ }
156
+ })
157
+ })
158
+ }
159
+
160
+ /** Validate nodeArgs (extra node flags for scriptFile mode, e.g. --test / --check). Forbids
161
+ * eval-like flags that would conflict with scriptFile mode or re-open inline injection. */
162
+ function validateNodeArgs(nodeArgs) {
163
+ if (!nodeArgs) return []
164
+ const arr = Array.isArray(nodeArgs) ? nodeArgs : String(nodeArgs).split(/\s+/).filter(Boolean)
165
+ const forbidden = /^(--eval|-e|--input-type|--print|-p|--inspect|--inspect-brk)(=|$)/i
166
+ for (const a of arr) {
167
+ if (forbidden.test(a)) throw new Error(`nodeArgs flag not allowed: ${a}`)
168
+ }
169
+ return arr
170
+ }
171
+
172
+ export const executeTool = {
173
+ name: "execute",
174
+ description: DESC("execute"),
175
+ parameters: {
176
+ type: "object",
177
+ properties: {
178
+ code: {
179
+ type: "string",
180
+ description: "JavaScript code to execute (top-level await and dynamic import() supported). Pure node ESM — no preloaded globals; import node: modules (fs/path) yourself when needed. File reads/writes go through the dedicated read/ls/glob/grep/write/edit tools. Use this OR scriptFile.",
181
+ },
182
+ scriptFile: {
183
+ type: "string",
184
+ description: "Run a .mjs/.js file with node (self-contained — the file imports what it needs). Path relative to workdir — no directory restriction. Use this OR code. For `node <script>` / `node --test <file>` / `node --check <file>`.",
185
+ },
186
+ nodeArgs: {
187
+ type: "array",
188
+ items: { type: "string" },
189
+ description: "(scriptFile) Extra node flags before the script, e.g. [\"--test\"], [\"--check\"]. Eval-like flags (--eval/--input-type/--inspect) are rejected.",
190
+ },
191
+ workdir: {
192
+ type: "string",
193
+ description: "Run in this directory (relative to cwd — no directory restriction; default cwd)",
194
+ },
195
+ filter: {
196
+ type: "string",
197
+ description: "Optional: only return output lines matching this regex (case-insensitive)",
198
+ },
199
+ timeoutMs: {
200
+ type: "integer",
201
+ minimum: 1,
202
+ maximum: 600000,
203
+ description: `Timeout in milliseconds (default ${DEFAULT_TIMEOUT}, max 600000)`,
204
+ },
205
+ },
206
+ required: [],
207
+ },
208
+ readonly: false,
209
+
210
+ async execute(args, ctx) {
211
+ let baseDir
212
+ try { baseDir = resolveBaseDir(ctx.cwd, args.workdir) }
213
+ catch (e) { return `Error: ${e.message}` }
214
+
215
+ const t = Number(args.timeoutMs)
216
+ const timeoutMs = Number.isFinite(t) && t > 0 ? Math.min(t, 600_000) : DEFAULT_TIMEOUT
217
+
218
+ let childArgs
219
+ if (args.scriptFile) {
220
+ if (args.code?.trim()) return "Error: pass code OR scriptFile, not both"
221
+ // scriptFile mode: run a .mjs/.js file with node [nodeArgs...]. Self-contained —
222
+ // a real node process imports what it needs. No directory restriction.
223
+ const scriptAbs = resolve(baseDir, args.scriptFile)
224
+ let nodeArgs
225
+ try { nodeArgs = validateNodeArgs(args.nodeArgs) }
226
+ catch (e) { return `Error: ${e.message}` }
227
+ childArgs = [...nodeArgs, scriptAbs]
228
+ } else {
229
+ const code = args.code ?? ""
230
+ if (!code.trim()) return "Error: either code or scriptFile is required"
231
+ if (code.length > MAX_SCRIPT) {
232
+ return `Error: script too large (${code.length} > ${MAX_SCRIPT} bytes). Split into smaller scripts or use individual tools.`
233
+ }
234
+ // inline mode: pure node ESM — no prelude, nothing injected (TOOLS.md §12).
235
+ childArgs = ["--input-type=module", "--eval", code]
236
+ }
237
+
238
+ const { text, ok } = await runNode(childArgs, baseDir, timeoutMs, ctx.signal)
239
+ // Only filter successful output — never swallow an error report behind a filter.
240
+ if (!ok) return text
241
+ return args.filter ? applyFilter(text, args.filter) : text
242
+ },
243
+ }