agentix-cli 0.114.0 → 0.114.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{agent-5F4EKGQN.js → agent-NGCPRTBH.js} +2 -2
  2. package/dist/{board-dashboard-GGW5ZZLT.js → board-dashboard-7B2B564A.js} +244 -244
  3. package/dist/{board-dashboard-GGW5ZZLT.js.map → board-dashboard-7B2B564A.js.map} +1 -1
  4. package/dist/{chunk-O2RVUM2B.js → chunk-CSLVHAYV.js} +2 -2
  5. package/dist/{chunk-QQIWLN3H.js → chunk-DOQ6Y7GZ.js} +5 -5
  6. package/dist/chunk-DOQ6Y7GZ.js.map +1 -0
  7. package/dist/{chunk-4COYYJJY.js → chunk-EVB23EHN.js} +2 -2
  8. package/dist/chunk-JVSTKXMI.js +6 -0
  9. package/dist/chunk-JVSTKXMI.js.map +1 -0
  10. package/dist/{chunk-XTO4GRGN.js → chunk-QBWJGDKE.js} +3 -3
  11. package/dist/{chunk-XX7NXI3Y.js → chunk-TZIPWZY6.js} +2 -2
  12. package/dist/{chunk-HPY54DU6.js → chunk-UMFFIUU6.js} +2 -2
  13. package/dist/{chunk-YU5NDKPA.js → chunk-UZB5UO35.js} +2 -2
  14. package/dist/{chunk-VQM77L54.js → chunk-Z6OPM7EH.js} +2 -2
  15. package/dist/{chunk-6WNQY6K2.js → chunk-ZXQYOSHP.js} +2 -2
  16. package/dist/cli.js +1 -1
  17. package/dist/index.js +1 -1
  18. package/dist/{program-IEMICVO3.js → program-4IMJEMDM.js} +7 -7
  19. package/dist/{promote-S7SKOKX2.js → promote-4VTXBVUC.js} +2 -2
  20. package/dist/{query-GOBQ3GL5.js → query-B32UDQJS.js} +2 -2
  21. package/dist/{registry-HUJEORRW.js → registry-GY4X2GZX.js} +2 -2
  22. package/dist/{wiki-FZHSX4K3.js → wiki-LVYHDK2R.js} +2 -2
  23. package/package.json +1 -1
  24. package/dist/chunk-ILGLEA3I.js +0 -6
  25. package/dist/chunk-ILGLEA3I.js.map +0 -1
  26. package/dist/chunk-QQIWLN3H.js.map +0 -1
  27. /package/dist/{agent-5F4EKGQN.js.map → agent-NGCPRTBH.js.map} +0 -0
  28. /package/dist/{chunk-O2RVUM2B.js.map → chunk-CSLVHAYV.js.map} +0 -0
  29. /package/dist/{chunk-4COYYJJY.js.map → chunk-EVB23EHN.js.map} +0 -0
  30. /package/dist/{chunk-XTO4GRGN.js.map → chunk-QBWJGDKE.js.map} +0 -0
  31. /package/dist/{chunk-XX7NXI3Y.js.map → chunk-TZIPWZY6.js.map} +0 -0
  32. /package/dist/{chunk-HPY54DU6.js.map → chunk-UMFFIUU6.js.map} +0 -0
  33. /package/dist/{chunk-YU5NDKPA.js.map → chunk-UZB5UO35.js.map} +0 -0
  34. /package/dist/{chunk-VQM77L54.js.map → chunk-Z6OPM7EH.js.map} +0 -0
  35. /package/dist/{chunk-6WNQY6K2.js.map → chunk-ZXQYOSHP.js.map} +0 -0
  36. /package/dist/{program-IEMICVO3.js.map → program-4IMJEMDM.js.map} +0 -0
  37. /package/dist/{promote-S7SKOKX2.js.map → promote-4VTXBVUC.js.map} +0 -0
  38. /package/dist/{query-GOBQ3GL5.js.map → query-B32UDQJS.js.map} +0 -0
  39. /package/dist/{registry-HUJEORRW.js.map → registry-GY4X2GZX.js.map} +0 -0
  40. /package/dist/{wiki-FZHSX4K3.js.map → wiki-LVYHDK2R.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/decisions/seats/wiki-rerank.ts","../src/graph/starter-schema.ts","../src/graph/store.ts","../src/graph/classifier.ts","../src/decisions/seats/intent-path.ts"],"sourcesContent":["import { choice, noul } from \"../questions\"\nimport type { AnswersFor, ChoiceAnswer, NoulAnswer, StateValue } from \"../types\"\n\n// Re-ranking a BM25 shortlist.\n//\n// Measured on the operator's own wiki (11,656 articles, 12 queries):\n// BM25 alone gets R@1 33.3%, R@3 41.7%. Two thirds of queries do not put\n// the right article first. BM25 is a bag-of-words match, so it does well\n// on \"gitlab token expired\" and badly on \"the bot keeps repeating itself\n// what is wrong\" — the synonym cases score 33% / 33%.\n//\n// Shape follows TypeSafe's semantic_find cookbook rather than their\n// rerank one. Rerank scores each query-candidate pair separately, which\n// is N calls; semantic_find scores 218 line ids against one query with a\n// single Choice, and the per-option probabilities ARE the ranking. One\n// call for the whole shortlist, and the candidates are compared against\n// each other rather than each judged alone — which is what ranking means.\n//\n// The companion Noul is the part that keeps this honest. A Choice must\n// return something: asked which of 30 articles answers a question the\n// wiki has no answer to, it will still name one, with a confident-looking\n// probability. `answerable` is the question that can say no, and code\n// reads it first.\n\nexport const WIKI_RERANK_SEAT = \"wiki-rerank\"\n\n// Measured on the 12-query eval, sweeping candidate count against\n// excerpt length under a fixed state budget:\n//\n// k=120 x 150 chars R@1 25.0% (worse than BM25's 33.3%)\n// k=60 x 300 chars R@1 41.7%\n// k=30 x 600 chars R@1 41.7%\n// k=30 x 900 chars R@1 50.0% <- default\n// k=20 x 1100 chars R@1 50.0%\n//\n// Candidates and excerpt trade against one another, and excerpt wins.\n// More candidates raises the ceiling — what the shortlist could contain —\n// while starving the re-ranker of the text it needs to rank with. At 150\n// characters a candidate is barely more than its title, and the result is\n// worse than no re-ranking at all. Raise k only alongside the state\n// budget, never instead of the excerpt.\nexport const DEFAULT_SHORTLIST = 30\nexport const DEFAULT_EXCERPT_CHARS = 900\n\nexport interface RerankCandidate {\n id: string\n title: string\n tags?: string[]\n excerpt: string\n}\n\nexport interface RerankInput {\n query: string\n candidates: RerankCandidate[]\n /** Per-candidate excerpt budget. See DEFAULT_EXCERPT_CHARS — this is\n * the lever that actually moves accuracy. */\n excerptChars?: number\n}\n\nexport function rerankState(input: RerankInput): StateValue {\n const budget = input.excerptChars ?? DEFAULT_EXCERPT_CHARS\n return {\n query: input.query,\n candidates: input.candidates.map((c) => ({\n id: c.id,\n title: c.title,\n tags: c.tags?.length ? c.tags.join(\", \") : null,\n excerpt: clip(c.excerpt, budget),\n })),\n }\n}\n\n/** Built per call, because the option set IS the candidate set. */\nexport function rerankQuestions(candidates: RerankCandidate[]) {\n const criteria: Record<string, string> = {}\n for (const c of candidates) criteria[c.id] = c.title\n return {\n best: choice(criteria, \"Which candidate answers the query?\"),\n answerable: noul(\"Do any of the candidates answer the query?\"),\n }\n}\n\nexport type RerankAnswers = AnswersFor<ReturnType<typeof rerankQuestions>>\n\nexport interface RerankResult {\n /** Candidate ids, best first, by the Choice's per-option probability. */\n ranked: string[]\n scores: Record<string, number>\n /** P(the shortlist contains an answer at all). */\n answerable: number\n /** The Choice's own confidence — flat means it could not separate them. */\n confidence: number\n}\n\n/**\n * Turn one answer into a ranking.\n *\n * Ties keep the incoming order, so a Choice that genuinely cannot\n * separate the candidates degrades to BM25's ranking rather than to an\n * arbitrary one.\n */\nexport function toRanking(\n answers: RerankAnswers,\n candidates: RerankCandidate[],\n): RerankResult {\n const best = answers.best as ChoiceAnswer\n const scores: Record<string, number> = {}\n for (const c of candidates) scores[c.id] = best.probabilities[c.id] ?? 0\n\n const order = new Map(candidates.map((c, i) => [c.id, i]))\n const ranked = [...candidates.map((c) => c.id)].sort((a, b) => {\n const diff = scores[b] - scores[a]\n return diff !== 0 ? diff : (order.get(a) ?? 0) - (order.get(b) ?? 0)\n })\n\n return {\n ranked,\n scores,\n answerable: (answers.answerable as NoulAnswer).noul,\n confidence: best.confidence,\n }\n}\n\nfunction clip(text: string, max: number): string {\n const flat = String(text ?? \"\").replace(/\\s+/g, \" \").trim()\n return flat.length > max ? flat.slice(0, max) : flat\n}\n","import type { GraphSchema } from \"./types\"\n\n/**\n * Ships with the install. When .agentx/graph/schema.json is missing,\n * the store seeds this so a fresh daemon has something to classify\n * against.\n *\n * v2 — Phase 1 of the classifier-retire plan: verb-level intent\n * taxonomy. The classifier now answers ONLY \"what kind of work is\n * this\" — it does not embed client/project/agent in the leaf because\n * those already live on the event metadata (project, subject,\n * agentId). One copy of \"review.merge-request\" instead of one per\n * client.\n *\n * Two levels:\n * category — broad work area (code/ops/support/admin/knowledge/social/system)\n * verb — specific intent (review.merge-request, deploy.staging, etc.)\n *\n * The category is closed-set; the verb is open-set so the operator\n * can add new ones via `agentx graph review` over time. Most paths\n * will reuse the seeded verbs below.\n *\n * Why so few levels: every level is information the classifier\n * actually has to decide. The previous v1 schema (scope > location >\n * org > unit > activity) re-encoded data already on the event —\n * forcing a 5-LLM-question prompt to recover what we already knew.\n */\nexport const STARTER_SCHEMA: GraphSchema = {\n version: 2,\n levels: [\n {\n id: \"category\",\n description: \"Top-level kind of work. Closed set — pick the closest fit.\",\n axes: [\n {\n name: \"kind\",\n type: \"enum\",\n values: [\n \"code\", // anything code-related: review, fix, implement, refactor\n \"ops\", // infra: deploy, monitor, rollback, scale, incident\n \"support\", // human-facing: answer question, triage, route\n \"admin\", // governance: config change, scheduling, token, role\n \"knowledge\", // wiki, docs, research, summarize\n \"social\", // marketing, comms, brief, content\n \"system\", // internal infrastructure (rare; mostly classifier itself)\n ],\n description: \"Which category this work belongs in.\",\n },\n ],\n },\n {\n id: \"verb\",\n description: \"Specific intent — verb-level only, NOT client/project-bound.\",\n axes: [\n {\n name: \"name\",\n type: \"free\",\n description:\n \"Lower-kebab dot-namespaced verb. Examples: review.merge-request, fix.bug, \" +\n \"investigate.error, deploy.staging, deploy.production, rollback.release, \" +\n \"spec.feature, triage.issue, document.feature, chat.greeting, \" +\n \"chat.support-request, schedule.add, config.change, ingest.contact, \" +\n \"transcribe.audio, brief.daily, summarize.thread, plan.work.\",\n },\n ],\n },\n ],\n leafInput: {\n name: \"input\",\n type: \"enum\",\n values: [\n \"telegram\",\n \"whatsapp\",\n \"slack\",\n \"discord\",\n \"gitlab-mr\",\n \"gitlab-issue\",\n \"github-issue\",\n \"github-pr\",\n \"cron\",\n \"webhook\",\n \"mesh\",\n \"http\",\n ],\n description: \"Where the message entered the system.\",\n },\n leafOutput: {\n name: \"output\",\n type: \"free\",\n description:\n \"One short sentence describing what the agent is expected to produce. \" +\n \"Generic — refers to the verb's outcome, not the specific client/project \" +\n \"(those are on the event metadata already).\",\n },\n}\n\n/** Pre-seeded verbs shipped with v2. The classifier picks from these\n * first; new verbs go through the review queue before they're\n * committed (so the taxonomy doesn't drift). Adding to this list is\n * cheap — the seed runs once when nodes.json is empty. */\nexport const STARTER_VERB_NODES: Array<{\n category: \"code\" | \"ops\" | \"support\" | \"admin\" | \"knowledge\" | \"social\" | \"system\"\n verb: string\n description?: string\n}> = [\n // code\n { category: \"code\", verb: \"review.merge-request\", description: \"Review a GitLab MR or GitHub PR\" },\n { category: \"code\", verb: \"review.code-change\", description: \"General code review (snippet, diff)\" },\n { category: \"code\", verb: \"fix.bug\", description: \"Fix a reported bug\" },\n { category: \"code\", verb: \"implement.feature\", description: \"Build a new feature\" },\n { category: \"code\", verb: \"investigate.error\", description: \"Investigate a failing test, exception, pipeline\" },\n { category: \"code\", verb: \"refactor.code\", description: \"Restructure existing code without behaviour change\" },\n { category: \"code\", verb: \"spec.feature\", description: \"Write or update a feature spec / RFC\" },\n { category: \"code\", verb: \"triage.issue\", description: \"Classify, label, or assign an issue\" },\n { category: \"code\", verb: \"answer.code-question\", description: \"Answer a code-related question\" },\n\n // ops\n { category: \"ops\", verb: \"deploy.staging\", description: \"Deploy to a staging/preview environment\" },\n { category: \"ops\", verb: \"deploy.production\", description: \"Deploy to production\" },\n { category: \"ops\", verb: \"rollback.release\", description: \"Roll back a release\" },\n { category: \"ops\", verb: \"investigate.incident\", description: \"Investigate a production incident\" },\n { category: \"ops\", verb: \"monitor.system\", description: \"Check system status / metrics\" },\n { category: \"ops\", verb: \"rotate.credential\", description: \"Rotate a token, key, certificate\" },\n { category: \"ops\", verb: \"audit.security\", description: \"Review for security issues\" },\n\n // support\n { category: \"support\", verb: \"chat.greeting\", description: \"A casual hello, no actionable request\" },\n { category: \"support\", verb: \"chat.support-request\", description: \"End-user / customer asking for help\" },\n { category: \"support\", verb: \"chat.casual\", description: \"Informal back-and-forth chat\" },\n { category: \"support\", verb: \"answer.question\", description: \"Answer an open-ended question\" },\n { category: \"support\", verb: \"route.request\", description: \"Forward a request to the right agent\" },\n\n // admin\n { category: \"admin\", verb: \"config.change\", description: \"Change agentx.json / environment / config\" },\n { category: \"admin\", verb: \"schedule.add\", description: \"Add a new cron / scheduled task\" },\n { category: \"admin\", verb: \"schedule.remove\", description: \"Remove or disable a schedule\" },\n { category: \"admin\", verb: \"token.create\", description: \"Mint a scoped API token\" },\n { category: \"admin\", verb: \"agent.add\", description: \"Configure a new agent\" },\n { category: \"admin\", verb: \"channel.add\", description: \"Wire up a new channel\" },\n\n // knowledge\n { category: \"knowledge\", verb: \"document.feature\", description: \"Write or update documentation\" },\n { category: \"knowledge\", verb: \"summarize.thread\", description: \"Summarize a long conversation/thread\" },\n { category: \"knowledge\", verb: \"research.topic\", description: \"Investigate / gather info on a topic\" },\n { category: \"knowledge\", verb: \"wiki.absorb\", description: \"Roll raw entries into typed wiki articles\" },\n\n // social\n { category: \"social\", verb: \"brief.daily\", description: \"Compose a daily brief / digest\" },\n { category: \"social\", verb: \"draft.post\", description: \"Draft a marketing post / announcement\" },\n { category: \"social\", verb: \"report.weekly\", description: \"Compose a weekly status report\" },\n\n // system (the classifier itself; rare to expose)\n { category: \"system\", verb: \"classify.intent\", description: \"Internal: graph-classifier sub-call\" },\n]\n\n","import {\n readFileSync,\n writeFileSync,\n appendFileSync,\n existsSync,\n mkdirSync,\n renameSync,\n statSync,\n} from \"fs\"\nimport { resolve, dirname } from \"path\"\nimport { createHash } from \"crypto\"\nimport {\n graphSchemaSchema,\n nodesFileSchema,\n indexFileSchema,\n fingerprintEntrySchema,\n classificationSchema,\n type GraphSchema,\n type GraphNode,\n type NodesFile,\n type IndexFile,\n type Classification,\n type FingerprintEntry,\n} from \"./types\"\nimport { STARTER_SCHEMA, STARTER_VERB_NODES } from \"./starter-schema\"\n\n// --- Intent Knowledge Graph filesystem store ---\n//\n// Mirrors the .agentx/wiki pattern: plain JSON on disk, one directory, no DB.\n// Not thread-safe across processes — writes go through safeWrite (tmp + rename).\n\nexport interface GraphStoreOptions {\n baseDir?: string\n log?: (...args: unknown[]) => void\n}\n\nexport class GraphStore {\n readonly baseDir: string\n private log: (...args: unknown[]) => void\n /** Parsed nodes/index files, keyed by path. The classifier reads both\n * several times per message and they grow without bound, so re-reading\n * and re-validating them each time blocked the daemon for seconds. */\n private parsed = new Map<string, { mtimeMs: number; size: number; data: unknown }>()\n\n constructor(opts: GraphStoreOptions = {}) {\n this.baseDir = opts.baseDir ?? resolve(process.cwd(), \".agentx/graph\")\n this.log = opts.log ?? console.error.bind(console, \"[graph]\")\n mkdirSync(this.baseDir, { recursive: true })\n }\n\n // --- Paths ---\n\n schemaPath(): string { return resolve(this.baseDir, \"schema.json\") }\n nodesPath(): string { return resolve(this.baseDir, \"nodes.json\") }\n indexPath(): string { return resolve(this.baseDir, \"index.json\") }\n classificationsPath(): string { return resolve(this.baseDir, \"classifications.jsonl\") }\n\n // --- Schema ---\n\n /** Load the graph schema, seeding STARTER_SCHEMA if the file is missing.\n * Phase 1 of the classifier-retire plan ships a v2 verb-level schema;\n * installs with the v1 hierarchical schema (scope > org > unit >\n * activity) get auto-migrated on next load: the v1 file is renamed\n * to schema.v1.bak.json, the v2 starter is written, and the\n * per-event nodes.json + classifications.jsonl are left alone for\n * historical reading. New events classify under v2 from then on. */\n loadSchema(): GraphSchema {\n const p = this.schemaPath()\n if (!existsSync(p)) {\n this.saveSchema(STARTER_SCHEMA)\n this.log(\"seeded starter schema at\", p)\n return STARTER_SCHEMA\n }\n const raw = readFileSync(p, \"utf-8\")\n const parsed = graphSchemaSchema.safeParse(JSON.parse(raw))\n if (!parsed.success) {\n throw new Error(`Invalid ${p}: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n if ((parsed.data.version ?? 1) < (STARTER_SCHEMA.version ?? 2)) {\n const backup = p.replace(/\\.json$/, `.v${parsed.data.version ?? 1}.bak.json`)\n try {\n writeFileSync(backup, raw)\n this.log(`migrated graph schema v${parsed.data.version ?? 1} -> v${STARTER_SCHEMA.version}; old saved at ${backup}`)\n } catch (e: any) {\n this.log(`could not back up old schema: ${e?.message ?? e}`)\n }\n this.saveSchema(STARTER_SCHEMA)\n // Same treatment for nodes.json — the v1 nodes were parented under\n // levels that don't exist in v2 and would fail validation. Archive\n // them and re-seed.\n const np = this.nodesPath()\n if (existsSync(np)) {\n const nbackup = np.replace(/\\.json$/, `.v1.bak.json`)\n try {\n writeFileSync(nbackup, readFileSync(np, \"utf-8\"))\n this.log(`archived v1 nodes.json -> ${nbackup}`)\n } catch (e: any) {\n this.log(`could not back up old nodes: ${e?.message ?? e}`)\n }\n try {\n const seeded = seedNodesFile()\n this.saveNodes(seeded)\n } catch (e: any) {\n this.log(`could not seed v2 nodes: ${e?.message ?? e}`)\n }\n }\n return STARTER_SCHEMA\n }\n return parsed.data\n }\n\n saveSchema(schema: GraphSchema): void {\n const parsed = graphSchemaSchema.safeParse(schema)\n if (!parsed.success) {\n throw new Error(`Refusing to save invalid schema: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n safeWriteJson(this.schemaPath(), parsed.data)\n }\n\n // --- Nodes ---\n\n loadNodes(): NodesFile {\n const p = this.nodesPath()\n if (!existsSync(p)) {\n // Seed the verb-level taxonomy on first load. Lets the classifier\n // pick from a stable set of common verbs out of the box, instead of\n // inventing slightly-different variants for every event.\n const seeded = seedNodesFile()\n this.saveNodes(seeded)\n return seeded\n }\n // A copy, so callers can edit it before saveNodes without touching the cache.\n return structuredClone(this.readParsed(p, (raw) => {\n const parsed = nodesFileSchema.safeParse(JSON.parse(raw))\n if (!parsed.success) {\n throw new Error(`Invalid ${p}: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n return parsed.data\n }))\n }\n\n saveNodes(file: NodesFile): void {\n const parsed = nodesFileSchema.safeParse(file)\n if (!parsed.success) {\n throw new Error(`Refusing to save invalid nodes file: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n this.writeParsed(this.nodesPath(), parsed.data)\n }\n\n /** The parsed file, re-read only when its mtime or size changes, so a\n * write from another process (CLI, dashboard) is still picked up. */\n private readParsed<T>(p: string, parse: (raw: string) => T): T {\n const { mtimeMs, size } = statSync(p)\n const hit = this.parsed.get(p)\n if (hit && hit.mtimeMs === mtimeMs && hit.size === size) return hit.data as T\n const data = parse(readFileSync(p, \"utf-8\"))\n this.parsed.set(p, { mtimeMs, size, data })\n return data\n }\n\n /** Write the file and keep the parsed copy, so the writer never re-reads\n * what it just wrote. `data` must not be mutated afterwards. */\n private writeParsed(p: string, data: unknown): void {\n this.parsed.delete(p)\n safeWriteJson(p, data)\n const { mtimeMs, size } = statSync(p)\n this.parsed.set(p, { mtimeMs, size, data })\n }\n\n /** Validate a node against the schema AND uniqueness within the nodes file. */\n validateNode(node: GraphNode, schema: GraphSchema, all: GraphNode[]): void {\n const level = schema.levels.find((l) => l.id === node.level)\n if (!level) throw new Error(`Unknown level: \"${node.level}\"`)\n\n const missing = level.axes\n .filter((a) => !a.optional)\n .filter((a) => !(a.name in node.axes))\n if (missing.length) {\n throw new Error(\n `Node ${node.id} (${node.level}) missing required axes: ${missing.map((a) => a.name).join(\", \")}`,\n )\n }\n\n for (const a of level.axes) {\n const v = node.axes[a.name]\n if (v == null) continue\n if (a.type === \"enum\" && a.values && !a.values.includes(v)) {\n throw new Error(`Node ${node.id}: axis ${a.name}=\"${v}\" not in enum ${JSON.stringify(a.values)}`)\n }\n if (a.type === \"ref\") {\n if (!all.some((n) => n.id === v)) {\n throw new Error(`Node ${node.id}: axis ${a.name}=\"${v}\" refers to unknown node`)\n }\n if (a.refLevel) {\n const target = all.find((n) => n.id === v)!\n if (target.level !== a.refLevel) {\n throw new Error(\n `Node ${node.id}: axis ${a.name} expected level \"${a.refLevel}\" but got \"${target.level}\"`,\n )\n }\n }\n }\n }\n\n const levelIdx = schema.levels.findIndex((l) => l.id === node.level)\n if (levelIdx === 0) {\n if (node.parentId !== null) {\n throw new Error(`Root-level node ${node.id} must have parentId: null`)\n }\n } else {\n if (!node.parentId) {\n throw new Error(`Non-root node ${node.id} must have a parentId`)\n }\n const parent = all.find((n) => n.id === node.parentId)\n if (!parent) throw new Error(`Node ${node.id}: parent \"${node.parentId}\" not found`)\n const parentLevelIdx = schema.levels.findIndex((l) => l.id === parent.level)\n // Parent may be at any higher level — intermediate levels can be skipped\n // for orgs that don't use them (e.g. remote-first: no location node).\n if (parentLevelIdx < 0 || parentLevelIdx >= levelIdx) {\n throw new Error(\n `Node ${node.id} at level \"${node.level}\" has parent at level \"${parent.level}\" — parent must be at a higher level`,\n )\n }\n }\n }\n\n /**\n * Commit a full classification path: for each path element that doesn't\n * yet exist, infer its schema level by matching the proposed axes against\n * the level's required axes (first match ≥ cursor wins — this handles\n * paths that skip levels, like business → acme where location is\n * absent). Refreshes the node list between adds so callers in a loop\n * don't hit \"Node id already exists\" on the second iteration.\n */\n commitNodesAlongPath(\n path: string[],\n proposedAxes: Record<string, Record<string, string>>,\n schema: GraphSchema,\n createdBy?: string,\n ): { added: GraphNode[]; skipped: string[] } {\n const added: GraphNode[] = []\n const skipped: string[] = []\n const now = new Date().toISOString()\n\n for (let i = 0; i < path.length; i++) {\n const id = path[i]\n // Load fresh each iteration — cheap O(N) file read, avoids stale snapshots.\n const current = this.loadNodes().nodes\n const existing = current.find((n) => n.id === id)\n if (existing) {\n skipped.push(id)\n continue\n }\n\n // Cursor: how deep the schema-levels we've already filled up to.\n let cursor = 0\n // Scan backwards through path[0..i-1] to find the deepest already-committed\n // or just-added node and resume after its level.\n for (let j = i - 1; j >= 0; j--) {\n const priorId = path[j]\n const priorNode = current.find((n) => n.id === priorId) || added.find((n) => n.id === priorId)\n if (priorNode) {\n cursor = schema.levels.findIndex((l) => l.id === priorNode.level) + 1\n break\n }\n }\n\n const axes = proposedAxes[id] ?? {}\n // Find the first level at or after the cursor whose required axes are\n // satisfied by proposedAxes[id]. \"Required\" here = listed in level.axes;\n // we accept a partial match (axes present in proposal, even if some\n // schema axes are missing — validateNode will catch truly broken ones).\n let chosenLevel: string | null = null\n for (let j = cursor; j < schema.levels.length; j++) {\n const level = schema.levels[j]\n const missing = level.axes\n .filter((a) => !a.optional)\n .filter((a) => !(a.name in axes))\n if (missing.length === 0) {\n chosenLevel = level.id\n cursor = j + 1\n break\n }\n }\n // Fallback: if no level's axes fully match, try the NEXT level after\n // the cursor anyway. Better to commit at the likely level and let\n // validateNode reject on write than to silently drop the whole path.\n if (!chosenLevel && cursor < schema.levels.length) {\n chosenLevel = schema.levels[cursor].id\n cursor++\n }\n if (!chosenLevel) {\n throw new Error(`Can't place node ${id} — no schema level left (cursor=${cursor}, levels=${schema.levels.length})`)\n }\n\n // Parent is the DEEPEST already-placed node at a level < chosenLevel.\n let parentId: string | null = null\n const chosenIdx = schema.levels.findIndex((l) => l.id === chosenLevel)\n for (let j = i - 1; j >= 0; j--) {\n const priorId = path[j]\n const prior = current.find((n) => n.id === priorId) || added.find((n) => n.id === priorId)\n if (!prior) continue\n const priorIdx = schema.levels.findIndex((l) => l.id === prior.level)\n if (priorIdx >= 0 && priorIdx < chosenIdx) { parentId = priorId; break }\n }\n\n const node: GraphNode = {\n id, level: chosenLevel, parentId, axes,\n createdAt: now,\n createdBy,\n }\n this.addNode(node) // throws if schema validation fails\n added.push(node)\n }\n\n return { added, skipped }\n }\n\n /** Append a node. Caller is responsible for id uniqueness; we check and throw. */\n addNode(node: GraphNode): GraphNode {\n const schema = this.loadSchema()\n const file = this.loadNodes()\n if (file.nodes.some((n) => n.id === node.id)) {\n throw new Error(`Node id already exists: ${node.id}`)\n }\n this.validateNode(node, schema, [...file.nodes, node])\n file.nodes.push(node)\n this.saveNodes(file)\n return node\n }\n\n // --- Classifications (append-only log) ---\n\n appendClassification(c: Classification): boolean {\n const parsed = classificationSchema.safeParse(c)\n if (!parsed.success) {\n // Before the fix this threw and took down the whole task. Now we drop\n // the bad classification and log enough detail (the offending path +\n // axes keys) for the operator to diagnose the prompt. Returning false\n // lets callers decide whether to retry with a cleaner proposal.\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\" · \")\n this.log(\n `skipped invalid classification — ${issues} ·`,\n `path=${JSON.stringify(c.path)}`,\n `axes-keys=${JSON.stringify(Object.keys(c.proposedAxes || {}))}`,\n )\n return false\n }\n const line = JSON.stringify(parsed.data) + \"\\n\"\n const p = this.classificationsPath()\n if (!existsSync(p)) {\n mkdirSync(dirname(p), { recursive: true })\n }\n appendFileSync(p, line)\n return true\n }\n\n /**\n * All pending classifications, newest first. Collapses the append-only\n * ledger by msgHash — the most recent entry per message wins (so if an\n * entry was updated via updateClassificationStatus, we see the current\n * state, not the original).\n */\n listPendingClassifications(limit = 200): Classification[] {\n return this.listByStatus(\"pending\", limit)\n }\n\n /** Every classification, newest-first, collapsed one row per message.\n * The human-label queue needs the whole population rather than one\n * status, because the rows worth checking are the auto-approved ones. */\n listAllClassifications(): Classification[] {\n return this.readAllCollapsed()\n }\n\n /** Same contract as listPendingClassifications but filtered to any status. */\n listByStatus(status: Classification[\"status\"], limit = 200): Classification[] {\n const all = this.readAllCollapsed()\n return all.filter((c) => c.status === status).slice(0, limit)\n }\n\n /**\n * Read every classification, collapse by msgHash keeping the newest entry\n * per message. Used by listByStatus + the review loop to see current state.\n * Returns newest-first order.\n */\n private readAllCollapsed(): Classification[] {\n const p = this.classificationsPath()\n if (!existsSync(p)) return []\n const raw = readFileSync(p, \"utf-8\")\n const lines = raw.split(\"\\n\").filter(Boolean)\n const byHash = new Map<string, Classification>()\n for (const line of lines) {\n try {\n const parsed = classificationSchema.safeParse(JSON.parse(line))\n if (!parsed.success) continue\n // Later entries overwrite earlier ones for the same msgHash.\n byHash.set(parsed.data.msgHash, parsed.data)\n } catch {\n // skip malformed\n }\n }\n // Newest-first — sort by ts desc.\n return Array.from(byHash.values()).sort((a, b) => (a.ts < b.ts ? 1 : -1))\n }\n\n /**\n * Append a status-update entry for an existing classification. The ledger\n * stays append-only; readers collapse by msgHash. Returns the updated\n * classification, or null if no prior entry for msgHash exists.\n */\n updateClassificationStatus(\n msgHash: string,\n update: {\n status: Classification[\"status\"]\n reviewer?: string\n reviewReason?: string\n },\n ): Classification | null {\n const all = this.readAllCollapsed()\n const prior = all.find((c) => c.msgHash === msgHash)\n if (!prior) return null\n const next: Classification = {\n ...prior,\n status: update.status,\n ts: new Date().toISOString(),\n }\n // Store reviewer + reason in the preview field so readers can see who\n // flipped what without needing a separate review-log file. Non-breaking:\n // preview is already optional text meant for UI display.\n if (update.reviewer || update.reviewReason) {\n const suffix = ` · ${update.reviewer ? `reviewer=${update.reviewer}` : \"\"}${update.reviewReason ? ` · ${update.reviewReason}` : \"\"}`\n next.preview = ((prior.preview || \"\") + suffix).slice(0, 500)\n }\n this.appendClassification(next)\n return next\n }\n\n /** Read the last N classifications, newest first. Cheap enough for the admin UI. */\n readRecentClassifications(limit = 50): Classification[] {\n const p = this.classificationsPath()\n if (!existsSync(p)) return []\n const raw = readFileSync(p, \"utf-8\")\n const lines = raw.split(\"\\n\").filter(Boolean)\n const out: Classification[] = []\n for (let i = lines.length - 1; i >= 0 && out.length < limit; i--) {\n try {\n const parsed = classificationSchema.safeParse(JSON.parse(lines[i]))\n if (parsed.success) out.push(parsed.data)\n } catch {\n // skip malformed line — append-only log; don't fail reads on one bad line\n }\n }\n return out\n }\n\n // --- Fingerprint index ---\n\n /**\n * Hash that represents \"this kind of message\" for snap-to-path caching.\n *\n * Keyed on the normalized wording plus the channel. The sender used to be\n * part of the key too, and the exact text was: measured over the last\n * 2,000 classifications, 89% missed, so nearly every task paid an LLM\n * call for a taxonomy label. Identifiers, numbers, links and mentions\n * are what differ between two messages that mean the same thing, so\n * they are folded away (see `normalizeForFingerprint`).\n */\n fingerprint(msg: {\n text: string\n channel?: string\n sender?: string\n }): string {\n const norm = normalizeForFingerprint(msg.text)\n const payload = [norm, msg.channel ?? \"\"].join(\"\\u0001\")\n return createHash(\"sha256\").update(payload).digest(\"hex\").slice(0, 32)\n }\n\n loadIndex(): IndexFile {\n return structuredClone(this.index())\n }\n\n saveIndex(file: IndexFile): void {\n const parsed = indexFileSchema.safeParse(file)\n if (!parsed.success) {\n throw new Error(`Refusing to save invalid index: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n this.writeParsed(this.indexPath(), parsed.data)\n }\n\n /** The cached index. Read-only: copy before handing it to a caller. */\n private index(): IndexFile {\n const p = this.indexPath()\n if (!existsSync(p)) return { version: 1, entries: {} }\n return this.readParsed(p, (raw) => {\n const parsed = indexFileSchema.safeParse(JSON.parse(raw))\n if (!parsed.success) {\n throw new Error(`Invalid ${p}: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n return parsed.data\n })\n }\n\n getFingerprint(fp: string): FingerprintEntry | undefined {\n const entry = this.index().entries[fp]\n return entry && structuredClone(entry)\n }\n\n setFingerprint(fp: string, entry: Omit<FingerprintEntry, \"fingerprint\" | \"updatedAt\">): void {\n // Validate only the new entry: the rest of the index was validated when\n // it was read, and re-checking thousands of entries per message is the\n // cost this cache exists to avoid.\n const parsed = fingerprintEntrySchema.safeParse({\n ...entry,\n fingerprint: fp,\n updatedAt: new Date().toISOString(),\n })\n if (!parsed.success) {\n throw new Error(`Refusing to save invalid index entry: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n const current = this.index()\n this.writeParsed(this.indexPath(), { ...current, entries: { ...current.entries, [fp]: parsed.data } })\n }\n\n /** Update the axes on an existing node. Schema-validates the result. */\n updateNodeAxes(id: string, axes: Record<string, string>): GraphNode {\n const schema = this.loadSchema()\n const file = this.loadNodes()\n const node = file.nodes.find((n) => n.id === id)\n if (!node) throw new Error(`Node not found: ${id}`)\n const updated: GraphNode = { ...node, axes }\n this.validateNode(updated, schema, file.nodes.map((n) => (n.id === id ? updated : n)))\n file.nodes = file.nodes.map((n) => (n.id === id ? updated : n))\n this.saveNodes(file)\n return updated\n }\n\n /** Remove a node. Refuses to delete if any other node still references it\n * (child via parentId, or axis ref). */\n deleteNode(id: string): void {\n const file = this.loadNodes()\n const child = file.nodes.find((n) => n.parentId === id)\n if (child) {\n throw new Error(`Cannot delete \"${id}\" — child node \"${child.id}\" still points to it`)\n }\n const refHolder = file.nodes.find((n) =>\n Object.values(n.axes).some((v) => v === id),\n )\n if (refHolder) {\n throw new Error(`Cannot delete \"${id}\" — node \"${refHolder.id}\" references it via an axis`)\n }\n file.nodes = file.nodes.filter((n) => n.id !== id)\n this.saveNodes(file)\n }\n\n /** Approve a pending classification. Commits any new nodes along its path,\n * stamps the fingerprint index so future similar messages hit the cache,\n * and appends an audit entry. Idempotent — no-op if already cached. */\n approveClassification(c: Classification): void {\n if (this.getFingerprint(c.msgHash)) return // already approved\n // Commit any path nodes the LLM proposed but hadn't been confirmed yet.\n const schema = this.loadSchema()\n const known = new Set(this.loadNodes().nodes.map((n) => n.id))\n for (let i = 0; i < c.path.length; i++) {\n const id = c.path[i]\n if (known.has(id)) continue\n const level = schema.levels[i]?.id\n if (!level) break\n const axes = c.proposedAxes?.[id] ?? {}\n const parentId = i === 0 ? null : c.path[i - 1]\n this.addNode({\n id,\n level,\n parentId,\n axes,\n createdAt: new Date().toISOString(),\n createdBy: c.agentId,\n })\n known.add(id)\n }\n this.setFingerprint(c.msgHash, { path: c.path, leaf: c.leaf })\n this.appendClassification({ ...c, status: \"approved\", ts: new Date().toISOString() })\n }\n\n /** Record rejection — no nodes committed, no fingerprint set. */\n rejectClassification(c: Classification): void {\n this.appendClassification({ ...c, status: \"rejected\", ts: new Date().toISOString() })\n }\n}\n\n/** Atomic write: tmp file + rename. Avoids half-written JSON on crashes. */\nfunction safeWriteJson(path: string, data: unknown): void {\n mkdirSync(dirname(path), { recursive: true })\n const tmp = `${path}.tmp-${process.pid}`\n writeFileSync(tmp, JSON.stringify(data, null, 2))\n renameSync(tmp, path)\n}\n\n/** Build the seed nodes.json — one category node per enum value, plus\n * one verb node per STARTER_VERB_NODES entry parented under its\n * category. Idempotent shape: same input always produces the same\n * file, so re-seeding is safe. */\nfunction seedNodesFile(): NodesFile {\n const now = new Date().toISOString()\n const nodes: NodesFile[\"nodes\"] = []\n const seenCats = new Set<string>()\n for (const v of STARTER_VERB_NODES) {\n if (!seenCats.has(v.category)) {\n nodes.push({\n id: v.category,\n level: \"category\",\n parentId: null,\n axes: { kind: v.category },\n createdAt: now,\n createdBy: \"starter\",\n })\n seenCats.add(v.category)\n }\n }\n for (const v of STARTER_VERB_NODES) {\n nodes.push({\n id: v.verb,\n level: \"verb\",\n parentId: v.category,\n axes: { name: v.verb, ...(v.description ? { description: v.description } : {}) },\n createdAt: now,\n createdBy: \"starter\",\n })\n }\n return { version: 1, nodes }\n}\n\n/**\n * Fold a message down to the words that carry its intent. Links, mentions,\n * issue or MR references, hashes and numbers vary between two requests of\n * the same kind, so they collapse to placeholders; punctuation and case go.\n * Long webhook bodies are cut so a note on the same thread hashes alike.\n */\nexport function normalizeForFingerprint(text: string): string {\n return text\n .toLowerCase()\n .replace(/https?:\\/\\/\\S+/g, \" url \")\n .replace(/[@#][\\w.\\/-]+/g, \" ref \")\n .replace(/\\b[0-9a-f]{7,}\\b/g, \" id \")\n .replace(/\\d+/g, \" num \")\n .replace(/[^\\p{L}\\p{N}\\s]+/gu, \" \")\n .replace(/\\s+/g, \" \")\n .trim()\n .slice(0, 400)\n}\n","import { createHash } from \"crypto\"\nimport { extractJson } from \"@/utils/extract-json\"\nimport { GraphStore } from \"./store\"\nimport {\n type GraphSchema,\n type GraphNode,\n type Classification,\n} from \"./types\"\nimport { createProvider, type ProviderName, type AgentProvider } from \"@/agent/providers\"\nimport { getSeatMode } from \"@/decisions/seat\"\nimport { INTENT_PATH_SEAT, proposePathViaSeat } from \"@/decisions/seats/intent-path\"\n\n/** A path for a cache miss, with where it came from. */\ninterface Proposal {\n path: string[]\n proposedAxes: Record<string, Record<string, string>>\n confidence?: number\n leaf: { input?: string; output?: string }\n source: \"llm\" | \"seat\"\n}\n\n// --- Intent Knowledge Graph classifier ---\n//\n// For every incoming message, returns a path through the taxonomy:\n// 1. fingerprint → cache hit → reuse path (0 LLM calls, source=cache)\n// 2. miss → LLM draftAgent proposes { path, axes } → validated → pending\n// 3. autoApproveConfidence met → committed + cached\n//\n// Pending paths are still usable immediately (caller tags artifacts with\n// them); approval flips the flag and populates the fingerprint index so\n// the next similar message hits the cache.\n\nexport interface ClassifyInput {\n text: string\n channel?: string\n sender?: string\n /** Conversation id; stored on the classification so `agentx_wiki_query`\n * can look up the path of the request it is serving. */\n chatId?: string\n /** Agent that will RECEIVE the message after classification. When this\n * equals `draftAgent`, classification is skipped to prevent a deadlock —\n * the classifier's sub-task would otherwise queue behind the in-progress\n * main task on the same agent. */\n agentId?: string\n}\n\nexport interface ClassifyResult {\n /** Node ids from root to leaf. Shorter-than-full paths are allowed. */\n path: string[]\n /** Stable hash of the path — used as the `graph:<pathId>` wiki tag. */\n pathId: string\n /** Human-readable \"A › B › C\" path for logs + UI. */\n pathLabel: string\n /** Axis values asserted by the classifier. Keyed by node id. */\n axes: Record<string, Record<string, string>>\n leaf: { input?: string; output?: string }\n source: \"cache\" | \"llm\" | \"seat\"\n status: \"pending\" | \"approved\"\n confidence?: number\n}\n\nexport type ApprovalStructurePolicy = \"strict\" | \"extend-leaves\" | \"any\"\n\nexport interface ClassifierOptions {\n store: GraphStore\n /** Base URL of the daemon — used to POST /task for LLM proposals. */\n daemonUrl: string\n /** Optional bearer token for the daemon. */\n token?: string\n /** Which agent makes the proposal. Required if caller ever expects a\n * non-cached classification (cache hits work without an agent). */\n draftAgent?: string\n /** Structural approval policy — see config.ts for semantics. Default\n * \"extend-leaves\" (auto-approve pure reuse + single-leaf additions). */\n autoApproveStructure?: ApprovalStructurePolicy\n /** Minimum confidence (0..1) to commit without human approval. 1.0 = never.\n * OR'd with the structural policy. */\n autoApproveConfidence?: number\n /** Provider used for the in-process classifier call. Defaults to\n * \"claude-code\", which auto-detects auth via `loadAuthConfig()` —\n * Claude Max OAuth (CLI subprocess), API key, etc. NEVER opens a\n * paid path beyond what the operator has already configured. */\n classifierProvider?: ProviderName\n /** Model id passed to the provider. For claude-code OAuth this is\n * mapped through `CLI_MODEL_ALIASES` (sonnet/opus/haiku). Cheap\n * default — classification is metadata, not work. */\n classifierModel?: string\n log?: (...args: unknown[]) => void\n}\n\nexport class Classifier {\n private store: GraphStore\n private daemonUrl: string\n private token?: string\n private draftAgent?: string\n private autoApprove: number\n private autoApproveStructure: ApprovalStructurePolicy\n private classifierModel: string\n private classifierProviderName: ProviderName\n private providerInstance?: AgentProvider\n private log: (...args: unknown[]) => void\n\n constructor(opts: ClassifierOptions) {\n this.store = opts.store\n this.daemonUrl = opts.daemonUrl.replace(/\\/+$/, \"\")\n this.token = opts.token\n this.draftAgent = opts.draftAgent\n this.autoApprove = opts.autoApproveConfidence ?? 1.0\n this.autoApproveStructure = opts.autoApproveStructure ?? \"extend-leaves\"\n this.classifierModel = opts.classifierModel ?? \"claude-haiku-4-5-20251001\"\n this.classifierProviderName = opts.classifierProvider ?? \"claude-code\"\n this.log = opts.log ?? console.error.bind(console, \"[classifier]\")\n }\n\n /** Lazily build the provider instance. Lazy because constructing\n * ClaudeCodeProvider hits auth-store; deferring it keeps the\n * classifier importable in tests / dry-run paths that never call\n * classify(). */\n private getProvider(): AgentProvider {\n if (!this.providerInstance) {\n this.providerInstance = createProvider(this.classifierProviderName)\n }\n return this.providerInstance\n }\n\n async classify(input: ClassifyInput): Promise<ClassifyResult | null> {\n const fp = this.store.fingerprint({\n text: input.text,\n channel: input.channel,\n sender: input.sender,\n })\n\n // 1. Cache hit — instant return, zero LLM. Logged to classifications.jsonl\n // so the cache-hit rate is measurable from the persisted record alone\n // (otherwise hits leave no trace and ROI of the classifier is invisible).\n const cached = this.store.getFingerprint(fp)\n if (cached) {\n this.store.appendClassification({\n ts: new Date().toISOString(),\n msgHash: fp,\n agentId: input.agentId,\n channel: input.channel,\n sender: input.sender,\n chatId: input.chatId,\n path: cached.path,\n proposedAxes: {},\n leaf: cached.leaf,\n source: \"cache\",\n status: \"approved\",\n preview: input.text.slice(0, 200),\n })\n return {\n path: cached.path,\n pathId: hashPath(cached.path),\n pathLabel: pathLabel(cached.path, this.store.loadNodes().nodes),\n axes: {},\n leaf: cached.leaf,\n source: \"cache\",\n status: \"approved\",\n }\n }\n\n // 2. Cache miss — need an LLM proposal. Phase 2 of classifier-retire\n // replaced the /task → graph-agent dispatch with a direct Anthropic\n // Messages call, so we no longer need a draftAgent and the\n // self-dispatch deadlock guard (`agentId === draftAgent`) is moot.\n // `graph.enabled` is the only gate now.\n const schema = this.store.loadSchema()\n const nodes = this.store.loadNodes().nodes\n const proposal = await this.propose(input, schema, nodes)\n if (!proposal) return null\n\n // 3. Validate + persist as pending. Any NEW node the LLM proposed is\n // only committed if the whole classification auto-approves below.\n const { path, proposedAxes, confidence, leaf } = proposal\n if (path.length === 0) return null\n\n const classification: Classification = {\n ts: new Date().toISOString(),\n msgHash: fp,\n agentId: input.agentId,\n channel: input.channel,\n sender: input.sender,\n chatId: input.chatId,\n path,\n proposedAxes,\n leaf,\n source: proposal.source,\n status: \"pending\",\n confidence,\n preview: input.text.slice(0, 200),\n }\n\n // --- Auto-approval policy (see config.ts for full semantics) ---\n //\n // Two conditions, OR'd. Either approves the classification:\n // 1. Structure policy — what KIND of change the proposed path makes:\n // strict: nothing auto — always pending\n // extend-leaves: auto if the path either (a) reuses only existing\n // nodes, or (b) adds exactly one new node at the\n // DEEPEST level (a new leaf). Structural changes\n // (new mid-path or root node) still queue.\n // any: auto regardless of structure.\n // 2. Confidence — LLM-self-reported confidence >= autoApproveConfidence.\n //\n // Pending classifications still persist to the log, but don't commit nodes\n // and don't populate the fingerprint cache (so similar messages re-query).\n const existingIds = new Set(nodes.map((n) => n.id))\n const newNodeIndices = path\n .map((id, idx) => (existingIds.has(id) ? -1 : idx))\n .filter((idx) => idx >= 0)\n\n let structureOk = false\n if (this.autoApproveStructure === \"any\") {\n structureOk = true\n } else if (this.autoApproveStructure === \"extend-leaves\") {\n const isReuseOnly = newNodeIndices.length === 0\n const isSingleLeafAddition =\n newNodeIndices.length === 1 && newNodeIndices[0] === path.length - 1\n structureOk = isReuseOnly || isSingleLeafAddition\n }\n const confidenceOk = confidence !== undefined && confidence >= this.autoApprove\n\n let status: \"pending\" | \"approved\" = \"pending\"\n if (structureOk || confidenceOk) {\n // Commit any new nodes; if ANY fail schema validation (missing axes,\n // bad ref, etc.) fall back to pending for the whole classification\n // rather than half-committing the graph and bubbling the error up to\n // the task handler.\n try {\n this.commitNewNodes(path, proposedAxes, schema, nodes, input.sender)\n this.store.setFingerprint(fp, { path, leaf })\n status = \"approved\"\n } catch (e: any) {\n this.log(\n `auto-approve failed — staying pending ·`,\n `path=${JSON.stringify(path)}`,\n `reason=${(e?.message || e).toString().slice(0, 200)}`,\n )\n }\n }\n classification.status = status\n this.store.appendClassification(classification)\n\n return {\n path,\n pathId: hashPath(path),\n pathLabel: pathLabel(path, this.store.loadNodes().nodes),\n axes: proposedAxes,\n leaf,\n source: proposal.source,\n status,\n confidence,\n }\n }\n\n /**\n * Where a cache miss gets its path from, by the intent-path seat's mode:\n *\n * off the LLM, as before\n * shadow the LLM decides; the seat runs afterwards with the LLM's path\n * as incumbent, so agreement is measured before it can act\n * active the seat decides when both stages clear their thresholds;\n * the LLM only answers what the seat could not\n *\n * The seat reuses existing nodes only, so a seat proposal never adds to\n * the taxonomy; the LLM keeps that role in every mode.\n */\n private async propose(\n input: ClassifyInput,\n schema: GraphSchema,\n nodes: GraphNode[],\n ): Promise<Proposal | null> {\n const mode = getSeatMode(INTENT_PATH_SEAT)\n const seatInput = { message: input.text, channel: input.channel, sender: input.sender, agent: input.agentId }\n\n if (mode === \"active\") {\n const viaSeat = await proposePathViaSeat(seatInput, nodes).catch((e) => {\n this.log(\"intent-path seat failed:\", e?.message || e)\n return null\n })\n if (viaSeat?.confident) {\n return { path: viaSeat.path, proposedAxes: {}, leaf: {}, confidence: viaSeat.confidence, source: \"seat\" }\n }\n }\n\n const viaLlm = await this.proposePath(input, schema, nodes).catch((e) => {\n this.log(\"LLM proposal failed:\", e?.message || e)\n return null\n })\n if (mode === \"shadow\" && viaLlm) {\n void proposePathViaSeat(seatInput, nodes, { incumbent: viaLlm.path }).catch(() => {})\n }\n return viaLlm ? { ...viaLlm, source: \"llm\" } : null\n }\n\n /** Direct call to Anthropic to propose a path. Pure I/O; no side effects.\n * Bypasses the mesh / dispatch / ledger pipeline so a classification\n * doesn't surface as a sibling task in the activity graph. */\n private async proposePath(\n input: ClassifyInput,\n schema: GraphSchema,\n nodes: GraphNode[],\n ): Promise<{\n path: string[]\n proposedAxes: Record<string, Record<string, string>>\n confidence?: number\n leaf: { input?: string; output?: string }\n } | null> {\n const nodesForPrompt = nodes.map((n) => ({\n id: n.id,\n level: n.level,\n parentId: n.parentId,\n axes: n.axes,\n }))\n const userPrompt = [\n `You are the intent classifier for AgentX. You answer ONE question:`,\n `\"what kind of work does this message describe?\" — verb-level only.`,\n ``,\n `IMPORTANT — what NOT to encode in the path:`,\n `- Client / company / project / team names. Those are already on the event`,\n ` metadata (project, channel, agentId). Embedding them in the path causes`,\n ` duplicate nodes (one per client) for the same verb.`,\n `- The specific subject (issue number, file name, person name). Same reason.`,\n `- The agent's name or role. Pick the verb the *requester* intended.`,\n ``,\n `SCHEMA (fixed levels — pick one node per level):`,\n \"```json\",\n JSON.stringify(schema, null, 2),\n \"```\",\n ``,\n `EXISTING NODES (prefer reusing these; only invent a new node when no`,\n `existing one fits):`,\n \"```json\",\n JSON.stringify(nodesForPrompt, null, 2),\n \"```\",\n ``,\n `MESSAGE:`,\n \"```\",\n input.text.slice(0, 2000),\n \"```\",\n `channel: ${input.channel ?? \"?\"}`,\n `sender: ${input.sender ?? \"?\"}`,\n ``,\n `TASK:`,\n `1. Classify into 'category' (closed enum): code, ops, support, admin,`,\n ` knowledge, social, system. Pick the closest fit.`,\n `2. Pick or propose a 'verb' node. Verb ids are dot-namespaced lower-kebab,`,\n ` e.g. \"review.merge-request\", \"deploy.staging\", \"investigate.error\",`,\n ` \"chat.greeting\", \"fix.bug\". Reuse an existing verb when the message is`,\n ` the same kind of work as something already classified, even when the`,\n ` client / project / subject differs.`,\n ``,\n `Examples (right vs wrong):`,\n `- \"Please review MR #957 on globex/system\" → [\"code\", \"review.merge-request\"]`,\n ` NOT [\"business\", \"acme\", \"globex-v2\", \"review-mr-957-system\"]`,\n `- \"Deploy initech-v2 to staging please\" → [\"ops\", \"deploy.staging\"]`,\n ` NOT [\"business\", \"acme\", \"initech-v2\", \"deploy-initech-v2-to-staging\"]`,\n `- \"Hello Atlas\" → [\"support\", \"chat.greeting\"]`,\n ``,\n `NODE ID RULES (strict — invalid ids get dropped):`,\n `- lowercase-kebab + dots only: [a-z0-9][a-z0-9._-]*`,\n `- no spaces, no uppercase, no Arabic/other non-Latin`,\n `- verb ids should use a \"category.specific\" or \"verb.modifier\" shape`,\n ``,\n `Return ONE JSON object on one line, no prose, no fences:`,\n ` { \"path\": string[], \"proposedAxes\": { [nodeId]: { [axisName]: string } }, \"leaf\": { \"input\"?: string, \"output\"?: string }, \"confidence\": number }`,\n `confidence in [0,1]. Prefer low confidence over guessing.`,\n ].join(\"\\n\")\n\n // Phase 2 of classifier-retire: in-process provider call.\n // Replaces the `/task → graph-agent → mesh` dispatch path that\n // generated a sibling ledger event per classification (60–70% of\n // dispatches in the activity graph). Reuses whatever auth the\n // operator already configured (Claude Max OAuth via CLI subprocess,\n // Anthropic API key, etc.) — never opens a paid path the operator\n // didn't already opt into. The provider returns a plain JSON-text\n // GenerationResult, so we parse the same way as before.\n const text = await this.callProvider(userPrompt)\n const parsed = extractJson(text)\n if (!parsed || !Array.isArray(parsed.path)) return null\n\n // LLM often returns human-readable labels (\"Business\", \"Sales Manager\").\n // The node-id schema requires a lowercase slug, so normalize here and\n // remember the original → slug mapping so proposedAxes stays attached.\n // We also drop any element that can't be slugified into a valid id (e.g.\n // a path entry of pure Arabic script) — better to classify with a shorter\n // path than to have the store reject the whole classification.\n const NODE_ID_RE = /^[a-z0-9][a-z0-9._-]*$/\n const slugMap = new Map<string, string>()\n const path: string[] = parsed.path\n .filter((s: unknown): s is string => typeof s === \"string\" && s.length > 0)\n .map((s: string) => {\n const trimmed = s.trim()\n const slugged = slugifyNodeId(trimmed)\n if (trimmed !== slugged) slugMap.set(trimmed, slugged)\n return slugged\n })\n .filter((s: string) => NODE_ID_RE.test(s))\n if (path.length === 0) return null\n\n const proposedAxes: Record<string, Record<string, string>> = {}\n if (parsed.proposedAxes && typeof parsed.proposedAxes === \"object\") {\n for (const [rawNodeId, axes] of Object.entries(parsed.proposedAxes as Record<string, any>)) {\n if (!axes || typeof axes !== \"object\") continue\n const nodeId = slugMap.get(rawNodeId) ?? slugifyNodeId(rawNodeId)\n if (!nodeId) continue\n const clean: Record<string, string> = {}\n for (const [k, v] of Object.entries(axes)) {\n if (typeof v === \"string\") clean[k] = v\n }\n // If the LLM didn't volunteer a human-readable name but we slugified\n // one away, preserve the original as a `name` axis so the UI can\n // render \"Sales Manager\" not \"sales-manager\".\n if (!clean.name && rawNodeId !== nodeId) clean.name = rawNodeId.trim()\n proposedAxes[nodeId] = clean\n }\n }\n\n const leaf: { input?: string; output?: string } = {}\n if (parsed.leaf && typeof parsed.leaf === \"object\") {\n if (typeof parsed.leaf.input === \"string\") leaf.input = parsed.leaf.input\n if (typeof parsed.leaf.output === \"string\") leaf.output = parsed.leaf.output\n }\n\n const confidence = typeof parsed.confidence === \"number\"\n ? Math.max(0, Math.min(1, parsed.confidence))\n : undefined\n\n return { path, proposedAxes, confidence, leaf }\n }\n\n /** Create any nodes along `path` that don't exist yet. Delegates to the\n * store so level inference (for paths that skip levels) + fresh reloads\n * between adds are shared with the review path. */\n private commitNewNodes(\n path: string[],\n proposedAxes: Record<string, Record<string, string>>,\n schema: GraphSchema,\n _existing: GraphNode[],\n createdBy?: string,\n ): void {\n this.store.commitNodesAlongPath(path, proposedAxes, schema, createdBy)\n }\n\n /** Run the prompt through whichever AgentProvider the operator already\n * configured (Claude Max subscription via CLI, Anthropic API key, …).\n * Hard rule: NEVER open a paid path the operator didn't opt into —\n * this is just a thin wrapper that reuses existing auth. 30s timeout\n * is enough for Haiku/CLI cold-start; any longer and the classifier\n * is stalling the caller. */\n private async callProvider(userPrompt: string): Promise<string> {\n const provider = this.getProvider()\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), 30_000)\n try {\n const res = await provider.generate(\n [\n {\n role: \"system\",\n content:\n \"You are a taxonomy classifier. Respond with a single JSON object on one line. No prose, no code fences.\",\n },\n { role: \"user\", content: userPrompt },\n ],\n { model: this.classifierModel, maxTokens: 800, abortSignal: ac.signal, bare: true },\n )\n return res.content || \"\"\n } finally {\n clearTimeout(timer)\n }\n }\n}\n\n/** Normalize an LLM-proposed node label to a valid node id. Matches the\n * schema's `^[a-z0-9][a-z0-9._-]*$` — lowercase, alnum/./-/_, leading alnum.\n * Dots are preserved for verb ids like \"review.merge-request\". */\nfunction slugifyNodeId(raw: string): string {\n const s = raw\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^[-_]+|[-_]+$/g, \"\")\n .replace(/-{2,}/g, \"-\")\n if (!s) return \"\"\n // Ensure leading char is alnum (schema rejects leading _ or -).\n return /^[a-z0-9]/.test(s) ? s : `n-${s}`.replace(/[-_]+$/, \"\")\n}\n\n// --- path helpers used by both classifier + wiki retrieval ---\n\n/** Deterministic hash of a path, used as the `graph:<pathId>` wiki tag. */\nexport function hashPath(path: string[]): string {\n return createHash(\"sha1\")\n .update(path.join(\"\\u0001\"))\n .digest(\"hex\")\n .slice(0, 16)\n}\n\n/** \"Business › Acme › DevOps › Review MR\" — for the UI + context render. */\nexport function pathLabel(path: string[], nodes: GraphNode[]): string {\n return path\n .map((id) => {\n const n = nodes.find((x) => x.id === id)\n if (!n) return id\n // Prefer a `name` axis when the schema has one; fall back to id.\n const name = n.axes?.name || n.axes?.what || id\n return name\n })\n .join(\" › \")\n}\n\n/**\n * Depth of the deepest common ancestor between two paths, normalized to [0,1].\n * Used to score wiki articles: exact match = 1, shared grandparent = 0.5, none = 0.\n */\nexport function ancestryScore(a: string[], b: string[]): number {\n if (a.length === 0 || b.length === 0) return 0\n let shared = 0\n for (let i = 0; i < Math.min(a.length, b.length); i++) {\n if (a[i] === b[i]) shared++\n else break\n }\n if (shared === 0) return 0\n const depth = Math.max(a.length, b.length)\n return shared / depth\n}\n\n","import { choice } from \"../questions\"\nimport { askSeat, getSeatMode } from \"../seat\"\nimport type { SeatMode } from \"../store\"\nimport type { ChoiceAnswer, StateValue } from \"../types\"\nimport type { GraphNode } from \"@/graph/types\"\n\n// Which intent-graph path a message belongs to.\n//\n// The intent graph is a closed set: seven categories, a few hundred verbs,\n// and in the last few thousand classifications the LLM classifier never\n// proposed a node that did not already exist. Picking one label out of a\n// known list is what a decision seat does in under a second for a fraction\n// of a cent; asking a chat model to write the label out cost a fresh CLI\n// process per task, twenty seconds of wall time on average.\n//\n// Two stages because Jev caps a Choice at 255 options and the largest\n// category holds about a hundred verbs: first the category, then the verb\n// within it. Each stage carries the LLM's answer as the incumbent while the\n// seat runs in shadow, so `agentx decisions stats` reports agreement before\n// the seat is allowed to decide.\n//\n// Fail-open, as every seat: a missing or unconfident answer means \"no\n// proposal\", and the classifier falls back to the LLM it uses today. The\n// seat can only ever reuse existing nodes; growing the taxonomy stays the\n// LLM's job.\n\nexport const INTENT_PATH_SEAT = \"intent-path\"\n\n/** Below these the seat's answer is not acted on. A Choice always returns\n * an argmax, so the probability mass behind it is the only signal that\n * the pick is a match rather than the least bad option. */\nexport const INTENT_PATH_MIN_CATEGORY_P = 0.5\nexport const INTENT_PATH_MIN_VERB_P = 0.3\n/** A verb between the floor above and this keeps only its category. In the\n * first days active, 30% of seat labels sat at 0.30-0.50 and most of the\n * nonsensical verbs in a spot check were under 0.45, while the category\n * stayed right. Dropping the verb keeps the sub-second answer instead of\n * handing the message to an LLM fallback that failed about half the time. */\nexport const INTENT_PATH_FULL_VERB_P = 0.45\n\nexport interface IntentPathInput {\n message: string\n channel?: string\n sender?: string\n agent?: string\n}\n\nexport interface IntentPathProposal {\n /** `[category]` or `[category, verb]`, existing node ids only. A verb\n * under `INTENT_PATH_FULL_VERB_P` is dropped from `path`. */\n path: string[]\n /** min over the stages' argmax probability, over the stages kept in\n * `path`. */\n confidence: number\n /** Both stages cleared their thresholds; the caller may act on `path`. */\n confident: boolean\n mode: SeatMode\n}\n\nexport function intentPathState(input: IntentPathInput): StateValue {\n return {\n message: clip(input.message, 2000),\n channel: input.channel ?? null,\n sender: input.sender ?? null,\n agent: input.agent ?? null,\n }\n}\n\n/** One Choice over the given nodes. The option text is the node's own\n * description when it has one, so the seat reads what a reviewer reads. */\nexport function nodeChoice(nodes: GraphNode[], instructions: string) {\n const criteria: Record<string, string | null> = {}\n for (const n of nodes) criteria[n.id] = describe(n)\n return choice(criteria, instructions)\n}\n\nexport function categoryQuestions(categories: GraphNode[]) {\n return {\n category: nodeChoice(\n categories,\n \"Which category of work does this message ask for or report on? Pick the one a reviewer would file it under.\",\n ),\n }\n}\n\nexport function verbQuestions(verbs: GraphNode[]) {\n return {\n verb: nodeChoice(\n verbs,\n \"Within that category, which specific action does the message ask for or report on? Prefer the most specific verb that fits; a casual or ambiguous message belongs to the most general one.\",\n ),\n }\n}\n\nexport interface ProposeViaSeatOptions {\n /** The LLM's path for the same message, recorded as the incumbent so the\n * shadow report can measure agreement. */\n incumbent?: string[]\n}\n\n/**\n * Two-stage classification through the seat. Returns null when the seat is\n * off, unreachable or the taxonomy is too small to ask; otherwise a proposal\n * whose `confident` flag says whether the thresholds were met.\n */\nexport async function proposePathViaSeat(\n input: IntentPathInput,\n nodes: GraphNode[],\n opts: ProposeViaSeatOptions = {},\n): Promise<IntentPathProposal | null> {\n if (getSeatMode(INTENT_PATH_SEAT) === \"off\") return null\n const categories = nodes.filter((n) => n.parentId === null)\n if (categories.length < 2) return null\n\n const state = intentPathState(input)\n const features = { agent: input.agent ?? \"unknown\", channel: input.channel ?? \"unknown\" }\n const links = input.agent ? [{ kind: \"agent\", id: input.agent }] : undefined\n\n const stage1 = await askSeat(INTENT_PATH_SEAT, state, categoryQuestions(categories), {\n incumbent: opts.incumbent?.[0] ? { category: opts.incumbent[0] } : undefined,\n features: { ...features, stage: \"category\" },\n links,\n })\n if (!stage1) return null\n const cat = stage1.answers.category as ChoiceAnswer\n let confidence = cat.pMax\n let confident = cat.pMax >= INTENT_PATH_MIN_CATEGORY_P\n const path = [cat.choice]\n\n const verbs = nodes.filter((n) => n.parentId === cat.choice)\n if (verbs.length >= 2) {\n const sameCategory = opts.incumbent?.[0] === cat.choice\n const stage2 = await askSeat(INTENT_PATH_SEAT, { ...(state as Record<string, StateValue>), category: cat.choice }, verbQuestions(verbs), {\n // The LLM's verb is only a fair incumbent when it lives in the same\n // category; otherwise it is not among the options at all.\n incumbent: sameCategory && opts.incumbent?.[1] ? { verb: opts.incumbent[1] } : undefined,\n features: { ...features, stage: \"verb\", category: cat.choice },\n links,\n })\n if (!stage2) return null\n const verb = stage2.answers.verb as ChoiceAnswer\n confident = confident && verb.pMax >= INTENT_PATH_MIN_VERB_P\n if (verb.pMax >= INTENT_PATH_FULL_VERB_P || !confident) {\n confidence = Math.min(confidence, verb.pMax)\n path.push(verb.choice)\n }\n } else if (verbs.length === 1) {\n path.push(verbs[0].id)\n }\n\n return { path, confidence, confident, mode: stage1.mode }\n}\n\nfunction describe(n: GraphNode): string | null {\n const d = n.axes?.description?.trim() || undefined\n const rawName = n.axes?.name?.trim()\n // A name that merely repeats the id adds nothing the label does not say.\n const name = rawName && rawName !== n.id ? rawName : undefined\n if (d && name) return `${name}: ${d}`\n return d ?? name ?? null\n}\n\nfunction clip(s: string, max: number): string {\n return s.length > max ? `${s.slice(0, max)}…` : s\n}\n"],"mappings":"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","names":["init_questions","WIKI_RERANK_SEAT","DEFAULT_SHORTLIST","DEFAULT_EXCERPT_CHARS","rerankState","input","budget","c","clip","rerankQuestions","candidates","criteria","choice","noul","toRanking","answers","best","scores","order","i","a","b","diff","text","max","flat","STARTER_SCHEMA","STARTER_VERB_NODES","readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","renameSync","statSync","resolve","dirname","createHash","GraphStore","opts","resolve","mkdirSync","p","existsSync","STARTER_SCHEMA","raw","readFileSync","parsed","graphSchemaSchema","i","backup","writeFileSync","e","np","nbackup","seeded","seedNodesFile","schema","safeWriteJson","nodesFileSchema","file","parse","mtimeMs","size","statSync","hit","data","node","all","level","l","missing","a","v","n","target","levelIdx","parent","parentLevelIdx","path","proposedAxes","createdBy","added","skipped","now","id","current","cursor","j","priorId","priorNode","axes","chosenLevel","parentId","chosenIdx","prior","priorIdx","c","classificationSchema","issues","line","dirname","appendFileSync","limit","status","lines","byHash","b","msgHash","update","next","suffix","out","msg","payload","normalizeForFingerprint","createHash","indexFileSchema","fp","entry","fingerprintEntrySchema","updated","child","refHolder","known","tmp","renameSync","nodes","seenCats","STARTER_VERB_NODES","text","init_extract_json","init_providers","init_seat","createHash","init_questions","init_seat","INTENT_PATH_SEAT","INTENT_PATH_MIN_CATEGORY_P","INTENT_PATH_MIN_VERB_P","INTENT_PATH_FULL_VERB_P","intentPathState","input","clip","nodeChoice","nodes","instructions","criteria","n","describe","choice","categoryQuestions","categories","verbQuestions","verbs","proposePathViaSeat","opts","getSeatMode","state","features","links","stage1","askSeat","cat","confidence","confident","path","sameCategory","stage2","verb","d","rawName","name","s","max","Classifier","opts","createProvider","input","fp","cached","hashPath","pathLabel","schema","nodes","proposal","path","proposedAxes","confidence","leaf","classification","existingIds","n","newNodeIndices","id","idx","structureOk","isReuseOnly","isSingleLeafAddition","confidenceOk","status","e","mode","getSeatMode","INTENT_PATH_SEAT","seatInput","viaSeat","proposePathViaSeat","viaLlm","nodesForPrompt","userPrompt","text","parsed","extractJson","NODE_ID_RE","slugMap","s","trimmed","slugged","slugifyNodeId","rawNodeId","axes","nodeId","clean","k","v","_existing","createdBy","provider","ac","timer","raw","createHash","x","ancestryScore","b","shared","i","depth"]}