@thincoder/core 0.9.1 → 0.9.3

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/LICENSE +21 -0
  3. package/README.md +90 -0
  4. package/advisor/loop.mjs +2 -2
  5. package/advisor/run.mjs +1 -1
  6. package/agent/completion.mjs +3 -1
  7. package/agent/family-tools.mjs +24 -11
  8. package/agent/helpers.mjs +11 -2
  9. package/agent/run-stages.mjs +27 -5
  10. package/agent/setup-reminders.mjs +67 -11
  11. package/agent/setup.mjs +6 -0
  12. package/agent/write-gate.mjs +5 -5
  13. package/agent-tools/advisor-async.mjs +4 -4
  14. package/agent-tools/advisor.mjs +3 -3
  15. package/agent-tools/async-discard.mjs +1 -1
  16. package/agent-tools/audit-block.mjs +106 -0
  17. package/agent-tools/batch-lifecycle.mjs +301 -0
  18. package/agent-tools/batch-segment.mjs +16 -263
  19. package/agent-tools/batch-skeleton.mjs +156 -0
  20. package/agent-tools/batch.mjs +410 -0
  21. package/agent-tools/context.mjs +174 -0
  22. package/agent-tools/eng.mjs +4 -0
  23. package/agent-tools/goal.mjs +7 -0
  24. package/agent-tools/parent-channel.mjs +18 -1
  25. package/agent-tools/plan.mjs +39 -5
  26. package/agent-tools/read-history.mjs +122 -24
  27. package/agent-tools/settings.mjs +4 -2
  28. package/agent-tools/subagent-async.mjs +3 -3
  29. package/agent-tools/subagent-spawn.mjs +29 -101
  30. package/agent-tools/task.mjs +11 -0
  31. package/agent-tools.mjs +9 -2
  32. package/agent.mjs +10 -4
  33. package/config.mjs +1 -1
  34. package/context.mjs +66 -121
  35. package/fts-text.mjs +41 -0
  36. package/generate-title.mjs +6 -6
  37. package/i18n.mjs +4 -4
  38. package/ledger-cmd.mjs +30 -7
  39. package/ledger-db.mjs +22 -2
  40. package/ledger-executors.mjs +103 -0
  41. package/ledger-surface.mjs +15 -8
  42. package/ledger.mjs +15 -4
  43. package/manifest.mjs +172 -60
  44. package/memory/core.mjs +4 -18
  45. package/memory/schema.mjs +4 -11
  46. package/package.json +22 -1
  47. package/prompts/advisor-design.md +1 -1
  48. package/prompts/advisor-round2.md +1 -1
  49. package/prompts/advisor-round3.md +1 -1
  50. package/prompts/common.md +3 -3
  51. package/prompts/discipline-engineering.md +17 -2
  52. package/prompts/persona-eng-coder.md +1 -1
  53. package/prompts/persona-eng-designer.md +1 -1
  54. package/prompts/persona-engineering.md +9 -6
  55. package/session-gc.mjs +129 -74
  56. package/session-index-build.mjs +298 -0
  57. package/session-index-cmd.mjs +61 -0
  58. package/session-index-pass.mjs +95 -0
  59. package/session-index-query.mjs +102 -0
  60. package/session-index.mjs +285 -0
  61. package/session-lifecycle.mjs +18 -5
  62. package/session-slots-manifest.mjs +55 -3
  63. package/session-stale.mjs +247 -0
  64. package/token-window.mjs +188 -0
  65. package/tools/bash.mjs +4 -15
  66. package/tools/execute.mjs +5 -13
  67. package/tools/git-checkpoint.mjs +1 -1
  68. package/tools/git-ext.mjs +23 -20
  69. package/tools/git-run.mjs +141 -0
  70. package/tools/git.mjs +56 -45
  71. package/tools/index.mjs +3 -1
  72. package/tools/process-tree.mjs +20 -0
  73. package/tools/shared.mjs +8 -4
  74. package/traces/trace-cleanup.mjs +109 -0
  75. package/traces/trace-store.mjs +32 -36
@@ -20,8 +20,9 @@
20
20
  * **单遍序列化**:脱敏内联进输出缓冲(不构造复制图 + 二次全量字符串)——字段名/次序
21
21
  * 与 `JSON.stringify` 形态同构(T-TR1 等价断言)。
22
22
  * - 容量 / 清理(§2.5 #116 / A21 已裁 · 按建议——取 VSC 侧):**完整落盘**(不截断、
23
- * 不丢行、不丢记录);**清理 = 每写一次 prune**(写盘成功后顺带 `cleanupTraces`——
24
- * 与写盘同在 fire-and-forget 异步体内)。目录按日组织(YYYY-MM-DD),可按日/会话过滤。
23
+ * 不丢行、不丢记录);**清理 = 每写一次 prune**(写盘成功后顺带 `maybePruneTraces`——
24
+ * 与写盘同在 fire-and-forget 异步体内)。2026-09-21(TRACES.md §6.4):判据改**目录级三段梯**
25
+ * + 每写 prune **节流**;清理实现外提 `trace-cleanup.mjs`(本档包装保既有 import 面)。
25
26
  * - seq = 当日目录内最大已有 seq + 1(D-TR3——跨会话/进程重启不覆写既有旧轨迹——
26
27
  * 18.6.1 评审 #3);**进程内 seqCache(§23.3.2——消除逐调用同步扫目录)**:首次
27
28
  * readdirSync 后进程内递增预留,目录被清理(retention)后取下界重扫一次(防御)。
@@ -35,7 +36,8 @@
35
36
  * 记录不输出 round 字段(全设计无 round 定义——删——不发明无来源字段)。
36
37
  */
37
38
  import { readdirSync, existsSync } from "node:fs"
38
- import { appendFile, mkdir, readdir, stat, unlink, rmdir } from "node:fs/promises"
39
+ import { appendFile, mkdir } from "node:fs/promises"
40
+ import { cleanupTraces as cleanupTracesImpl } from "./trace-cleanup.mjs"
39
41
  import { join } from "node:path"
40
42
  import { createHash } from "node:crypto"
41
43
  import { configDir, loadConfig } from "../config.mjs"
@@ -87,17 +89,33 @@ export function tracesDirFor(dateStr) {
87
89
  return join(tracesRoot(), dateStr)
88
90
  }
89
91
 
90
- // ─── 容量 / 清理策略(§2.5 #116 / A21 已裁 · 按建议——取 VSC 侧)───────────────
92
+ // ─── 容量 / 清理策略(§2.5 #116 / A21 已裁 · 按建议——取 VSC 侧)(§6.4 判据面)─────────
91
93
  // 完整落盘:不截断、不丢行(无单消息/单记录额度,无在途丢弃);清理 = 每写一次 prune
92
- // (写盘成功后顺带 cleanupTraces——与写盘同在 fire-and-forget 异步体内,永不阻塞 chat)。
94
+ // (写盘成功后顺带清理——与写盘同在 fire-and-forget 异步体内,永不阻塞 chat)。
93
95
 
94
- // 进程内 seq 缓存(§23.3.2:dir → 已见/已预留 max——首次 readdirSync 后递增预留;
95
- // 目录被清理(retention)后取下界重扫一次(防御))。
96
+ // 进程内 seq 缓存(§23.3.2:dir → 已预留 max;首次读盘后递增预留,目录被清理后取下界重扫一次)。
96
97
  const _seqCache = new Map()
98
+ // 每写 prune 节流(D-TR12 · §6.4):窗 = 10 分钟 + 在飞合并(同窗 3 连写 ⇒ 扫描 ≤1 次)。
99
+ export const PRUNE_THROTTLE_MS = 10 * 60_000
100
+ let _pruneAt = 0, _pruneInFlight = null
97
101
 
98
- /** 测试缝:清进程内状态(seqCache)。 */
102
+ /** 测试缝:清进程内状态(seqCache + 节流窗/在飞——**跨用例残留**,用例须显式复位)。 */
99
103
  export function _resetTraceStateForTest() {
100
104
  _seqCache.clear()
105
+ _pruneAt = 0
106
+ _pruneInFlight = null
107
+ }
108
+
109
+ /** 每写 prune(`recordChatTrace` 写盘成功后调用):窗内 / 在飞 ⇒ 不发起新扫描。
110
+ * 返回 = 新扫描 promise(首次发起)/ `false`(窗内 / 在飞——不重复扫);`now` = 测试注入缝。 */
111
+ export function maybePruneTraces({ dir = tracesRoot(), retentionHours = 24, now = Date.now() } = {}) {
112
+ if (_pruneInFlight) return false // 在飞合并:同窗并发不重复发起扫描
113
+ if (now - _pruneAt < PRUNE_THROTTLE_MS) return false
114
+ _pruneAt = now
115
+ _pruneInFlight = cleanupTracesImpl({ dir, retentionHours })
116
+ .catch(() => 0) // 清理失败静默——不影响写盘(fire-and-forget 同纪律)
117
+ .finally(() => { _pruneInFlight = null })
118
+ return _pruneInFlight
101
119
  }
102
120
 
103
121
  function scanMaxSeq(dir) {
@@ -266,38 +284,16 @@ export function recordChatTrace(provider, opts = {}, result = null, error = null
266
284
  await _traceHooks.append(join(dir, `${sessionKey}-${seq}.jsonl`), record + "\n")
267
285
  let retentionHours = 24
268
286
  try { retentionHours = loadConfig()?.traces?.retentionHours ?? 24 } catch { /* 默认 24h */ }
269
- await cleanupTraces({ dir: tracesRoot(), retentionHours })
287
+ await maybePruneTraces({ dir: tracesRoot(), retentionHours }) // D-TR12:每写节流(窗内 ⇒ false 即返)
270
288
  } catch {
271
289
  // F-TR3:落盘/prune 失败静默降级——不抛错、不阻塞 chat() 返回
272
290
  }
273
291
  })()
274
292
  }
275
293
 
276
- /**
277
- * D-TR10(用户裁定——发布隐私 + 磁盘卫生):清理——删除 traces 根下
278
- * mtime 超过保留期的轨迹文件(保留期 = config.traces.retentionHours,默认 24h);
279
- * 删空的日期目录(YYYY-MM-DD)。目录里非 .jsonl 文件不碰。
280
- * §2.5 #116 / A21(已裁 · 按建议):调用点 = 每写一次 prune(`recordChatTrace` 写盘成功
281
- * 后顺带调用——fire-and-forget 异步体内);CLI 启动点的启动清理保留(本端不动)。
282
- * 返回删除文件数。
283
- */
284
- export async function cleanupTraces({ dir = tracesRoot(), retentionHours = 24 } = {}) {
285
- const cutoff = Date.now() - retentionHours * 3_600_000
286
- let days
287
- try { days = await readdir(dir) } catch { return 0 } // 目录不存在/不可读 → 无事可做
288
- let removed = 0
289
- for (const day of days) {
290
- const dayDir = join(dir, day)
291
- try { if (!(await stat(dayDir)).isDirectory()) continue } catch { continue }
292
- let names
293
- try { names = await readdir(dayDir) } catch { continue }
294
- for (const n of names) {
295
- if (!n.endsWith(".jsonl")) continue
296
- try {
297
- if ((await stat(join(dayDir, n))).mtimeMs < cutoff) { await unlink(join(dayDir, n)); removed++ }
298
- } catch { /* 单个文件失败不影响其余 */ }
299
- }
300
- try { if ((await readdir(dayDir)).length === 0) await rmdir(dayDir) } catch {}
301
- }
302
- return removed
294
+ /** D-TR10 清理面(D-TR11 目录级三段梯)——实现外提 `traces/trace-cleanup.mjs`;此处包装保
295
+ * 既有 import 面与缺省 `dir`(`tracesRoot()` 单源——测试经 `THINCODER_TRACES_DIR` 隔离)。
296
+ * 调用点 = 每写 prune(经 `maybePruneTraces`)+ 启动清理(壳侧);返回删除文件数。 */
297
+ export function cleanupTraces({ dir = tracesRoot(), retentionHours = 24 } = {}) {
298
+ return cleanupTracesImpl({ dir, retentionHours })
303
299
  }