feat(assistant): #91 steps humains en direct + outils web_search/fetch_url (labels backend, SSE step, garde SSRF)
CI / lint (push) Successful in 1m22s
CI / security (push) Successful in 55s
CI / test (push) Successful in 3m0s
CI / build (push) Successful in 51s
CI / e2e (push) Successful in 10m41s

This commit is contained in:
2026-09-15 17:46:51 -04:00
parent 18b7dee2f6
commit 19d5b931c4
14 changed files with 745 additions and 32 deletions
+13 -1
View File
@@ -335,7 +335,7 @@ async function main() {
await test("agent tool calls collapse into a discreet steps block", () => {
const b = new BooksLM();
const trace = b._renderToolActivity([
{ name: "read_file", ok: true },
{ name: "read_file", ok: true, step: { key: "file_read", params: { value: "a.md" } } },
{ name: "search", ok: true },
]);
assert.equal(trace.tagName, "DETAILS", "collapsible details element");
@@ -345,6 +345,18 @@ async function main() {
assert.equal(trace.querySelectorAll(".bookslm-tool-line").length, 2, "each step listed inside");
});
await test("_stepText uses the backend label key, falls back to tool name", () => {
const b = new BooksLM();
// No locales loaded in the JSDOM harness → t() returns the key, so the
// fallback path (legacy name) is what surfaces; assert it never throws
// and produces a non-empty string for both shapes.
const withStep = b._stepText({ name: "read_file", step: { key: "file_read", params: { value: "a.md" } } });
const legacy = b._stepText({ name: "read_file" });
assert.equal(typeof withStep, "string");
assert.equal(typeof legacy, "string");
assert.ok(legacy.length > 0);
});
// ── 5. Source guards: no window.prompt() and authenticated BooksLM ──
await test("ai.js no longer uses window.prompt()", async () => {
const { readFileSync } = await import("node:fs");