thincoder 0.12.58 → 0.12.59

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 (114) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/README.md +1 -1
  3. package/bin/thincoder.mjs +8 -0
  4. package/package.json +1 -1
  5. package/src/acp/bridge.mjs +132 -26
  6. package/src/advisor/messages.mjs +34 -1
  7. package/src/advisor/run.mjs +89 -51
  8. package/src/advisor.mjs +15 -7
  9. package/src/agent/dispatch.mjs +91 -14
  10. package/src/agent/helpers.mjs +35 -4
  11. package/src/agent/setup.mjs +90 -19
  12. package/src/agent/spawn-child.mjs +25 -0
  13. package/src/agent-tools/advisor.mjs +24 -2
  14. package/src/agent-tools/consult.mjs +37 -6
  15. package/src/agent-tools/eng.mjs +2 -1
  16. package/src/agent-tools/goal.mjs +11 -1
  17. package/src/agent-tools/read-history.mjs +160 -0
  18. package/src/agent-tools/settings.mjs +162 -0
  19. package/src/agent-tools/skill.mjs +2 -1
  20. package/src/agent-tools/subagent-actions.mjs +432 -0
  21. package/src/agent-tools/subagent-async.mjs +427 -0
  22. package/src/agent-tools/subagent-scheduler.mjs +319 -0
  23. package/src/agent-tools/subagent.mjs +467 -193
  24. package/src/agent-tools/task.mjs +4 -3
  25. package/src/agent-tools/timer.mjs +9 -4
  26. package/src/agent-tools/verify.mjs +161 -49
  27. package/src/agent-tools.mjs +1 -0
  28. package/src/agent.mjs +161 -125
  29. package/src/auto-think.mjs +14 -0
  30. package/src/cli/make-agent.mjs +2 -1
  31. package/src/cli/permission.mjs +8 -1
  32. package/src/config.mjs +5 -0
  33. package/src/context.mjs +87 -27
  34. package/src/distill.mjs +19 -1
  35. package/src/escape.mjs +6 -4
  36. package/src/log.mjs +195 -0
  37. package/src/memory/code-sync.mjs +1 -1
  38. package/src/memory/core.mjs +126 -0
  39. package/src/memory/docs.mjs +196 -87
  40. package/src/memory.mjs +1 -1
  41. package/src/model-specs.mjs +15 -1
  42. package/src/prompts/advisor-design.md +46 -0
  43. package/src/prompts/advisor-round1.md +49 -2
  44. package/src/prompts/advisor-round2.md +47 -0
  45. package/src/prompts/advisor-round3.md +47 -0
  46. package/src/prompts/coder.md +22 -0
  47. package/src/prompts/consult-base.md +13 -0
  48. package/src/prompts/discipline.md +10 -5
  49. package/src/prompts/eng-coder.md +2 -2
  50. package/src/prompts/engineering-sub.md +23 -1
  51. package/src/prompts/engineering.md +106 -56
  52. package/src/prompts/explore.md +1 -2
  53. package/src/prompts/main.md +11 -6
  54. package/src/prompts/methodology-template.md +14 -0
  55. package/src/prompts/system.md +4 -2
  56. package/src/provider/core.mjs +56 -2
  57. package/src/tools/apply_patch.md +3 -1
  58. package/src/tools/bash.md +1 -1
  59. package/src/tools/delete.md +1 -0
  60. package/src/tools/edit-batch.mjs +31 -43
  61. package/src/tools/edit-diff.mjs +265 -0
  62. package/src/tools/edit.md +10 -8
  63. package/src/tools/execute.md +7 -7
  64. package/src/tools/execute.mjs +24 -20
  65. package/src/tools/file.mjs +18 -68
  66. package/src/tools/file_ops.md +2 -1
  67. package/src/tools/get_current_time.md +3 -1
  68. package/src/tools/hashline_edit.md +2 -0
  69. package/src/tools/index.mjs +3 -2
  70. package/src/tools/insert_after.md +2 -1
  71. package/src/tools/lint.md +2 -0
  72. package/src/tools/lsp.md +4 -1
  73. package/src/tools/patch.mjs +84 -13
  74. package/src/tools/pdf-parse-text.mjs +497 -0
  75. package/src/tools/pdf-parse-xref.mjs +499 -0
  76. package/src/tools/pdf.mjs +155 -0
  77. package/src/tools/question.md +2 -1
  78. package/src/tools/read.md +1 -0
  79. package/src/tools/read_pdf.md +21 -0
  80. package/src/tools/repomap.mjs +1 -1
  81. package/src/tools/shared.mjs +4 -12
  82. package/src/tools/system.mjs +6 -21
  83. package/src/tools/tree.md +2 -1
  84. package/src/tools/web.mjs +5 -3
  85. package/src/tools/websearch.md +2 -1
  86. package/src/tools/write.md +2 -0
  87. package/src/traces/trace-store.mjs +224 -0
  88. package/src/tui/agent-turn.mjs +385 -22
  89. package/src/tui/clipboard.mjs +15 -4
  90. package/src/tui/cmd-config.mjs +29 -9
  91. package/src/tui/cmd-extract.mjs +1 -1
  92. package/src/tui/cmd-mcp.mjs +9 -0
  93. package/src/tui/cmd-think.mjs +1 -1
  94. package/src/tui/index.mjs +29 -95
  95. package/src/tui/interaction.mjs +13 -2
  96. package/src/tui/key-handler.mjs +105 -155
  97. package/src/tui/key-modes.mjs +215 -0
  98. package/src/tui/layout.mjs +22 -1
  99. package/src/tui/mouse.mjs +40 -0
  100. package/src/tui/pickers.mjs +11 -3
  101. package/src/tui/render-conversation.mjs +13 -161
  102. package/src/tui/render-frame.mjs +27 -10
  103. package/src/tui/render-loop.mjs +4 -1
  104. package/src/tui/render-segments.mjs +165 -0
  105. package/src/tui/startup.mjs +36 -0
  106. package/src/tui/subagent-blocks.mjs +322 -144
  107. package/src/tui/subagent-panel.mjs +88 -13
  108. package/src/tui/tool-args.mjs +10 -2
  109. package/src/tui/tool-events.mjs +132 -100
  110. package/src/tui/update-notice.mjs +72 -0
  111. package/src/tui/wizard.mjs +36 -6
  112. package/src/agent-tools/escalate.mjs +0 -179
  113. package/src/agent-tools/subagent-check.mjs +0 -107
  114. package/src/tools/exec-prelude.mjs +0 -84
@@ -0,0 +1,165 @@
1
+ /**
2
+ * render-segments.mjs — 对话行三类特殊段渲染(2026-09-03 D-S2 自 render-conversation.mjs 拆出):
3
+ * tool 块 / frozenSubTask 冻结子agent 块 / frozenAdvisor 冻结评审块。各段自带独立 WeakMap
4
+ * 段级缓存(行对象载体系互斥——_toolBlock/_frozenSubTask/_frozenAdvisor 三型不共存于一行,
5
+ * 分支互斥 if——与共享缓存行为等价);签名由各段集中计算(该段输出的所有决定因素,漏一项
6
+ * → 缓存失效不全 → 显示 stale)。buildConvLines 主循环只留三分支 ~3 行调用。
7
+ */
8
+ import { C } from "./ansi.mjs"
9
+ import { formatTables, sanitizeDisplay, sliceByWidth, wrapText } from "./render.mjs"
10
+ import {
11
+ foldTailLines, isExpanded, renderBlockTimeline, renderExpandedBlock, renderFoldedHead, renderMathAndMarkdown,
12
+ } from "./fold-block.mjs"
13
+ import { styleSubLabelRow } from "./subagent-panel.mjs"
14
+ import { SUBAGENT_ROLES } from "./subagent-blocks.mjs"
15
+
16
+ const _toolSegCache = new WeakMap()
17
+ const _frozenSubCache = new WeakMap()
18
+ const _frozenAdvCache = new WeakMap()
19
+
20
+ /** 工具块段(2026-08-31 段缓存抽出——真实会话 106 个工具块每帧全量 wrap 32ms 的根治)。
21
+ * 返回 conv 行数组(工具块的折叠头+尾/展开窗口+控制行),逻辑与原 L221-258 逐字同构。 */
22
+ function buildToolBlockSeg(state, l, i, cols, maxRows) {
23
+ const b = l._toolBlock
24
+ const foldKey = `tool-${l._lineId ?? i}`
25
+ const out = []
26
+ const status = !b.done
27
+ ? "running"
28
+ : `${b.elapsed !== null ? b.elapsed + "ms" : ""}${b.summary ? (b.elapsed !== null ? " · " : "") + sliceByWidth(b.summary, 50) : ""}`.trim() || "done"
29
+ if (isExpanded(state, foldKey)) {
30
+ const body = []
31
+ const pushWrapped = (raw, color) => {
32
+ for (const w of wrapText(raw, cols - 4)) body.push({ text: " " + w, color, _skipDimFold: true })
33
+ }
34
+ for (const jl of b.argsJson) pushWrapped(jl, C.dim)
35
+ for (const ol of b.output) pushWrapped(ol, C.tool)
36
+ if (b.result) for (const rl of b.result) pushWrapped(rl, C.dim)
37
+ out.push(...renderExpandedBlock({ body, foldKey, state, maxRows, cols, label: `${b.name}${b.roundTag || ""} ${b.argsSummary}`.trim() }))
38
+ } else {
39
+ const headText = sliceByWidth(
40
+ `❯ ${b.name}${b.roundTag || ""}${b.argsSummary ? " " + b.argsSummary : ""} · ${status}`,
41
+ Math.max(20, cols - 2),
42
+ )
43
+ const body = []
44
+ for (const jl of b.argsJson) for (const w of wrapText(jl, cols - 4)) body.push({ text: w, color: C.dim, _skipDimFold: true })
45
+ for (const ol of b.output.slice(-3)) for (const w of wrapText(ol, cols - 4)) body.push({ text: w, color: C.dim, _skipDimFold: true })
46
+ if (b.result) for (const rl of b.result) for (const w of wrapText(rl, cols - 4)) body.push({ text: w, color: C.dim, _skipDimFold: true })
47
+ out.push(...renderFoldedHead({ header: { text: headText, color: C.tool, _foldToggle: foldKey }, body, cols }))
48
+ }
49
+ return out
50
+ }
51
+
52
+ /** 工具块段入口(2026-08-31 段缓存:工具块签名含三缓冲长度+done/elapsed/summary+该块展开态——
53
+ * 流式 append 使 output.length 变 → 失效;real 会话 106 个工具块每帧全量 wrap 实测
54
+ * 32ms(rebuild 40ms 的大头),入缓存后命中只算签名拼接)。 */
55
+ export function toolSeg(state, l, i, cols, maxRows) {
56
+ const b = l._toolBlock
57
+ const toolFoldKey = `tool-${l._lineId ?? i}`
58
+ const tSig = [
59
+ cols, maxRows ?? 0, b.argsJson?.length ?? 0, b.output?.length ?? 0, b.result?.length ?? 0,
60
+ b.done ? 1 : 0, b.elapsed ?? "", b.summary ?? "", b.name ?? "", b.roundTag ?? "",
61
+ l._lineId ?? "", state.foldEnabled === false ? 0 : 1,
62
+ state.expandedBlocks?.has(toolFoldKey) ? 1 : 0, state._foldScroll?.get(toolFoldKey) ?? 0,
63
+ ].join("|")
64
+ const hit = _toolSegCache.get(l)
65
+ if (hit && hit.textRef === b && hit.sig === tSig) return hit.rows
66
+ const rows = buildToolBlockSeg(state, l, i, cols, maxRows)
67
+ _toolSegCache.set(l, { textRef: b, sig: tSig, rows })
68
+ return rows
69
+ }
70
+
71
+ /** 冻结子agent 活动块渲染(§7.2 D4):state.lines 载体 {_frozenSubTask: sub}(subagent-blocks.mjs
72
+ * freezeSubTaskLines 推入)。折叠 = 身份头 + tail 3;展开 = 共享组件(60% 屏幕封顶 + 底部可达
73
+ * 折叠控制)。折叠键 `sub-${key}`——与运行中面板区块同键,折叠态跨冻结边界无缝延续。 */
74
+ function frozenSubTaskLines(state, sub, cols, maxRows) {
75
+ const foldKey = `sub-${sub.key}`
76
+ const elapsed = Math.floor(((sub.doneAt ?? Date.now()) - sub.started) / 1000)
77
+ // §19.5 D-M7b ②: 冻结头保留 sync/async 标识(done 头含历史语义——与 model 标识
78
+ // 同生命周期);仅真实 subagent 角色(compress 冻结等无语义)。整行 dim——
79
+ // 无需 ANSI 注入(running 面板头则套 dim + 恢复行色——subagent-panel.mjs)。
80
+ const isSubRole = SUBAGENT_ROLES.includes(sub.role)
81
+ // §20:冻结的 queued 块(中止清场兜底——interrupted)标 waiting——不误标 sync
82
+ const modePart = isSubRole
83
+ ? ` · ${sub.queued ? "waiting" : (sub.async === true ? "async" : "sync")}`
84
+ : ""
85
+ const modelPart = sub.model ? ` · ${sub.model}` : ""
86
+ const turnPart = sub.maxTurns > 0 ? ` · turn ${sub.turn}/${sub.maxTurns}` : ""
87
+ const errPart = sub.lastError ? ` — ${sub.lastError}` : ""
88
+ const icon = sub.approval ? "⏸" : "✓"
89
+ const verb = sub.stopped ? "stopped" : "done" // §19.5: cancel 冻结标题 "stopped"
90
+ const header = `[${icon} ${sub.key}${modePart}${modelPart} · ${verb} ${elapsed}s${turnPart}${errPart}]`
91
+ const out = []
92
+ if (isExpanded(state, foldKey)) {
93
+ // Expanded: shared component renders blank + ▼ control + full timeline,
94
+ // capped at 60% of the screen with a bottom collapse control.
95
+ const body = renderBlockTimeline(sub.blocks, cols).map(styleSubLabelRow)
96
+ out.push(...renderExpandedBlock({ body, foldKey, state, maxRows, cols, label: "subagent activity" }))
97
+ } else {
98
+ // Folded: the header line itself is the control (▶ affordance), then tail 3.
99
+ out.push({
100
+ text: `▶ ${header} … subagent activity — click to expand`,
101
+ color: C.dim,
102
+ _foldToggle: foldKey,
103
+ })
104
+ for (const line of foldTailLines(sub.blocks)) {
105
+ out.push({ text: `│ ${sliceByWidth(line, cols - 4)}`, color: C.dim, _skipDimFold: true })
106
+ }
107
+ }
108
+ return out
109
+ }
110
+
111
+ /** 冻结子agent 段入口(2026-08-31 段缓存:冻结后内容不变——签名含 sub.key + blocks 计数;
112
+ * 注:foldKey 由位置键升级为 sub.key(loadOlder unshift 后不重绑——sub 自身携带 key)。 */
113
+ export function frozenSubSeg(state, l, i, cols, maxRows) {
114
+ const fKey = `sub-${l._frozenSubTask.key}`
115
+ const fSig = [
116
+ cols, maxRows ?? 0, l._frozenSubTask.key, l._frozenSubTask.blocks?.length ?? 0,
117
+ l._frozenSubTask.done ? 1 : 0, state.foldEnabled === false ? 0 : 1,
118
+ state.expandedBlocks?.has(fKey) ? 1 : 0, state._foldScroll?.get(fKey) ?? 0,
119
+ ].join("|")
120
+ const hit = _frozenSubCache.get(l)
121
+ if (hit && hit.textRef === l._frozenSubTask && hit.sig === fSig) return hit.rows
122
+ const rows = frozenSubTaskLines(state, l._frozenSubTask, cols, maxRows)
123
+ _frozenSubCache.set(l, { textRef: l._frozenSubTask, sig: fSig, rows })
124
+ return rows
125
+ }
126
+
127
+ /** frozenAdvisor 段渲染——冻结后文本不变(markdown 渲染、无 gutter——review 历史惯例);
128
+ * foldKey 由 advisor-done-${i}(位置键)升级为 advisor-done-${_lineId ?? i}(同 long-N
129
+ * 判例——loadOlder unshift 后不重绑)。 */
130
+ function buildFrozenAdvSeg(state, l, i, cols, maxRows) {
131
+ const frozenAdvKey = `advisor-done-${l._lineId ?? i}`
132
+ const out = []
133
+ if (isExpanded(state, frozenAdvKey)) {
134
+ const body = []
135
+ const rendered = renderMathAndMarkdown(sanitizeDisplay(l._frozenAdvisor))
136
+ for (const line of formatTables(rendered, cols - 1)) {
137
+ for (const wrapped of wrapText(line, cols - 1)) {
138
+ body.push({ text: wrapped, color: C.reason, _skipDimFold: true })
139
+ }
140
+ }
141
+ out.push(...renderExpandedBlock({ body, foldKey: frozenAdvKey, state, maxRows, cols, label: "[advisor · review done]" }))
142
+ } else {
143
+ out.push({
144
+ text: `▶ [advisor · review done] … click to expand`,
145
+ color: C.fold,
146
+ _foldToggle: frozenAdvKey,
147
+ })
148
+ }
149
+ return out
150
+ }
151
+
152
+ /** frozenAdvisor 段入口(2026-08-31 段缓存:frozenAdvisor 文本冻结不变——签名含文本长度 + 展开态)。 */
153
+ export function frozenAdvSeg(state, l, i, cols, maxRows) {
154
+ const frozenAdvKey = `advisor-done-${l._lineId ?? i}`
155
+ const aSig = [
156
+ cols, maxRows ?? 0, (l._frozenAdvisor ?? "").length,
157
+ state.foldEnabled === false ? 0 : 1,
158
+ state.expandedBlocks?.has(frozenAdvKey) ? 1 : 0, state._foldScroll?.get(frozenAdvKey) ?? 0,
159
+ ].join("|")
160
+ const hit = _frozenAdvCache.get(l)
161
+ if (hit && hit.textRef === l._frozenAdvisor && hit.sig === aSig) return hit.rows
162
+ const rows = buildFrozenAdvSeg(state, l, i, cols, maxRows)
163
+ _frozenAdvCache.set(l, { textRef: l._frozenAdvisor, sig: aSig, rows })
164
+ return rows
165
+ }
@@ -2,6 +2,7 @@ import { listSlots } from "../session.mjs"
2
2
  import { ansi, C } from "./ansi.mjs"
3
3
  import { describeToolArgs, toolArgsLines } from "./tool-args.mjs"
4
4
  import { slimToolResultForDisplay } from "./tool-events.mjs"
5
+ import { countConvLines } from "./render-conversation.mjs"
5
6
 
6
7
  /** Lazy history window (parity with VS Code HISTORY_PAGE_SIZE): first paint loads
7
8
  * the latest INITIAL_HISTORY_MESSAGES, then PgUp-at-top loads HISTORY_PAGE_MESSAGES
@@ -133,6 +134,41 @@ export function restoreLines(state, history) {
133
134
  state.lines.unshift({ text: `… ${start} more earlier messages (PgUp at top to load)`, color: C.dim })
134
135
  }
135
136
  }
137
+ /** 懒加载更早历史(2026-08-31 用户约定:"滚动到头自动加载"——滚轮/PgUp 到顶皆触发;
138
+ * 2026-08-31 前只挂 PgUp 键 = 违约,滚轮到头无反应)。加载后滚动补偿保持锚定。
139
+ * 2026-09-03 D-S1b 自 index.mjs 迁入(startup 职责本含懒加载历史窗口——TUI.md §1 归属修复):
140
+ * 调用方(index.mjs)= stdin data 滚轮分支 + createKeyHandler ctx(PgUp 分支)。
141
+ * ctx: { agent, state, render } */
142
+ export function createLoadOlder({ agent, state, render }) {
143
+ return () => {
144
+ if (!state._hasOlder) return
145
+ const full = agent._fullHistory ?? []
146
+ const loaded = state._historyLoaded
147
+ const start = Math.max(0, full.length - loaded - HISTORY_PAGE_MESSAGES)
148
+ const end = full.length - loaded
149
+ if (start >= end) return
150
+
151
+ const d = state.dims ? state.dims.get() : {}
152
+ const cols = d.cols ?? ((state.dims?.get() ?? {}).cols ?? (process.stdout.columns || 80))
153
+ const before = countConvLines(state, cols, d.rows ?? (process.stdout.rows || 24))
154
+
155
+ if (state.lines[0]?.text?.startsWith("… ")) state.lines.shift()
156
+ state._lineIdCounter = state._lineIdCounter ?? 0
157
+ const older = historyToLines(full, start, end)
158
+ for (const l of older) l._lineId = ++state._lineIdCounter
159
+ state.lines.unshift(...older)
160
+ state._historyLoaded += end - start
161
+ state._hasOlder = start > 0
162
+ if (state._hasOlder) {
163
+ state.lines.unshift({ text: `… ${start} more earlier messages (scroll to top to load)`, color: C.dim })
164
+ }
165
+
166
+ const after = countConvLines(state, cols, (state.dims?.get() ?? {}).rows ?? (process.stdout.rows || 24))
167
+ state.scroll += Math.max(0, after - before)
168
+ render()
169
+ }
170
+ }
171
+
136
172
  /** Startup screen + session recovery + background indexing.
137
173
  * Extracted from index.mjs.
138
174
  * ctx: { agent, state, opts, pushLine, pushLabel, render, startWizard } */