thincoder 0.12.23 → 0.12.25
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/README.md +2 -2
- package/package.json +2 -2
- package/src/advisor.mjs +9 -3
- package/src/agent/setup.mjs +5 -0
- package/src/agent.mjs +2 -2
- package/src/config.mjs +6 -9
- package/src/tools/bash.md +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Design philosophy (the entire meaning of the name): if the Node standard library
|
|
|
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
|
-
- **Session persistence** ⭐0.5.0:
|
|
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
23
|
- **Concurrent subagents**: three roles — `explore`/`plan`/`coder` — dispatched in parallel, streaming output visible, reports land in the conversation; per-subagent model override (`subagent` tool `model` arg or `agent.subagentModel` config — e.g. discuss with `glm-5.2`, let `deepseek-v4-flash` implement)
|
|
24
24
|
- **Plan Mode**: read-only exploration + design, implement after user approval
|
|
25
25
|
- **AUTO mode**: `/auto` full authorization, no confirmations on long tasks
|
|
@@ -183,7 +183,7 @@ src/
|
|
|
183
183
|
memory/ three-layer memory — schema.mjs (DDL/constants), core.mjs (CRUD + retrieval),
|
|
184
184
|
code-index.mjs + code-sync.mjs (code_chunks), docs.mjs (doc_chunks)
|
|
185
185
|
memory.mjs re-export shim → src/memory/*
|
|
186
|
-
session.mjs session persistence (
|
|
186
|
+
session.mjs session persistence (unlimited archive slots, isolated by project cwd, process-level isolation via sessionId + slotSessions)
|
|
187
187
|
skills.mjs skill discovery/loading (.thincoder/skills/*.md)
|
|
188
188
|
markdown.mjs entry format (frontmatter parse/serialize)
|
|
189
189
|
git/ checkpoint.mjs (git patch snapshots / rewind), gitmem.mjs (Team layer git sync)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thincoder",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.25",
|
|
4
4
|
"description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
30
|
-
"url": "https://
|
|
30
|
+
"url": "https://github.com/xinbo-tech/thincoder.git"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"test": "node --test \"test/*.mjs\"",
|
package/src/advisor.mjs
CHANGED
|
@@ -92,6 +92,12 @@ try { ADVISOR_DESIGN = readFileSync(join(__dirname, "prompts", "advisor-design.m
|
|
|
92
92
|
* @param {string} [reviewType] — "design" for design review, undefined/"code" for code review
|
|
93
93
|
* @returns {string} the system prompt
|
|
94
94
|
*/
|
|
95
|
+
/** Append local time so the reviewer knows "now" (same grounding as the agent loop). */
|
|
96
|
+
function withTime(prompt) {
|
|
97
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "local"
|
|
98
|
+
return prompt + `\n\nCurrent time: ${new Date().toLocaleString("sv-SE")} (${timeZone}).`
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
export function buildAdvisorSystemPrompt(agent, prior, reviewType) {
|
|
96
102
|
// Round decision is DETERMINISTIC (decision 2026-08-08): _advisorRound > 0
|
|
97
103
|
// with a stored review output means convergence (round 2+); 0 means round 1.
|
|
@@ -215,7 +221,7 @@ export function prepareAdvisorMessages(agent, reviewType, designToken = null, do
|
|
|
215
221
|
// after a failed design review). Fresh session.
|
|
216
222
|
if (reviewType === "design" && (agent._advisorRound || 0) === 0) {
|
|
217
223
|
return [
|
|
218
|
-
{ role: "system", content: buildAdvisorSystemPrompt(agent, prior, reviewType) },
|
|
224
|
+
{ role: "system", content: withTime(buildAdvisorSystemPrompt(agent, prior, reviewType)) },
|
|
219
225
|
{ role: "user", content: escapeLiteralEscapes(buildAdvisorUserMessage(agent, prior, reviewType, designToken, documents, paths)) },
|
|
220
226
|
]
|
|
221
227
|
}
|
|
@@ -243,7 +249,7 @@ export function prepareAdvisorMessages(agent, reviewType, designToken = null, do
|
|
|
243
249
|
// Mutations exist → KEEP the round (cap keeps advancing through retries).
|
|
244
250
|
const user = buildAdvisorUserMessage(agent, prior, reviewType, designToken, documents, paths)
|
|
245
251
|
return [
|
|
246
|
-
{ role: "system", content: buildAdvisorSystemPrompt(agent, prior, reviewType) },
|
|
252
|
+
{ role: "system", content: withTime(buildAdvisorSystemPrompt(agent, prior, reviewType)) },
|
|
247
253
|
{
|
|
248
254
|
role: "user",
|
|
249
255
|
// NOTE (2026-08-06): the leading prefix is a PLAIN "System reminder:",
|
|
@@ -271,7 +277,7 @@ export function prepareAdvisorMessages(agent, reviewType, designToken = null, do
|
|
|
271
277
|
// review surface.
|
|
272
278
|
const scopeFiles = resolveScopeFiles(agent, paths)
|
|
273
279
|
return [
|
|
274
|
-
{ role: "system", content: buildAdvisorSystemPrompt(agent, prior, reviewType) },
|
|
280
|
+
{ role: "system", content: withTime(buildAdvisorSystemPrompt(agent, prior, reviewType)) },
|
|
275
281
|
{ role: "user", content: escapeLiteralEscapes(buildAdvisorFollowUp(agent, prior, scopeFiles)) },
|
|
276
282
|
]
|
|
277
283
|
}
|
package/src/agent/setup.mjs
CHANGED
|
@@ -221,6 +221,11 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
221
221
|
? `${base}\n\n${mainOverlay}`
|
|
222
222
|
: base
|
|
223
223
|
|
|
224
|
+
// Local time + timezone on every agent (main, subagent, consult) — without it "today"/
|
|
225
|
+
// "just now"/"recent" in user messages and search freshness are ungrounded.
|
|
226
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "local"
|
|
227
|
+
systemPrompt += `\n\nCurrent time: ${new Date().toLocaleString("sv-SE")} (${timeZone}).`
|
|
228
|
+
|
|
224
229
|
const projectRules = await loadProjectInstructions(agent.cwd)
|
|
225
230
|
if (projectRules) {
|
|
226
231
|
systemPrompt += `\n\nProject instructions (follow these as project conventions):\n<untrusted_project_instructions>\n${escapeXml(projectRules)}\n</untrusted_project_instructions>`
|
package/src/agent.mjs
CHANGED
|
@@ -346,7 +346,7 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
|
|
|
346
346
|
const parsed = JSON.parse(result)
|
|
347
347
|
if (parsed.images?.length) {
|
|
348
348
|
// tool message first — closes the tool_call pairing (OpenAI API requires tool result immediately after assistant with tool_calls)
|
|
349
|
-
pushReal(agent, { role: "tool", tool_call_id: toolCall.id, content: parsed.text })
|
|
349
|
+
pushReal(agent, { role: "tool", tool_call_id: toolCall.id, name: toolCall.name, content: parsed.text })
|
|
350
350
|
if (specForModel(agent.provider.model).multimodal) {
|
|
351
351
|
// then inject multimodal user message with base64 images for the model to actually "see" them on the next turn
|
|
352
352
|
deferredUserMsgs.push({
|
|
@@ -371,7 +371,7 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
|
|
|
371
371
|
}
|
|
372
372
|
} catch { /* Parse failure doesn't affect normal tool messages */ }
|
|
373
373
|
}
|
|
374
|
-
pushReal(agent, { role: "tool", tool_call_id: toolCall.id, content: result })
|
|
374
|
+
pushReal(agent, { role: "tool", tool_call_id: toolCall.id, name: toolCall.name, content: result })
|
|
375
375
|
if (tool && ok) {
|
|
376
376
|
if (FILE_MUTATORS.has(toolCall.name)) {
|
|
377
377
|
// Direct file edit — code was changed. The prior advisor review and
|
package/src/config.mjs
CHANGED
|
@@ -18,6 +18,7 @@ export const PROVIDER_PRESETS = {
|
|
|
18
18
|
kimi: { baseURL: "https://api.moonshot.cn/v1", model: "kimi-k3", thinking: null, reasoningEffort: "max", maxTokens: 131072, desc: "Kimi / Moonshot" },
|
|
19
19
|
"kimi-code": { baseURL: "https://api.kimi.com/coding/v1", model: "k3", thinking: null, reasoningEffort: "max", maxTokens: 131072, desc: "Kimi For Coding (platform.kimi.com — sk-kimi- keys; NOT interchangeable with Moonshot)" },
|
|
20
20
|
glm: { baseURL: "https://open.bigmodel.cn/api/paas/v4", model: "glm-5.2", thinking: { type: "enabled" }, reasoningEffort: "max", maxTokens: 128000, desc: "Zhipu GLM" },
|
|
21
|
+
"glm-code": { baseURL: "https://open.bigmodel.cn/api/coding/paas/v4", model: "glm-5.2", thinking: { type: "enabled" }, reasoningEffort: "max", maxTokens: 128000, desc: "Zhipu GLM Coding Plan (coding endpoint — same key as GLM; server-forced thinking)" },
|
|
21
22
|
qwen: { baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", model: "qwen3.7-max", reasoningEffort: "high", maxTokens: 131072, desc: "Qwen / Alibaba" },
|
|
22
23
|
qwenplan: { baseURL: "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1", model: "qwen3.7-max", reasoningEffort: "high", maxTokens: 131072, desc: "Qwen Token Plan (百炼套餐)" },
|
|
23
24
|
minimax: { baseURL: "https://api.minimaxi.com/v1", model: "MiniMax-M3", thinking: { type: "adaptive" }, maxTokens: 128000, chatPath: "/text/chatcompletion_v2", desc: "MiniMax" },
|
|
@@ -91,18 +92,14 @@ const DEFAULTS = {
|
|
|
91
92
|
*/
|
|
92
93
|
const MODEL_SPECS = [
|
|
93
94
|
// DeepSeek V4 series
|
|
94
|
-
["deepseek-v4-pro", { context: 1_000_000, maxOutput: 384_000, thinking: true, prefixMode: true, cacheMode: "
|
|
95
|
-
["deepseek-v4-flash", { context: 1_000_000, maxOutput: 384_000, thinking: true, prefixMode: true, cacheMode: "
|
|
96
|
-
["deepseek-reasoner", { context: 256_000, maxOutput: 384_000, thinking: true, prefixMode: true, cacheMode: "prompt", thinkApi: "type", reasoningEcho: "required", reasoningEffortEnum: ["high", "max"], tempRange: [0, 2] }],
|
|
97
|
-
["deepseek-chat", { context: 256_000, maxOutput: 384_000, thinking: false, prefixMode: true, cacheMode: "prompt", thinkApi: "type", reasoningEcho: "required", reasoningEffortEnum: ["high", "max"], tempRange: [0, 2] }],
|
|
95
|
+
["deepseek-v4-pro", { context: 1_000_000, maxOutput: 384_000, thinking: true, prefixMode: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"], tempRange: [0, 2] }],
|
|
96
|
+
["deepseek-v4-flash", { context: 1_000_000, maxOutput: 384_000, thinking: true, prefixMode: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"], tempRange: [0, 2] }],
|
|
98
97
|
// Kimi series
|
|
99
98
|
["kimi-k3", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, cacheMode: "auto", thinkApi: "effort", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"] }],
|
|
100
99
|
// Qwen router prefixes model IDs with provider namespace: kimi/kimi-k3 → kimi-k3 (IK7K4V)
|
|
101
100
|
["kimi/kimi-k3", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, cacheMode: "auto", thinkApi: "effort", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"] }],
|
|
102
101
|
// Kimi For Coding endpoint uses the short model ID "k3" (same specs as kimi-k3) — IK5VGJ
|
|
103
102
|
["k3", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, cacheMode: "auto", thinkApi: "effort", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"] }],
|
|
104
|
-
["kimi-k2", { context: 256_000, maxOutput: 128_000, thinking: false, partialMode: true, multimodal: true, cacheMode: "none" }],
|
|
105
|
-
["moonshot", { context: 128_000, maxOutput: 32_000, thinking: false, cacheMode: "none" }],
|
|
106
103
|
// GLM series
|
|
107
104
|
["glm-5.2", { context: 1_000_000, maxOutput: 128_000, thinking: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "optional", reasoningEffortEnum: ["max", "xhigh", "high", "medium", "low", "minimal", "none"], tempRange: [0, 1], noUsageStream: true }],
|
|
108
105
|
["glm-5", { context: 1_000_000, maxOutput: 128_000, thinking: true, cacheMode: "auto", thinkApi: "type", reasoningEcho: "optional", reasoningEffortEnum: ["max", "xhigh", "high", "medium", "low", "minimal", "none"], tempRange: [0, 1], noUsageStream: true }],
|
|
@@ -115,9 +112,9 @@ const MODEL_SPECS = [
|
|
|
115
112
|
// qwen3.7-max rejects image parts outright (DashScope 400 "Unexpected item type in content") — text-only
|
|
116
113
|
["qwen3.7-max", { context: 1_000_000, maxOutput: 128_000, thinking: true, partialMode: true, cacheMode: "none", thinkApi: "effort", reasoningEffortEnum: ["xhigh", "high"], tempRange: [0, 2] }],
|
|
117
114
|
["qwen3.8-max", { context: 1_000_000, maxOutput: 128_000, thinking: true, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", reasoningEffortEnum: ["xhigh", "high"], tempRange: [0, 2] }],
|
|
118
|
-
["qwen-max", { context: 1_000_000, maxOutput:
|
|
119
|
-
["qwen-plus", { context: 1_000_000, maxOutput:
|
|
120
|
-
["qwen", { context: 1_000_000, maxOutput:
|
|
115
|
+
["qwen-max", { context: 1_000_000, maxOutput: 131_072, thinking: false, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", tempRange: [0, 2] }],
|
|
116
|
+
["qwen-plus", { context: 1_000_000, maxOutput: 131_072, thinking: false, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", tempRange: [0, 2] }],
|
|
117
|
+
["qwen", { context: 1_000_000, maxOutput: 131_072, thinking: false, partialMode: true, multimodal: true, cacheMode: "none", thinkApi: "effort", tempRange: [0, 2] }],
|
|
121
118
|
// MiniMax series
|
|
122
119
|
["MiniMax-M3", { context: 1_000_000, maxOutput: 128_000, thinking: true, multimodal: true, cacheMode: "auto", thinkApi: "type", thinkEnabledValue: "adaptive", tempRange: [0, 2], noUsageStream: true }],
|
|
123
120
|
["minimax-m3", { context: 1_000_000, maxOutput: 128_000, thinking: true, multimodal: true, cacheMode: "auto", thinkApi: "type", thinkEnabledValue: "adaptive", tempRange: [0, 2], noUsageStream: true }],
|
package/src/tools/bash.md
CHANGED
|
@@ -28,7 +28,7 @@ Notes:
|
|
|
28
28
|
- The environment sets GIT_PAGER=cat, PAGER=cat, EDITOR=true, TERM=dumb — but still always use non-interactive flags
|
|
29
29
|
- Output is capped at ~200K chars; if you need more, redirect to a file and read it. Truncated output ends with a `[... truncated: N chars omitted]` marker — the missing tail may contain errors.
|
|
30
30
|
- Check `[stderr]` for error messages, warnings, and diagnostic output — it is separated from `[stdout]` so you can quickly identify problems.
|
|
31
|
-
- On Windows
|
|
31
|
+
- On Windows the shell is **cmd.exe** (NOT Git Bash, NOT PowerShell): `&&`/`||` chaining works, use cmd built-ins (`del`, `dir`, `type`, `findstr`, `tasklist`) and `/dev/null`→`NUL`, `2>/dev/null`→`>nul 2>&1`. Bash-isms (`rm -rf`, `cp -r`, `head`, `$(...)`) FAIL. For complex logic prefer `node -e` over shell gymnastics.
|
|
32
32
|
- Never use bash to read, copy, or transmit secret files (.env, keys, tokens)
|
|
33
33
|
- Do NOT run destructive commands (rm -rf, force-push, drop table) without explicit user confirmation
|
|
34
34
|
- After commands that change files (git checkout, npm install, etc.), repo_outline and code_search may be stale — re-run them to get current results.
|