thincoder 0.12.20 → 0.12.21
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/tui/startup.mjs +29 -5
package/package.json
CHANGED
package/src/tui/startup.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { listSlots } from "../session.mjs"
|
|
2
|
-
import { sliceByWidth } from "./render.mjs"
|
|
3
2
|
import { ansi, C } from "./ansi.mjs"
|
|
4
3
|
|
|
5
4
|
/** Lazy history window (parity with VS Code HISTORY_PAGE_SIZE): first paint loads
|
|
@@ -15,6 +14,16 @@ export const HISTORY_PAGE_MESSAGES = 50
|
|
|
15
14
|
*/
|
|
16
15
|
export function historyToLines(history, startIdx, endIdx) {
|
|
17
16
|
const lines = []
|
|
17
|
+
// Cross-page turn state: if the message BEFORE this page is a tool/assistant,
|
|
18
|
+
// the page starts mid-turn and must NOT emit a fresh "❯ ThinCoder:" label
|
|
19
|
+
// (a turn gets ONE label in the live run; history stores one assistant
|
|
20
|
+
// message per LLM call, so a multi-call turn would otherwise paint a label
|
|
21
|
+
// on every segment — the reported "why so many ❯ ThinCoder:" bug).
|
|
22
|
+
let inTurn = false
|
|
23
|
+
if (startIdx > 0) {
|
|
24
|
+
const prev = history[startIdx - 1]
|
|
25
|
+
inTurn = prev?.role === "assistant" || prev?.role === "tool"
|
|
26
|
+
}
|
|
18
27
|
for (let i = startIdx; i < endIdx; i++) {
|
|
19
28
|
const m = history[i]
|
|
20
29
|
if (m.role === "user") {
|
|
@@ -22,15 +31,30 @@ export function historyToLines(history, startIdx, endIdx) {
|
|
|
22
31
|
if (lines.length > 0) lines.push({ text: "", color: C.dim })
|
|
23
32
|
lines.push({ text: "❯ You:", color: ansi.bold + C.user })
|
|
24
33
|
if (typeof m.content === "string" && m.content) lines.push({ text: m.content, color: C.text })
|
|
34
|
+
inTurn = false
|
|
25
35
|
} else if (m.role === "assistant") {
|
|
26
|
-
if (
|
|
27
|
-
|
|
36
|
+
if (!inTurn) {
|
|
37
|
+
// Turn start — the only place the assistant label is emitted.
|
|
38
|
+
if (lines.length > 0) lines.push({ text: "", color: C.dim })
|
|
39
|
+
lines.push({ text: "❯ ThinCoder:", color: ansi.bold + C.assistant })
|
|
40
|
+
}
|
|
41
|
+
inTurn = true
|
|
42
|
+
// Reasoning restored as dim lines (folded by the consecutive-dim rule when
|
|
43
|
+
// long) — matches the live thinking stream instead of vanishing on restore.
|
|
44
|
+
const reasoning = m.reasoning_content ?? m.reasoning
|
|
45
|
+
if (typeof reasoning === "string" && reasoning.trim()) {
|
|
46
|
+
for (const line of reasoning.split("\n")) lines.push({ text: " " + line, color: C.dim })
|
|
47
|
+
}
|
|
28
48
|
if (typeof m.content === "string" && m.content) lines.push({ text: m.content, color: C.text })
|
|
29
49
|
for (const tc of m.tool_calls ?? []) {
|
|
30
50
|
const toolResult = history[i + 1]
|
|
31
51
|
const hasResult = toolResult?.role === "tool" && toolResult?.tool_call_id === tc.id
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
lines.push({ text: ` [tool] ${tc.function?.name ?? "?"}`, color: C.tool })
|
|
53
|
+
if (hasResult && String(toolResult.content).trim()) {
|
|
54
|
+
// FULL tool result as dim lines (auto-folded when > 8) — the old
|
|
55
|
+
// first-line-only summary made restore feel nothing like the live run.
|
|
56
|
+
for (const line of String(toolResult.content).split("\n")) lines.push({ text: " " + line, color: C.dim })
|
|
57
|
+
}
|
|
34
58
|
}
|
|
35
59
|
}
|
|
36
60
|
}
|