Files
handler/frontend/lib/nav.ts
T
Claude dc80e7bc28 Add agent memory: linked note store, MCP server, recall hook, and graph UI
The distilled knowledge layer over the raw log/transcript history, inspired by
TencentDB-Agent-Memory's memory-hub model, adapted to handler's invariants
(state lives only in the database; workers stay stateless and disposable):

- memory_notes + memory_links tables (migration 0014) with a portable DAL:
  scoped listing/search (project + global), idempotent linking, and a one-read
  graph. Deleting an agent nulls attribution but keeps its notes; deleting a
  project removes its notes and edges, leaving global knowledge intact.
- /memory API routes: reads on the normal token, note/link authoring on the
  admin token, plus GET /memory/graph for the dashboard.
- Bundled handler-memory MCP server (python -m handler.mcpserver), a
  dependency-free stdio JSON-RPC implementation injected into every launch's
  --mcp-config ahead of the DB connectors and allowlisted in generated
  settings, exposing memory_search / memory_get / memory_save / memory_link.
  Identity and DATABASE_URL arrive via the spawn env, same as hooks.
- SessionStart recall hook: injects the most recent notes in scope as
  additional context at session start, best-effort, never blocking.
- Memory page in the web UI: a hand-rolled force-directed SVG graph of the
  note web (colored by kind, hover highlights, click-through details), plus
  note/link authoring — the app's first visualization, no chart dependency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WYqkoYPX8NAo1V2KyXr1pk
2026-08-04 13:49:47 +00:00

34 lines
1.5 KiB
TypeScript

/* The left-nav map: one entry per section, each its own route/page. This is the single
* source of truth shared by the sidebar (which renders the links) and the auth frame
* (which seeds the store's active section from the URL on first load). Runs is the root. */
import type { Section } from "@/components/store";
export interface NavRoute {
key: Section;
href: string;
label: string;
}
export const NAV_ROUTES: NavRoute[] = [
{ key: "runs", href: "/", label: "Runs" },
{ key: "repositories", href: "/repositories", label: "Repositories" },
{ key: "agents", href: "/agents", label: "Agents" },
{ key: "schedules", href: "/schedules", label: "Schedules" },
{ key: "approvals", href: "/approvals", label: "Approvals" },
{ key: "servers", href: "/servers", label: "Git Servers" },
{ key: "activity", href: "/activity", label: "Activity" },
{ key: "shared", href: "/shared", label: "Shared" },
{ key: "memory", href: "/memory", label: "Memory" },
{ key: "claude", href: "/claude", label: "Claude" },
];
/* Map a browser path back to its section key. Trailing slashes (Next emits them under
* `trailingSlash: true`) are normalized away; anything unrecognized falls back to Runs.
* "/login" still maps to the Claude page — the login flow moved into it, and the old
* route redirects there. */
export function sectionFromPath(pathname: string): Section {
const clean = pathname.replace(/\/+$/, "") || "/";
if (clean === "/login") return "claude";
return NAV_ROUTES.find((r) => r.href === clean)?.key ?? "runs";
}