@poncho-ai/cli 0.4.2 → 0.5.0
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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +17 -0
- package/dist/chunk-2AZ3Y6R2.js +4121 -0
- package/dist/chunk-3273VMA7.js +4182 -0
- package/dist/chunk-6REJ5J4T.js +4142 -0
- package/dist/chunk-BSW557BB.js +4058 -0
- package/dist/chunk-GJYE4S3D.js +4164 -0
- package/dist/chunk-HGZTVHBT.js +4089 -0
- package/dist/chunk-HNYADV2K.js +4164 -0
- package/dist/chunk-IE47LJ33.js +4166 -0
- package/dist/chunk-MFVXK3SX.js +4177 -0
- package/dist/chunk-N7ZAHMBR.js +4178 -0
- package/dist/chunk-TYL4SGJE.js +4177 -0
- package/dist/chunk-VIX7Y2YC.js +4169 -0
- package/dist/chunk-WCIUVLV3.js +4171 -0
- package/dist/chunk-WXFCSFXF.js +4178 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/run-interactive-ink-4EWW4AJ6.js +463 -0
- package/dist/run-interactive-ink-642LZIYZ.js +494 -0
- package/dist/run-interactive-ink-72H5IUTC.js +494 -0
- package/dist/run-interactive-ink-7P4WWB2Y.js +494 -0
- package/dist/run-interactive-ink-EP7GIKLH.js +463 -0
- package/dist/run-interactive-ink-FASKW7SN.js +463 -0
- package/dist/run-interactive-ink-LLNLDCES.js +494 -0
- package/dist/run-interactive-ink-MO6MGLEY.js +494 -0
- package/dist/run-interactive-ink-OQZN5DQE.js +463 -0
- package/dist/run-interactive-ink-QKB6CG3W.js +494 -0
- package/dist/run-interactive-ink-RJCA5IQA.js +494 -0
- package/dist/run-interactive-ink-RU2PH6R5.js +494 -0
- package/dist/run-interactive-ink-T36C6TJ2.js +463 -0
- package/dist/run-interactive-ink-ZL6RAS2O.js +494 -0
- package/package.json +3 -2
- package/src/index.ts +46 -10
- package/src/run-interactive-ink.ts +45 -10
- package/src/web-ui.ts +206 -103
|
@@ -443,6 +443,9 @@ export const runInteractiveInk = async ({
|
|
|
443
443
|
let sawChunk = false;
|
|
444
444
|
let toolEvents = 0;
|
|
445
445
|
const toolTimeline: string[] = [];
|
|
446
|
+
const sections: Array<{ type: "text" | "tools"; content: string | string[] }> = [];
|
|
447
|
+
let currentText = "";
|
|
448
|
+
let currentTools: string[] = [];
|
|
446
449
|
let runFailed = false;
|
|
447
450
|
let usage: TokenUsage | undefined;
|
|
448
451
|
let latestRunId = "";
|
|
@@ -459,8 +462,14 @@ export const runInteractiveInk = async ({
|
|
|
459
462
|
}
|
|
460
463
|
if (event.type === "model:chunk") {
|
|
461
464
|
sawChunk = true;
|
|
465
|
+
// If we have tools accumulated and text starts again, push tools as a section
|
|
466
|
+
if (currentTools.length > 0) {
|
|
467
|
+
sections.push({ type: "tools", content: currentTools });
|
|
468
|
+
currentTools = [];
|
|
469
|
+
}
|
|
462
470
|
responseText += event.content;
|
|
463
471
|
streamedText += event.content;
|
|
472
|
+
currentText += event.content;
|
|
464
473
|
|
|
465
474
|
if (!thinkingCleared) {
|
|
466
475
|
clearThinking();
|
|
@@ -485,11 +494,18 @@ export const runInteractiveInk = async ({
|
|
|
485
494
|
clearThinking();
|
|
486
495
|
|
|
487
496
|
if (event.type === "tool:started") {
|
|
497
|
+
// If we have text accumulated, push it as a text section
|
|
498
|
+
if (currentText.length > 0) {
|
|
499
|
+
sections.push({ type: "text", content: currentText });
|
|
500
|
+
currentText = "";
|
|
501
|
+
}
|
|
488
502
|
const preview = showToolPayloads
|
|
489
503
|
? compactPreview(event.input, 400)
|
|
490
504
|
: compactPreview(event.input, 100);
|
|
491
505
|
console.log(yellow(`tools> start ${event.tool} input=${preview}`));
|
|
492
|
-
|
|
506
|
+
const toolText = `- start \`${event.tool}\``;
|
|
507
|
+
toolTimeline.push(toolText);
|
|
508
|
+
currentTools.push(toolText);
|
|
493
509
|
toolEvents += 1;
|
|
494
510
|
} else if (event.type === "tool:completed") {
|
|
495
511
|
const preview = showToolPayloads
|
|
@@ -503,29 +519,37 @@ export const runInteractiveInk = async ({
|
|
|
503
519
|
if (showToolPayloads) {
|
|
504
520
|
console.log(yellow(`tools> output ${preview}`));
|
|
505
521
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
);
|
|
522
|
+
const toolText = `- done \`${event.tool}\` in ${formatDuration(event.duration)}`;
|
|
523
|
+
toolTimeline.push(toolText);
|
|
524
|
+
currentTools.push(toolText);
|
|
509
525
|
} else if (event.type === "tool:error") {
|
|
510
526
|
console.log(
|
|
511
527
|
red(`tools> error ${event.tool}: ${event.error}`),
|
|
512
528
|
);
|
|
513
|
-
|
|
529
|
+
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
530
|
+
toolTimeline.push(toolText);
|
|
531
|
+
currentTools.push(toolText);
|
|
514
532
|
} else if (event.type === "tool:approval:required") {
|
|
515
533
|
console.log(
|
|
516
534
|
magenta(`tools> approval required for ${event.tool}`),
|
|
517
535
|
);
|
|
518
|
-
|
|
536
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
537
|
+
toolTimeline.push(toolText);
|
|
538
|
+
currentTools.push(toolText);
|
|
519
539
|
} else if (event.type === "tool:approval:granted") {
|
|
520
540
|
console.log(
|
|
521
541
|
gray(`tools> approval granted (${event.approvalId})`),
|
|
522
542
|
);
|
|
523
|
-
|
|
543
|
+
const toolText = `- approval granted (${event.approvalId})`;
|
|
544
|
+
toolTimeline.push(toolText);
|
|
545
|
+
currentTools.push(toolText);
|
|
524
546
|
} else if (event.type === "tool:approval:denied") {
|
|
525
547
|
console.log(
|
|
526
548
|
magenta(`tools> approval denied (${event.approvalId})`),
|
|
527
549
|
);
|
|
528
|
-
|
|
550
|
+
const toolText = `- approval denied (${event.approvalId})`;
|
|
551
|
+
toolTimeline.push(toolText);
|
|
552
|
+
currentTools.push(toolText);
|
|
529
553
|
}
|
|
530
554
|
} else if (event.type === "run:error") {
|
|
531
555
|
clearThinking();
|
|
@@ -588,13 +612,24 @@ export const runInteractiveInk = async ({
|
|
|
588
612
|
activeConversationId = created.conversationId;
|
|
589
613
|
}
|
|
590
614
|
|
|
615
|
+
// Finalize sections
|
|
616
|
+
if (currentTools.length > 0) {
|
|
617
|
+
sections.push({ type: "tools", content: currentTools });
|
|
618
|
+
}
|
|
619
|
+
if (currentText.length > 0) {
|
|
620
|
+
sections.push({ type: "text", content: currentText });
|
|
621
|
+
}
|
|
622
|
+
|
|
591
623
|
messages.push({ role: "user", content: trimmed });
|
|
592
624
|
messages.push({
|
|
593
625
|
role: "assistant",
|
|
594
626
|
content: responseText,
|
|
595
627
|
metadata:
|
|
596
|
-
toolTimeline.length > 0
|
|
597
|
-
? ({
|
|
628
|
+
toolTimeline.length > 0 || sections.length > 0
|
|
629
|
+
? ({
|
|
630
|
+
toolActivity: toolTimeline,
|
|
631
|
+
sections: sections.length > 0 ? sections : undefined,
|
|
632
|
+
} as Message["metadata"])
|
|
598
633
|
: undefined,
|
|
599
634
|
});
|
|
600
635
|
turn = computeTurn(messages);
|
package/src/web-ui.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import {
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, resolve, join } from "node:path";
|
|
4
5
|
import { homedir } from "node:os";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
5
7
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
6
8
|
import type { Message } from "@poncho-ai/sdk";
|
|
7
9
|
|
|
10
|
+
// Load marked library at module initialization (ESM compatible)
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const markedPackagePath = require.resolve("marked");
|
|
13
|
+
const markedDir = dirname(markedPackagePath);
|
|
14
|
+
const markedSource = readFileSync(join(markedDir, "marked.umd.js"), "utf-8");
|
|
15
|
+
|
|
8
16
|
export interface WebUiConversation {
|
|
9
17
|
conversationId: string;
|
|
10
18
|
title: string;
|
|
@@ -783,8 +791,72 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
783
791
|
font-size: 13px;
|
|
784
792
|
line-height: 1.5;
|
|
785
793
|
}
|
|
794
|
+
.tool-activity-inline {
|
|
795
|
+
margin: 8px 0;
|
|
796
|
+
font-size: 12px;
|
|
797
|
+
line-height: 1.45;
|
|
798
|
+
color: #8a8a8a;
|
|
799
|
+
}
|
|
800
|
+
.tool-activity-inline code {
|
|
801
|
+
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
802
|
+
background: rgba(255,255,255,0.04);
|
|
803
|
+
border: 1px solid rgba(255,255,255,0.08);
|
|
804
|
+
padding: 4px 8px;
|
|
805
|
+
border-radius: 6px;
|
|
806
|
+
color: #bcbcbc;
|
|
807
|
+
font-size: 11px;
|
|
808
|
+
}
|
|
809
|
+
.tool-status {
|
|
810
|
+
color: #8a8a8a;
|
|
811
|
+
font-style: italic;
|
|
812
|
+
}
|
|
813
|
+
.tool-done {
|
|
814
|
+
color: #6a9955;
|
|
815
|
+
}
|
|
816
|
+
.tool-error {
|
|
817
|
+
color: #f48771;
|
|
818
|
+
}
|
|
819
|
+
.assistant-content table {
|
|
820
|
+
border-collapse: collapse;
|
|
821
|
+
width: 100%;
|
|
822
|
+
margin: 14px 0;
|
|
823
|
+
font-size: 13px;
|
|
824
|
+
border: 1px solid rgba(255,255,255,0.08);
|
|
825
|
+
border-radius: 8px;
|
|
826
|
+
overflow: hidden;
|
|
827
|
+
display: block;
|
|
828
|
+
max-width: 100%;
|
|
829
|
+
overflow-x: auto;
|
|
830
|
+
white-space: nowrap;
|
|
831
|
+
}
|
|
832
|
+
.assistant-content th {
|
|
833
|
+
background: rgba(255,255,255,0.06);
|
|
834
|
+
padding: 10px 12px;
|
|
835
|
+
text-align: left;
|
|
836
|
+
font-weight: 600;
|
|
837
|
+
border-bottom: 1px solid rgba(255,255,255,0.12);
|
|
838
|
+
color: #fff;
|
|
839
|
+
min-width: 100px;
|
|
840
|
+
}
|
|
841
|
+
.assistant-content td {
|
|
842
|
+
padding: 10px 12px;
|
|
843
|
+
border-bottom: 1px solid rgba(255,255,255,0.06);
|
|
844
|
+
min-width: 100px;
|
|
845
|
+
}
|
|
846
|
+
.assistant-content tr:last-child td {
|
|
847
|
+
border-bottom: none;
|
|
848
|
+
}
|
|
849
|
+
.assistant-content tbody tr:hover {
|
|
850
|
+
background: rgba(255,255,255,0.02);
|
|
851
|
+
}
|
|
852
|
+
.assistant-content hr {
|
|
853
|
+
border: 0;
|
|
854
|
+
border-top: 1px solid rgba(255,255,255,0.1);
|
|
855
|
+
margin: 20px 0;
|
|
856
|
+
}
|
|
786
857
|
.tool-activity {
|
|
787
858
|
margin-top: 12px;
|
|
859
|
+
margin-bottom: 12px;
|
|
788
860
|
border: 1px solid rgba(255,255,255,0.08);
|
|
789
861
|
background: rgba(255,255,255,0.03);
|
|
790
862
|
border-radius: 10px;
|
|
@@ -793,6 +865,9 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
793
865
|
color: #bcbcbc;
|
|
794
866
|
max-width: 300px;
|
|
795
867
|
}
|
|
868
|
+
.assistant-content > .tool-activity:first-child {
|
|
869
|
+
margin-top: 0;
|
|
870
|
+
}
|
|
796
871
|
.tool-activity-disclosure {
|
|
797
872
|
display: block;
|
|
798
873
|
}
|
|
@@ -1064,6 +1139,15 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
1064
1139
|
</div>
|
|
1065
1140
|
|
|
1066
1141
|
<script>
|
|
1142
|
+
// Marked library (inlined)
|
|
1143
|
+
${markedSource}
|
|
1144
|
+
|
|
1145
|
+
// Configure marked for GitHub Flavored Markdown (tables, etc.)
|
|
1146
|
+
marked.setOptions({
|
|
1147
|
+
gfm: true,
|
|
1148
|
+
breaks: true
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1067
1151
|
const state = {
|
|
1068
1152
|
csrfToken: "",
|
|
1069
1153
|
conversations: [],
|
|
@@ -1149,75 +1233,17 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
1149
1233
|
.replace(/"/g, """)
|
|
1150
1234
|
.replace(/'/g, "'");
|
|
1151
1235
|
|
|
1152
|
-
const renderInlineMarkdown = (value) => {
|
|
1153
|
-
let html = escapeHtml(value);
|
|
1154
|
-
html = html.replace(/\\*\\*([^*]+)\\*\\*/g, "<strong>$1</strong>");
|
|
1155
|
-
html = html.replace(/\\x60([^\\x60]+)\\x60/g, "<code>$1</code>");
|
|
1156
|
-
return html;
|
|
1157
|
-
};
|
|
1158
|
-
|
|
1159
|
-
const renderMarkdownBlock = (value) => {
|
|
1160
|
-
const lines = String(value || "").split("\\n");
|
|
1161
|
-
let html = "";
|
|
1162
|
-
let inList = false;
|
|
1163
|
-
|
|
1164
|
-
for (const rawLine of lines) {
|
|
1165
|
-
const line = rawLine.trimEnd();
|
|
1166
|
-
const trimmed = line.trim();
|
|
1167
|
-
const headingMatch = trimmed.match(/^(#{1,3})\\s+(.+)$/);
|
|
1168
|
-
|
|
1169
|
-
if (headingMatch) {
|
|
1170
|
-
if (inList) {
|
|
1171
|
-
html += "</ul>";
|
|
1172
|
-
inList = false;
|
|
1173
|
-
}
|
|
1174
|
-
const level = Math.min(3, headingMatch[1].length);
|
|
1175
|
-
const tag = level === 1 ? "h2" : level === 2 ? "h3" : "p";
|
|
1176
|
-
html += "<" + tag + ">" + renderInlineMarkdown(headingMatch[2]) + "</" + tag + ">";
|
|
1177
|
-
continue;
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
if (/^\\s*-\\s+/.test(line)) {
|
|
1181
|
-
if (!inList) {
|
|
1182
|
-
html += "<ul>";
|
|
1183
|
-
inList = true;
|
|
1184
|
-
}
|
|
1185
|
-
html += "<li>" + renderInlineMarkdown(line.replace(/^\\s*-\\s+/, "")) + "</li>";
|
|
1186
|
-
continue;
|
|
1187
|
-
}
|
|
1188
|
-
if (inList) {
|
|
1189
|
-
html += "</ul>";
|
|
1190
|
-
inList = false;
|
|
1191
|
-
}
|
|
1192
|
-
if (trimmed.length === 0) {
|
|
1193
|
-
continue;
|
|
1194
|
-
}
|
|
1195
|
-
html += "<p>" + renderInlineMarkdown(line) + "</p>";
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
if (inList) {
|
|
1199
|
-
html += "</ul>";
|
|
1200
|
-
}
|
|
1201
|
-
return html;
|
|
1202
|
-
};
|
|
1203
|
-
|
|
1204
1236
|
const renderAssistantMarkdown = (value) => {
|
|
1205
|
-
const source = String(value || "");
|
|
1206
|
-
|
|
1207
|
-
let html = "";
|
|
1208
|
-
let lastIndex = 0;
|
|
1209
|
-
let match;
|
|
1210
|
-
|
|
1211
|
-
while ((match = fenceRegex.exec(source))) {
|
|
1212
|
-
const before = source.slice(lastIndex, match.index);
|
|
1213
|
-
html += renderMarkdownBlock(before);
|
|
1214
|
-
const codeText = String(match[1] || "").replace(/^\\n+|\\n+$/g, "");
|
|
1215
|
-
html += "<pre><code>" + escapeHtml(codeText) + "</code></pre>";
|
|
1216
|
-
lastIndex = match.index + match[0].length;
|
|
1217
|
-
}
|
|
1237
|
+
const source = String(value || "").trim();
|
|
1238
|
+
if (!source) return "<p></p>";
|
|
1218
1239
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1240
|
+
try {
|
|
1241
|
+
return marked.parse(source);
|
|
1242
|
+
} catch (error) {
|
|
1243
|
+
console.error("Markdown parsing error:", error);
|
|
1244
|
+
// Fallback to escaped text
|
|
1245
|
+
return "<p>" + escapeHtml(source) + "</p>";
|
|
1246
|
+
}
|
|
1221
1247
|
};
|
|
1222
1248
|
|
|
1223
1249
|
const extractToolActivity = (value) => {
|
|
@@ -1360,23 +1386,13 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
1360
1386
|
const content = document.createElement("div");
|
|
1361
1387
|
content.className = "assistant-content";
|
|
1362
1388
|
const text = String(m.content || "");
|
|
1363
|
-
|
|
1364
|
-
const metadataToolActivity =
|
|
1365
|
-
m.metadata && Array.isArray(m.metadata.toolActivity)
|
|
1366
|
-
? m.metadata.toolActivity
|
|
1367
|
-
: [];
|
|
1368
|
-
const toolActivity =
|
|
1369
|
-
Array.isArray(m._toolActivity) && m._toolActivity.length > 0
|
|
1370
|
-
? m._toolActivity
|
|
1371
|
-
: metadataToolActivity.length > 0
|
|
1372
|
-
? metadataToolActivity
|
|
1373
|
-
: parsed.activities;
|
|
1389
|
+
|
|
1374
1390
|
if (m._error) {
|
|
1375
1391
|
const errorEl = document.createElement("div");
|
|
1376
1392
|
errorEl.className = "message-error";
|
|
1377
1393
|
errorEl.innerHTML = "<strong>Error</strong><br>" + escapeHtml(m._error);
|
|
1378
1394
|
content.appendChild(errorEl);
|
|
1379
|
-
} else if (isStreaming && i === messages.length - 1 && !
|
|
1395
|
+
} else if (isStreaming && i === messages.length - 1 && !text && (!m._chunks || m._chunks.length === 0)) {
|
|
1380
1396
|
const spinner = document.createElement("span");
|
|
1381
1397
|
spinner.className = "thinking-indicator";
|
|
1382
1398
|
const starFrames = ["✶","✸","✹","✺","✹","✷"];
|
|
@@ -1385,10 +1401,44 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
1385
1401
|
spinner._interval = setInterval(() => { frame = (frame + 1) % starFrames.length; spinner.textContent = starFrames[frame]; }, 70);
|
|
1386
1402
|
content.appendChild(spinner);
|
|
1387
1403
|
} else {
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1404
|
+
// Check for sections in _sections (streaming) or metadata.sections (stored)
|
|
1405
|
+
const sections = m._sections || (m.metadata && m.metadata.sections);
|
|
1406
|
+
|
|
1407
|
+
if (sections && sections.length > 0) {
|
|
1408
|
+
// Render sections interleaved
|
|
1409
|
+
sections.forEach(section => {
|
|
1410
|
+
if (section.type === "text") {
|
|
1411
|
+
const textDiv = document.createElement("div");
|
|
1412
|
+
textDiv.innerHTML = renderAssistantMarkdown(section.content);
|
|
1413
|
+
content.appendChild(textDiv);
|
|
1414
|
+
} else if (section.type === "tools") {
|
|
1415
|
+
content.insertAdjacentHTML("beforeend", renderToolActivity(section.content));
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
// While streaming, show current tools if any
|
|
1419
|
+
if (isStreaming && i === messages.length - 1 && m._currentTools && m._currentTools.length > 0) {
|
|
1420
|
+
content.insertAdjacentHTML("beforeend", renderToolActivity(m._currentTools));
|
|
1421
|
+
}
|
|
1422
|
+
// Show current text being typed
|
|
1423
|
+
if (isStreaming && i === messages.length - 1 && m._currentText) {
|
|
1424
|
+
const textDiv = document.createElement("div");
|
|
1425
|
+
textDiv.innerHTML = renderAssistantMarkdown(m._currentText);
|
|
1426
|
+
content.appendChild(textDiv);
|
|
1427
|
+
}
|
|
1428
|
+
} else {
|
|
1429
|
+
// Fallback: render text and tools the old way (for old messages without sections)
|
|
1430
|
+
if (text) {
|
|
1431
|
+
const parsed = extractToolActivity(text);
|
|
1432
|
+
content.innerHTML = renderAssistantMarkdown(parsed.content);
|
|
1433
|
+
}
|
|
1434
|
+
const metadataToolActivity =
|
|
1435
|
+
m.metadata && Array.isArray(m.metadata.toolActivity)
|
|
1436
|
+
? m.metadata.toolActivity
|
|
1437
|
+
: [];
|
|
1438
|
+
if (metadataToolActivity.length > 0) {
|
|
1439
|
+
content.insertAdjacentHTML("beforeend", renderToolActivity(metadataToolActivity));
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1392
1442
|
}
|
|
1393
1443
|
wrap.appendChild(content);
|
|
1394
1444
|
row.appendChild(wrap);
|
|
@@ -1496,7 +1546,14 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
1496
1546
|
return;
|
|
1497
1547
|
}
|
|
1498
1548
|
const localMessages = [...(state.activeMessages || []), { role: "user", content: messageText }];
|
|
1499
|
-
let assistantMessage = {
|
|
1549
|
+
let assistantMessage = {
|
|
1550
|
+
role: "assistant",
|
|
1551
|
+
content: "",
|
|
1552
|
+
_sections: [], // Array of {type: 'text'|'tools', content: string|array}
|
|
1553
|
+
_currentText: "",
|
|
1554
|
+
_currentTools: [],
|
|
1555
|
+
metadata: { toolActivity: [] }
|
|
1556
|
+
};
|
|
1500
1557
|
localMessages.push(assistantMessage);
|
|
1501
1558
|
state.activeMessages = localMessages;
|
|
1502
1559
|
renderMessages(localMessages, true);
|
|
@@ -1526,44 +1583,88 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
1526
1583
|
buffer += decoder.decode(value, { stream: true });
|
|
1527
1584
|
buffer = parseSseChunk(buffer, (eventName, payload) => {
|
|
1528
1585
|
if (eventName === "model:chunk") {
|
|
1529
|
-
|
|
1586
|
+
const chunk = String(payload.content || "");
|
|
1587
|
+
// If we have tools accumulated and text starts again, push tools as a section
|
|
1588
|
+
if (assistantMessage._currentTools.length > 0 && chunk.length > 0) {
|
|
1589
|
+
assistantMessage._sections.push({ type: "tools", content: assistantMessage._currentTools });
|
|
1590
|
+
assistantMessage._currentTools = [];
|
|
1591
|
+
}
|
|
1592
|
+
assistantMessage.content += chunk;
|
|
1593
|
+
assistantMessage._currentText += chunk;
|
|
1530
1594
|
renderMessages(localMessages, true);
|
|
1531
1595
|
}
|
|
1532
1596
|
if (eventName === "tool:started") {
|
|
1533
|
-
|
|
1597
|
+
const toolName = payload.tool || "tool";
|
|
1598
|
+
// If we have text accumulated, push it as a text section
|
|
1599
|
+
if (assistantMessage._currentText.length > 0) {
|
|
1600
|
+
assistantMessage._sections.push({ type: "text", content: assistantMessage._currentText });
|
|
1601
|
+
assistantMessage._currentText = "";
|
|
1602
|
+
}
|
|
1603
|
+
const toolText = "- start \\x60" + toolName + "\\x60";
|
|
1604
|
+
assistantMessage._currentTools.push(toolText);
|
|
1605
|
+
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
1606
|
+
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
1607
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
1534
1608
|
renderMessages(localMessages, true);
|
|
1535
1609
|
}
|
|
1536
1610
|
if (eventName === "tool:completed") {
|
|
1611
|
+
const toolName = payload.tool || "tool";
|
|
1537
1612
|
const duration = typeof payload.duration === "number" ? payload.duration : null;
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
);
|
|
1613
|
+
const toolText = "- done \\x60" + toolName + "\\x60" + (duration !== null ? " (" + duration + "ms)" : "");
|
|
1614
|
+
assistantMessage._currentTools.push(toolText);
|
|
1615
|
+
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
1616
|
+
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
1617
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
1544
1618
|
renderMessages(localMessages, true);
|
|
1545
1619
|
}
|
|
1546
1620
|
if (eventName === "tool:error") {
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
);
|
|
1621
|
+
const toolName = payload.tool || "tool";
|
|
1622
|
+
const errorMsg = payload.error || "unknown error";
|
|
1623
|
+
const toolText = "- error \\x60" + toolName + "\\x60: " + errorMsg;
|
|
1624
|
+
assistantMessage._currentTools.push(toolText);
|
|
1625
|
+
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
1626
|
+
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
1627
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
1551
1628
|
renderMessages(localMessages, true);
|
|
1552
1629
|
}
|
|
1553
1630
|
if (eventName === "tool:approval:required") {
|
|
1554
|
-
|
|
1631
|
+
const toolName = payload.tool || "tool";
|
|
1632
|
+
const toolText = "- approval required \\x60" + toolName + "\\x60";
|
|
1633
|
+
assistantMessage._currentTools.push(toolText);
|
|
1634
|
+
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
1635
|
+
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
1636
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
1555
1637
|
renderMessages(localMessages, true);
|
|
1556
1638
|
}
|
|
1557
1639
|
if (eventName === "tool:approval:granted") {
|
|
1558
|
-
|
|
1640
|
+
const toolText = "- approval granted";
|
|
1641
|
+
assistantMessage._currentTools.push(toolText);
|
|
1642
|
+
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
1643
|
+
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
1644
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
1559
1645
|
renderMessages(localMessages, true);
|
|
1560
1646
|
}
|
|
1561
1647
|
if (eventName === "tool:approval:denied") {
|
|
1562
|
-
|
|
1648
|
+
const toolText = "- approval denied";
|
|
1649
|
+
assistantMessage._currentTools.push(toolText);
|
|
1650
|
+
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
1651
|
+
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
1652
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
1563
1653
|
renderMessages(localMessages, true);
|
|
1564
1654
|
}
|
|
1565
|
-
if (eventName === "run:completed"
|
|
1566
|
-
assistantMessage.content
|
|
1655
|
+
if (eventName === "run:completed") {
|
|
1656
|
+
if (!assistantMessage.content || assistantMessage.content.length === 0) {
|
|
1657
|
+
assistantMessage.content = String(payload.result?.response || "");
|
|
1658
|
+
}
|
|
1659
|
+
// Finalize sections: push any remaining tools and text
|
|
1660
|
+
if (assistantMessage._currentTools.length > 0) {
|
|
1661
|
+
assistantMessage._sections.push({ type: "tools", content: assistantMessage._currentTools });
|
|
1662
|
+
assistantMessage._currentTools = [];
|
|
1663
|
+
}
|
|
1664
|
+
if (assistantMessage._currentText.length > 0) {
|
|
1665
|
+
assistantMessage._sections.push({ type: "text", content: assistantMessage._currentText });
|
|
1666
|
+
assistantMessage._currentText = "";
|
|
1667
|
+
}
|
|
1567
1668
|
renderMessages(localMessages, false);
|
|
1568
1669
|
}
|
|
1569
1670
|
if (eventName === "run:error") {
|
|
@@ -1574,8 +1675,10 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
|
|
|
1574
1675
|
}
|
|
1575
1676
|
});
|
|
1576
1677
|
}
|
|
1678
|
+
// Update the state with our local messages (don't reload and lose tool chips)
|
|
1679
|
+
state.activeMessages = localMessages;
|
|
1577
1680
|
await loadConversations();
|
|
1578
|
-
|
|
1681
|
+
// Don't reload the conversation - we already have the latest state with tool chips
|
|
1579
1682
|
} finally {
|
|
1580
1683
|
setStreaming(false);
|
|
1581
1684
|
elements.prompt.focus();
|