thincoder 0.12.27 → 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 +1 -1
- package/src/agent/setup.mjs +29 -13
- package/src/provider/core.mjs +3 -1
- package/src/session.mjs +7 -1
- package/src/skills.mjs +4 -0
package/package.json
CHANGED
package/src/agent/setup.mjs
CHANGED
|
@@ -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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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))) {
|
|
@@ -124,13 +132,6 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
124
132
|
})
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
|
-
// Time grounding for EVERY agent depth (subagents are short-lived; they also need
|
|
128
|
-
// to know "now"). Transient — dropped on persist, fresh at every run start.
|
|
129
|
-
agent.history.push({
|
|
130
|
-
role: "user",
|
|
131
|
-
content: `[System reminder: current time is ${timeNowLocal()} (local; timezone ${Intl.DateTimeFormat().resolvedOptions().timeZone || "local"}).]`,
|
|
132
|
-
transient: true,
|
|
133
|
-
})
|
|
134
135
|
if (depth === 0) {
|
|
135
136
|
// Checklist injection: inject pending + in_progress items from .thincoder/checklist.md
|
|
136
137
|
try {
|
|
@@ -147,6 +148,15 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
147
148
|
}
|
|
148
149
|
pushReal(agent, { role: "user", content: input })
|
|
149
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
|
+
})
|
|
150
160
|
|
|
151
161
|
if (agent._pendingReminders.length > 0) {
|
|
152
162
|
for (const reminder of agent._pendingReminders) {
|
|
@@ -183,7 +193,13 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
183
193
|
} : subagentTool
|
|
184
194
|
|
|
185
195
|
const depthOnly = depth === 0 ? [filteredSubagent, skillTool, goalTool, engTool, verifyTool, recentChangesTool, advisorTool]
|
|
186
|
-
|
|
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
|
+
: []
|
|
187
203
|
const tools = [...agent.tools, taskTool, planTool, timerTool, ...depthOnly]
|
|
188
204
|
const toolSchemas = tools.map(toOpenAISchema)
|
|
189
205
|
const toolByName = new Map(tools.map((t) => [t.name, t]))
|
package/src/provider/core.mjs
CHANGED
|
@@ -240,7 +240,9 @@ export function normalizeToolPairing(messages) {
|
|
|
240
240
|
rest.push(m)
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
|
-
if (toolById.size === 0
|
|
243
|
+
if (toolById.size === 0 && !messages.some((m) => m.role === "assistant" && m.tool_calls?.length)) {
|
|
244
|
+
return messages // no tool messages AND no tool_calls declared — nothing to enforce
|
|
245
|
+
}
|
|
244
246
|
const out = []
|
|
245
247
|
for (const m of rest) {
|
|
246
248
|
out.push(m)
|
package/src/session.mjs
CHANGED
|
@@ -325,8 +325,14 @@ export function saveSession(agent) {
|
|
|
325
325
|
// _fullHistory is written at the source via pushReal — no flush needed here.
|
|
326
326
|
// history = FULL, never-compacted (human-readable; VS Code panel & CLI resume read this)
|
|
327
327
|
// contextHistory = machine context (possibly compacted) so CLI resume keeps the token savings
|
|
328
|
+
// Human line: transient machine injections never enter the readable record.
|
|
328
329
|
const history = (agent._fullHistory ?? agent.history).filter((m) => !m.transient && !isLegacyTransient(m))
|
|
329
|
-
|
|
330
|
+
// Machine line (contextHistory): KEEP transient messages — resume must rebuild the
|
|
331
|
+
// machine line byte-identical to what the provider cache saw. Dropping them made every
|
|
332
|
+
// process restart diverge at the first injection position (git/OS/time reminders are
|
|
333
|
+
// re-injected with FRESH content) → whole-prefix cache miss on the CLI's very first
|
|
334
|
+
// request of each session (2026-08-16 cache-hit report; Kimi review).
|
|
335
|
+
const contextHistory = agent.history.filter((m) => !isLegacyTransient(m))
|
|
330
336
|
const data = {
|
|
331
337
|
version: 2,
|
|
332
338
|
cwd: agent.cwd,
|
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
|
}
|