thincoder 0.12.39 → 0.12.40

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  本文件记录 ThinCoder CLI 的发布历史。格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),版本遵循[语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.12.40] — 2026-08-23
6
+
7
+ ### Fixed
8
+
9
+ - **`shrinkOversized` 数据丢失**:原地改共享消息对象会污染持久化的人读线(巨型粘贴内容被永久截断)——改为复制-on-write(机器线替换、人读线不动)
10
+ - 轮末探索蒸馏边界只在压缩**真重建**时重置(shrink 路径不再误重置)
11
+ - `createAgent` 补 `_emptyRetries` 初始化;删 advisor 分支无用 JSON.parse
12
+
5
13
  ## [0.12.39] — 2026-08-23
6
14
 
7
15
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thincoder",
3
- "version": "0.12.39",
3
+ "version": "0.12.40",
4
4
  "description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
5
5
  "keywords": [
6
6
  "ai",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "repository": {
30
30
  "type": "git",
31
- "url": "https://github.com/xinbo-tech/thincoder.git"
31
+ "url": "git+https://github.com/xinbo-tech/thincoder.git"
32
32
  },
33
33
  "scripts": {
34
34
  "test": "node --test \"test/*.mjs\"",
package/src/agent.mjs CHANGED
@@ -95,6 +95,7 @@ export function createAgent({
95
95
  _sessionStart: sessionStart,
96
96
  _lastPromptTokens: null, _usageAtLen: null,
97
97
  _compressFailures: 0,
98
+ _emptyRetries: 0, // empty-response retry budget (per-run; reset on a fresh user turn)
98
99
  _runStartHistoryLen: 0, // machine-line length at the start of the current run — end-of-run exploration distillation slices from here
99
100
  _currentTurn: 0, _maxTurns: 100, // turn counter for status bar display
100
101
  }
@@ -418,11 +419,6 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
418
419
  // how many reviews have run (round 1→2→3→4→5), not how many succeeded.
419
420
  // A failed/interrupted review is still a review attempt and should use
420
421
  // the next round's prompt on retry.
421
- try {
422
- JSON.parse(toolCall.arguments || "{}")
423
- } catch {
424
- /* arguments unparseable — still counts as a review attempt */
425
- }
426
422
  agent._advisorRound++
427
423
  }
428
424
  if (FILE_MUTATORS.has(toolCall.name)) {
package/src/context.mjs CHANGED
@@ -272,19 +272,28 @@ const OVERSIZE_CONTENT_LIMIT = 8_000
272
272
  */
273
273
  function shrinkOversized(agent, limit = OVERSIZE_CONTENT_LIMIT) {
274
274
  let shrunk = false
275
- for (const m of agent.history) {
276
- if ((m.role !== "user" && m.role !== "tool") || typeof m.content !== "string") continue
277
- if (m.content.length <= limit) continue
275
+ // Copy-on-write: build a NEW array and replace only truncated entries. pushReal stores the SAME
276
+ // message object in both `agent.history` (machine line) and `agent._fullHistory` (human/persistence
277
+ // line), so in-place `m.content = ...` would ALSO truncate the never-compacted human line and lose
278
+ // the original pasted content on session persist (session.mjs persists _fullHistory). VS Code port
279
+ // already copies (`history.map(m => ({ ...m }))`); this brings CLI to parity.
280
+ const next = agent.history.map((m) => {
281
+ if ((m.role !== "user" && m.role !== "tool") || typeof m.content !== "string") return m
282
+ if (m.content.length <= limit) return m
278
283
  // Truncate keeping head + tail, insert stub in between; keepHead/keepTail proportional but not exceeding 50%/25% of limit
279
284
  const keepHead = Math.min(Math.floor(limit * 0.5), 4000)
280
285
  const keepTail = Math.min(Math.floor(limit * 0.25), 2000)
281
- m.content =
282
- m.content.slice(0, keepHead) +
283
- `\n[... ${m.content.length - keepHead - keepTail} chars truncated — single message too large for context window ...]\n` +
284
- m.content.slice(-keepTail)
285
286
  shrunk = true
286
- }
287
+ return {
288
+ ...m,
289
+ content:
290
+ m.content.slice(0, keepHead) +
291
+ `\n[... ${m.content.length - keepHead - keepTail} chars truncated — single message too large for context window ...]\n` +
292
+ m.content.slice(-keepTail),
293
+ }
294
+ })
287
295
  if (shrunk) {
296
+ agent.history = next
288
297
  // Same as compaction: measured token baseline is invalidated by the changed history, fall back to estimation until next response
289
298
  agent._lastPromptTokens = null
290
299
  agent._usageAtLen = null