This commit is contained in:
seb
2026-09-25 06:26:59 +02:00
parent b7ef321e05
commit 2efd9bda39
7 changed files with 383 additions and 19 deletions

View File

@@ -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;
}