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,
|
||||
|
||||
@@ -1780,7 +1780,37 @@
|
||||
"bookslm.insert_hint": "Append the answer to the document open in the editor",
|
||||
"bookslm.inserted": "Answer added to the document",
|
||||
"bookslm.insert_no_editor": "No document open in the editor",
|
||||
"ai.steps_count": "{count} steps",
|
||||
"ai.steps_count": "{count} step",
|
||||
"ai.steps_count_plural": "{count} steps",
|
||||
"ai.activity_thinking": "Thinking…",
|
||||
"ai.step.thought": "Thought: {value}",
|
||||
"ai.step.backlinks": "Analyzed backlinks",
|
||||
"ai.step.backup_diff": "Compared backup: {value}",
|
||||
"ai.step.backup_restore": "Restored backup: {value}",
|
||||
"ai.step.backups": "Listed backups",
|
||||
"ai.step.dir_create": "Proposed directory: {value}",
|
||||
"ai.step.dir_delete": "Deleted directory: {value}",
|
||||
"ai.step.dir_rename": "Renamed directory: {value}",
|
||||
"ai.step.directory": "Explored directory: {value}",
|
||||
"ai.step.file_append": "Appended to file: {value}",
|
||||
"ai.step.file_create": "Proposed file: {value}",
|
||||
"ai.step.file_delete": "Deleted file: {value}",
|
||||
"ai.step.file_edit": "Edited file: {value}",
|
||||
"ai.step.file_read": "Read file: {value}",
|
||||
"ai.step.file_rename": "Renamed file: {value}",
|
||||
"ai.step.files": "Listed files",
|
||||
"ai.step.generic": "Used tool: {tool}",
|
||||
"ai.step.graph": "Consulted the vault graph",
|
||||
"ai.step.move": "Moved: {value}",
|
||||
"ai.step.recent": "Listed recent files",
|
||||
"ai.step.replace": "Replacements: {value}",
|
||||
"ai.step.search": "Searched the vault: {value}",
|
||||
"ai.step.search_paths": "Searched paths: {value}",
|
||||
"ai.step.tags": "Listed tags",
|
||||
"ai.step.tags_suggest": "Suggested tags for {value}",
|
||||
"ai.step.vaults": "Listed the vaults",
|
||||
"ai.step.fetch_url": "Opened a web page: {value}",
|
||||
"ai.step.web_search": "Searched the web: {value}",
|
||||
"bookslm.copied": "Copied to clipboard",
|
||||
"bookslm.error": "AI service error",
|
||||
"bookslm.regenerate": "Regenerate",
|
||||
|
||||
@@ -1780,7 +1780,37 @@
|
||||
"bookslm.insert_hint": "Ajouter la réponse au document ouvert dans l'éditeur",
|
||||
"bookslm.inserted": "Réponse ajoutée au document",
|
||||
"bookslm.insert_no_editor": "Aucun document ouvert dans l'éditeur",
|
||||
"ai.steps_count": "{count} étapes",
|
||||
"ai.steps_count": "{count} étape",
|
||||
"ai.steps_count_plural": "{count} étapes",
|
||||
"ai.activity_thinking": "Réflexion…",
|
||||
"ai.step.thought": "Réflexion : {value}",
|
||||
"ai.step.backlinks": "Backlinks analysés",
|
||||
"ai.step.backup_diff": "Comparaison de sauvegarde : {value}",
|
||||
"ai.step.backup_restore": "Sauvegarde restaurée : {value}",
|
||||
"ai.step.backups": "Sauvegardes consultées",
|
||||
"ai.step.dir_create": "Dossier proposé : {value}",
|
||||
"ai.step.dir_delete": "Dossier supprimé : {value}",
|
||||
"ai.step.dir_rename": "Dossier renommé : {value}",
|
||||
"ai.step.directory": "Répertoire exploré : {value}",
|
||||
"ai.step.file_append": "Fichier complété : {value}",
|
||||
"ai.step.file_create": "Fichier proposé : {value}",
|
||||
"ai.step.file_delete": "Fichier supprimé : {value}",
|
||||
"ai.step.file_edit": "Fichier modifié : {value}",
|
||||
"ai.step.file_read": "Fichier lu : {value}",
|
||||
"ai.step.file_rename": "Fichier renommé : {value}",
|
||||
"ai.step.files": "Fichiers listés",
|
||||
"ai.step.generic": "Outil utilisé : {tool}",
|
||||
"ai.step.graph": "Graphe du vault consulté",
|
||||
"ai.step.move": "Élément déplacé : {value}",
|
||||
"ai.step.recent": "Fichiers récents consultés",
|
||||
"ai.step.replace": "Remplacements : {value}",
|
||||
"ai.step.search": "Recherche dans le vault : {value}",
|
||||
"ai.step.search_paths": "Chemins recherchés : {value}",
|
||||
"ai.step.tags": "Tags consultés",
|
||||
"ai.step.tags_suggest": "Tags suggérés pour {value}",
|
||||
"ai.step.vaults": "Liste des vaults consultée",
|
||||
"ai.step.fetch_url": "Page web consultée : {value}",
|
||||
"ai.step.web_search": "Recherche sur le web : {value}",
|
||||
"bookslm.copied": "Réponse copiée dans le presse-papiers",
|
||||
"bookslm.error": "Erreur du service AI",
|
||||
"bookslm.regenerate": "Régénérer",
|
||||
|
||||
Reference in New Issue
Block a user