// Tests for the sidecar's tool_use -> action-chip mapping. node test/actions.test.mjs import assert from "node:assert/strict"; import { actionsFromToolUse, CHIP_TOOLS } from "../agent/actions.mjs"; import { sanitizeActions } from "../web/static/ui.js"; let failures = 0; async function test(name, fn) { try { await fn(); console.log(`PASS ${name}`); } catch (e) { failures++; console.log(`FAIL ${name} — ${e.message.split("\n")[0]}`); } } await test("maps tool_use blocks to actions, dropping junk", () => { const actions = actionsFromToolUse([ { type: "text", text: "Here you go —" }, { type: "tool_use", name: "set_theme", input: { theme: "plum" } }, { type: "tool_use", name: "set_theme", input: { theme: "neon" } }, // invalid theme { type: "tool_use", name: "set_appearance", input: { appearance: "dark" } }, { type: "tool_use", name: "rm_rf", input: {} }, // not a tool we offer { type: "tool_use", name: "open_profile", input: {} }, ]); assert.deepEqual(actions, [ { type: "set_theme", theme: "plum" }, { type: "set_appearance", appearance: "dark" }, { type: "open_profile" }, ]); }); await test("every action the sidecar can emit survives the client whitelist", () => { // The two whitelists (agent/actions.mjs and ui.js) must stay in step: a chip // Eleven proposes that the client would drop is a silent no-op button. const emitted = actionsFromToolUse([ { type: "tool_use", name: "set_theme", input: { theme: "litroom" } }, { type: "tool_use", name: "set_appearance", input: { appearance: "system" } }, { type: "tool_use", name: "open_theme_picker", input: {} }, { type: "tool_use", name: "open_profile", input: {} }, ]); assert.equal(emitted.length, CHIP_TOOLS.length); assert.equal(sanitizeActions(emitted).length, emitted.length); }); console.log(failures === 0 ? "\nALL PASSED" : `\n${failures} FAILED`); process.exit(failures === 0 ? 0 : 1);