@agent-link/server 0.1.183 → 0.1.185
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/web/app.js +18 -27
- package/web/css/base.css +19 -0
- package/web/css/file-browser.css +0 -4
- package/web/css/input.css +1 -5
- package/web/css/loop.css +3 -11
- package/web/css/sidebar.css +1 -11
- package/web/css/team.css +9 -9
- package/web/css/tools.css +2 -2
- package/web/modules/appHelpers.js +2 -1
- package/web/modules/backgroundRouting.js +12 -2
- package/web/modules/connection.js +8 -1
- package/web/modules/messageHelpers.js +17 -4
- package/web/modules/streaming.js +10 -6
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -406,26 +406,17 @@ const App = {
|
|
|
406
406
|
});
|
|
407
407
|
setFilePreview(filePreview);
|
|
408
408
|
|
|
409
|
-
// Track mobile state on resize
|
|
410
|
-
let
|
|
409
|
+
// Track mobile state on resize (rAF-throttled)
|
|
410
|
+
let _resizeRafId = 0;
|
|
411
|
+
let _resizeHandler = () => {
|
|
412
|
+
if (_resizeRafId) return;
|
|
413
|
+
_resizeRafId = requestAnimationFrame(() => {
|
|
414
|
+
_resizeRafId = 0;
|
|
415
|
+
isMobile.value = window.innerWidth <= 768;
|
|
416
|
+
});
|
|
417
|
+
};
|
|
411
418
|
window.addEventListener('resize', _resizeHandler);
|
|
412
419
|
|
|
413
|
-
// Fix Chrome mobile: when virtual keyboard dismisses, Chrome doesn't
|
|
414
|
-
// restore scroll position (Safari does this natively). Detect keyboard
|
|
415
|
-
// dismiss via visualViewport height increase and reset page scroll.
|
|
416
|
-
let _vvResizeHandler = null;
|
|
417
|
-
if (window.visualViewport) {
|
|
418
|
-
let _lastVVHeight = window.visualViewport.height;
|
|
419
|
-
_vvResizeHandler = () => {
|
|
420
|
-
const h = window.visualViewport.height;
|
|
421
|
-
if (h > _lastVVHeight && !document.activeElement?.matches?.('input, textarea')) {
|
|
422
|
-
window.scrollTo(0, 0);
|
|
423
|
-
}
|
|
424
|
-
_lastVVHeight = h;
|
|
425
|
-
};
|
|
426
|
-
window.visualViewport.addEventListener('resize', _vvResizeHandler);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
420
|
// Close workdir menu on outside click or Escape
|
|
430
421
|
let _workdirMenuClickHandler = (e) => {
|
|
431
422
|
if (!workdirMenuOpen.value) return;
|
|
@@ -442,10 +433,11 @@ const App = {
|
|
|
442
433
|
|
|
443
434
|
// ── Computed ──
|
|
444
435
|
const hasInput = computed(() => !!(inputText.value.trim() || attachments.value.length > 0));
|
|
436
|
+
const hasPendingQuestion = computed(() => messages.value.some(m => m.role === 'ask-question' && !m.answered));
|
|
445
437
|
const canSend = computed(() =>
|
|
446
|
-
status.value === 'Connected' && hasInput.value && !isCompacting.value
|
|
447
|
-
&& !messages.value.some(m => m.role === 'ask-question' && !m.answered)
|
|
438
|
+
status.value === 'Connected' && hasInput.value && !isCompacting.value && !hasPendingQuestion.value
|
|
448
439
|
);
|
|
440
|
+
const hasStreamingMessage = computed(() => messages.value.some(m => m.isStreaming));
|
|
449
441
|
|
|
450
442
|
// ── Slash command menu ──
|
|
451
443
|
const slashMenuVisible = computed(() => {
|
|
@@ -701,7 +693,6 @@ const App = {
|
|
|
701
693
|
onUnmounted(() => {
|
|
702
694
|
closeWs(); streaming.cleanup(); cleanupScroll(); cleanupHighlight();
|
|
703
695
|
window.removeEventListener('resize', _resizeHandler);
|
|
704
|
-
if (_vvResizeHandler && window.visualViewport) window.visualViewport.removeEventListener('resize', _vvResizeHandler);
|
|
705
696
|
document.removeEventListener('click', _workdirMenuClickHandler);
|
|
706
697
|
document.removeEventListener('click', _slashMenuClickOutside);
|
|
707
698
|
document.removeEventListener('keydown', _workdirMenuKeyHandler);
|
|
@@ -712,7 +703,7 @@ const App = {
|
|
|
712
703
|
status, agentName, hostname, workDir, sessionId, error,
|
|
713
704
|
serverVersion, agentVersion, latency,
|
|
714
705
|
messages, visibleMessages, hasMoreMessages, loadMoreMessages,
|
|
715
|
-
inputText, isProcessing, isCompacting, canSend, hasInput, inputRef, queuedMessages, usageStats,
|
|
706
|
+
inputText, isProcessing, isCompacting, canSend, hasInput, hasStreamingMessage, inputRef, queuedMessages, usageStats,
|
|
716
707
|
slashMenuVisible, filteredSlashCommands, slashMenuIndex, slashMenuOpen, selectSlashCommand, openSlashMenu,
|
|
717
708
|
sendMessage, handleKeydown, cancelExecution, removeQueuedMessage, onMessageListScroll,
|
|
718
709
|
// Side question (/btw)
|
|
@@ -2050,7 +2041,7 @@ const App = {
|
|
|
2050
2041
|
</span>
|
|
2051
2042
|
<span class="tool-toggle">{{ msg.expanded ? '\u{25B2}' : '\u{25BC}' }}</span>
|
|
2052
2043
|
</div>
|
|
2053
|
-
<div v-
|
|
2044
|
+
<div v-if="msg.expanded" class="tool-expand">
|
|
2054
2045
|
<div v-if="isEditTool(msg) && getEditDiffHtml(msg)" class="tool-diff" v-html="getEditDiffHtml(msg)"></div>
|
|
2055
2046
|
<div v-else-if="getFormattedToolInput(msg)" class="tool-input-formatted" v-html="getFormattedToolInput(msg)"></div>
|
|
2056
2047
|
<pre v-else-if="msg.toolInput" class="tool-block">{{ msg.toolInput }}</pre>
|
|
@@ -2107,7 +2098,7 @@ const App = {
|
|
|
2107
2098
|
</span>
|
|
2108
2099
|
<span class="tool-toggle">{{ msg.expanded ? '\u{25B2}' : '\u{25BC}' }}</span>
|
|
2109
2100
|
</div>
|
|
2110
|
-
<div v-
|
|
2101
|
+
<div v-if="msg.expanded" class="tool-expand">
|
|
2111
2102
|
<div v-if="isEditTool(msg) && getEditDiffHtml(msg)" class="tool-diff" v-html="getEditDiffHtml(msg)"></div>
|
|
2112
2103
|
<div v-else-if="getFormattedToolInput(msg)" class="tool-input-formatted" v-html="getFormattedToolInput(msg)"></div>
|
|
2113
2104
|
<pre v-else-if="msg.toolInput" class="tool-block">{{ msg.toolInput }}</pre>
|
|
@@ -2411,7 +2402,7 @@ const App = {
|
|
|
2411
2402
|
</span>
|
|
2412
2403
|
<span class="tool-toggle">{{ msg.expanded ? '\u{25B2}' : '\u{25BC}' }}</span>
|
|
2413
2404
|
</div>
|
|
2414
|
-
<div v-
|
|
2405
|
+
<div v-if="msg.expanded" class="tool-expand team-agent-tool-expand">
|
|
2415
2406
|
<pre v-if="msg.toolInput" class="tool-block">{{ msg.toolInput }}</pre>
|
|
2416
2407
|
<div v-if="msg.toolOutput" class="team-agent-tool-result">
|
|
2417
2408
|
<div class="team-agent-tool-result-label">{{ t('team.agentResult') }}</div>
|
|
@@ -2432,7 +2423,7 @@ const App = {
|
|
|
2432
2423
|
</span>
|
|
2433
2424
|
<span class="tool-toggle">{{ msg.expanded ? '\u{25B2}' : '\u{25BC}' }}</span>
|
|
2434
2425
|
</div>
|
|
2435
|
-
<div v-
|
|
2426
|
+
<div v-if="msg.expanded" class="tool-expand">
|
|
2436
2427
|
<div v-if="isEditTool(msg) && getEditDiffHtml(msg)" class="tool-diff" v-html="getEditDiffHtml(msg)"></div>
|
|
2437
2428
|
<div v-else-if="getFormattedToolInput(msg)" class="tool-input-formatted" v-html="getFormattedToolInput(msg)"></div>
|
|
2438
2429
|
<pre v-else-if="msg.toolInput" class="tool-block">{{ msg.toolInput }}</pre>
|
|
@@ -2507,7 +2498,7 @@ const App = {
|
|
|
2507
2498
|
</div>
|
|
2508
2499
|
</div>
|
|
2509
2500
|
|
|
2510
|
-
<div v-if="isProcessing && !
|
|
2501
|
+
<div v-if="isProcessing && !hasStreamingMessage" class="typing-indicator">
|
|
2511
2502
|
<span></span><span></span><span></span>
|
|
2512
2503
|
</div>
|
|
2513
2504
|
</div>
|
package/web/css/base.css
CHANGED
|
@@ -236,6 +236,25 @@ body {
|
|
|
236
236
|
margin-top: 0.75rem;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/* ── Shared keyframes ── */
|
|
240
|
+
@keyframes spin {
|
|
241
|
+
to { transform: rotate(360deg); }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@keyframes pulse {
|
|
245
|
+
0%, 100% { opacity: 1; }
|
|
246
|
+
50% { opacity: 0.3; }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* ── Reduced motion ── */
|
|
250
|
+
@media (prefers-reduced-motion: reduce) {
|
|
251
|
+
*, *::before, *::after {
|
|
252
|
+
animation-duration: 0.01ms !important;
|
|
253
|
+
animation-iteration-count: 1 !important;
|
|
254
|
+
transition-duration: 0.01ms !important;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
239
258
|
/* ── Main body (sidebar + chat) ── */
|
|
240
259
|
.main-body {
|
|
241
260
|
flex: 1;
|
package/web/css/file-browser.css
CHANGED
package/web/css/input.css
CHANGED
|
@@ -225,7 +225,6 @@
|
|
|
225
225
|
align-items: center;
|
|
226
226
|
justify-content: center;
|
|
227
227
|
gap: 16px;
|
|
228
|
-
backdrop-filter: blur(2px);
|
|
229
228
|
}
|
|
230
229
|
.workdir-switching-spinner {
|
|
231
230
|
width: 36px;
|
|
@@ -233,10 +232,7 @@
|
|
|
233
232
|
border: 3px solid rgba(255, 255, 255, 0.2);
|
|
234
233
|
border-top-color: rgba(255, 255, 255, 0.8);
|
|
235
234
|
border-radius: 50%;
|
|
236
|
-
animation:
|
|
237
|
-
}
|
|
238
|
-
@keyframes workdir-spin {
|
|
239
|
-
to { transform: rotate(360deg); }
|
|
235
|
+
animation: spin 0.7s linear infinite;
|
|
240
236
|
}
|
|
241
237
|
.workdir-switching-text {
|
|
242
238
|
color: rgba(255, 255, 255, 0.9);
|
package/web/css/loop.css
CHANGED
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
border: 1px solid var(--border);
|
|
186
186
|
border-radius: 6px;
|
|
187
187
|
cursor: pointer;
|
|
188
|
-
transition:
|
|
188
|
+
transition: color 0.15s, border-color 0.15s;
|
|
189
189
|
white-space: nowrap;
|
|
190
190
|
}
|
|
191
191
|
.loop-action-btn:hover:not(:disabled) {
|
|
@@ -353,11 +353,7 @@
|
|
|
353
353
|
}
|
|
354
354
|
.loop-exec-status-running {
|
|
355
355
|
color: var(--accent);
|
|
356
|
-
animation:
|
|
357
|
-
}
|
|
358
|
-
@keyframes loop-spin {
|
|
359
|
-
from { transform: rotate(0deg); }
|
|
360
|
-
to { transform: rotate(360deg); }
|
|
356
|
+
animation: spin 1s linear infinite;
|
|
361
357
|
}
|
|
362
358
|
.loop-exec-status-success {
|
|
363
359
|
color: #10B981;
|
|
@@ -428,11 +424,7 @@
|
|
|
428
424
|
height: 8px;
|
|
429
425
|
border-radius: 50%;
|
|
430
426
|
background: var(--accent);
|
|
431
|
-
animation:
|
|
432
|
-
}
|
|
433
|
-
@keyframes loop-pulse {
|
|
434
|
-
0%, 100% { opacity: 1; }
|
|
435
|
-
50% { opacity: 0.3; }
|
|
427
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
436
428
|
}
|
|
437
429
|
|
|
438
430
|
/* ── Modal dialog (generic) ── */
|
package/web/css/sidebar.css
CHANGED
|
@@ -210,11 +210,6 @@
|
|
|
210
210
|
transform: rotate(-90deg);
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
@keyframes spin {
|
|
214
|
-
from { transform: rotate(0deg); }
|
|
215
|
-
to { transform: rotate(360deg); }
|
|
216
|
-
}
|
|
217
|
-
|
|
218
213
|
.spinning {
|
|
219
214
|
animation: spin 0.8s linear infinite;
|
|
220
215
|
}
|
|
@@ -329,12 +324,7 @@
|
|
|
329
324
|
background: var(--accent);
|
|
330
325
|
margin-right: 6px;
|
|
331
326
|
vertical-align: middle;
|
|
332
|
-
animation: pulse
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
@keyframes pulse-dot {
|
|
336
|
-
0%, 100% { opacity: 1; }
|
|
337
|
-
50% { opacity: 0.3; }
|
|
327
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
338
328
|
}
|
|
339
329
|
|
|
340
330
|
.session-meta {
|
package/web/css/team.css
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
font-size: 0.75rem;
|
|
22
22
|
font-weight: 500;
|
|
23
23
|
cursor: pointer;
|
|
24
|
-
transition:
|
|
24
|
+
transition: color 0.15s, background 0.15s, box-shadow 0.15s;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
.team-mode-btn.active {
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
color: var(--text-secondary);
|
|
195
195
|
font-size: 0.78rem;
|
|
196
196
|
cursor: pointer;
|
|
197
|
-
transition:
|
|
197
|
+
transition: color 0.15s, border-color 0.15s;
|
|
198
198
|
}
|
|
199
199
|
.team-lead-prompt-reset:hover {
|
|
200
200
|
color: var(--text-primary);
|
|
@@ -258,7 +258,7 @@
|
|
|
258
258
|
color: var(--text-secondary);
|
|
259
259
|
font-size: 0.85rem;
|
|
260
260
|
cursor: pointer;
|
|
261
|
-
transition:
|
|
261
|
+
transition: color 0.15s, border-color 0.15s;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
.team-create-cancel:hover {
|
|
@@ -293,7 +293,7 @@
|
|
|
293
293
|
padding: 14px 16px;
|
|
294
294
|
border: 1px solid var(--border);
|
|
295
295
|
border-radius: 10px;
|
|
296
|
-
transition:
|
|
296
|
+
transition: border-color 0.15s, background 0.15s;
|
|
297
297
|
background: linear-gradient(135deg, rgba(255,255,255,0.02) 0%, transparent 100%);
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -354,7 +354,7 @@
|
|
|
354
354
|
border: 1px solid var(--accent);
|
|
355
355
|
border-radius: 6px;
|
|
356
356
|
cursor: pointer;
|
|
357
|
-
transition:
|
|
357
|
+
transition: color 0.15s, background 0.15s;
|
|
358
358
|
white-space: nowrap;
|
|
359
359
|
}
|
|
360
360
|
|
|
@@ -500,7 +500,7 @@
|
|
|
500
500
|
color: var(--error);
|
|
501
501
|
font-size: 0.8rem;
|
|
502
502
|
cursor: pointer;
|
|
503
|
-
transition:
|
|
503
|
+
transition: background 0.15s;
|
|
504
504
|
}
|
|
505
505
|
|
|
506
506
|
.team-dissolve-btn:hover {
|
|
@@ -515,7 +515,7 @@
|
|
|
515
515
|
color: var(--text-secondary);
|
|
516
516
|
font-size: 0.8rem;
|
|
517
517
|
cursor: pointer;
|
|
518
|
-
transition:
|
|
518
|
+
transition: color 0.15s, border-color 0.15s;
|
|
519
519
|
}
|
|
520
520
|
|
|
521
521
|
.team-back-btn:hover {
|
|
@@ -531,7 +531,7 @@
|
|
|
531
531
|
color: var(--accent);
|
|
532
532
|
font-size: 0.8rem;
|
|
533
533
|
cursor: pointer;
|
|
534
|
-
transition:
|
|
534
|
+
transition: background 0.15s;
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
.team-new-btn:hover {
|
|
@@ -1111,7 +1111,7 @@
|
|
|
1111
1111
|
color: var(--text-secondary);
|
|
1112
1112
|
font-size: 0.78rem;
|
|
1113
1113
|
cursor: pointer;
|
|
1114
|
-
transition:
|
|
1114
|
+
transition: color 0.15s, border-color 0.15s;
|
|
1115
1115
|
margin-right: 4px;
|
|
1116
1116
|
}
|
|
1117
1117
|
|
package/web/css/tools.css
CHANGED
|
@@ -44,7 +44,8 @@ export function createHighlightScheduler() {
|
|
|
44
44
|
_hlTimer = setTimeout(() => {
|
|
45
45
|
_hlTimer = null;
|
|
46
46
|
if (typeof hljs !== 'undefined') {
|
|
47
|
-
document.
|
|
47
|
+
const root = document.querySelector('.message-list') || document;
|
|
48
|
+
root.querySelectorAll('pre code:not([data-highlighted])').forEach(block => {
|
|
48
49
|
hljs.highlightElement(block);
|
|
49
50
|
block.dataset.highlighted = 'true';
|
|
50
51
|
});
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// ── History batch building & background conversation routing ──────────────────
|
|
2
2
|
import { isContextSummary } from './messageHelpers.js';
|
|
3
3
|
|
|
4
|
+
function findLast(arr, predicate) {
|
|
5
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
6
|
+
if (predicate(arr[i])) return arr[i];
|
|
7
|
+
}
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
/**
|
|
5
12
|
* Convert a history array (from conversation_resumed) into a batch of UI messages.
|
|
6
13
|
* @param {Array} history - Array of {role, content, ...} from the agent
|
|
@@ -133,11 +140,14 @@ export function routeToBackgroundConversation(deps, convId, msg) {
|
|
|
133
140
|
const msgs = cache.messages;
|
|
134
141
|
const last = msgs.length > 0 ? msgs[msgs.length - 1] : null;
|
|
135
142
|
if (last && last.role === 'assistant' && last.isStreaming) {
|
|
136
|
-
last.
|
|
143
|
+
if (!last._chunks) last._chunks = [last.content];
|
|
144
|
+
last._chunks.push(data.delta);
|
|
145
|
+
last.content = last._chunks.join('');
|
|
137
146
|
} else {
|
|
138
147
|
msgs.push({
|
|
139
148
|
id: ++cache.messageIdCounter, role: 'assistant',
|
|
140
149
|
content: data.delta, isStreaming: true, timestamp: new Date(),
|
|
150
|
+
_chunks: [data.delta],
|
|
141
151
|
});
|
|
142
152
|
}
|
|
143
153
|
} else if (data.type === 'tool_use' && data.tools) {
|
|
@@ -206,7 +216,7 @@ export function routeToBackgroundConversation(deps, convId, msg) {
|
|
|
206
216
|
});
|
|
207
217
|
} else if (msg.status === 'completed') {
|
|
208
218
|
cache.isCompacting = false;
|
|
209
|
-
const startMsg =
|
|
219
|
+
const startMsg = findLast(cache.messages, m => m.isCompactStart && !m.compactDone);
|
|
210
220
|
if (startMsg) {
|
|
211
221
|
startMsg.content = 'Context compacted';
|
|
212
222
|
startMsg.compactDone = true;
|
|
@@ -7,6 +7,13 @@ const MAX_RECONNECT_ATTEMPTS = 50;
|
|
|
7
7
|
const RECONNECT_BASE_DELAY = 1000;
|
|
8
8
|
const RECONNECT_MAX_DELAY = 15000;
|
|
9
9
|
|
|
10
|
+
function findLast(arr, predicate) {
|
|
11
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
12
|
+
if (predicate(arr[i])) return arr[i];
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
/**
|
|
11
18
|
* Creates the WebSocket connection controller.
|
|
12
19
|
* @param {object} deps - All reactive state and callbacks needed
|
|
@@ -469,7 +476,7 @@ export function createConnection(deps) {
|
|
|
469
476
|
} else if (msg.status === 'completed') {
|
|
470
477
|
isCompacting.value = false;
|
|
471
478
|
// Update the start message to show completed
|
|
472
|
-
const startMsg =
|
|
479
|
+
const startMsg = findLast(messages.value, m => m.isCompactStart && !m.compactDone);
|
|
473
480
|
if (startMsg) {
|
|
474
481
|
startMsg.content = t('system.contextCompacted');
|
|
475
482
|
startMsg.compactDone = true;
|
|
@@ -4,6 +4,13 @@ import { renderMarkdown } from './markdown.js';
|
|
|
4
4
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
5
5
|
const CONTEXT_SUMMARY_PREFIX = 'This session is being continued from a previous conversation';
|
|
6
6
|
|
|
7
|
+
function parseToolInput(msg) {
|
|
8
|
+
if (msg._parsedInput !== undefined) return msg._parsedInput;
|
|
9
|
+
try { msg._parsedInput = JSON.parse(msg.toolInput); }
|
|
10
|
+
catch { msg._parsedInput = null; }
|
|
11
|
+
return msg._parsedInput;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
export function isContextSummary(text) {
|
|
8
15
|
return typeof text === 'string' && text.trimStart().startsWith(CONTEXT_SUMMARY_PREFIX);
|
|
9
16
|
}
|
|
@@ -28,6 +35,10 @@ export function formatTimestamp(ts) {
|
|
|
28
35
|
|
|
29
36
|
export function getRenderedContent(msg) {
|
|
30
37
|
if (msg.role !== 'assistant' && !msg.isCommandOutput) return msg.content;
|
|
38
|
+
if (msg.isStreaming) {
|
|
39
|
+
const t = msg.content || '';
|
|
40
|
+
return t.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>');
|
|
41
|
+
}
|
|
31
42
|
return renderMarkdown(msg.content);
|
|
32
43
|
}
|
|
33
44
|
|
|
@@ -55,9 +66,9 @@ export function toggleTool(msg) {
|
|
|
55
66
|
|
|
56
67
|
export function getToolSummary(msg, t) {
|
|
57
68
|
const name = msg.toolName;
|
|
58
|
-
const
|
|
69
|
+
const obj = parseToolInput(msg);
|
|
70
|
+
if (!obj) return '';
|
|
59
71
|
try {
|
|
60
|
-
const obj = JSON.parse(input);
|
|
61
72
|
if (name === 'Read' && obj.file_path) return obj.file_path;
|
|
62
73
|
if (name === 'Edit' && obj.file_path) return obj.file_path;
|
|
63
74
|
if (name === 'Write' && obj.file_path) return obj.file_path;
|
|
@@ -83,8 +94,9 @@ export function isEditTool(msg) {
|
|
|
83
94
|
|
|
84
95
|
export function getFormattedToolInput(msg, t) {
|
|
85
96
|
if (!msg.toolInput) return null;
|
|
97
|
+
const obj = parseToolInput(msg);
|
|
98
|
+
if (!obj) return null;
|
|
86
99
|
try {
|
|
87
|
-
const obj = JSON.parse(msg.toolInput);
|
|
88
100
|
const esc = s => s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
89
101
|
const name = msg.toolName;
|
|
90
102
|
|
|
@@ -169,8 +181,9 @@ export function getFormattedToolInput(msg, t) {
|
|
|
169
181
|
}
|
|
170
182
|
|
|
171
183
|
export function getEditDiffHtml(msg, t) {
|
|
184
|
+
const obj = parseToolInput(msg);
|
|
185
|
+
if (!obj) return null;
|
|
172
186
|
try {
|
|
173
|
-
const obj = JSON.parse(msg.toolInput);
|
|
174
187
|
if (!obj.old_string && !obj.new_string) return null;
|
|
175
188
|
const esc = s => s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
176
189
|
const filePath = obj.file_path || '';
|
package/web/modules/streaming.js
CHANGED
|
@@ -34,19 +34,20 @@ export function createStreaming({ messages, scrollToBottom }) {
|
|
|
34
34
|
? messages.value.find(m => m.id === streamingMessageId)
|
|
35
35
|
: null;
|
|
36
36
|
|
|
37
|
+
const chunk = pendingText.slice(0, CHARS_PER_TICK);
|
|
38
|
+
pendingText = pendingText.slice(CHARS_PER_TICK);
|
|
39
|
+
|
|
37
40
|
if (!streamMsg) {
|
|
38
41
|
const id = ++messageIdCounter;
|
|
39
|
-
const chunk = pendingText.slice(0, CHARS_PER_TICK);
|
|
40
|
-
pendingText = pendingText.slice(CHARS_PER_TICK);
|
|
41
42
|
messages.value.push({
|
|
42
43
|
id, role: 'assistant', content: chunk,
|
|
43
44
|
isStreaming: true, timestamp: new Date(),
|
|
45
|
+
_chunks: [chunk],
|
|
44
46
|
});
|
|
45
47
|
streamingMessageId = id;
|
|
46
48
|
} else {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
streamMsg.content += chunk;
|
|
49
|
+
streamMsg._chunks.push(chunk);
|
|
50
|
+
streamMsg.content = streamMsg._chunks.join('');
|
|
50
51
|
}
|
|
51
52
|
scrollToBottom();
|
|
52
53
|
if (pendingText) revealTimer = setTimeout(revealTick, TICK_MS);
|
|
@@ -58,12 +59,15 @@ export function createStreaming({ messages, scrollToBottom }) {
|
|
|
58
59
|
const streamMsg = streamingMessageId !== null
|
|
59
60
|
? messages.value.find(m => m.id === streamingMessageId) : null;
|
|
60
61
|
if (streamMsg) {
|
|
61
|
-
streamMsg.
|
|
62
|
+
if (!streamMsg._chunks) streamMsg._chunks = [streamMsg.content];
|
|
63
|
+
streamMsg._chunks.push(pendingText);
|
|
64
|
+
streamMsg.content = streamMsg._chunks.join('');
|
|
62
65
|
} else {
|
|
63
66
|
const id = ++messageIdCounter;
|
|
64
67
|
messages.value.push({
|
|
65
68
|
id, role: 'assistant', content: pendingText,
|
|
66
69
|
isStreaming: true, timestamp: new Date(),
|
|
70
|
+
_chunks: [pendingText],
|
|
67
71
|
});
|
|
68
72
|
streamingMessageId = id;
|
|
69
73
|
}
|