mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
Companion fixes from a full dashboard QA pass (every page dogfooded live), on top of the cherry-picked #31863 header-slot fix: - ChatPage: harden the header-slot effect further — useLayoutEffect and never write the slot while inactive, so the handoff commentary and ownership rule live next to the code. - LogsPage: level classification used raw substring matching, so INFO lines carrying 'parse_errors=0' (or paths like errors.log) rendered red. New unit-tested classifier (web/src/lib/log-classify.ts) anchors on the hermes_logging level token with a word-boundary fallback. - Channels API: plugin platforms (irc, ntfy, photon, teams, …) rendered as nameless title-cased cards ('Irc', 'Ntfy') with empty descriptions. Two root causes: (1) plugin discovery never ran in the dashboard server process, so plugin_entries() was empty; (2) Platform enum pseudo-members claimed plugin ids before the registry could attach labels. The catalog now discovers plugins explicitly and resolves plugin metadata first; added descriptions + docs links for bundled plugin platforms and the msgraph_webhook / whatsapp_cloud / relay enum members. Regression test sabotage-verified against the old enum-first ordering. - Config schema: updates.refresh_cua_driver declared type 'bool' (schema vocabulary is 'boolean'), so the switch rendered as a text input holding 'true'. - Page titles: '/mcp' rendered as 'Mcp' via the naive capitalize fallback; literal-label table now covers MCP/Files/Channels/Webhooks/ Pairing/System (unit-tested). - AuthWidget: skip the guaranteed-401 /api/auth/me probe in loopback mode — every dashboard load logged a console error for nothing. - Model picker: with no filter, providers that actually have models float above the wall of '0 models' rows. - Cron: empty state now carries an actionable Create button.
50 lines
2 KiB
TypeScript
50 lines
2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { classifyLine } from "./log-classify";
|
|
|
|
const TS = "2026-07-26 13:07:45,228";
|
|
|
|
describe("classifyLine", () => {
|
|
it("classifies by the structured level token", () => {
|
|
expect(classifyLine(`${TS} INFO run_agent: client created`)).toBe("info");
|
|
expect(classifyLine(`${TS} DEBUG registry: scanned 12 tools`)).toBe(
|
|
"debug",
|
|
);
|
|
expect(classifyLine(`${TS} WARNING hermes_state: WAL disabled`)).toBe(
|
|
"warning",
|
|
);
|
|
expect(classifyLine(`${TS} ERROR gateway: connect failed`)).toBe("error");
|
|
expect(classifyLine(`${TS} CRITICAL agent: giving up`)).toBe("error");
|
|
});
|
|
|
|
it("does NOT flag INFO lines whose payload mentions errors", () => {
|
|
// Regression: these rendered red with the old substring heuristic.
|
|
expect(
|
|
classifyLine(
|
|
`${TS} INFO tui_gateway.ws: ws closed peer=127.0.0.1:43212 parse_errors=0 dispatch_crashes=0 send_failures=1`,
|
|
),
|
|
).toBe("info");
|
|
expect(classifyLine(`${TS} INFO logs: rotating errors.log`)).toBe("info");
|
|
expect(
|
|
classifyLine(`${TS} INFO retry: recovered from FatalError subclass`),
|
|
).toBe("info");
|
|
});
|
|
|
|
it("does NOT let payload text spoof a higher level", () => {
|
|
expect(
|
|
classifyLine(`${TS} INFO chat: user said "ERROR ERROR ERROR"`),
|
|
).toBe("info");
|
|
expect(classifyLine(`${TS} DEBUG probe: WARNING string seen`)).toBe(
|
|
"debug",
|
|
);
|
|
});
|
|
|
|
it("falls back to word-boundary matching for untimestamped lines", () => {
|
|
expect(classifyLine("ValueError: bad input")).toBe("info"); // no bare token
|
|
expect(classifyLine("ERROR: something broke")).toBe("error");
|
|
expect(classifyLine("Traceback (most recent call last):")).toBe("error");
|
|
expect(classifyLine(" WARNING low disk")).toBe("warning");
|
|
expect(classifyLine("errors=0 all good")).toBe("info");
|
|
expect(classifyLine("wrote to errors.log")).toBe("info");
|
|
expect(classifyLine("just a plain line")).toBe("info");
|
|
});
|
|
});
|