thincoder 0.12.28 → 0.12.29

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thincoder",
3
- "version": "0.12.28",
3
+ "version": "0.12.29",
4
4
  "description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
5
5
  "keywords": [
6
6
  "ai",
@@ -80,12 +80,20 @@ export async function prepareRun(agent, input, callbacks, {
80
80
  const platform = { win32: 'Windows', darwin: 'macOS', linux: 'Linux' }[process.platform] ?? process.platform
81
81
  const wasRestored = agent._sessionStart != null
82
82
  agent._sessionStart ??= new Date().toISOString()
83
- if (tree) {
84
- agent.history.push({ role: "user", content: `[System reminder: OS: ${platform}. Working directory: ${agent.cwd}. Session start: ${agent._sessionStart}. Working directory snapshot:\n<untrusted_cwd_listing>\n${escapeXml(tree)}\n</untrusted_cwd_listing>]`, transient: true })
85
- } else {
86
- agent.history.push({ role: "user", content: `[System reminder: OS: ${platform}. Working directory: ${agent.cwd}. Session start: ${agent._sessionStart}.]`, transient: true })
83
+ // Inject OS/cwd + cwd-tree only ONCE per process (restored sessionStart keeps the
84
+ // content byte-identical across restarts 2026-08-16 cache audit). Without the guard
85
+ // every run re-pushed the same reminder (history bloat, wasted tokens).
86
+ if (!agent._osReminderInjected) {
87
+ agent._osReminderInjected = true
88
+ if (tree) {
89
+ agent.history.push({ role: "user", content: `[System reminder: OS: ${platform}. Working directory: ${agent.cwd}. Session start: ${agent._sessionStart}. Working directory snapshot:\n<untrusted_cwd_listing>\n${escapeXml(tree)}\n</untrusted_cwd_listing>]`, transient: true })
90
+ } else {
91
+ agent.history.push({ role: "user", content: `[System reminder: OS: ${platform}. Working directory: ${agent.cwd}. Session start: ${agent._sessionStart}.]`, transient: true })
92
+ }
93
+
87
94
  }
88
- if (wasRestored) {
95
+ if (wasRestored && !agent._restartReminderInjected) {
96
+ agent._restartReminderInjected = true
89
97
  agent.history.push({ role: "user", content: `[System reminder: process restarted at ${new Date().toISOString()}.]`, transient: true })
90
98
  }
91
99
  if (agent.memory && !agent.history.some((m) => typeof m.content === "string" && m.content.startsWith(OUTLINE_INJECT_PREFIX))) {
@@ -139,16 +147,16 @@ export async function prepareRun(agent, input, callbacks, {
139
147
  } catch { /* checklist not available — suppress error */ }
140
148
  }
141
149
  pushReal(agent, { role: "user", content: input })
142
- // Time grounding for EVERY agent depth, pushed LAST (after the user input): transient,
143
- // dropped on persist, fresh at every run start. Tail position keeps the second-precision
144
- // content out of any prefix — caches stay hit (plugin hit a position-drift regression
145
- // when the reminder was interleaved before the input; CLI is aligned preemptively).
146
- agent.history.push({
147
- role: "user",
148
- content: `[System reminder: current time is ${timeNowLocal()} (local; timezone ${Intl.DateTimeFormat().resolvedOptions().timeZone || "local"}).]`,
149
- transient: true,
150
- })
151
150
  }
151
+ // Time grounding for EVERY agent depth AND every resume, pushed LAST (after the user
152
+ // input): transient, dropped on persist, fresh at every run start — including resumes
153
+ // (an interrupt-continuation must know NOW, not the pre-interrupt time; 2026-08-16).
154
+ // Tail position keeps the second-precision content out of any prefix — caches stay hit.
155
+ agent.history.push({
156
+ role: "user",
157
+ content: `[System reminder: current time is ${timeNowLocal()} (local; timezone ${Intl.DateTimeFormat().resolvedOptions().timeZone || "local"}).`,
158
+ transient: true,
159
+ })
152
160
 
153
161
  if (agent._pendingReminders.length > 0) {
154
162
  for (const reminder of agent._pendingReminders) {
@@ -185,7 +193,13 @@ export async function prepareRun(agent, input, callbacks, {
185
193
  } : subagentTool
186
194
 
187
195
  const depthOnly = depth === 0 ? [filteredSubagent, skillTool, goalTool, engTool, verifyTool, recentChangesTool, advisorTool]
188
- : agent._role === "eng-coder" ? [advisorTool, verifyTool] : []
196
+ // Write-permission coder sub-agents (subagent role="coder" + escalate surgeon): the
197
+ // system prompt names verify (system.md) and advisor (discipline.md) — without them a
198
+ // surgeon hit "unknown tool" and fell back to bash node --check / npm test to
199
+ // self-verify (2026-08-16 deepseek surgeon diagnosis; plugin parity).
200
+ : agent._role === "eng-coder" ? [advisorTool, verifyTool]
201
+ : agent._role === "coder" ? [verifyTool, advisorTool]
202
+ : []
189
203
  const tools = [...agent.tools, taskTool, planTool, timerTool, ...depthOnly]
190
204
  const toolSchemas = tools.map(toOpenAISchema)
191
205
  const toolByName = new Map(tools.map((t) => [t.name, t]))
package/src/skills.mjs CHANGED
@@ -52,6 +52,10 @@ async function loadSkillsFromDir(dir) {
52
52
  let entries
53
53
  try {
54
54
  entries = await readdir(dir, { withFileTypes: true })
55
+ // Deterministic order: readdir order is filesystem-dependent; an unsorted scan would
56
+ // reshuffle the skills listing → system prompt byte change with zero content change
57
+ // (2026-08-16 cache audit — that silently misses the provider prefix cache).
58
+ entries.sort((a, b) => a.name.localeCompare(b.name))
55
59
  } catch {
56
60
  return []
57
61
  }