const assert = require("assert"); const fs = require("fs"); const { ensureDataDirs, PLUGIN_ROOT, PLUGIN_DATA, resolveData } = require("../backend/paths"); const { canUse } = require("../backend/permissions"); const { ToolRegistry } = require("../backend/tool_router"); const { RequestQueue } = require("../backend/queue_manager"); const { RuntimeManager, runCaptured } = require("../backend/runtime_manager"); const { getRuntimeState } = require("../backend/config_manager"); const { AiProvider } = require("../backend/ai_provider"); const { shouldAutoResume } = require("../index"); const { normalizeExitCode, classifyLaunchError } = require("../backend/error_codes"); const { redact } = require("../backend/diagnostics"); const { validateArchivePath, classifyError } = require("../backend/downloader"); const { EventEmitter } = require("events"); async function run() { ensureDataDirs(); assert(PLUGIN_DATA.startsWith(PLUGIN_ROOT)); assert(resolveData("models", "model.gguf").startsWith(PLUGIN_ROOT)); assert.throws(() => resolveData("..", "..", "outside"), /escapes/); assert.throws(() => validateArchivePath("../../outside.exe"), /traversal/); assert.doesNotThrow(() => validateArchivePath("bin/llama-server.exe")); assert.equal(classifyError(new Error("source unavailable (404)")).category, "http_404"); assert.equal(classifyError(new Error("hash mismatch")).category, "hash_mismatch"); const accessViolation = normalizeExitCode(-1073741819, null, "win32"); assert.equal(accessViolation.unsigned_exit_code, 3221225477); assert.equal(accessViolation.hex_exit_code, "0xC0000005"); assert.equal(accessViolation.code, "STATUS_ACCESS_VIOLATION"); assert.equal(normalizeExitCode(139, null, "linux").code, "SIGSEGV"); assert.equal(classifyLaunchError({ code: "EACCES" }, "linux").category, "permission_denied"); assert.deepEqual(redact({ token: "secret", nested: { password: "secret", value: "ok" } }), { token: "[REDACTED]", nested: { password: "[REDACTED]", value: "ok" } }); const captured = await runCaptured(process.execPath, ["-e", "console.log('llama server usage')"], process.cwd(), 3000); assert.equal(captured.code, 0); assert.match(captured.stdout, /llama server usage/); const config = { assistant_visibility: { admins: true, mods: false, users: true } }; assert.equal(canUse({ id: "a", isAdmin: true }, config), true); assert.equal(canUse({ id: "m", isMod: true }, config), false); assert.equal(canUse({ id: "u" }, config), true); assert.equal(canUse(null, config), false); const audit = []; const calls = []; const registry = new ToolRegistry((entry) => audit.push(entry)); registry.register({ tool_id: "test.action", display_name: "Test action", description: "Runs a test workflow.", owning_plugin: "test", required_role: "user", required_permission: "test.self", permission_check: ({ user }) => user.id === "user-1", schema: { amount: "integer", recipient: "string" }, confirmation_required: true, risk_level: "sensitive", audit_category: "test", workflow_handler: async (input) => { calls.push(input); return { ok: true }; } }); assert.throws(() => registry.prepare({ tool: "test.action", args: { amount: "bad", recipient: "x" }, user: { id: "user-1" }, role: "user", sessionId: "s1" }), /integer/); assert.throws(() => registry.prepare({ tool: "test.action", args: { amount: 1, recipient: "x" }, user: { id: "other" }, role: "user", sessionId: "s1" }), /permission/); const prepared = registry.prepare({ tool: "test.action", args: { amount: 2, recipient: "x" }, user: { id: "user-1" }, role: "user", sessionId: "s1" }); await assert.rejects(() => registry.confirm({ id: prepared.confirmation.id, user: { id: "user-1" }, sessionId: "wrong" }), /invalid or expired/); const expiring = registry.prepare({ tool: "test.action", args: { amount: 2, recipient: "x" }, user: { id: "user-1" }, role: "user", sessionId: "s1" }); registry.confirmations.get(expiring.confirmation.id).expiresAt = Date.now() - 1; await assert.rejects(() => registry.confirm({ id: expiring.confirmation.id, user: { id: "user-1" }, sessionId: "s1" }), /invalid or expired/); const valid = registry.prepare({ tool: "test.action", args: { amount: 3, recipient: "x" }, user: { id: "user-1" }, role: "user", sessionId: "s1" }); await registry.confirm({ id: valid.confirmation.id, user: { id: "user-1" }, sessionId: "s1" }); assert.equal(calls[0].user.id, "user-1"); assert.equal(calls[0].initiated_via_ai, true); assert.equal(audit[0].tool_executed, true); const queueConfig = { concurrency: 1, max_queue_length: 1, per_user_requests_per_minute: 20 }; const queue = new RequestQueue(() => queueConfig); let release; const blocked = new Promise((resolve) => { release = resolve; }); const first = queue.run("u1", "user", () => blocked); const second = queue.run("u2", "user", async () => "second"); await assert.rejects(() => queue.run("u3", "user", async () => "third"), /busy/); release("first"); assert.equal(await first, "first"); assert.equal(await second, "second"); const fakeMetrics = { record() {} }; const provider = new AiProvider({ getConfig: () => ({ instructions: { out_of_scope_response: "OUT" } }), runtime: { infer: async () => { throw new Error("must not run"); } }, queue, tools: registry, metrics: fakeMetrics, getContext: () => [] }); const refused = await provider.generate({ message: "What is the capital of France?", user: { id: "u1" }, sessionId: "s1" }); assert.equal(refused.refusal_reason, "out_of_scope"); const routed = await provider.generate({ message: "Where can I find Twitch configuration?", user: { id: "u1" }, sessionId: "s1" }); assert.equal(routed.success, true); assert.match(routed.text, /twitch-wizard/); const ambiguousProvider = new AiProvider({ getConfig: () => ({ selected_model_id: "test", request_timeout_ms: 1000, logging: {}, instructions: { identity: "Lumi", style: "Brief", allowed_topics: "Lumi", maximum_answer_length: 700, out_of_scope_response: "OUT" } }), runtime: { infer: async () => ({ choices: [{ message: { content: "Open the relevant Lumi settings page." }, finish_reason: "stop" }] }) }, queue, tools: registry, metrics: fakeMetrics, getContext: () => [] }); const ambiguous = await ambiguousProvider.generate({ message: "How do I change this option?", user: { id: "u1" }, sessionId: "s1" }); assert.equal(ambiguous.success, true); let diagnosticMessages; const testProvider = new AiProvider({ getConfig: () => ({ selected_model_id: "test", instructions: { maximum_answer_length: 700 } }), runtime: { infer: async (messages) => { diagnosticMessages = messages; return { choices: [{ message: { content: "There are 3 Rs." }, finish_reason: "stop" }] }; } }, queue, tools: registry, metrics: fakeMetrics, getContext: () => [] }); const diagnosticTest = await testProvider.test({ message: 'How many "R"s are in Strawberry?', user: { id: "u1" }, includeRaw: true }); assert.equal(diagnosticMessages[1].content, 'How many "R"s are in Strawberry?'); assert.equal(diagnosticTest.text, "There are 3 Rs."); assert.match(diagnosticTest.raw_prompt, /local model diagnostic/); const statePath = resolveData("config", "runtime_state.json"); const originalState = fs.readFileSync(statePath, "utf8"); try { const runtime = new RuntimeManager({ getConfig: () => ({}), getModel: () => null, runtimeManifest: {} }); runtime.child = fakeChild(); await runtime.stop({ manual: false, reason: "bot_shutdown" }); assert.equal(getRuntimeState().desired_state, "running"); assert.equal(shouldAutoResume({ enabled: true }, getRuntimeState()), true); runtime.child = fakeChild(); await runtime.stop({ manual: true, reason: "admin_stop" }); assert.equal(getRuntimeState().desired_state, "stopped"); assert.equal(shouldAutoResume({ enabled: true }, getRuntimeState()), false); assert.equal(shouldAutoResume({ enabled: true }, { desired_state: "running", last_manual_stop: false, last_crashed: true }), false); } finally { fs.writeFileSync(statePath, originalState); } console.log("Lumi AI verification passed."); } function fakeChild() { const child = new EventEmitter(); child.killed = false; child.exitCode = null; child.kill = function kill() { this.killed = true; this.exitCode = 0; this.emit("exit", 0, null); }; return child; } run().catch((error) => { console.error(error); process.exitCode = 1; });