feat(assistant): #91 steps humains en direct + outils web_search/fetch_url (labels backend, SSE step, garde SSRF)
This commit is contained in:
+41
-16
@@ -2058,22 +2058,35 @@ class BooksLM {
|
||||
|
||||
// ── Tool activity & confirmations (agent mode) ──────────────────────
|
||||
|
||||
/** Agent tool calls collapse into a discreet “N steps” line (Notion-style). */
|
||||
_renderToolActivity(toolCalls) {
|
||||
const wrap = document.createElement('details');
|
||||
wrap.className = 'bookslm-tool-trace';
|
||||
const summary = document.createElement('summary');
|
||||
summary.textContent = t('ai.steps_count', { count: toolCalls.length });
|
||||
wrap.appendChild(summary);
|
||||
for (const call of toolCalls) {
|
||||
const line = document.createElement('div');
|
||||
line.className = 'bookslm-tool-line' + (call.ok === false ? ' failed' : '');
|
||||
line.textContent = `${call.ok === false ? '⚠' : '🔧'} ${t('ai.tool_call', { name: call.name })}`;
|
||||
if (call.ok === false) line.title = t('ai.tool_call_failed', { name: call.name });
|
||||
wrap.appendChild(line);
|
||||
/** Human one-liner for a recorded step (backend label key or legacy name). */
|
||||
_stepText(call) {
|
||||
const step = call.step;
|
||||
if (step && step.key) {
|
||||
const key = `ai.step.${step.key}`;
|
||||
const label = t(key, step.params || {});
|
||||
if (label && label !== key) return label;
|
||||
}
|
||||
return t('ai.tool_call', { name: call.name });
|
||||
}
|
||||
|
||||
/** Agent tool calls collapse into a discreet “N steps” line (Notion-style). */
|
||||
_renderToolActivity(toolCalls) {
|
||||
const wrap = document.createElement('details');
|
||||
wrap.className = 'bookslm-tool-trace';
|
||||
const summary = document.createElement('summary');
|
||||
const countKey = toolCalls.length > 1 ? 'ai.steps_count_plural' : 'ai.steps_count';
|
||||
summary.textContent = t(countKey, { count: toolCalls.length });
|
||||
wrap.appendChild(summary);
|
||||
for (const call of toolCalls) {
|
||||
const line = document.createElement('div');
|
||||
line.className = 'bookslm-tool-line' + (call.ok === false ? ' failed' : '');
|
||||
line.textContent = `${call.ok === false ? '⚠' : '·'} ${this._stepText(call)}`;
|
||||
if (call.ok === false) line.title = t('ai.tool_call_failed', { name: call.name });
|
||||
else if (call.step && call.step.params && call.step.params.value) line.title = call.step.params.value;
|
||||
wrap.appendChild(line);
|
||||
}
|
||||
return wrap;
|
||||
}
|
||||
return wrap;
|
||||
}
|
||||
|
||||
_renderConfirmationCard(msg) {
|
||||
const conf = msg.confirmation;
|
||||
@@ -2393,10 +2406,22 @@ class BooksLM {
|
||||
}
|
||||
|
||||
if (currentEvent === 'tool') {
|
||||
assistantMsg.toolCalls.push({ name: data.name, ok: data.ok !== false, arguments: data.arguments || {} });
|
||||
assistantMsg.toolCalls.push({
|
||||
name: data.name,
|
||||
ok: data.ok !== false,
|
||||
arguments: data.arguments || {},
|
||||
step: data.step || null,
|
||||
});
|
||||
this._setActivity('working', t('ai.activity_tool', { name: data.name }));
|
||||
return;
|
||||
}
|
||||
// Intermediate reasoning note streamed by the agent (Notion-style
|
||||
// ". thought" line inside the steps block).
|
||||
if (currentEvent === 'step' && data.step) {
|
||||
assistantMsg.toolCalls.push({ name: '', ok: true, arguments: {}, step: data.step });
|
||||
this._setActivity('working', t('ai.activity_thinking'));
|
||||
return;
|
||||
}
|
||||
if (currentEvent === 'confirmation') {
|
||||
assistantMsg.confirmation = {
|
||||
pending: data.pending || data,
|
||||
|
||||
Reference in New Issue
Block a user