thincoder 0.12.27 → 0.12.28

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.27",
3
+ "version": "0.12.28",
4
4
  "description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
5
5
  "keywords": [
6
6
  "ai",
@@ -124,13 +124,6 @@ export async function prepareRun(agent, input, callbacks, {
124
124
  })
125
125
  }
126
126
  }
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
127
  if (depth === 0) {
135
128
  // Checklist injection: inject pending + in_progress items from .thincoder/checklist.md
136
129
  try {
@@ -146,6 +139,15 @@ export async function prepareRun(agent, input, callbacks, {
146
139
  } catch { /* checklist not available — suppress error */ }
147
140
  }
148
141
  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
+ })
149
151
  }
150
152
 
151
153
  if (agent._pendingReminders.length > 0) {
@@ -240,7 +240,9 @@ export function normalizeToolPairing(messages) {
240
240
  rest.push(m)
241
241
  }
242
242
  }
243
- if (toolById.size === 0) return messages // no tool messages nothing to enforce
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
- const contextHistory = agent.history.filter((m) => !m.transient && !isLegacyTransient(m))
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,