u
This commit is contained in:
99
src/fido.js
99
src/fido.js
@@ -38,7 +38,12 @@ import { printToolCall, emitGenericToolResult } from './tools/notify.js';
|
||||
import { COMMANDS, REASONING_LEVELS, findCommand } from './slash-commands.js';
|
||||
import { compactPatchToolCalls, invalidateStaleReadResults } from './compact.js';
|
||||
import { mount, unmount, ui } from './ui/index.js';
|
||||
import { getBlockContentWidth, renderAssistantBlock, renderToolBlock } from './ui/blocks.js';
|
||||
import {
|
||||
getBlockContentWidth,
|
||||
renderAssistantBlock,
|
||||
renderToolBlock,
|
||||
renderUserBlock,
|
||||
} from './ui/blocks.js';
|
||||
|
||||
// \u2500\u2500\u2500 Markdown \u2192 console renderer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
||||
|
||||
@@ -130,6 +135,88 @@ function toolErrorLine(name, error) {
|
||||
return renderToolBlock(chalk.red(`\u2716 Tool error [${name}]: ${error}`));
|
||||
}
|
||||
|
||||
const RECALL_TOOL_LIMIT = 4000;
|
||||
|
||||
/** Convert the provider's content variants into text suitable for a block. */
|
||||
function messageText(content) {
|
||||
if (typeof content === 'string') return content;
|
||||
if (Array.isArray(content)) {
|
||||
return content.map((part) => {
|
||||
if (typeof part === 'string') return part;
|
||||
if (part && typeof part === 'object') return part.text ?? part.content ?? '';
|
||||
return '';
|
||||
}).filter(Boolean).join('\n');
|
||||
}
|
||||
if (content == null) return '';
|
||||
if (typeof content === 'object') {
|
||||
try {
|
||||
return JSON.stringify(content) ?? String(content);
|
||||
} catch {
|
||||
return String(content);
|
||||
}
|
||||
}
|
||||
return String(content);
|
||||
}
|
||||
|
||||
function clipRecallText(text, limit = RECALL_TOOL_LIMIT) {
|
||||
const value = String(text ?? '');
|
||||
if (value.length <= limit) return value;
|
||||
return value.slice(0, limit - 20) + '\n… [recalled message truncated] …';
|
||||
}
|
||||
|
||||
/** Render one restored provider message in the same role blocks as live turns. */
|
||||
function recalledMessageBlock(message) {
|
||||
if (!message || typeof message !== 'object') return null;
|
||||
|
||||
const content = messageText(message.content);
|
||||
if (message.role === 'user') {
|
||||
if (!content.trim()) return null;
|
||||
return renderUserBlock(`› You ${content.replace(/\n/g, '\n ')}`);
|
||||
}
|
||||
|
||||
if (message.role === 'assistant') {
|
||||
if (content.trim()) return assistantBlock(content);
|
||||
|
||||
const calls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
||||
if (calls.length === 0) return null;
|
||||
const lines = calls.map((call) => {
|
||||
const fn = call?.function || {};
|
||||
let args = fn.arguments ?? '';
|
||||
if (typeof args !== 'string') {
|
||||
try {
|
||||
args = JSON.stringify(args);
|
||||
} catch {
|
||||
args = String(args);
|
||||
}
|
||||
}
|
||||
args = String(args).replace(/\n/g, ' ').trim();
|
||||
if (args.length > 240) args = args.slice(0, 237) + '...';
|
||||
return `⚙ Tool: ${fn.name || 'unknown'}${args ? `(${args})` : ''}`;
|
||||
});
|
||||
return renderToolBlock(chalk.dim(lines.join('\n')));
|
||||
}
|
||||
|
||||
if (message.role === 'tool') {
|
||||
if (!content.trim()) return null;
|
||||
return renderToolBlock(chalk.dim(`↳ ${clipRecallText(content)}`));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Add the restored conversation to the visible transcript. */
|
||||
function logRecalledMessages(restored) {
|
||||
let shown = 0;
|
||||
for (const message of restored) {
|
||||
const block = recalledMessageBlock(message);
|
||||
if (block) {
|
||||
ui.log(block);
|
||||
shown++;
|
||||
}
|
||||
}
|
||||
return shown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a bash_exec result as a complete tool block: status, stdout/stderr,
|
||||
* and any truncation notes share the same teal background and amber rail.
|
||||
@@ -379,16 +466,22 @@ async function main() {
|
||||
continue;
|
||||
}
|
||||
const snap = suspends[choice];
|
||||
if (!snap) {
|
||||
ui.log(chalk.yellow(' Recall cancelled.'));
|
||||
continue;
|
||||
}
|
||||
const restored = Array.isArray(snap.messages) ? snap.messages : [];
|
||||
const restoredMessages = restored.filter((m) => m && m.role !== 'system');
|
||||
// Keep the freshly built system prompt (workspace may have changed)
|
||||
// and restore the rest of the conversation verbatim.
|
||||
messages = [
|
||||
{ role: 'system', content: systemPrompt },
|
||||
...restored.filter((m) => m.role !== 'system'),
|
||||
...restoredMessages,
|
||||
];
|
||||
const turns = messages.length - 1;
|
||||
ui.log(chalk.green(` \u25b6 Recalled: ${snap.description}`));
|
||||
ui.log(chalk.dim(` ${turns} message${turns === 1 ? '' : 's'} restored.`));
|
||||
ui.log(chalk.dim(` ${turns} message${turns === 1 ? '' : 's'} restored; showing transcript below.`));
|
||||
logRecalledMessages(restoredMessages);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user