/* Matrix Context Console — Phase 0 (Compatible Mode), zero-dependency SPA. * Talks to the live backend through window.MC (api.js). No build step, no CDN. */ (function () { "use strict"; const MC = window.MC; const INGEST_NATIVE = false; // Phase-2 native ingest stays OFF in Phase 0 const $ = (s, r) => (r || document).querySelector(s); const el = (html) => { const t = document.createElement("template"); t.innerHTML = html.trim(); return t.content.firstChild; }; const esc = (s) => String(s == null ? "" : s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c])); const state = { tab: "overview", experts: [], scopes: [], items: [], online: false, version: null, health: null }; // ----------------------------------------------------------------- client classifier (Compatible Mode) const SAMPLE = `# Matrix-Context architecture notes ## Decision: dual surface Matrix Context exposes both a Python SDK and an MCP server so agents and human operators share one memory plane. ## Decision: SQLite is the default backend Ship SQLite as the default store; vectors are an accelerator, SQL is the source of truth. ## Policy: never store secrets The agent must never store API keys, tokens, or PII in durable memory. ## Preference: local-first The team prefers local-first tools over cloud services by default. ## Roadmap: defer Milvus to v2 Milvus support is deferred to v2; pgvector lands in v1.`; const EXPERT_FOR_TYPE = { decision: "semantic", fact: "semantic", rule: "policy", policy: "policy", preference: "profile", profile: "profile", event: "episodic", episode: "episodic", document: "document", architecture: "semantic" }; function classify(text) { if (!text || !text.trim()) return []; const blocks = text.split(/\n(?=#)|\n\s*\n/).map((b) => b.replace(/^#+\s*/, "").replace(/\s+/g, " ").trim()).filter((b) => b.length > 12); return blocks.slice(0, 20).map((b, i) => { const low = b.toLowerCase(); let type = "fact"; if (/\b(decide|decision|chose|default|will use)\b/.test(low)) type = "decision"; else if (/\b(policy|must|never|forbidden|require)\b/.test(low)) type = "rule"; else if (/\b(prefer|preference|like)\b/.test(low)) type = "preference"; else if (/\b(roadmap|defer|milestone|phase|v1|v2)\b/.test(low)) type = "decision"; const expertExists = (id) => state.experts.some((e) => e.id === id); let expert = EXPERT_FOR_TYPE[type] || "semantic"; if (!expertExists(expert)) expert = (state.experts[0] && state.experts[0].id) || "semantic"; const importance = Math.min(0.98, 0.5 + (type === "decision" ? 0.35 : type === "rule" ? 0.4 : 0.2) + Math.min(0.1, b.length / 4000)); const confidence = Math.round((0.7 + Math.random() * 0.25) * 100) / 100; const tags = Array.from(new Set((low.match(/\b(mcp|sdk|api|sqlite|milvus|pgvector|policy|secrets|local-first|memory|scope|expert)\b/g) || []))).slice(0, 4); return { id: "cand_" + (i + 1), content: b.slice(0, 280), type, expert, importance: Math.round(importance * 100) / 100, confidence, tags: tags.length ? tags : ["context"], approved: true }; }); } // ----------------------------------------------------------------- boot async function boot() { try { state.health = await MC.health(); state.version = await MC.version(); state.online = true; } catch (e) { state.online = false; } if (state.online) { try { state.experts = await MC.experts(); } catch (e) {} try { state.scopes = await MC.scopes(); } catch (e) {} try { state.items = (await MC.items()).items; } catch (e) {} } paintChrome(); render(); } function paintChrome() { const pill = $("#health-pill"); pill.className = "pill " + (state.online ? "ok" : "bad"); pill.textContent = state.online ? "● backend online" : "● backend offline"; $("#ver-pill").textContent = state.version ? ("contract " + state.version.contract + " · " + state.version.build) : "—"; $("#offline-banner").classList.toggle("show", !state.online); } function counts(field) { const m = {}; state.items.forEach((it) => { m[it[field]] = (m[it[field]] || 0) + 1; }); return m; } // ----------------------------------------------------------------- tabs const TABS = [ ["overview", "Overview", "01"], ["ingest", "Ingest", "02"], ["memory", "Memory", "03"], ["inspector", "Inspector", "04"], ["experts", "Experts", "05"], ]; function render() { document.querySelectorAll(".tab").forEach((t) => t.classList.toggle("active", t.dataset.tab === state.tab)); const v = $("#view"); v.innerHTML = ""; ({ overview: viewOverview, ingest: viewIngest, memory: viewMemory, inspector: viewInspector, experts: viewExperts }[state.tab] || viewOverview)(v); } // ----------------------------------------------------------------- Overview function viewOverview(v) { const expCount = counts("expert"), scCount = counts("scope"); v.appendChild(el(`
Inspectable, typed memory for AI agents — MoC Contract v1 backend.
Paste content, review the typed candidates, approve, then commit. Nothing is written until you commit. Compatible Mode — client-side chunking → POST /v1/remember
| ✓ | Type | Content | Expert | Imp. | Tags |
|---|---|---|---|---|---|
| ${esc(c.type)} | ${esc(c.content)} | ${c.importance} | ${c.tags.map((t) => `${esc(t)}`).join(" ")} |
Browse and forget stored items.
| Expert | Content | Scope | Imp. | Tags | |
|---|---|---|---|---|---|
| ${esc(it.expert)} | ${esc(it.content)} | ${esc(it.scope)} | ${it.importance} | ${(it.tags || []).map((t) => `${esc(t)}`).join(" ")} |
See why each item was routed, kept, or dropped — the inspectability contract.
running…
'; let r; try { r = await MC.inspect($("#q").value, { max_tokens: +$("#mt").value, top_experts: +$("#te").value }); } catch (e) { out.innerHTML = `${esc(e.message)}
`; return; } const max = Math.max.apply(null, r.routing.scores.map((s) => s.score).concat([0.0001])); out.innerHTML = ""; out.appendChild(el(`| ${on ? "● " : ""}${esc(s.expert)} | ${s.score.toFixed(3)} |
| Expert | Content | Score | rel / imp / rec / −red |
|---|---|---|---|
| ${esc(p.expert)} | ${esc(p.content)} | ${p.final_score} | ${b.relevance} / ${b.importance} / ${b.recency} / ${b.redundancy} |
| ${esc(d.expert)} | ${esc(d.id)} | ${esc(d.reason)} |
${esc(r.pack.prompt)}The typed context partitions the router selects between.
| Expert | Description | Items |
|---|---|---|
| ${esc(e.name)} | ${esc(e.desc)} | ${c[e.id] || 0} |