leedab 0.2.0 → 0.2.2
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/dist/dashboard/routes.js
CHANGED
|
@@ -65,7 +65,8 @@ export function createRoutes(config) {
|
|
|
65
65
|
result.text ??
|
|
66
66
|
result.content ??
|
|
67
67
|
stdout.trim();
|
|
68
|
-
|
|
68
|
+
const thoughts = await readLatestThoughts(stateDir, session ?? "console");
|
|
69
|
+
json(res, { reply, thoughts, session: session ?? "console" });
|
|
69
70
|
}
|
|
70
71
|
catch {
|
|
71
72
|
json(res, {
|
|
@@ -88,6 +89,7 @@ export function createRoutes(config) {
|
|
|
88
89
|
const jsonlPath = resolve(sessionsDir, `${session}.jsonl`);
|
|
89
90
|
const raw = await readFile(jsonlPath, "utf-8");
|
|
90
91
|
const messages = [];
|
|
92
|
+
let pendingThoughts = [];
|
|
91
93
|
for (const line of raw.split("\n")) {
|
|
92
94
|
if (!line.trim())
|
|
93
95
|
continue;
|
|
@@ -99,11 +101,16 @@ export function createRoutes(config) {
|
|
|
99
101
|
if (!msg || (msg.role !== "user" && msg.role !== "assistant"))
|
|
100
102
|
continue;
|
|
101
103
|
let text = "";
|
|
104
|
+
const theseThoughts = [];
|
|
102
105
|
if (Array.isArray(msg.content)) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
for (const b of msg.content) {
|
|
107
|
+
if (b?.type === "text" && typeof b.text === "string") {
|
|
108
|
+
text += (text ? "\n" : "") + b.text;
|
|
109
|
+
}
|
|
110
|
+
else if (b?.type === "thinking" && typeof b.thinking === "string") {
|
|
111
|
+
theseThoughts.push(b.thinking);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
107
114
|
}
|
|
108
115
|
else if (typeof msg.content === "string") {
|
|
109
116
|
text = msg.content;
|
|
@@ -111,12 +118,22 @@ export function createRoutes(config) {
|
|
|
111
118
|
// Strip all OpenClaw "(untrusted metadata)" blocks and timestamp prefix
|
|
112
119
|
text = text.replace(/^(\w[\w\s]*\(untrusted metadata\):\n```json\n[\s\S]*?\n```\n\n)+/, "");
|
|
113
120
|
text = text.replace(/^\[[\w]{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2} \w+\] /, "");
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
if (msg.role === "assistant") {
|
|
122
|
+
// Accumulate thoughts across tool-use turns until we see text
|
|
123
|
+
pendingThoughts.push(...theseThoughts);
|
|
124
|
+
if (text) {
|
|
125
|
+
messages.push({
|
|
126
|
+
role: msg.role,
|
|
127
|
+
text,
|
|
128
|
+
thoughts: pendingThoughts.length ? pendingThoughts : undefined,
|
|
129
|
+
timestamp: entry.timestamp,
|
|
130
|
+
});
|
|
131
|
+
pendingThoughts = [];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (text) {
|
|
135
|
+
messages.push({ role: msg.role, text, timestamp: entry.timestamp });
|
|
136
|
+
pendingThoughts = [];
|
|
120
137
|
}
|
|
121
138
|
}
|
|
122
139
|
catch { }
|
|
@@ -703,6 +720,49 @@ function parseSessionFirstMessage(text) {
|
|
|
703
720
|
userText = userText.replace(/\nUntrusted context \(metadata[\s\S]*$/, "");
|
|
704
721
|
return { senderName, cleanText: userText.trim() };
|
|
705
722
|
}
|
|
723
|
+
/**
|
|
724
|
+
* Walk the session JSONL backwards and collect `thinking` block content from
|
|
725
|
+
* the most recent assistant turn(s) since the last user message. Returns an
|
|
726
|
+
* ordered list of thought strings (oldest → newest).
|
|
727
|
+
*/
|
|
728
|
+
async function readLatestThoughts(stateDir, session) {
|
|
729
|
+
try {
|
|
730
|
+
const jsonlPath = resolve(stateDir, "agents", "main", "sessions", `${session}.jsonl`);
|
|
731
|
+
const raw = await readFile(jsonlPath, "utf-8");
|
|
732
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
733
|
+
const thoughts = [];
|
|
734
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
735
|
+
let entry;
|
|
736
|
+
try {
|
|
737
|
+
entry = JSON.parse(lines[i]);
|
|
738
|
+
}
|
|
739
|
+
catch {
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (entry.type !== "message")
|
|
743
|
+
continue;
|
|
744
|
+
const msg = entry.message;
|
|
745
|
+
if (!msg)
|
|
746
|
+
continue;
|
|
747
|
+
// Stop once we hit the user message that triggered this turn.
|
|
748
|
+
if (msg.role === "user")
|
|
749
|
+
break;
|
|
750
|
+
if (msg.role !== "assistant")
|
|
751
|
+
continue;
|
|
752
|
+
if (!Array.isArray(msg.content))
|
|
753
|
+
continue;
|
|
754
|
+
for (const block of msg.content) {
|
|
755
|
+
if (block?.type === "thinking" && typeof block.thinking === "string") {
|
|
756
|
+
thoughts.unshift(block.thinking);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return thoughts;
|
|
761
|
+
}
|
|
762
|
+
catch {
|
|
763
|
+
return [];
|
|
764
|
+
}
|
|
765
|
+
}
|
|
706
766
|
function json(res, data, status = 200) {
|
|
707
767
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
708
768
|
res.end(JSON.stringify(data));
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<style>
|
|
12
12
|
.page-header { display:flex; align-items:center; justify-content:space-between; padding:0 20px; height:52px; border-bottom:1px solid var(--border); background:var(--bg); }
|
|
13
13
|
.page-header-left { display:flex; align-items:center; gap:10px; }
|
|
14
|
-
.page-header-title { font-size:
|
|
14
|
+
.page-header-title { font-size:13px; font-weight:700; letter-spacing:-0.01em; }
|
|
15
15
|
.page-nav { display:flex; align-items:center; gap:2px; }
|
|
16
16
|
.page-nav a, .page-nav button { color:var(--text-dim); text-decoration:none; display:flex; align-items:center; gap:5px; padding:6px 12px; border-radius:8px; font-size:13px; font-weight:450; transition:all 0.15s; background:none; border:none; cursor:pointer; font-family:inherit; }
|
|
17
17
|
.page-nav a:hover, .page-nav button:hover { color:var(--text-secondary); background:var(--surface-raised); }
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
.header-title {
|
|
78
|
-
font-size:
|
|
79
|
-
font-weight:
|
|
78
|
+
font-size: 13px;
|
|
79
|
+
font-weight: 700;
|
|
80
80
|
letter-spacing: -0.01em;
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -302,6 +302,39 @@
|
|
|
302
302
|
font-size: 13px;
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
/* Thoughts (collapsible, shown before final answer) */
|
|
306
|
+
.thoughts {
|
|
307
|
+
margin-bottom: 8px;
|
|
308
|
+
border: 1px solid var(--border);
|
|
309
|
+
border-radius: var(--radius);
|
|
310
|
+
background: var(--surface);
|
|
311
|
+
font-size: 13px;
|
|
312
|
+
}
|
|
313
|
+
.thoughts > summary {
|
|
314
|
+
cursor: pointer;
|
|
315
|
+
list-style: none;
|
|
316
|
+
padding: 8px 12px;
|
|
317
|
+
color: var(--text-dim);
|
|
318
|
+
display: flex;
|
|
319
|
+
align-items: center;
|
|
320
|
+
gap: 6px;
|
|
321
|
+
user-select: none;
|
|
322
|
+
}
|
|
323
|
+
.thoughts > summary::-webkit-details-marker { display: none; }
|
|
324
|
+
.thoughts > summary::before {
|
|
325
|
+
content: "▸";
|
|
326
|
+
font-size: 10px;
|
|
327
|
+
transition: transform 0.15s;
|
|
328
|
+
}
|
|
329
|
+
.thoughts[open] > summary::before { transform: rotate(90deg); }
|
|
330
|
+
.thoughts .thought {
|
|
331
|
+
padding: 8px 12px;
|
|
332
|
+
border-top: 1px solid var(--border);
|
|
333
|
+
color: var(--text-secondary);
|
|
334
|
+
white-space: pre-wrap;
|
|
335
|
+
line-height: 1.5;
|
|
336
|
+
}
|
|
337
|
+
|
|
305
338
|
.thinking-spinner {
|
|
306
339
|
width: 14px;
|
|
307
340
|
height: 14px;
|
|
@@ -676,7 +709,7 @@
|
|
|
676
709
|
if (data.error) {
|
|
677
710
|
appendMessage("Something went wrong. Is the gateway running?", "agent");
|
|
678
711
|
} else {
|
|
679
|
-
appendMessage(data.reply, "agent");
|
|
712
|
+
appendMessage(data.reply, "agent", data.thoughts);
|
|
680
713
|
}
|
|
681
714
|
} catch (err) {
|
|
682
715
|
thinkingEl.remove();
|
|
@@ -695,7 +728,7 @@
|
|
|
695
728
|
if (emptyState) emptyState.remove();
|
|
696
729
|
for (const m of messages) {
|
|
697
730
|
const type = m.role === "user" ? "user" : "agent";
|
|
698
|
-
appendMessage(m.text || m.content || "", type);
|
|
731
|
+
appendMessage(m.text || m.content || "", type, m.thoughts);
|
|
699
732
|
}
|
|
700
733
|
}
|
|
701
734
|
} catch {}
|
|
@@ -705,7 +738,7 @@
|
|
|
705
738
|
return new Date().toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
|
|
706
739
|
}
|
|
707
740
|
|
|
708
|
-
function appendMessage(text, type) {
|
|
741
|
+
function appendMessage(text, type, thoughts) {
|
|
709
742
|
const row = document.createElement("div");
|
|
710
743
|
row.className = `msg-row ${type}`;
|
|
711
744
|
|
|
@@ -728,6 +761,23 @@
|
|
|
728
761
|
meta.className = "msg-meta";
|
|
729
762
|
meta.innerHTML = `<span class="msg-name">${type === "agent" ? "LeedAB" : userName}</span><span class="msg-time">${now()}</span>`;
|
|
730
763
|
|
|
764
|
+
content.appendChild(meta);
|
|
765
|
+
|
|
766
|
+
if (type === "agent" && Array.isArray(thoughts) && thoughts.length) {
|
|
767
|
+
const details = document.createElement("details");
|
|
768
|
+
details.className = "thoughts";
|
|
769
|
+
const summary = document.createElement("summary");
|
|
770
|
+
summary.textContent = `Thought for a moment · ${thoughts.length} step${thoughts.length === 1 ? "" : "s"}`;
|
|
771
|
+
details.appendChild(summary);
|
|
772
|
+
for (const t of thoughts) {
|
|
773
|
+
const div = document.createElement("div");
|
|
774
|
+
div.className = "thought";
|
|
775
|
+
div.textContent = t;
|
|
776
|
+
details.appendChild(div);
|
|
777
|
+
}
|
|
778
|
+
content.appendChild(details);
|
|
779
|
+
}
|
|
780
|
+
|
|
731
781
|
const bubble = document.createElement("div");
|
|
732
782
|
bubble.className = `msg-bubble ${type}`;
|
|
733
783
|
if (type === "agent") {
|
|
@@ -736,7 +786,6 @@
|
|
|
736
786
|
bubble.textContent = text;
|
|
737
787
|
}
|
|
738
788
|
|
|
739
|
-
content.appendChild(meta);
|
|
740
789
|
content.appendChild(bubble);
|
|
741
790
|
row.appendChild(avatar);
|
|
742
791
|
row.appendChild(content);
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<style>
|
|
12
12
|
.page-header { display:flex; align-items:center; justify-content:space-between; padding:0 20px; height:52px; border-bottom:1px solid var(--border); background:var(--bg); }
|
|
13
13
|
.page-header-left { display:flex; align-items:center; gap:10px; }
|
|
14
|
-
.page-header-title { font-size:
|
|
14
|
+
.page-header-title { font-size:13px; font-weight:700; letter-spacing:-0.01em; }
|
|
15
15
|
.page-nav { display:flex; align-items:center; gap:2px; }
|
|
16
16
|
.page-nav a, .page-nav button { color:var(--text-dim); text-decoration:none; display:flex; align-items:center; gap:5px; padding:6px 12px; border-radius:8px; font-size:13px; font-weight:450; transition:all 0.15s; background:none; border:none; cursor:pointer; font-family:inherit; }
|
|
17
17
|
.page-nav a:hover, .page-nav button:hover { color:var(--text-secondary); background:var(--surface-raised); }
|
|
@@ -23,10 +23,6 @@
|
|
|
23
23
|
<a href="/" class="page-header-title" style="text-decoration:none;color:inherit">LeedAB</a>
|
|
24
24
|
</div>
|
|
25
25
|
<div class="page-nav">
|
|
26
|
-
<button class="theme-btn" onclick="toggleTheme()" title="Toggle theme">
|
|
27
|
-
<svg id="theme-icon-sun" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
|
|
28
|
-
<svg id="theme-icon-moon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
|
29
|
-
</button>
|
|
30
26
|
</div>
|
|
31
27
|
</div>
|
|
32
28
|
|
|
@@ -106,22 +102,6 @@
|
|
|
106
102
|
function initTheme() {
|
|
107
103
|
const saved = localStorage.getItem("leedab-theme") || "dark";
|
|
108
104
|
document.documentElement.setAttribute("data-theme", saved);
|
|
109
|
-
updateThemeIcon(saved);
|
|
110
|
-
}
|
|
111
|
-
function toggleTheme() {
|
|
112
|
-
const current = document.documentElement.getAttribute("data-theme") || "dark";
|
|
113
|
-
const next = current === "dark" ? "light" : "dark";
|
|
114
|
-
document.documentElement.setAttribute("data-theme", next);
|
|
115
|
-
localStorage.setItem("leedab-theme", next);
|
|
116
|
-
updateThemeIcon(next);
|
|
117
|
-
}
|
|
118
|
-
function updateThemeIcon(theme) {
|
|
119
|
-
const sun = document.getElementById("theme-icon-sun");
|
|
120
|
-
const moon = document.getElementById("theme-icon-moon");
|
|
121
|
-
if (sun && moon) {
|
|
122
|
-
sun.style.display = theme === "dark" ? "block" : "none";
|
|
123
|
-
moon.style.display = theme === "light" ? "block" : "none";
|
|
124
|
-
}
|
|
125
105
|
}
|
|
126
106
|
initTheme();
|
|
127
107
|
|