clideck 1.25.3 → 1.25.4
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/public/js/app.js +2 -3
- package/public/js/terminals.js +15 -0
- package/transcript.js +23 -3
package/package.json
CHANGED
package/public/js/app.js
CHANGED
|
@@ -90,6 +90,7 @@ function connect() {
|
|
|
90
90
|
}
|
|
91
91
|
break;
|
|
92
92
|
}
|
|
93
|
+
/* [OLD-STATUS] I/O burst heuristic — replaced by onRender detection in terminals.js
|
|
93
94
|
case 'stats': {
|
|
94
95
|
for (const [sid, st] of Object.entries(msg.stats)) {
|
|
95
96
|
const entry = state.terms.get(sid);
|
|
@@ -101,12 +102,9 @@ function connect() {
|
|
|
101
102
|
const userTyping = (st.rawRateIn || 0) > 0 && (st.rawRateIn || 0) < 50;
|
|
102
103
|
entry.prevBurst = st.burstMs || 0;
|
|
103
104
|
|
|
104
|
-
// Working: burst increasing + net >= 800B + no typing
|
|
105
105
|
const isWorking = burstUp && net >= 800 && !userTyping;
|
|
106
|
-
// Idle: burst not increasing + net < 800B
|
|
107
106
|
const isIdle = !burstUp && net < 800;
|
|
108
107
|
|
|
109
|
-
// Sustain for ~1.5s (2 ticks)
|
|
110
108
|
if (isWorking) entry.workTicks = (entry.workTicks || 0) + 1;
|
|
111
109
|
else entry.workTicks = 0;
|
|
112
110
|
if (isIdle) entry.idleTicks = (entry.idleTicks || 0) + 1;
|
|
@@ -122,6 +120,7 @@ function connect() {
|
|
|
122
120
|
}
|
|
123
121
|
break;
|
|
124
122
|
}
|
|
123
|
+
[OLD-STATUS] */
|
|
125
124
|
case 'transcript.cache':
|
|
126
125
|
state.transcriptCache = msg.cache;
|
|
127
126
|
for (const [id, text] of Object.entries(msg.cache)) {
|
package/public/js/terminals.js
CHANGED
|
@@ -338,6 +338,21 @@ export function addTerminal(id, name, themeId, commandId, projectId, muted, last
|
|
|
338
338
|
term.loadAddon(fit);
|
|
339
339
|
term.onData(data => send({ type: 'input', id, data }));
|
|
340
340
|
|
|
341
|
+
// [RENDER-STATUS] agent working/idle detection via onRender + onWriteParsed
|
|
342
|
+
let _lastTyping = 0, _renderWorking = false, _renderTimer = null, _hasRender = false, _hasParsed = false;
|
|
343
|
+
term.onData(() => { _lastTyping = Date.now(); });
|
|
344
|
+
function _statusTick() {
|
|
345
|
+
if (Date.now() - _lastTyping < 500) return;
|
|
346
|
+
const cmd = state.cfg.commands.find(c => c.id === commandId);
|
|
347
|
+
if (cmd?.bridge) return;
|
|
348
|
+
if (_hasRender && _hasParsed && !_renderWorking) {
|
|
349
|
+
_renderWorking = true;
|
|
350
|
+
send({ type: 'session.statusReport', id, working: true }); setStatus(id, true);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
term.onWriteParsed(() => { _hasParsed = true; _statusTick(); });
|
|
354
|
+
term.onRender(() => { _hasRender = true; _statusTick(); clearTimeout(_renderTimer); _renderTimer = setTimeout(() => { _renderWorking = false; _hasRender = false; _hasParsed = false; send({ type: 'session.statusReport', id, working: false }); setStatus(id, false); }, 2000); });
|
|
355
|
+
|
|
341
356
|
term.open(el);
|
|
342
357
|
attachToTerminal(term);
|
|
343
358
|
let fitted = false, pending = [];
|
package/transcript.js
CHANGED
|
@@ -91,11 +91,17 @@ function flush(id) {
|
|
|
91
91
|
// xterm buffer as rendered by the browser. No diffing, no JSONL — just the
|
|
92
92
|
// clean screen content. Mobile reads this for "last agent message".
|
|
93
93
|
function storeBuffer(id, lines) {
|
|
94
|
-
const isChrome = t => !t
|
|
94
|
+
const isChrome = t => !t
|
|
95
|
+
|| /^[─━═\u2500-\u257f]+$/.test(t) // box-drawing horizontal lines
|
|
96
|
+
|| /^[▀▄█▌▐░▒▓╭╮╰╯│╔╗╚╝║]+$/.test(t) // block elements, box corners, vertical bars
|
|
97
|
+
|| (/[█▀▄▌▐░▒▓]/.test(t) && /^[█▀▄▌▐░▒▓\s]+$/.test(t)) // ASCII art (blocks + whitespace, e.g. logos)
|
|
98
|
+
|| /^[❯>$%#]\s*$/.test(t) // bare prompt markers
|
|
99
|
+
|| /^(esc to interrupt|\? for shortcuts)$/i.test(t); // Claude Code chrome
|
|
95
100
|
const filtered = lines.filter(l => !isChrome(l.trim()));
|
|
96
101
|
while (filtered.length && !filtered[filtered.length - 1].trim()) filtered.pop();
|
|
97
102
|
const screenPath = join(DIR, `${id}.screen`);
|
|
98
103
|
if (filtered.length) writeFileSync(screenPath, filtered.join('\n'));
|
|
104
|
+
else try { unlinkSync(screenPath); } catch {}
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
// Read the clean screen snapshot for a session (if available).
|
|
@@ -111,7 +117,7 @@ const agentParsers = {
|
|
|
111
117
|
const turns = [];
|
|
112
118
|
let current = null;
|
|
113
119
|
for (const line of lines) {
|
|
114
|
-
const m = line.match(/^(?:[│ ]\s*)?([❯›]|[
|
|
120
|
+
const m = line.match(/^(?:[│ ]\s*)?([❯›]|[⏺•●])\s(.*)$/);
|
|
115
121
|
if (m) {
|
|
116
122
|
if (current) { current.text = current.text.replace(/\n+$/, ''); turns.push(current); }
|
|
117
123
|
current = { role: m[1] === '❯' || m[1] === '›' ? 'user' : 'agent', text: m[2] };
|
|
@@ -146,9 +152,20 @@ const agentParsers = {
|
|
|
146
152
|
return turns.length >= 2 ? turns : null;
|
|
147
153
|
},
|
|
148
154
|
'gemini-cli': (lines) => {
|
|
155
|
+
const geminiChrome = t => {
|
|
156
|
+
const s = t.trim();
|
|
157
|
+
return /^shift\+tab to accept/i.test(s)
|
|
158
|
+
|| /^(Type your message|@path\/to\/)/i.test(s)
|
|
159
|
+
|| /^(\/\w+ |no sandbox|\/model )/i.test(s)
|
|
160
|
+
|| /^[~\/\\].*\(main[*]?\)\s*$/i.test(s)
|
|
161
|
+
|| /^(Logged in with|Plan:|Tips for getting started)/i.test(s)
|
|
162
|
+
|| /^\d+\.\s+(Ask questions|Be specific|Create GEMINI)/i.test(s)
|
|
163
|
+
|| /^ℹ\s/.test(s);
|
|
164
|
+
};
|
|
149
165
|
const turns = [];
|
|
150
166
|
let current = null;
|
|
151
167
|
for (const line of lines) {
|
|
168
|
+
if (geminiChrome(line)) continue;
|
|
152
169
|
const isUser = line.startsWith(' > ');
|
|
153
170
|
const isAgent = line.startsWith('✦ ');
|
|
154
171
|
if (isUser || isAgent) {
|
|
@@ -212,7 +229,10 @@ function getScreenTurns(id, agent) {
|
|
|
212
229
|
if (!screen) return null;
|
|
213
230
|
const lines = screen.split('\n');
|
|
214
231
|
const parser = agentParsers[agent];
|
|
215
|
-
|
|
232
|
+
const turns = parser ? parser(lines) : anchorParse(id, lines);
|
|
233
|
+
// Drop trailing user turn — it's the empty prompt or unanswered input
|
|
234
|
+
if (turns?.length && turns[turns.length - 1].role === 'user') turns.pop();
|
|
235
|
+
return turns?.length >= 2 ? turns : null;
|
|
216
236
|
}
|
|
217
237
|
|
|
218
238
|
function getLastTurns(id, n) {
|