u-foo 3.0.25 → 3.0.26
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/tui/darwin-arm64/ufoo-tui +0 -0
- package/dist/tui/darwin-x64/ufoo-tui +0 -0
- package/dist/tui/linux-arm64/ufoo-tui +0 -0
- package/dist/tui/linux-x64/ufoo-tui +0 -0
- package/package.json +1 -1
- package/src/code/UcodeController.js +2 -10
- package/src/code/agent.js +105 -26
- package/src/code/context/assembler.js +0 -102
- package/src/code/context/transcript.js +5 -0
- package/src/code/conversation/sessionJournal.js +351 -0
- package/src/code/nativeRunner.js +26 -0
- package/src/code/sessionStore.js +9 -21
- package/src/code/taskDecomposer.js +7 -3
- package/src/runtime/contracts/uiProtocol.js +2 -0
- package/src/ui/format/index.js +100 -33
- package/src/ui/format/markdownRenderer.js +2 -2
- package/src/ui/rustChatHost.js +2 -0
- package/src/ui/rustUcodeHost.js +106 -30
- package/src/ui/toolMergeBridge.js +5 -7
package/src/ui/format/index.js
CHANGED
|
@@ -154,11 +154,10 @@ function buildUcodeBannerLines({
|
|
|
154
154
|
}
|
|
155
155
|
shortPath = path.normalize(shortPath);
|
|
156
156
|
|
|
157
|
-
// The
|
|
158
|
-
//
|
|
157
|
+
// The logo is literal content. The renderer receives the completed banner
|
|
158
|
+
// row as a dedicated raw entry, so the logo never goes through log styling.
|
|
159
159
|
const logoLines = UCODE_BANNER_LINES;
|
|
160
160
|
const infoLines = [];
|
|
161
|
-
infoLines.push(`Version: ${UCODE_VERSION}`);
|
|
162
161
|
infoLines.push(`Model: ${modelLabel}`);
|
|
163
162
|
if (planMode) {
|
|
164
163
|
infoLines.push("Mode: PLAN");
|
|
@@ -176,7 +175,7 @@ function buildUcodeBannerLines({
|
|
|
176
175
|
return Array.from({ length: rows }, (_, index) => {
|
|
177
176
|
const logoLine = logoLines[index] || logoPadding;
|
|
178
177
|
const info = infoLines[index] || "";
|
|
179
|
-
return
|
|
178
|
+
return `${logoLine} ${info}`;
|
|
180
179
|
});
|
|
181
180
|
}
|
|
182
181
|
|
|
@@ -325,6 +324,66 @@ function toolMessagePreview(message = {}) {
|
|
|
325
324
|
return raw;
|
|
326
325
|
}
|
|
327
326
|
|
|
327
|
+
function parseToolCallArguments(call = {}) {
|
|
328
|
+
const raw = call && call.function && call.function.arguments != null
|
|
329
|
+
? call.function.arguments
|
|
330
|
+
: (call && call.arguments != null ? call.arguments : call && call.input);
|
|
331
|
+
if (raw && typeof raw === "object") return raw;
|
|
332
|
+
if (typeof raw !== "string" || !raw.trim()) return {};
|
|
333
|
+
try {
|
|
334
|
+
const parsed = JSON.parse(raw);
|
|
335
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
336
|
+
} catch {
|
|
337
|
+
return {};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function formatToolDisplayName(tool = "") {
|
|
342
|
+
const words = String(tool || "tool")
|
|
343
|
+
.trim()
|
|
344
|
+
.split(/[_\-\s]+/)
|
|
345
|
+
.filter(Boolean);
|
|
346
|
+
return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ") || "Tool";
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function formatUcodeToolLine(tool = "", args = {}, payload = {}) {
|
|
350
|
+
const name = String(tool || "tool").trim().toLowerCase() || "tool";
|
|
351
|
+
const detail = normalizeToolLogDetail(name, args, payload);
|
|
352
|
+
return `• ${formatToolDisplayName(name)}${detail ? ` ${detail}` : ""}`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function collapseConsecutiveUcodeToolEntries(entries = []) {
|
|
356
|
+
const source = Array.isArray(entries) ? entries : [];
|
|
357
|
+
const collapsed = [];
|
|
358
|
+
for (let index = 0; index < source.length;) {
|
|
359
|
+
const entry = source[index];
|
|
360
|
+
if (!entry || entry.kind !== "tool") {
|
|
361
|
+
collapsed.push(entry);
|
|
362
|
+
index += 1;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let end = index + 1;
|
|
367
|
+
while (end < source.length && source[end] && source[end].kind === "tool") end += 1;
|
|
368
|
+
const group = source.slice(index, end);
|
|
369
|
+
if (group.length === 1) {
|
|
370
|
+
collapsed.push(entry);
|
|
371
|
+
} else {
|
|
372
|
+
const detail = group
|
|
373
|
+
.map((item) => String(item.text || "").replace(/^•\s*/, ""))
|
|
374
|
+
.join("\n");
|
|
375
|
+
collapsed.push({
|
|
376
|
+
...entry,
|
|
377
|
+
text: `${String(entry.text || "• Tool")} · +${group.length - 1} calls (Ctrl+O expand)`,
|
|
378
|
+
detail,
|
|
379
|
+
expanded: false,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
index = end;
|
|
383
|
+
}
|
|
384
|
+
return collapsed;
|
|
385
|
+
}
|
|
386
|
+
|
|
328
387
|
/**
|
|
329
388
|
* Collapse attached-image prompt prefixes / base64 blobs for TUI log display.
|
|
330
389
|
*/
|
|
@@ -357,9 +416,6 @@ function buildUcodeSessionLogEntries(messages = [], options = {}) {
|
|
|
357
416
|
: { inCodeBlock: false };
|
|
358
417
|
const idPrefix = String(options.idPrefix || "h");
|
|
359
418
|
let seq = Number.isFinite(options.startSeq) ? Math.max(0, Math.floor(options.startSeq)) : 0;
|
|
360
|
-
const maxToolPreviewLines = Number.isFinite(options.maxToolPreviewLines)
|
|
361
|
-
? Math.max(1, Math.floor(options.maxToolPreviewLines))
|
|
362
|
-
: 4;
|
|
363
419
|
const entries = [];
|
|
364
420
|
|
|
365
421
|
const pushLines = (text, kind) => {
|
|
@@ -375,14 +431,12 @@ function buildUcodeSessionLogEntries(messages = [], options = {}) {
|
|
|
375
431
|
} else {
|
|
376
432
|
lines = source.split(/\r?\n/);
|
|
377
433
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
seq += 1;
|
|
385
|
-
}
|
|
434
|
+
entries.push({
|
|
435
|
+
id: `${idPrefix}-${seq}`,
|
|
436
|
+
text: lines.map((line) => String(line || "")).join("\n"),
|
|
437
|
+
kind,
|
|
438
|
+
});
|
|
439
|
+
seq += 1;
|
|
386
440
|
};
|
|
387
441
|
|
|
388
442
|
for (const message of list) {
|
|
@@ -392,37 +446,35 @@ function buildUcodeSessionLogEntries(messages = [], options = {}) {
|
|
|
392
446
|
const text = redactUserMessageForLog(messageContentText(message));
|
|
393
447
|
if (!text.trim()) continue;
|
|
394
448
|
const lines = text.split(/\r?\n/);
|
|
395
|
-
lines.
|
|
396
|
-
pushLines(index === 0 ? `› ${line}` : line, "user");
|
|
397
|
-
});
|
|
449
|
+
pushLines(lines.map((line, index) => (index === 0 ? `› ${line}` : line)).join("\n"), "user");
|
|
398
450
|
continue;
|
|
399
451
|
}
|
|
400
452
|
if (role === "assistant") {
|
|
401
453
|
const text = messageContentText(message);
|
|
402
454
|
if (text) pushLines(text, "assistant");
|
|
403
455
|
const calls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
})
|
|
411
|
-
.map((name) => name.trim())
|
|
412
|
-
.filter(Boolean);
|
|
413
|
-
if (names.length > 0) pushLines(`⚙ ${names.join(" · ")}`, "system");
|
|
456
|
+
for (const call of calls) {
|
|
457
|
+
if (!call || typeof call !== "object") continue;
|
|
458
|
+
const name = String(
|
|
459
|
+
(call.function && call.function.name) || call.name || "tool"
|
|
460
|
+
).trim();
|
|
461
|
+
pushLines(formatUcodeToolLine(name, parseToolCallArguments(call)), "tool");
|
|
414
462
|
}
|
|
415
463
|
continue;
|
|
416
464
|
}
|
|
417
465
|
if (role === "tool") {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
466
|
+
// The corresponding assistant tool_call already carries the command or
|
|
467
|
+
// path. Tool-result artifacts are internal storage references and do
|
|
468
|
+
// not belong in the restored transcript presentation.
|
|
469
|
+
continue;
|
|
422
470
|
}
|
|
423
471
|
}
|
|
424
472
|
|
|
425
|
-
return {
|
|
473
|
+
return {
|
|
474
|
+
entries: collapseConsecutiveUcodeToolEntries(entries),
|
|
475
|
+
nextSeq: seq,
|
|
476
|
+
markdownState,
|
|
477
|
+
};
|
|
426
478
|
}
|
|
427
479
|
|
|
428
480
|
function shouldEnterAgentSelection(inputValue = "") {
|
|
@@ -828,6 +880,19 @@ function buildToolMergeRowText(entries = []) {
|
|
|
828
880
|
return `· ${summary}`;
|
|
829
881
|
}
|
|
830
882
|
|
|
883
|
+
function buildUcodeToolRowText(entries = []) {
|
|
884
|
+
const list = Array.isArray(entries)
|
|
885
|
+
? entries.map((item) => normalizeToolMergeEntry(item))
|
|
886
|
+
: [];
|
|
887
|
+
if (list.length === 0) return "• Tool";
|
|
888
|
+
const first = list[0];
|
|
889
|
+
const firstLine = `• ${formatToolDisplayName(first.tool)}${first.detail ? ` ${first.detail}` : ""}`;
|
|
890
|
+
if (list.length === 1) return firstLine;
|
|
891
|
+
const errorCount = list.filter((item) => item.isError).length;
|
|
892
|
+
const errorSuffix = errorCount > 0 ? ` · ${errorCount} error${errorCount === 1 ? "" : "s"}` : "";
|
|
893
|
+
return `${firstLine} · +${list.length - 1} calls${errorSuffix}`;
|
|
894
|
+
}
|
|
895
|
+
|
|
831
896
|
/**
|
|
832
897
|
* Lay out the global-mode project rail inside a single line. Like
|
|
833
898
|
* planAgentsFooter, but with two differences:
|
|
@@ -1318,6 +1383,8 @@ module.exports = {
|
|
|
1318
1383
|
buildMergedToolExpandedLines,
|
|
1319
1384
|
buildMergedToolSummaryText,
|
|
1320
1385
|
buildToolMergeRowText,
|
|
1386
|
+
buildUcodeToolRowText,
|
|
1387
|
+
collapseConsecutiveUcodeToolEntries,
|
|
1321
1388
|
buildCompletions,
|
|
1322
1389
|
buildUcodeBannerLines,
|
|
1323
1390
|
buildUcodeSessionLogEntries,
|
|
@@ -140,7 +140,7 @@ function createBlessedAdapters(escapeFn = (value) => String(value || "")) {
|
|
|
140
140
|
return `{bold}${value}{/bold}`;
|
|
141
141
|
},
|
|
142
142
|
quoteMarker: () => "{gray-fg}│{/gray-fg}",
|
|
143
|
-
bulletMarker: () => "{gray-fg}
|
|
143
|
+
bulletMarker: () => "{gray-fg}-{/gray-fg}",
|
|
144
144
|
orderedMarker: (value) => `{gray-fg}${escape(value)}.{/gray-fg}`,
|
|
145
145
|
rule: () => "{gray-fg}────────────────────────{/gray-fg}",
|
|
146
146
|
fenceOpen: (language) => (
|
|
@@ -181,7 +181,7 @@ function createAnsiAdapters() {
|
|
|
181
181
|
return paint.bold(text);
|
|
182
182
|
},
|
|
183
183
|
quoteMarker: () => paint.gray("│"),
|
|
184
|
-
bulletMarker: () => paint.gray("
|
|
184
|
+
bulletMarker: () => paint.gray("-"),
|
|
185
185
|
orderedMarker: (value) => paint.gray(`${value}.`),
|
|
186
186
|
rule: () => paint.gray("────────────────────────"),
|
|
187
187
|
fenceOpen: (language) => (
|
package/src/ui/rustChatHost.js
CHANGED
|
@@ -39,6 +39,7 @@ const {
|
|
|
39
39
|
resolveDaemonEndpoint,
|
|
40
40
|
routeDaemonRequest,
|
|
41
41
|
} = require("../runtime/daemon/endpoint");
|
|
42
|
+
const PACKAGE_VERSION = require("../../package.json").version;
|
|
42
43
|
|
|
43
44
|
function stripTags(value) {
|
|
44
45
|
return String(value || "").replace(/\{[^}]+\}/g, "");
|
|
@@ -979,6 +980,7 @@ async function runChatRust(projectRoot, options = {}) {
|
|
|
979
980
|
const projects = env.globalMode ? loadGlobalProjectRows(activeProjectRoot) : [];
|
|
980
981
|
return {
|
|
981
982
|
status: "ready",
|
|
983
|
+
package_version: PACKAGE_VERSION,
|
|
982
984
|
footer: agents.footer || "",
|
|
983
985
|
entries: historyToEntries(history),
|
|
984
986
|
input_history: Array.isArray(inputHistory) ? inputHistory.filter(Boolean) : [],
|
package/src/ui/rustUcodeHost.js
CHANGED
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
} = require("../code/UcodeController");
|
|
18
18
|
const { createEnvelope, encodeMessage } = require("../runtime/contracts/uiProtocol");
|
|
19
19
|
const fmt = require("./format");
|
|
20
|
+
const PACKAGE_VERSION = require("../../package.json").version;
|
|
20
21
|
|
|
21
22
|
function stripTags(value) {
|
|
22
23
|
return String(value || "").replace(/\{[^}]+\}/g, "");
|
|
@@ -162,6 +163,46 @@ function normalizeToolLogEntry(entry = {}) {
|
|
|
162
163
|
return fmt.normalizeToolMergeEntry({ tool, detail, isError, errorText });
|
|
163
164
|
}
|
|
164
165
|
|
|
166
|
+
function splitUcodeBannerRow(line, index) {
|
|
167
|
+
const row = String(line || "");
|
|
168
|
+
const logo = String((fmt.UCODE_BANNER_LINES || [])[index] || "");
|
|
169
|
+
if (logo && row.startsWith(logo)) {
|
|
170
|
+
return { logo, metadata: row.slice(logo.length).trimStart() };
|
|
171
|
+
}
|
|
172
|
+
return { logo: "", metadata: row.trimStart() };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Keep a model's reasoning in one mutable scrollback entry. The TUI owns the
|
|
177
|
+
* collapsed/expanded presentation; the host only sends an initial row plus
|
|
178
|
+
* append-only deltas for that row.
|
|
179
|
+
*/
|
|
180
|
+
function createThinkingLogPublisher(publish, thinkingStatus, streamId) {
|
|
181
|
+
const id = `${String(streamId || "stream")}-thinking`;
|
|
182
|
+
let started = false;
|
|
183
|
+
|
|
184
|
+
function onThinkingDelta(delta) {
|
|
185
|
+
const text = String(delta || "");
|
|
186
|
+
if (!text) return;
|
|
187
|
+
if (!started) {
|
|
188
|
+
started = true;
|
|
189
|
+
publish("thinking.start", { id });
|
|
190
|
+
}
|
|
191
|
+
publish("thinking.delta", { id, text });
|
|
192
|
+
if (thinkingStatus && typeof thinkingStatus.onThinkingDelta === "function") {
|
|
193
|
+
thinkingStatus.onThinkingDelta(text);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function stop() {
|
|
198
|
+
if (thinkingStatus && typeof thinkingStatus.reset === "function") {
|
|
199
|
+
thinkingStatus.reset();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return { id, onThinkingDelta, stop };
|
|
204
|
+
}
|
|
205
|
+
|
|
165
206
|
async function runUcodeRust(props = {}) {
|
|
166
207
|
const plan = resolveTuiLaunchPlan({
|
|
167
208
|
mode: props.tuiMode || process.env.UFOO_TUI || "rust",
|
|
@@ -321,25 +362,22 @@ async function runUcodeRust(props = {}) {
|
|
|
321
362
|
} else {
|
|
322
363
|
lines = raw.split(/\r?\n/);
|
|
323
364
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (/\x1b\[/.test(line)) {
|
|
338
|
-
last.text = line;
|
|
339
|
-
}
|
|
340
|
-
publish("transcript.append", last);
|
|
365
|
+
const block = lines.map((line) => String(line || "")).join("\n");
|
|
366
|
+
entrySeq += 1;
|
|
367
|
+
const entry = {
|
|
368
|
+
id: `u-${entrySeq}`,
|
|
369
|
+
kind,
|
|
370
|
+
text: stripTags(block),
|
|
371
|
+
// Keep ANSI in a parallel field if strip removes styling; prefer the
|
|
372
|
+
// rendered block for Rust when it contains escape sequences.
|
|
373
|
+
ansi: block,
|
|
374
|
+
speaker: "",
|
|
375
|
+
};
|
|
376
|
+
if (/\x1b\[/.test(block)) {
|
|
377
|
+
entry.text = block;
|
|
341
378
|
}
|
|
342
|
-
|
|
379
|
+
publish("transcript.append", entry);
|
|
380
|
+
return entry;
|
|
343
381
|
}
|
|
344
382
|
|
|
345
383
|
function replaceTranscript(entries = []) {
|
|
@@ -351,6 +389,8 @@ async function runUcodeRust(props = {}) {
|
|
|
351
389
|
kind: String((entry && entry.kind) || "system"),
|
|
352
390
|
text,
|
|
353
391
|
speaker: String((entry && entry.speaker) || ""),
|
|
392
|
+
detail: String((entry && entry.detail) || ""),
|
|
393
|
+
expanded: Boolean(entry && entry.expanded),
|
|
354
394
|
};
|
|
355
395
|
});
|
|
356
396
|
entrySeq = Math.max(entrySeq, normalized.length);
|
|
@@ -461,13 +501,25 @@ async function runUcodeRust(props = {}) {
|
|
|
461
501
|
);
|
|
462
502
|
return {
|
|
463
503
|
status: "ready",
|
|
504
|
+
package_version: PACKAGE_VERSION,
|
|
464
505
|
footer: agentsSnap.footer,
|
|
465
|
-
entries: banner.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
506
|
+
entries: banner.map((line, idx) => {
|
|
507
|
+
const { logo, metadata } = splitUcodeBannerRow(line, idx);
|
|
508
|
+
return {
|
|
509
|
+
id: `b-${idx}`,
|
|
510
|
+
kind: "banner",
|
|
511
|
+
// Logo and metadata are separate raw renderer segments. Neither
|
|
512
|
+
// ever passes through the ordinary log formatter.
|
|
513
|
+
text: logo,
|
|
514
|
+
detail: metadata,
|
|
515
|
+
speaker: "",
|
|
516
|
+
};
|
|
517
|
+
}).concat([{
|
|
518
|
+
id: `b-${banner.length}`,
|
|
519
|
+
kind: "spacer",
|
|
520
|
+
text: "",
|
|
469
521
|
speaker: "",
|
|
470
|
-
})
|
|
522
|
+
}]),
|
|
471
523
|
input_history: [],
|
|
472
524
|
agents: agentsSnap.agents,
|
|
473
525
|
attachment_count: pendingAttachments.length,
|
|
@@ -509,17 +561,28 @@ async function runUcodeRust(props = {}) {
|
|
|
509
561
|
tools.beginScope();
|
|
510
562
|
thinking.reset();
|
|
511
563
|
publish("stream.start", { id: streamId });
|
|
564
|
+
const thinkingLog = createThinkingLogPublisher(publish, thinking, streamId);
|
|
565
|
+
let responseStarted = false;
|
|
566
|
+
const beginResponse = () => {
|
|
567
|
+
if (responseStarted) return;
|
|
568
|
+
responseStarted = true;
|
|
569
|
+
thinkingLog.stop();
|
|
570
|
+
publish("status.set", { text: "Generating response…", busy: true });
|
|
571
|
+
};
|
|
512
572
|
try {
|
|
513
573
|
const result = await submit(answerText, props.state, {
|
|
514
574
|
signal: abort.signal,
|
|
515
575
|
onDelta: (delta) => {
|
|
576
|
+
beginResponse();
|
|
516
577
|
publish("stream.delta", { id: streamId, text: String(delta || "") });
|
|
517
578
|
},
|
|
518
|
-
onThinkingDelta:
|
|
579
|
+
onThinkingDelta: (delta) => {
|
|
580
|
+
if (!responseStarted) thinkingLog.onThinkingDelta(delta);
|
|
581
|
+
},
|
|
519
582
|
onToolLog: ingestToolLog,
|
|
520
583
|
});
|
|
521
584
|
tools.flush();
|
|
522
|
-
|
|
585
|
+
thinkingLog.stop();
|
|
523
586
|
publish("stream.done", { id: streamId });
|
|
524
587
|
if (!result || result.ok === false) {
|
|
525
588
|
appendLog(`Error: ${(result && result.error) || "resume failed"}`, "error");
|
|
@@ -535,7 +598,7 @@ async function runUcodeRust(props = {}) {
|
|
|
535
598
|
return { ok: true, waiting: false };
|
|
536
599
|
} catch (err) {
|
|
537
600
|
tools.flush();
|
|
538
|
-
|
|
601
|
+
thinkingLog.stop();
|
|
539
602
|
publish("stream.done", { id: streamId });
|
|
540
603
|
if (abort.signal.aborted) {
|
|
541
604
|
appendLog("Task cancelled.", "system");
|
|
@@ -557,20 +620,31 @@ async function runUcodeRust(props = {}) {
|
|
|
557
620
|
tools.beginScope();
|
|
558
621
|
thinking.reset();
|
|
559
622
|
publish("stream.start", { id: streamId });
|
|
623
|
+
const thinkingLog = createThinkingLogPublisher(publish, thinking, streamId);
|
|
624
|
+
let responseStarted = false;
|
|
625
|
+
const beginResponse = () => {
|
|
626
|
+
if (responseStarted) return;
|
|
627
|
+
responseStarted = true;
|
|
628
|
+
thinkingLog.stop();
|
|
629
|
+
publish("status.set", { text: "Generating response…", busy: true });
|
|
630
|
+
};
|
|
560
631
|
try {
|
|
561
632
|
const nlResult = await props.runNaturalLanguageTask(text, props.state, {
|
|
562
633
|
signal: abort.signal,
|
|
563
634
|
onDelta: (delta) => {
|
|
635
|
+
beginResponse();
|
|
564
636
|
publish("stream.delta", { id: streamId, text: String(delta || "") });
|
|
565
637
|
},
|
|
566
|
-
onThinkingDelta:
|
|
638
|
+
onThinkingDelta: (delta) => {
|
|
639
|
+
if (!responseStarted) thinkingLog.onThinkingDelta(delta);
|
|
640
|
+
},
|
|
567
641
|
onToolLog: ingestToolLog,
|
|
568
642
|
onPhase: (event) => {
|
|
569
643
|
if (!event || typeof event !== "object") return;
|
|
570
644
|
if (event.type === "request_start") {
|
|
571
645
|
publish("status.set", { text: "Waiting for model…", busy: true });
|
|
572
646
|
} else if (event.type === "text_delta") {
|
|
573
|
-
|
|
647
|
+
beginResponse();
|
|
574
648
|
} else if (event.type === "tool_request") {
|
|
575
649
|
const tool = String(event.name || "tool").trim() || "tool";
|
|
576
650
|
const label = (fmt.TOOL_LABELS && fmt.TOOL_LABELS[tool.toLowerCase()])
|
|
@@ -580,7 +654,7 @@ async function runUcodeRust(props = {}) {
|
|
|
580
654
|
},
|
|
581
655
|
});
|
|
582
656
|
tools.flush();
|
|
583
|
-
|
|
657
|
+
thinkingLog.stop();
|
|
584
658
|
publish("stream.done", { id: streamId });
|
|
585
659
|
if (nlResult && nlResult.summary) appendLog(nlResult.summary, "system");
|
|
586
660
|
if (typeof props.formatNlResult === "function" && nlResult) {
|
|
@@ -596,7 +670,7 @@ async function runUcodeRust(props = {}) {
|
|
|
596
670
|
return { ok: true, waiting: false, result: nlResult };
|
|
597
671
|
} catch (err) {
|
|
598
672
|
tools.flush();
|
|
599
|
-
|
|
673
|
+
thinkingLog.stop();
|
|
600
674
|
publish("stream.done", { id: streamId });
|
|
601
675
|
if (abort.signal.aborted) {
|
|
602
676
|
appendLog("Task cancelled.", "system");
|
|
@@ -994,6 +1068,8 @@ module.exports = {
|
|
|
994
1068
|
runUcodeRust,
|
|
995
1069
|
buildPlanSetPayload,
|
|
996
1070
|
normalizeToolLogEntry,
|
|
1071
|
+
splitUcodeBannerRow,
|
|
1072
|
+
createThinkingLogPublisher,
|
|
997
1073
|
buildUcodeAgentsSnapshot,
|
|
998
1074
|
buildUcodeCompletionItems,
|
|
999
1075
|
};
|
|
@@ -23,13 +23,11 @@ function createToolMergePublisher(publish) {
|
|
|
23
23
|
merge = null;
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
-
const summary = fmt.
|
|
26
|
+
const summary = fmt.buildUcodeToolRowText(merge.entries);
|
|
27
27
|
const detail = fmt.buildMergedToolExpandedLines(merge.entries).join("\n");
|
|
28
|
-
const row =
|
|
29
|
-
?
|
|
30
|
-
:
|
|
31
|
-
? `· ${summary} (Ctrl+O expand)`
|
|
32
|
-
: summary);
|
|
28
|
+
const row = merge.entries.length >= 2
|
|
29
|
+
? `${summary} (Ctrl+O expand)`
|
|
30
|
+
: summary;
|
|
33
31
|
publish("tool.group", {
|
|
34
32
|
id: `tool-merge-${merge.id}`,
|
|
35
33
|
summary: row || summary,
|
|
@@ -47,7 +45,7 @@ function createToolMergePublisher(publish) {
|
|
|
47
45
|
if (merge) {
|
|
48
46
|
publish("tool.start", {
|
|
49
47
|
id: `tool-merge-${merge.id}`,
|
|
50
|
-
summary: fmt.
|
|
48
|
+
summary: fmt.buildUcodeToolRowText(merge.entries),
|
|
51
49
|
});
|
|
52
50
|
}
|
|
53
51
|
}
|