@agentproto/apps 0.5.1 → 0.7.0

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/code-team/agents/implementer.ts","../../src/code-team/agents/reviewer.ts","../../src/code-team/agents/fixer.ts","../../src/code-team/workflows/deliver-change.ts","../../src/code-team/index.ts","../../src/content-team/agents/researcher.ts","../../src/content-team/agents/writer.ts","../../src/content-team/agents/editor.ts","../../src/content-team/agents/illustrator.ts","../../src/content-team/workflows/produce-content.ts","../../src/content-team/workflows/produce-cover.ts","../../src/content-team/index.ts","../../src/mail-triage/agents/triager.ts","../../src/mail-triage/workflows/triage-inbox.ts","../../src/mail-triage/ui.ts","../../src/mail-triage/index.ts","../../src/media-viewer/agents/cataloger.ts","../../src/media-viewer/workflows/scan-media.ts","../../src/media-viewer/ui.ts","../../src/media-viewer/index.ts","../../src/bin/sync.ts"],"names":["defineAgent","defineWorkflow","defineApp","APP_ID"],"mappings":"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","file":"sync.mjs","sourcesContent":["import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Writes the requested change. */\nexport const implementer: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/implementer\",\n description: \"Implements the requested code change.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Change only what the task asks\", \"Match the surrounding style\"],\n tools: [\"list_dir\", \"read_file\", \"write_file\", \"edit_file\", \"run_command\"],\n workflows: [{ ref: \"deliver-change\" }],\n }),\n body:\n \"You implement the requested change. Read the task and the surrounding code \" +\n \"first, make the smallest correct change that satisfies it, then run the \" +\n \"tests. Do not touch anything the task didn't ask for.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Reviews the diff; reports only. */\nexport const reviewer: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/reviewer\",\n description: \"Reviews the diff and reports findings.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Report findings only\", \"Never edit files\", \"Never run gh pr merge\"],\n // Read-only + run_command (to inspect the diff / run tests). No write/edit\n // tools — the \"never edit files\" boundary is enforced by capability too.\n tools: [\"list_dir\", \"read_file\", \"run_command\"],\n workflows: [{ ref: \"deliver-change\" }],\n }),\n body:\n \"You are a rigorous reviewer. Read the diff, and report each real issue as \" +\n \"`file:line — why`. Change nothing; you only report.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Applies the reviewer's findings, precisely and minimally. */\nexport const fixer: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/fixer\",\n description: \"Applies the reviewer's findings, precisely and minimally.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Apply only the listed findings\", \"No unrelated refactors\"],\n tools: [\"list_dir\", \"read_file\", \"write_file\", \"edit_file\", \"run_command\"],\n workflows: [{ ref: \"deliver-change\" }],\n }),\n body:\n \"You apply the reviewer's findings. Address each listed finding exactly, add \" +\n \"nothing extra, and re-run the tests when done.\",\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\n/**\n * The one workflow the code team delivers a change through. Every team agent\n * references it; the app-kit attachment invariant keeps that honest.\n *\n * Three declarative `kind:\"agent\"` steps — implementer → reviewer → fixer —\n * one per team agent, each `agent.ref` resolving (at compile time, against\n * this app's own install record) to that agent's emitted AGENT.md. This\n * replaces an earlier `ToolStep`-based draft (`apply_patch`/`read_diff`/\n * `run_tests`) that dispatched daemon-side, where those tool ids don't\n * exist — the daemon has no `apply_patch` tool, only live agent sessions\n * that can edit files. Agent steps are the adoption fix (WP-B4): each step\n * spawns the named team agent as a real session and lets it act.\n */\nexport const deliverChange = defineWorkflow({\n id: \"deliver-change\",\n name: \"Deliver a code change\",\n description: \"Implement the change, review the diff, apply fixes, verify.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n {\n id: \"implement\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/implementer\" },\n prompt: \"Implement the requested change.\",\n },\n {\n id: \"review\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/reviewer\" },\n prompt:\n \"Review the diff the implementer just produced. Report every real issue as `file:line — why`.\",\n },\n {\n id: \"fix\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/fixer\" },\n prompt: \"Apply the reviewer's findings, then re-run the tests.\",\n },\n ],\n})\n","/**\n * `code-team` — a ready-made agentproto app: a small team that ships a code\n * change. Three agents (implementer → reviewer → fixer) bound to one delivery\n * workflow, declared with `@agentproto/app-kit`.\n *\n * Generic on purpose — `@agentproto/…` ids, no product dependency, no home\n * workspace. A host imports it and uses any subset of its agents/workflows:\n * in-process via `codeTeam.toMastraAgents(...)`, or on disk via\n * `codeTeam.emit(dir)`.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { implementer } from \"./agents/implementer.js\"\nimport { reviewer } from \"./agents/reviewer.js\"\nimport { fixer } from \"./agents/fixer.js\"\nimport { deliverChange } from \"./workflows/deliver-change.js\"\n\nexport const codeTeam: AppHandle = defineApp({\n id: \"@agentproto/code-team\",\n name: \"Code Team\",\n version: \"0.1.0\",\n description: \"Implement, review, and fix code changes.\",\n agents: [implementer, reviewer, fixer],\n workflows: [deliverChange],\n})\n\nexport { implementer, reviewer, fixer, deliverChange }\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Gathers sources for the piece. */\nexport const researcher: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/researcher\",\n description: \"Reads the provided sources and summarizes them for the writer.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Cite every claim\", \"Prefer primary sources\"],\n tools: [\"list_dir\", \"read_file\", \"run_command\"],\n workflows: [{ ref: \"produce-content\" }],\n }),\n body:\n \"You read the source material already in the workspace (list_dir, then \" +\n \"read_file the sources), extract the key facts, and hand the writer a tight \" +\n \"brief with a citation for every claim. Use run_command only to fetch a URL \" +\n \"already named in the task; do not invent sources.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Drafts the piece from the brief. */\nexport const writer: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/writer\",\n description: \"Drafts the piece from the researcher's brief.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Stay on the brief\", \"One idea per paragraph\"],\n tools: [\"list_dir\", \"read_file\", \"write_file\", \"edit_file\"],\n workflows: [{ ref: \"produce-content\" }],\n }),\n body:\n \"You draft the piece from the brief and its sources. Lead with the point, \" +\n \"keep one idea per paragraph, and don't invent facts the brief doesn't have.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Tightens the draft. */\nexport const editor: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/editor\",\n description: \"Edits the draft for voice, clarity, and length.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Preserve the writer's meaning\", \"Cut, don't pad\"],\n tools: [\"read_file\", \"edit_file\"],\n workflows: [{ ref: \"produce-content\" }],\n }),\n body:\n \"You edit the draft. Tighten the voice, cut fluff, fix clarity — without \" +\n \"changing what the writer meant. Leave it shorter than you found it.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Art-directs the cover illustration for each article. */\nexport const illustrator: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/illustrator\",\n description: \"Art-directs the cover illustration for each article.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"One cover per article\", \"Text-free prompts only\"],\n tools: [\"read_file\", \"write_file\", \"run_command\"],\n workflows: [{ ref: \"produce-cover\" }],\n }),\n body:\n \"You art-direct a single cover illustration for the article. \" +\n \"Read the article (read_file) to extract the one visual idea that best \" +\n \"represents the piece, then write the art direction (write_file) as a \" +\n \"clean, text-free image prompt. Keep the visual discipline tight: bold \" +\n \"flat shapes, limited risograph-style palettes, strong negative space, and \" +\n \"a single focal subject. Never include text, lettering, logos, watermarks, \" +\n \"or UI chrome in the prompt. Produce exactly one cover per article; if the \" +\n \"article calls for a literal diagram or chart, fall back to a precise \" +\n \"gpt-image-1 style description and treat the diagram itself as the image \" +\n \"subject. Use run_command only to inspect generated assets or move files \" +\n \"during the upload step.\",\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\n/** The one workflow the content team produces a piece through. Every team\n * agent references it (app-kit attachment invariant). */\nexport const produceContent = defineWorkflow({\n id: \"produce-content\",\n name: \"Produce a piece of content\",\n description: \"Read the sources, draft the piece, edit it, then commit it.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n // Concrete workspace tools (fs + exec), so the workflow describes a process\n // a standard runtime can actually run — not abstract tools nothing provides.\n steps: [\n { id: \"research\", kind: \"tool\", tool: \"read_file\" },\n { id: \"draft\", kind: \"tool\", tool: \"write_file\" },\n { id: \"edit\", kind: \"tool\", tool: \"edit_file\" },\n { id: \"publish\", kind: \"tool\", tool: \"run_command\" },\n ],\n})\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\n/** The one workflow the illustrator uses to produce a cover. */\nexport const produceCover = defineWorkflow({\n id: \"produce-cover\",\n name: \"Produce a cover illustration\",\n description:\n \"Read the article, derive the cover concept, generate the base image, \" +\n \"clean it up, and upload the final cover.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n { id: \"concept-from-article\", kind: \"tool\", tool: \"content.cover-concept\" },\n { id: \"base-gen\", kind: \"tool\", tool: \"content.cover-base-gen\" },\n { id: \"cleanup\", kind: \"tool\", tool: \"content.cover-cleanup\" },\n { id: \"upload\", kind: \"tool\", tool: \"content.cover-upload\" },\n ],\n})\n","/**\n * `content-team` — a ready-made agentproto app: a small team that produces a\n * piece of content. Three agents (researcher → writer → editor) bound to one\n * production workflow, declared with `@agentproto/app-kit`.\n *\n * Generic on purpose — `@agentproto/…` ids, no product dependency. A host\n * imports it and uses any subset of its agents/workflows.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { researcher } from \"./agents/researcher.js\"\nimport { writer } from \"./agents/writer.js\"\nimport { editor } from \"./agents/editor.js\"\nimport { illustrator } from \"./agents/illustrator.js\"\nimport { produceContent } from \"./workflows/produce-content.js\"\nimport { produceCover } from \"./workflows/produce-cover.js\"\n\nexport const contentTeam: AppHandle = defineApp({\n id: \"@agentproto/content-team\",\n name: \"Content Team\",\n version: \"0.1.0\",\n description: \"Research, draft, edit, and illustrate content.\",\n agents: [researcher, writer, editor, illustrator],\n workflows: [produceContent, produceCover],\n})\n\nexport { researcher, writer, editor, illustrator, produceContent, produceCover }\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Triages your inbox — finds unread mail, categorizes, labels, archives. */\nexport const triager: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/triager\",\n description: \"Triages your inbox — finds unread mail, categorizes, labels, archives.\",\n model: \"claude-sonnet-5\",\n boundaries: [\n \"Always show the triage plan before applying\",\n \"Never delete — only archive, label, or mark read\",\n \"Ask before applying bulk actions on more than 20 messages\",\n ],\n tools: [\n \"mailbox_list\",\n \"mailbox_search\",\n \"mailbox_list_threads\",\n \"mailbox_get_thread\",\n \"mailbox_labels_list\",\n \"mailbox_label_create\",\n \"mailbox_triage_plan\",\n \"mailbox_triage_apply\",\n ],\n workflows: [{ ref: \"triage-inbox\" }],\n }),\n body:\n \"You scan the inbox for unread messages using mailbox_search. Categorize them \" +\n \"(urgent, needs-reply, newsletter, notification, spam). Create a triage plan: \" +\n \"label each category, archive newsletters+notifications, keep urgent+needs-reply \" +\n \"in inbox. Show the plan before applying. Use mailbox_triage_plan to build the \" +\n \"plan and mailbox_triage_apply to execute it after user confirmation.\",\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\n/** Single-agent workflow: triage the inbox. */\nexport const triageInbox = defineWorkflow({\n id: \"triage-inbox\",\n name: \"Triage Inbox\",\n description: \"Scan inbox, categorize unread messages, create and apply triage plan.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n {\n id: \"triage\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/triager\" },\n prompt:\n \"Scan the inbox for unread messages. Categorize them (urgent, needs-reply, \" +\n \"newsletter, notification, spam). Create a triage plan: label each category, \" +\n \"archive newsletters+notifications, keep urgent+needs-reply in inbox. \" +\n \"Show me the plan before applying.\",\n },\n ],\n})\n","/**\n * Mail Triage — a self-contained MCP App dashboard for `mail-triage`.\n *\n * Talks to the daemon only through the `window.McpApp.connect()` bridge\n * (never a direct `fetch`): every action round-trips through the app's own\n * `app_tool_call` gateway tool, `{ appId, tool, args }`. Mailbox access goes\n * through the imported `agentpush` MCP server (`imported:agentpush/<tool>`\n * ids); running the triager agent itself goes through the daemon's own\n * `app_run` / `app_status` / `agent_output` — a second path alongside the\n * deterministic plan/apply tools, so the panel can either drive the mailbox\n * tools directly or hand the whole job to the agent and watch it work.\n */\n\nexport const MAIL_TRIAGE_TOOLS = [\n \"imported:agentpush/mailbox_list\",\n \"imported:agentpush/mailbox_search\",\n \"imported:agentpush/mailbox_triage_plan\",\n \"imported:agentpush/mailbox_triage_apply\",\n \"app_run\",\n \"app_status\",\n \"agent_output\",\n] as const\n\nconst APP_ID = \"@agentproto/mail-triage\"\nconst AGENT_ID = \"@agentproto/triager\"\nconst CATEGORIES = [\"urgent\", \"needs-reply\", \"newsletter\", \"notification\"]\n\nexport const MAIL_TRIAGE_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Mail Triage</title>\n<style>\n*{box-sizing:border-box;margin:0;padding:0}\n:root{\n --bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;\n --text:#e6edf3;--text2:#8b949e;\n --green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;\n}\nhtml,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;font-size:13px;background:var(--bg);color:var(--text)}\n#app{display:flex;flex-direction:column;height:100%;overflow-y:auto}\n#toolbar{padding:10px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;background:var(--bg2);flex-shrink:0}\n#toolbar .title{font-size:13px;font-weight:600;flex:1}\nselect,input[type=text]{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 8px;border-radius:4px;font-size:12px;font-family:inherit}\n.abtn{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 10px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}\n.abtn:hover{background:var(--border)}\n.abtn:disabled{opacity:.4;cursor:default}\n.abtn.primary{background:var(--blue);border-color:var(--blue);color:#04101c}\n.abtn.primary:hover{opacity:.9}\n#content{padding:14px;display:flex;flex-direction:column;gap:16px}\nsection{background:var(--bg2);border:1px solid var(--border);border-radius:6px;padding:12px}\nsection h3{font-size:11px;color:var(--text2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:10px}\n#summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px}\n.tile{background:var(--bg3);border:1px solid var(--border);border-radius:6px;padding:10px;text-align:center}\n.tile .count{font-size:22px;font-weight:700}\n.tile .label{font-size:11px;color:var(--text2);text-transform:capitalize}\n.tile.urgent .count{color:var(--red)}\n.tile.needs-reply .count{color:var(--yellow)}\n.tile.newsletter .count{color:var(--blue)}\n.tile.notification .count{color:var(--purple)}\n#plan-list,#search-results,#log{max-height:260px;overflow-y:auto;font-size:12px}\n.plan-item{display:flex;justify-content:space-between;gap:10px;padding:6px 0;border-bottom:1px solid var(--border)}\n.plan-item .subj{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.plan-item .tag{color:var(--text2);font-size:11px;white-space:nowrap}\n#log{font-family:Menlo,Monaco,monospace;white-space:pre-wrap;word-break:break-word;background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:8px}\n.searchbar{display:flex;gap:8px;margin-bottom:8px}\n.searchbar input{flex:1}\n.actions{display:flex;gap:8px;margin-top:10px}\n#status{padding:6px 14px;font-size:11px;color:var(--text2);border-top:1px solid var(--border);background:var(--bg2);flex-shrink:0}\n.empty{color:var(--text2);padding:8px 0}\n</style>\n</head>\n<body>\n<div id=\"app\">\n <div id=\"toolbar\">\n <span class=\"title\">Mail Triage</span>\n <select id=\"mailbox-select\"></select>\n <button class=\"abtn\" id=\"refresh-btn\">Refresh</button>\n </div>\n <div id=\"content\">\n <section>\n <h3>Inbox summary</h3>\n <div id=\"summary\"></div>\n </section>\n\n <section>\n <h3>Search</h3>\n <div class=\"searchbar\">\n <input type=\"text\" id=\"search-input\" placeholder=\"e.g. is:unread\">\n <button class=\"abtn\" id=\"search-btn\">Search</button>\n </div>\n <div id=\"search-results\"><div class=\"empty\">No search run yet.</div></div>\n </section>\n\n <section>\n <h3>Triage plan</h3>\n <div class=\"actions\">\n <button class=\"abtn primary\" id=\"triage-btn\">Triage</button>\n <button class=\"abtn\" id=\"apply-btn\" disabled>Apply</button>\n </div>\n <div id=\"plan-list\" style=\"margin-top:10px\"><div class=\"empty\">No plan yet — click Triage.</div></div>\n </section>\n\n <section>\n <h3>Triager agent</h3>\n <div class=\"actions\">\n <button class=\"abtn\" id=\"run-agent-btn\">Run Triager Agent</button>\n </div>\n <div id=\"log\" style=\"margin-top:10px\"><div class=\"empty\">Agent has not run yet.</div></div>\n </section>\n </div>\n <div id=\"status\">Connecting&#8230;</div>\n</div>\n<script>\nvar APP_ID = ${JSON.stringify(APP_ID)};\nvar AGENT_ID = ${JSON.stringify(AGENT_ID)};\nvar CATEGORIES = ${JSON.stringify(CATEGORIES)};\nvar callTool = null;\nvar currentPlan = null;\nvar pollHandle = null;\n\nfunction setStatus(msg) {\n document.getElementById(\"status\").textContent = msg;\n}\n\n// See media-viewer/ui.ts for why this loop exists: app_tool_call wraps its\n// dispatch result in at least one more {content:[{type:\"text\",text:...}]}\n// shell than the underlying tool returned.\nfunction unwrapText(result) {\n var cur = result;\n for (var i = 0; i < 4; i++) {\n if (typeof cur === \"string\") {\n try {\n cur = JSON.parse(cur);\n continue;\n } catch (e) {\n return cur;\n }\n }\n if (cur && Array.isArray(cur.content) && cur.content[0] && typeof cur.content[0].text === \"string\") {\n cur = cur.content[0].text;\n continue;\n }\n break;\n }\n return cur;\n}\n\nfunction callApp(tool, args) {\n return callTool(\"app_tool_call\", { appId: APP_ID, tool: tool, args: args || {} }).then(unwrapText);\n}\n\nfunction asList(value, keys) {\n if (Array.isArray(value)) return value;\n if (value && typeof value === \"object\") {\n for (var i = 0; i < keys.length; i++) {\n if (Array.isArray(value[keys[i]])) return value[keys[i]];\n }\n }\n return [];\n}\n\nfunction renderSummary(counts) {\n var el = document.getElementById(\"summary\");\n el.innerHTML = \"\";\n CATEGORIES.forEach(function (cat) {\n var tile = document.createElement(\"div\");\n tile.className = \"tile \" + cat;\n var count = counts && counts[cat] !== undefined ? counts[cat] : \"–\";\n tile.innerHTML = '<div class=\"count\"></div><div class=\"label\"></div>';\n tile.querySelector(\".count\").textContent = String(count);\n tile.querySelector(\".label\").textContent = cat.replace(\"-\", \" \");\n el.appendChild(tile);\n });\n}\n\nfunction loadMailboxes() {\n setStatus(\"Loading mailboxes\\\\u2026\");\n return callApp(\"imported:agentpush/mailbox_list\", {})\n .then(function (result) {\n var mailboxes = asList(result, [\"mailboxes\", \"items\"]);\n var select = document.getElementById(\"mailbox-select\");\n select.innerHTML = \"\";\n if (mailboxes.length === 0) {\n var opt = document.createElement(\"option\");\n opt.textContent = \"inbox\";\n opt.value = \"inbox\";\n select.appendChild(opt);\n } else {\n mailboxes.forEach(function (mb) {\n var name = typeof mb === \"string\" ? mb : mb.name || mb.id || JSON.stringify(mb);\n var opt = document.createElement(\"option\");\n opt.value = name;\n opt.textContent = name;\n select.appendChild(opt);\n });\n }\n setStatus(\"Ready\");\n })\n .catch(function (err) {\n setStatus(\"Error loading mailboxes: \" + err.message);\n });\n}\n\nfunction renderSearchResults(items) {\n var el = document.getElementById(\"search-results\");\n el.innerHTML = \"\";\n if (items.length === 0) {\n el.innerHTML = '<div class=\"empty\">No results.</div>';\n return;\n }\n items.forEach(function (item) {\n var row = document.createElement(\"div\");\n row.className = \"plan-item\";\n var subj = typeof item === \"string\" ? item : item.subject || item.title || JSON.stringify(item);\n var from = item && item.from ? item.from : \"\";\n row.innerHTML = '<span class=\"subj\"></span><span class=\"tag\"></span>';\n row.querySelector(\".subj\").textContent = subj;\n row.querySelector(\".tag\").textContent = from;\n el.appendChild(row);\n });\n}\n\nfunction renderPlan(plan) {\n var items = asList(plan, [\"actions\", \"items\", \"plan\"]);\n var el = document.getElementById(\"plan-list\");\n el.innerHTML = \"\";\n if (items.length === 0) {\n el.innerHTML = '<div class=\"empty\">Plan is empty.</div>';\n return;\n }\n var counts = {};\n items.forEach(function (item) {\n var cat = (item && (item.category || item.tag)) || \"notification\";\n counts[cat] = (counts[cat] || 0) + 1;\n var row = document.createElement(\"div\");\n row.className = \"plan-item\";\n var subj = (item && (item.subject || item.title)) || JSON.stringify(item);\n var action = (item && (item.action || item.category)) || \"\";\n row.innerHTML = '<span class=\"subj\"></span><span class=\"tag\"></span>';\n row.querySelector(\".subj\").textContent = subj;\n row.querySelector(\".tag\").textContent = action;\n el.appendChild(row);\n });\n renderSummary(counts);\n}\n\ndocument.getElementById(\"refresh-btn\").addEventListener(\"click\", loadMailboxes);\n\ndocument.getElementById(\"search-btn\").addEventListener(\"click\", function () {\n var query = document.getElementById(\"search-input\").value;\n var mailbox = document.getElementById(\"mailbox-select\").value;\n setStatus(\"Searching\\\\u2026\");\n callApp(\"imported:agentpush/mailbox_search\", { mailbox: mailbox, query: query })\n .then(function (result) {\n renderSearchResults(asList(result, [\"messages\", \"items\", \"results\"]));\n setStatus(\"Ready\");\n })\n .catch(function (err) {\n setStatus(\"Search failed: \" + err.message);\n });\n});\n\ndocument.getElementById(\"triage-btn\").addEventListener(\"click\", function () {\n var mailbox = document.getElementById(\"mailbox-select\").value;\n setStatus(\"Building triage plan\\\\u2026\");\n document.getElementById(\"apply-btn\").disabled = true;\n callApp(\"imported:agentpush/mailbox_triage_plan\", { mailbox: mailbox })\n .then(function (plan) {\n currentPlan = plan;\n renderPlan(plan);\n document.getElementById(\"apply-btn\").disabled = false;\n setStatus(\"Plan ready \\\\u2014 review, then Apply.\");\n })\n .catch(function (err) {\n setStatus(\"Triage plan failed: \" + err.message);\n });\n});\n\ndocument.getElementById(\"apply-btn\").addEventListener(\"click\", function () {\n if (!currentPlan) return;\n setStatus(\"Applying triage plan\\\\u2026\");\n callApp(\"imported:agentpush/mailbox_triage_apply\", { plan: currentPlan })\n .then(function () {\n setStatus(\"Plan applied.\");\n document.getElementById(\"apply-btn\").disabled = true;\n })\n .catch(function (err) {\n setStatus(\"Apply failed: \" + err.message);\n });\n});\n\nfunction appendLog(text) {\n var el = document.getElementById(\"log\");\n if (el.querySelector(\".empty\")) el.innerHTML = \"\";\n el.textContent += (el.textContent ? \"\\\\n\" : \"\") + text;\n el.scrollTop = el.scrollHeight;\n}\n\nfunction pollAgentRun(appRunId) {\n if (pollHandle) clearTimeout(pollHandle);\n callApp(\"app_status\", { appRunId: appRunId })\n .then(function (status) {\n var sessions = (status && status.sessions) || [];\n var sessionId = sessions[0] && sessions[0].sessionId;\n var tail = sessionId\n ? callApp(\"agent_output\", { sessionId: sessionId, lastN: 200, clean: true })\n : Promise.resolve(\"\");\n return tail.then(function (output) {\n if (output) appendLog(typeof output === \"string\" ? output : JSON.stringify(output));\n var done = status && (status.status === \"ended\" || status.status === \"done\" || status.endedAt);\n if (!done) {\n pollHandle = setTimeout(function () { pollAgentRun(appRunId); }, 2000);\n } else {\n setStatus(\"Agent run finished.\");\n }\n });\n })\n .catch(function (err) {\n setStatus(\"Agent status failed: \" + err.message);\n });\n}\n\ndocument.getElementById(\"run-agent-btn\").addEventListener(\"click\", function () {\n document.getElementById(\"log\").innerHTML = \"\";\n setStatus(\"Starting triager agent\\\\u2026\");\n callApp(\"app_run\", { appId: APP_ID, agents: [AGENT_ID], prompt: \"Triage my inbox.\" })\n .then(function (run) {\n appendLog(\"Started app run \" + (run && run.appRunId));\n pollAgentRun(run.appRunId);\n })\n .catch(function (err) {\n setStatus(\"Run failed: \" + err.message);\n });\n});\n\nwindow.McpApp.connect()\n .then(function (bridge) {\n callTool = bridge.callTool;\n return loadMailboxes();\n })\n .catch(function (err) {\n setStatus(\"Standalone mode \\\\u2014 no host bridge (\" + err.message + \")\");\n renderSummary(null);\n });\n\nrenderSummary(null);\n</script>\n</body>\n</html>`\n","/**\n * `mail-triage` — a single-agent agentproto app for email triage.\n *\n * One agent (triager) scans inbox, categorizes unread mail, and applies\n * triage actions (label, archive). Proves the APP primitive works end-to-end\n * with a simple, useful real-world task.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { triager } from \"./agents/triager.js\"\nimport { triageInbox } from \"./workflows/triage-inbox.js\"\nimport { MAIL_TRIAGE_HTML, MAIL_TRIAGE_TOOLS } from \"./ui.js\"\n\nexport const mailTriage: AppHandle = defineApp({\n id: \"@agentproto/mail-triage\",\n name: \"Mail Triage\",\n version: \"0.1.0\",\n description: \"Triage your inbox — categorize, label, archive.\",\n agents: [triager],\n workflows: [triageInbox],\n ui: {\n html: MAIL_TRIAGE_HTML,\n title: \"Mail Triage\",\n tools: [...MAIL_TRIAGE_TOOLS],\n },\n})\n\nexport { triager, triageInbox }\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\nexport const cataloger: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/media-cataloger\",\n description:\n \"Scans a folder for media files (images, video, audio), collects metadata, and produces a structured catalog.\",\n model: \"claude-sonnet-5\",\n boundaries: [\n \"Read-only — never modify, rename, move, or delete files\",\n \"Only catalog files with recognized media extensions\",\n \"Report exact file sizes and paths, never estimate\",\n ],\n tools: [\"list_dir\", \"read_file\", \"file_info\"],\n workflows: [{ ref: \"scan-media\" }],\n }),\n body: [\n \"You scan a workspace folder for media files and produce a structured JSON catalog.\",\n \"\",\n \"Walk the target directory recursively using list_dir. For each file, check the\",\n \"extension against known media types:\",\n \"- Images: .jpg, .jpeg, .png, .gif, .webp, .svg, .bmp, .ico, .avif\",\n \"- Video: .mp4, .webm, .mov, .avi, .mkv\",\n \"- Audio: .mp3, .wav, .ogg, .flac, .aac, .m4a\",\n \"\",\n \"For each media file found, use file_info to get size and modification date.\",\n \"\",\n \"Output a JSON object with this shape:\",\n \"```json\",\n '{',\n ' \"folder\": \"<scanned path>\",',\n ' \"scannedAt\": \"<ISO timestamp>\",',\n ' \"summary\": { \"total\": N, \"images\": N, \"video\": N, \"audio\": N, \"totalSizeBytes\": N },',\n ' \"files\": [',\n ' {',\n ' \"path\": \"relative/path.jpg\",',\n ' \"name\": \"file.jpg\",',\n ' \"type\": \"image\",',\n ' \"extension\": \".jpg\",',\n ' \"sizeBytes\": 123456,',\n ' \"modifiedAt\": \"2026-08-01T12:00:00Z\"',\n \" }\",\n \" ]\",\n \"}\",\n \"```\",\n \"\",\n \"Sort files by type (images first, then video, then audio), then by name.\",\n ].join(\"\\n\"),\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\nexport const scanMedia = defineWorkflow({\n id: \"scan-media\",\n name: \"Scan Media\",\n description:\n \"Scan a folder for media files, collect metadata, and produce a structured catalog.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n {\n id: \"catalog\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/media-cataloger\" },\n prompt:\n \"Scan the workspace root folder for media files (images, video, audio). \" +\n \"Walk all subdirectories recursively. For each media file, collect its path, \" +\n \"name, type, extension, size, and modification date. Output the full catalog \" +\n \"as a single JSON object.\",\n },\n ],\n})\n","/**\n * Media Viewer — a self-contained MCP App gallery widget for `media-viewer`.\n *\n * Talks to the daemon only through the `window.McpApp.connect()` bridge\n * (never a direct `fetch`): every action round-trips through the app's own\n * `app_tool_call` gateway tool, `{ appId, tool, args }`. `app_tool_call`\n * double-wraps its dispatch result (it returns a text-result whose `text` is\n * the JSON-stringified inner tool result, itself an MCP content array) — see\n * `unwrapText` below, which peels that apart regardless of how many layers\n * the host's bridge already stripped.\n */\n\nexport const MEDIA_VIEWER_TOOLS = [\"directory_list\", \"file_info\", \"file_read\"] as const\n\nconst APP_ID = \"@agentproto/media-viewer\"\n\nexport const MEDIA_VIEWER_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Media Viewer</title>\n<style>\n*{box-sizing:border-box;margin:0;padding:0}\n:root{\n --bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;\n --text:#e6edf3;--text2:#8b949e;\n --green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;\n}\nhtml,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;font-size:13px;background:var(--bg);color:var(--text)}\n#app{display:flex;flex-direction:column;height:100%}\n#toolbar{padding:10px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;background:var(--bg2);flex-shrink:0}\n#toolbar .title{font-size:13px;font-weight:600;flex:1}\n#breadcrumb{font-size:12px;color:var(--text2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.abtn{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 10px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}\n.abtn:hover{background:var(--border)}\n#body{flex:1;display:flex;overflow:hidden}\n#grid-wrap{flex:1;overflow-y:auto;padding:14px}\n#grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}\n.cell{display:flex;flex-direction:column;align-items:center;gap:6px;padding:10px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg2);cursor:pointer;text-align:center}\n.cell:hover{background:var(--bg3);border-color:var(--blue)}\n.cell.selected{border-color:var(--blue);background:var(--bg3)}\n.icon{font-size:28px;line-height:1}\n.name{font-size:11px;color:var(--text);word-break:break-word;max-width:100%;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}\n#empty{color:var(--text2);padding:24px;text-align:center}\n#details{width:280px;border-left:1px solid var(--border);background:var(--bg2);padding:14px;overflow-y:auto;flex-shrink:0}\n#details h3{font-size:12px;color:var(--text2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px}\n#details .row{display:flex;justify-content:space-between;gap:8px;padding:4px 0;border-bottom:1px solid var(--border);font-size:12px}\n#details .row span:first-child{color:var(--text2)}\n#preview{margin-top:10px;padding:8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-family:Menlo,Monaco,monospace;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:240px;overflow-y:auto}\n#status{padding:6px 14px;font-size:11px;color:var(--text2);border-top:1px solid var(--border);background:var(--bg2);flex-shrink:0}\n</style>\n</head>\n<body>\n<div id=\"app\">\n <div id=\"toolbar\">\n <button class=\"abtn\" id=\"up-btn\" title=\"Up one level\">&uarr;</button>\n <span class=\"title\">Media Viewer</span>\n <span id=\"breadcrumb\">.</span>\n <button class=\"abtn\" id=\"refresh-btn\">Refresh</button>\n </div>\n <div id=\"body\">\n <div id=\"grid-wrap\">\n <div id=\"grid\"></div>\n <div id=\"empty\" style=\"display:none\">No files in this folder.</div>\n </div>\n <div id=\"details\" style=\"display:none\"></div>\n </div>\n <div id=\"status\">Connecting&#8230;</div>\n</div>\n<script>\nvar APP_ID = ${JSON.stringify(APP_ID)};\nvar currentPath = \".\";\nvar entries = [];\nvar callTool = null;\n\nfunction setStatus(msg) {\n document.getElementById(\"status\").textContent = msg;\n}\n\n// app_tool_call wraps its dispatch result at least once (its own\n// text-result body is the JSON-stringified inner MCP tool result); peel\n// back through nested {content:[{type:\"text\",text:...}]} shells until the\n// payload stops looking like one, so this works whichever layer the host's\n// own bridge already unwrapped.\nfunction unwrapText(result) {\n var cur = result;\n for (var i = 0; i < 4; i++) {\n if (typeof cur === \"string\") {\n try {\n cur = JSON.parse(cur);\n continue;\n } catch (e) {\n return cur;\n }\n }\n if (cur && Array.isArray(cur.content) && cur.content[0] && typeof cur.content[0].text === \"string\") {\n cur = cur.content[0].text;\n continue;\n }\n break;\n }\n return cur;\n}\n\nfunction callApp(tool, args) {\n return callTool(\"app_tool_call\", { appId: APP_ID, tool: tool, args: args || {} }).then(unwrapText);\n}\n\nfunction parseDirectoryListing(raw) {\n var text = typeof raw === \"string\" ? raw : \"\";\n return text\n .split(\"\\\\n\")\n .map(function (line) { return line.trim(); })\n .filter(function (line) { return line.length > 0; })\n .map(function (line) {\n var isDir = line.indexOf(\"[DIR]\") === 0;\n var name = line.replace(/^\\\\[(FILE|DIR)\\\\]\\\\s*/, \"\");\n return { name: name, isDir: isDir };\n });\n}\n\nfunction iconFor(entry) {\n if (entry.isDir) return \"\\\\uD83D\\\\uDCC1\";\n var ext = (entry.name.split(\".\").pop() || \"\").toLowerCase();\n if ([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\", \"bmp\", \"ico\", \"avif\"].indexOf(ext) !== -1) return \"\\\\uD83D\\\\uDDBC\\\\uFE0F\";\n if ([\"mp4\", \"webm\", \"mov\", \"avi\", \"mkv\"].indexOf(ext) !== -1) return \"\\\\uD83C\\\\uDFA5\";\n if ([\"mp3\", \"wav\", \"ogg\", \"flac\", \"aac\", \"m4a\"].indexOf(ext) !== -1) return \"\\\\uD83C\\\\uDFB5\";\n return \"\\\\uD83D\\\\uDCC4\";\n}\n\nfunction joinPath(base, name) {\n if (base === \".\" || base === \"\") return name;\n return base.replace(/\\\\/+$/, \"\") + \"/\" + name;\n}\n\nfunction parentPath(path) {\n if (path === \".\" || path.indexOf(\"/\") === -1) return \".\";\n return path.slice(0, path.lastIndexOf(\"/\")) || \".\";\n}\n\nfunction renderGrid() {\n document.getElementById(\"breadcrumb\").textContent = currentPath;\n var grid = document.getElementById(\"grid\");\n var empty = document.getElementById(\"empty\");\n grid.innerHTML = \"\";\n if (entries.length === 0) {\n empty.style.display = \"block\";\n return;\n }\n empty.style.display = \"none\";\n entries.forEach(function (entry, idx) {\n var cell = document.createElement(\"div\");\n cell.className = \"cell\";\n cell.dataset.idx = String(idx);\n cell.innerHTML = '<div class=\"icon\">' + iconFor(entry) + '</div><div class=\"name\"></div>';\n cell.querySelector(\".name\").textContent = entry.name;\n cell.addEventListener(\"click\", function () { onSelect(entry, cell); });\n grid.appendChild(cell);\n });\n}\n\nfunction onSelect(entry, cell) {\n Array.prototype.forEach.call(document.querySelectorAll(\".cell\"), function (c) {\n c.classList.remove(\"selected\");\n });\n cell.classList.add(\"selected\");\n if (entry.isDir) {\n loadDirectory(joinPath(currentPath, entry.name));\n return;\n }\n showFileDetails(joinPath(currentPath, entry.name));\n}\n\nfunction showFileDetails(path) {\n var details = document.getElementById(\"details\");\n details.style.display = \"block\";\n details.innerHTML = \"<h3>Loading&#8230;</h3>\";\n setStatus(\"Reading \" + path + \"\\\\u2026\");\n callApp(\"file_info\", { path: path })\n .then(function (info) {\n var rows = \"\";\n if (info && typeof info === \"object\") {\n [\"name\", \"type\", \"size\", \"modified\"].forEach(function (key) {\n if (info[key] !== undefined) {\n rows += '<div class=\"row\"><span>' + key + '</span><span></span></div>';\n }\n });\n }\n details.innerHTML = \"<h3>Details</h3>\" + rows + '<div id=\"preview\">Loading preview&#8230;</div>';\n var rowEls = details.querySelectorAll(\".row span:last-child\");\n var keys = [\"name\", \"type\", \"size\", \"modified\"].filter(function (key) {\n return info && info[key] !== undefined;\n });\n keys.forEach(function (key, i) {\n rowEls[i].textContent = String(info[key]);\n });\n return callApp(\"file_read\", { path: path });\n })\n .then(function (content) {\n var pre = document.getElementById(\"preview\");\n if (!pre) return;\n var text = typeof content === \"string\" ? content : JSON.stringify(content);\n pre.textContent = text.length > 2000 ? text.slice(0, 2000) + \"\\\\u2026 (truncated)\" : (text || \"(empty file)\");\n setStatus(\"Ready \\\\u00b7 \" + currentPath);\n })\n .catch(function (err) {\n var pre = document.getElementById(\"preview\");\n if (pre) pre.textContent = \"Preview unavailable (\" + err.message + \")\";\n setStatus(\"Ready \\\\u00b7 \" + currentPath);\n });\n}\n\nfunction loadDirectory(path) {\n document.getElementById(\"details\").style.display = \"none\";\n setStatus(\"Loading \" + path + \"\\\\u2026\");\n return callApp(\"directory_list\", { path: path })\n .then(function (raw) {\n currentPath = path;\n entries = parseDirectoryListing(raw).sort(function (a, b) {\n if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n renderGrid();\n setStatus(\"Ready \\\\u00b7 \" + entries.length + \" entries \\\\u00b7 \" + path);\n })\n .catch(function (err) {\n entries = [];\n renderGrid();\n setStatus(\"Error: \" + err.message);\n });\n}\n\ndocument.getElementById(\"up-btn\").addEventListener(\"click\", function () {\n loadDirectory(parentPath(currentPath));\n});\ndocument.getElementById(\"refresh-btn\").addEventListener(\"click\", function () {\n loadDirectory(currentPath);\n});\n\nwindow.McpApp.connect()\n .then(function (bridge) {\n callTool = bridge.callTool;\n return loadDirectory(\".\");\n })\n .catch(function (err) {\n setStatus(\"Standalone mode \\\\u2014 no host bridge (\" + err.message + \")\");\n entries = [];\n renderGrid();\n });\n</script>\n</body>\n</html>`\n","/**\n * `media-viewer` — a single-agent agentproto app for media cataloging.\n *\n * One agent (cataloger) scans a workspace folder for images, video, and audio\n * files, collects metadata, and produces a structured JSON catalog. Paired\n * with a Cowork McpApp gallery widget for visual browsing.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { cataloger } from \"./agents/cataloger.js\"\nimport { scanMedia } from \"./workflows/scan-media.js\"\nimport { MEDIA_VIEWER_HTML, MEDIA_VIEWER_TOOLS } from \"./ui.js\"\n\nexport const mediaViewer: AppHandle = defineApp({\n id: \"@agentproto/media-viewer\",\n name: \"Media Viewer\",\n version: \"0.1.0\",\n description: \"Scan a folder for images, video, and audio — gallery view.\",\n agents: [cataloger],\n workflows: [scanMedia],\n ui: {\n html: MEDIA_VIEWER_HTML,\n title: \"Media Viewer\",\n tools: [...MEDIA_VIEWER_TOOLS],\n },\n})\n\nexport { cataloger, scanMedia }\n","#!/usr/bin/env node\n/**\n * `agentproto-apps-sync` — emit every bundled `@agentproto/apps` app to disk\n * and write a catalog of what landed where.\n *\n * Each app's `AGENT.md` / `WORKFLOW.md` / `APP.md` manifests are written\n * under `<baseDir>/<slug>` via `AppHandle.emit` (see `@agentproto/app-kit`'s\n * `emitApp`); the catalog is a flat index a host can read without walking\n * every app's directory (`app_install`'s own discovery path already reads\n * each `APP.md` individually — this is a cheaper \"what's on disk\" summary\n * for the sync bin's own log, not a replacement for that).\n */\n\nimport { mkdir, writeFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { join, dirname } from \"node:path\"\nimport { parseArgs } from \"node:util\"\nimport type { AppHandle } from \"@agentproto/app-kit\"\nimport { codeTeam } from \"../code-team/index.js\"\nimport { contentTeam } from \"../content-team/index.js\"\nimport { mailTriage } from \"../mail-triage/index.js\"\nimport { mediaViewer } from \"../media-viewer/index.js\"\n\ninterface CatalogEntry {\n readonly appId: string\n readonly name: string\n readonly description: string\n readonly dir: string\n readonly category: string\n}\n\nconst APPS: readonly { readonly slug: string; readonly category: string; readonly handle: AppHandle }[] = [\n { slug: \"code-team\", category: \"team\", handle: codeTeam },\n { slug: \"content-team\", category: \"team\", handle: contentTeam },\n { slug: \"mail-triage\", category: \"app\", handle: mailTriage },\n { slug: \"media-viewer\", category: \"app\", handle: mediaViewer },\n]\n\nexport async function syncApps(baseDir: string): Promise<CatalogEntry[]> {\n const catalog: CatalogEntry[] = []\n for (const { slug, category, handle } of APPS) {\n const dir = join(baseDir, slug)\n await handle.emit(dir)\n catalog.push({\n appId: handle.id ?? slug,\n name: handle.name ?? slug,\n description: handle.description ?? \"\",\n dir,\n category,\n })\n console.log(`emitted ${handle.id ?? slug} -> ${dir}`)\n }\n return catalog\n}\n\nexport async function writeCatalog(baseDir: string, catalog: readonly CatalogEntry[]): Promise<string> {\n const catalogPath = join(baseDir, \"..\", \"app-catalog.json\")\n await mkdir(dirname(catalogPath), { recursive: true })\n await writeFile(catalogPath, JSON.stringify({ apps: catalog }, null, 2) + \"\\n\", \"utf8\")\n return catalogPath\n}\n\nasync function main(argv: readonly string[]): Promise<void> {\n const { values } = parseArgs({\n args: [...argv],\n allowPositionals: false,\n strict: true,\n options: { \"base-dir\": { type: \"string\" } },\n })\n const baseDir = values[\"base-dir\"] ?? join(homedir(), \".agentproto\", \"apps\")\n\n await mkdir(baseDir, { recursive: true })\n const catalog = await syncApps(baseDir)\n const catalogPath = await writeCatalog(baseDir, catalog)\n console.log(`wrote catalog (${catalog.length} apps) -> ${catalogPath}`)\n}\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n main(process.argv.slice(2)).catch(err => {\n console.error(err instanceof Error ? err.message : String(err))\n process.exitCode = 1\n })\n}\n"]}
1
+ {"version":3,"sources":["../../src/code-team/agents/implementer.ts","../../src/code-team/agents/reviewer.ts","../../src/code-team/agents/fixer.ts","../../src/code-team/workflows/deliver-change.ts","../../src/code-team/index.ts","../../src/content-team/agents/researcher.ts","../../src/content-team/agents/writer.ts","../../src/content-team/agents/editor.ts","../../src/content-team/agents/illustrator.ts","../../src/content-team/workflows/produce-content.ts","../../src/content-team/workflows/produce-cover.ts","../../src/content-team/index.ts","../../src/mail-triage/agents/triager.ts","../../src/mail-triage/workflows/triage-inbox.ts","../../src/mail-triage/ui.ts","../../src/mail-triage/index.ts","../../src/media-viewer/agents/cataloger.ts","../../src/media-viewer/workflows/scan-media.ts","../../src/media-viewer/ui.ts","../../src/media-viewer/index.ts","../../src/session-viewer/agents/narrator.ts","../../src/session-viewer/workflows/narrate-session.ts","../../src/session-viewer/ui.ts","../../src/session-viewer/index.ts","../../src/bin/sync.ts"],"names":["defineAgent","defineWorkflow","defineApp","APP_ID"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,OAAA,CAAA;;;AC1LhD,IAAM,aAAwBC,SAAAA,CAAU;AAAA,EAC7C,EAAA,EAAI,yBAAA;AAAA,EACJ,IAAA,EAAM,aAAA;AAAA,EACN,OAAA,EAAS,OAAA;AAAA,EACT,WAAA,EAAa,sDAAA;AAAA,EACb,MAAA,EAAQ,CAAC,OAAO,CAAA;AAAA,EAChB,SAAA,EAAW,CAAC,WAAW,CAAA;AAAA,EACvB,EAAA,EAAI;AAAA,IACF,IAAA,EAAM,gBAAA;AAAA,IACN,KAAA,EAAO,aAAA;AAAA,IACP,KAAA,EAAO,CAAC,GAAG,iBAAiB;AAAA;AAEhC,CAAC,CAAA;ACtBM,IAAM,SAAA,GAAwB;AAAA,EACnC,OAAOF,WAAAA,CAAY;AAAA,IACjB,MAAA,EAAQ,UAAA;AAAA,IACR,EAAA,EAAI,6BAAA;AAAA,IACJ,WAAA,EACE,8GAAA;AAAA,IACF,KAAA,EAAO,iBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,8DAAA;AAAA,MACA,qDAAA;AAAA,MACA;AAAA,KACF;AAAA,IACA,KAAA,EAAO,CAAC,UAAA,EAAY,WAAA,EAAa,WAAW,CAAA;AAAA,IAC5C,SAAA,EAAW,CAAC,EAAE,GAAA,EAAK,cAAc;AAAA,GAClC,CAAA;AAAA,EACD,IAAA,EAAM;AAAA,IACJ,oFAAA;AAAA,IACA,EAAA;AAAA,IACA,gFAAA;AAAA,IACA,sCAAA;AAAA,IACA,mEAAA;AAAA,IACA,wCAAA;AAAA,IACA,8CAAA;AAAA,IACA,EAAA;AAAA,IACA,6EAAA;AAAA,IACA,EAAA;AAAA,IACA,uCAAA;AAAA,IACA,SAAA;AAAA,IACA,GAAA;AAAA,IACA,+BAAA;AAAA,IACA,mCAAA;AAAA,IACA,wFAAA;AAAA,IACA,cAAA;AAAA,IACA,OAAA;AAAA,IACA,oCAAA;AAAA,IACA,2BAAA;AAAA,IACA,wBAAA;AAAA,IACA,4BAAA;AAAA,IACA,4BAAA;AAAA,IACA,4CAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,GAAA;AAAA,IACA,KAAA;AAAA,IACA,EAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,IAAI;AACb,CAAA;AChDO,IAAM,YAAYC,cAAAA,CAAe;AAAA,EACtC,EAAA,EAAI,YAAA;AAAA,EACJ,IAAA,EAAM,YAAA;AAAA,EACN,WAAA,EACE,oFAAA;AAAA,EACF,OAAA,EAAS,OAAA;AAAA,EACT,QAAQ,EAAC;AAAA,EACT,SAAS,EAAC;AAAA,EACV,KAAA,EAAO;AAAA,IACL;AAAA,MACE,EAAA,EAAI,SAAA;AAAA,MACJ,IAAA,EAAM,OAAA;AAAA,MACN,KAAA,EAAO,EAAE,GAAA,EAAK,6BAAA,EAA8B;AAAA,MAC5C,MAAA,EACE;AAAA;AAIJ;AAEJ,CAAC,CAAA;;;ACVM,IAAM,kBAAA,GAAqB,CAAC,gBAAA,EAAkB,WAAA,EAAa,WAAW,CAAA;AAE7E,IAAME,OAAAA,GAAS,0BAAA;AAER,IAAM,iBAAA,GAAoB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAAA,EAuDlB,IAAA,CAAK,SAAA,CAAUA,OAAM,CAAC,CAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAA,CAAA;;;AC1D9B,IAAM,cAAyBD,SAAAA,CAAU;AAAA,EAC9C,EAAA,EAAI,0BAAA;AAAA,EACJ,IAAA,EAAM,cAAA;AAAA,EACN,OAAA,EAAS,OAAA;AAAA,EACT,WAAA,EAAa,iEAAA;AAAA,EACb,MAAA,EAAQ,CAAC,SAAS,CAAA;AAAA,EAClB,SAAA,EAAW,CAAC,SAAS,CAAA;AAAA,EACrB,EAAA,EAAI;AAAA,IACF,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,cAAA;AAAA,IACP,KAAA,EAAO,CAAC,GAAG,kBAAkB;AAAA;AAEjC,CAAC,CAAA;ACtBM,IAAM,QAAA,GAAuB;AAAA,EAClC,OAAOF,WAAAA,CAAY;AAAA,IACjB,MAAA,EAAQ,UAAA;AAAA,IACR,EAAA,EAAI,8BAAA;AAAA,IACJ,WAAA,EACE,2HAAA;AAAA,IACF,KAAA,EAAO,iBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,qEAAA;AAAA,MACA;AAAA,KACF;AAAA,IACA,KAAA,EAAO,CAAC,mBAAA,EAAqB,cAAc,CAAA;AAAA,IAC3C,SAAA,EAAW,CAAC,EAAE,GAAA,EAAK,mBAAmB;AAAA,GACvC,CAAA;AAAA,EACD,IAAA,EAAM;AAAA,IACJ,sGAAA;AAAA,IACA,EAAA;AAAA,IACA,uFAAA;AAAA,IACA,2FAAA;AAAA,IACA,uFAAA;AAAA,IACA,EAAA;AAAA,IACA,wCAAA;AAAA,IACA,4BAAA;AAAA,IACA,2FAAA;AAAA,IACA,uFAAA;AAAA,IACA,EAAA;AAAA,IACA,0FAAA;AAAA,IACA,8FAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,IAAI;AACb,CAAA;AC/BO,IAAM,iBAAiBC,cAAAA,CAAe;AAAA,EAC3C,EAAA,EAAI,iBAAA;AAAA,EACJ,IAAA,EAAM,iBAAA;AAAA,EACN,WAAA,EAAa,qEAAA;AAAA,EACb,OAAA,EAAS,OAAA;AAAA,EACT,QAAQ,EAAC;AAAA,EACT,SAAS,EAAC;AAAA,EACV,KAAA,EAAO;AAAA,IACL;AAAA,MACE,EAAA,EAAI,SAAA;AAAA,MACJ,IAAA,EAAM,OAAA;AAAA,MACN,KAAA,EAAO,EAAE,GAAA,EAAK,8BAAA,EAA+B;AAAA,MAC7C,MAAA,EACE;AAAA;AAGJ;AAEJ,CAAC,CAAA;;;ACLM,IAAM,oBAAA,GAAuB,CAAC,cAAA,EAAgB,mBAAmB,CAAA;AAExE,IAAME,OAAAA,GAAS,4BAAA;AAER,IAAM,mBAAA,GAAsB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAAA,EAqGpB,IAAA,CAAK,SAAA,CAAUA,OAAM,CAAC,CAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAA,CAAA;;;ACtG9B,IAAM,gBAA2BD,SAAAA,CAAU;AAAA,EAChD,EAAA,EAAI,4BAAA;AAAA,EACJ,IAAA,EAAM,gBAAA;AAAA,EACN,OAAA,EAAS,OAAA;AAAA,EACT,WAAA,EAAa,qFAAA;AAAA,EACb,MAAA,EAAQ,CAAC,QAAQ,CAAA;AAAA,EACjB,SAAA,EAAW,CAAC,cAAc,CAAA;AAAA,EAC1B,EAAA,EAAI;AAAA,IACF,IAAA,EAAM,mBAAA;AAAA,IACN,KAAA,EAAO,gBAAA;AAAA,IACP,KAAA,EAAO,CAAC,GAAG,oBAAoB;AAAA;AAEnC,CAAC,CAAA;;;ACED,IAAM,IAAA,GAAoG;AAAA,EACxG,EAAE,IAAA,EAAM,WAAA,EAAa,QAAA,EAAU,MAAA,EAAQ,QAAQ,QAAA,EAAS;AAAA,EACxD,EAAE,IAAA,EAAM,cAAA,EAAgB,QAAA,EAAU,MAAA,EAAQ,QAAQ,WAAA,EAAY;AAAA,EAC9D,EAAE,IAAA,EAAM,aAAA,EAAe,QAAA,EAAU,KAAA,EAAO,QAAQ,UAAA,EAAW;AAAA,EAC3D,EAAE,IAAA,EAAM,cAAA,EAAgB,QAAA,EAAU,KAAA,EAAO,QAAQ,WAAA,EAAY;AAAA,EAC7D,EAAE,IAAA,EAAM,gBAAA,EAAkB,QAAA,EAAU,KAAA,EAAO,QAAQ,aAAA;AACrD,CAAA;AAEA,eAAsB,SAAS,OAAA,EAA0C;AACvE,EAAA,MAAM,UAA0B,EAAC;AACjC,EAAA,KAAA,MAAW,EAAE,IAAA,EAAM,QAAA,EAAU,MAAA,MAAY,IAAA,EAAM;AAC7C,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,OAAA,EAAS,IAAI,CAAA;AAC9B,IAAA,MAAM,MAAA,CAAO,KAAK,GAAG,CAAA;AACrB,IAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,MACX,KAAA,EAAO,OAAO,EAAA,IAAM,IAAA;AAAA,MACpB,IAAA,EAAM,OAAO,IAAA,IAAQ,IAAA;AAAA,MACrB,WAAA,EAAa,OAAO,WAAA,IAAe,EAAA;AAAA,MACnC,GAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAA,OAAA,CAAQ,IAAI,CAAA,QAAA,EAAW,MAAA,CAAO,MAAM,IAAI,CAAA,IAAA,EAAO,GAAG,CAAA,CAAE,CAAA;AAAA,EACtD;AACA,EAAA,OAAO,OAAA;AACT;AAEA,eAAsB,YAAA,CAAa,SAAiB,OAAA,EAAmD;AACrG,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,OAAA,EAAS,IAAA,EAAM,kBAAkB,CAAA;AAC1D,EAAA,MAAM,MAAM,OAAA,CAAQ,WAAW,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACrD,EAAA,MAAM,SAAA,CAAU,WAAA,EAAa,IAAA,CAAK,SAAA,CAAU,EAAE,IAAA,EAAM,OAAA,EAAQ,EAAG,IAAA,EAAM,CAAC,CAAA,GAAI,IAAA,EAAM,MAAM,CAAA;AACtF,EAAA,OAAO,WAAA;AACT;AAEA,eAAe,KAAK,IAAA,EAAwC;AAC1D,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,CAAU;AAAA,IAC3B,IAAA,EAAM,CAAC,GAAG,IAAI,CAAA;AAAA,IACd,gBAAA,EAAkB,KAAA;AAAA,IAClB,MAAA,EAAQ,IAAA;AAAA,IACR,SAAS,EAAE,UAAA,EAAY,EAAE,IAAA,EAAM,UAAS;AAAE,GAC3C,CAAA;AACD,EAAA,MAAM,OAAA,GAAU,OAAO,UAAU,CAAA,IAAK,KAAK,OAAA,EAAQ,EAAG,eAAe,MAAM,CAAA;AAE3E,EAAA,MAAM,KAAA,CAAM,OAAA,EAAS,EAAE,SAAA,EAAW,MAAM,CAAA;AACxC,EAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,OAAO,CAAA;AACtC,EAAA,MAAM,WAAA,GAAc,MAAM,YAAA,CAAa,OAAA,EAAS,OAAO,CAAA;AACvD,EAAA,OAAA,CAAQ,IAAI,CAAA,eAAA,EAAkB,OAAA,CAAQ,MAAM,CAAA,UAAA,EAAa,WAAW,CAAA,CAAE,CAAA;AACxE;AAEA,IAAI,YAAY,GAAA,KAAQ,CAAA,OAAA,EAAU,QAAQ,IAAA,CAAK,CAAC,CAAC,CAAA,CAAA,EAAI;AACnD,EAAA,IAAA,CAAK,QAAQ,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA,CAAE,MAAM,CAAA,GAAA,KAAO;AACvC,IAAA,OAAA,CAAQ,MAAM,GAAA,YAAe,KAAA,GAAQ,IAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA;AAC9D,IAAA,OAAA,CAAQ,QAAA,GAAW,CAAA;AAAA,EACrB,CAAC,CAAA;AACH","file":"sync.mjs","sourcesContent":["import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Writes the requested change. */\nexport const implementer: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/implementer\",\n description: \"Implements the requested code change.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Change only what the task asks\", \"Match the surrounding style\"],\n tools: [\"list_dir\", \"read_file\", \"write_file\", \"edit_file\", \"run_command\"],\n workflows: [{ ref: \"deliver-change\" }],\n }),\n body:\n \"You implement the requested change. Read the task and the surrounding code \" +\n \"first, make the smallest correct change that satisfies it, then run the \" +\n \"tests. Do not touch anything the task didn't ask for.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Reviews the diff; reports only. */\nexport const reviewer: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/reviewer\",\n description: \"Reviews the diff and reports findings.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Report findings only\", \"Never edit files\", \"Never run gh pr merge\"],\n // Read-only + run_command (to inspect the diff / run tests). No write/edit\n // tools — the \"never edit files\" boundary is enforced by capability too.\n tools: [\"list_dir\", \"read_file\", \"run_command\"],\n workflows: [{ ref: \"deliver-change\" }],\n }),\n body:\n \"You are a rigorous reviewer. Read the diff, and report each real issue as \" +\n \"`file:line — why`. Change nothing; you only report.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Applies the reviewer's findings, precisely and minimally. */\nexport const fixer: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/fixer\",\n description: \"Applies the reviewer's findings, precisely and minimally.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Apply only the listed findings\", \"No unrelated refactors\"],\n tools: [\"list_dir\", \"read_file\", \"write_file\", \"edit_file\", \"run_command\"],\n workflows: [{ ref: \"deliver-change\" }],\n }),\n body:\n \"You apply the reviewer's findings. Address each listed finding exactly, add \" +\n \"nothing extra, and re-run the tests when done.\",\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\n/**\n * The one workflow the code team delivers a change through. Every team agent\n * references it; the app-kit attachment invariant keeps that honest.\n *\n * Three declarative `kind:\"agent\"` steps — implementer → reviewer → fixer —\n * one per team agent, each `agent.ref` resolving (at compile time, against\n * this app's own install record) to that agent's emitted AGENT.md. This\n * replaces an earlier `ToolStep`-based draft (`apply_patch`/`read_diff`/\n * `run_tests`) that dispatched daemon-side, where those tool ids don't\n * exist — the daemon has no `apply_patch` tool, only live agent sessions\n * that can edit files. Agent steps are the adoption fix (WP-B4): each step\n * spawns the named team agent as a real session and lets it act.\n */\nexport const deliverChange = defineWorkflow({\n id: \"deliver-change\",\n name: \"Deliver a code change\",\n description: \"Implement the change, review the diff, apply fixes, verify.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n {\n id: \"implement\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/implementer\" },\n prompt: \"Implement the requested change.\",\n },\n {\n id: \"review\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/reviewer\" },\n prompt:\n \"Review the diff the implementer just produced. Report every real issue as `file:line — why`.\",\n },\n {\n id: \"fix\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/fixer\" },\n prompt: \"Apply the reviewer's findings, then re-run the tests.\",\n },\n ],\n})\n","/**\n * `code-team` — a ready-made agentproto app: a small team that ships a code\n * change. Three agents (implementer → reviewer → fixer) bound to one delivery\n * workflow, declared with `@agentproto/app-kit`.\n *\n * Generic on purpose — `@agentproto/…` ids, no product dependency, no home\n * workspace. A host imports it and uses any subset of its agents/workflows:\n * in-process via `codeTeam.toMastraAgents(...)`, or on disk via\n * `codeTeam.emit(dir)`.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { implementer } from \"./agents/implementer.js\"\nimport { reviewer } from \"./agents/reviewer.js\"\nimport { fixer } from \"./agents/fixer.js\"\nimport { deliverChange } from \"./workflows/deliver-change.js\"\n\nexport const codeTeam: AppHandle = defineApp({\n id: \"@agentproto/code-team\",\n name: \"Code Team\",\n version: \"0.1.0\",\n description: \"Implement, review, and fix code changes.\",\n agents: [implementer, reviewer, fixer],\n workflows: [deliverChange],\n})\n\nexport { implementer, reviewer, fixer, deliverChange }\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Gathers sources for the piece. */\nexport const researcher: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/researcher\",\n description: \"Reads the provided sources and summarizes them for the writer.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Cite every claim\", \"Prefer primary sources\"],\n tools: [\"list_dir\", \"read_file\", \"run_command\"],\n workflows: [{ ref: \"produce-content\" }],\n }),\n body:\n \"You read the source material already in the workspace (list_dir, then \" +\n \"read_file the sources), extract the key facts, and hand the writer a tight \" +\n \"brief with a citation for every claim. Use run_command only to fetch a URL \" +\n \"already named in the task; do not invent sources.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Drafts the piece from the brief. */\nexport const writer: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/writer\",\n description: \"Drafts the piece from the researcher's brief.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Stay on the brief\", \"One idea per paragraph\"],\n tools: [\"list_dir\", \"read_file\", \"write_file\", \"edit_file\"],\n workflows: [{ ref: \"produce-content\" }],\n }),\n body:\n \"You draft the piece from the brief and its sources. Lead with the point, \" +\n \"keep one idea per paragraph, and don't invent facts the brief doesn't have.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Tightens the draft. */\nexport const editor: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/editor\",\n description: \"Edits the draft for voice, clarity, and length.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"Preserve the writer's meaning\", \"Cut, don't pad\"],\n tools: [\"read_file\", \"edit_file\"],\n workflows: [{ ref: \"produce-content\" }],\n }),\n body:\n \"You edit the draft. Tighten the voice, cut fluff, fix clarity — without \" +\n \"changing what the writer meant. Leave it shorter than you found it.\",\n}\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Art-directs the cover illustration for each article. */\nexport const illustrator: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/illustrator\",\n description: \"Art-directs the cover illustration for each article.\",\n model: \"claude-sonnet-5\",\n boundaries: [\"One cover per article\", \"Text-free prompts only\"],\n tools: [\"read_file\", \"write_file\", \"run_command\"],\n workflows: [{ ref: \"produce-cover\" }],\n }),\n body:\n \"You art-direct a single cover illustration for the article. \" +\n \"Read the article (read_file) to extract the one visual idea that best \" +\n \"represents the piece, then write the art direction (write_file) as a \" +\n \"clean, text-free image prompt. Keep the visual discipline tight: bold \" +\n \"flat shapes, limited risograph-style palettes, strong negative space, and \" +\n \"a single focal subject. Never include text, lettering, logos, watermarks, \" +\n \"or UI chrome in the prompt. Produce exactly one cover per article; if the \" +\n \"article calls for a literal diagram or chart, fall back to a precise \" +\n \"gpt-image-1 style description and treat the diagram itself as the image \" +\n \"subject. Use run_command only to inspect generated assets or move files \" +\n \"during the upload step.\",\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\n/** The one workflow the content team produces a piece through. Every team\n * agent references it (app-kit attachment invariant). */\nexport const produceContent = defineWorkflow({\n id: \"produce-content\",\n name: \"Produce a piece of content\",\n description: \"Read the sources, draft the piece, edit it, then commit it.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n // Concrete workspace tools (fs + exec), so the workflow describes a process\n // a standard runtime can actually run — not abstract tools nothing provides.\n steps: [\n { id: \"research\", kind: \"tool\", tool: \"read_file\" },\n { id: \"draft\", kind: \"tool\", tool: \"write_file\" },\n { id: \"edit\", kind: \"tool\", tool: \"edit_file\" },\n { id: \"publish\", kind: \"tool\", tool: \"run_command\" },\n ],\n})\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\n/** The one workflow the illustrator uses to produce a cover. */\nexport const produceCover = defineWorkflow({\n id: \"produce-cover\",\n name: \"Produce a cover illustration\",\n description:\n \"Read the article, derive the cover concept, generate the base image, \" +\n \"clean it up, and upload the final cover.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n { id: \"concept-from-article\", kind: \"tool\", tool: \"content.cover-concept\" },\n { id: \"base-gen\", kind: \"tool\", tool: \"content.cover-base-gen\" },\n { id: \"cleanup\", kind: \"tool\", tool: \"content.cover-cleanup\" },\n { id: \"upload\", kind: \"tool\", tool: \"content.cover-upload\" },\n ],\n})\n","/**\n * `content-team` — a ready-made agentproto app: a small team that produces a\n * piece of content. Three agents (researcher → writer → editor) bound to one\n * production workflow, declared with `@agentproto/app-kit`.\n *\n * Generic on purpose — `@agentproto/…` ids, no product dependency. A host\n * imports it and uses any subset of its agents/workflows.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { researcher } from \"./agents/researcher.js\"\nimport { writer } from \"./agents/writer.js\"\nimport { editor } from \"./agents/editor.js\"\nimport { illustrator } from \"./agents/illustrator.js\"\nimport { produceContent } from \"./workflows/produce-content.js\"\nimport { produceCover } from \"./workflows/produce-cover.js\"\n\nexport const contentTeam: AppHandle = defineApp({\n id: \"@agentproto/content-team\",\n name: \"Content Team\",\n version: \"0.1.0\",\n description: \"Research, draft, edit, and illustrate content.\",\n agents: [researcher, writer, editor, illustrator],\n workflows: [produceContent, produceCover],\n})\n\nexport { researcher, writer, editor, illustrator, produceContent, produceCover }\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\n/** Triages your inbox — finds unread mail, categorizes, labels, archives. */\nexport const triager: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/triager\",\n description: \"Triages your inbox — finds unread mail, categorizes, labels, archives.\",\n model: \"claude-sonnet-5\",\n boundaries: [\n \"Always show the triage plan before applying\",\n \"Never delete — only archive, label, or mark read\",\n \"Ask before applying bulk actions on more than 20 messages\",\n ],\n tools: [\n \"mailbox_list\",\n \"mailbox_search\",\n \"mailbox_list_threads\",\n \"mailbox_get_thread\",\n \"mailbox_labels_list\",\n \"mailbox_label_create\",\n \"mailbox_triage_plan\",\n \"mailbox_triage_apply\",\n ],\n workflows: [{ ref: \"triage-inbox\" }],\n }),\n body:\n \"You start by calling mailbox_list to discover connected mailboxes. Pick one \" +\n \"with `capabilities.triage: true` (triage requires the gmail.modify scope). \" +\n \"Every subsequent mailbox tool requires the `mailbox` parameter (UUID or alias \" +\n \"from this list).\\n\" +\n \"\\n\" +\n \"Scan the mailbox for unread messages using mailbox_search with \" +\n 'query=\"is:unread\" and the mailbox from step 1. Categorize them ' +\n \"(urgent, needs-reply, newsletter, notification, spam). Create one triage plan \" +\n \"per bulk action with mailbox_triage_plan — its exact contract is \" +\n '`{ mailbox, criteria: { query } | { messageIds }, action: { type: \"markRead\"|\"archive\"|\"trash\"|\"label\", addLabelIds?, removeLabelIds? } }` ' +\n \"— e.g. label each category, archive newsletters+notifications, keep \" +\n \"urgent+needs-reply in inbox. Each plan returns a `plan_id`, a `count`, and a \" +\n \"`sample`; it expires after 15 minutes. Show the plan before applying. Execute \" +\n \"only after user confirmation, with mailbox_triage_apply \" +\n \"`{ plan_id, confirm: true }` — that is the sole mutation path.\",\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\n/** Single-agent workflow: triage the inbox. */\nexport const triageInbox = defineWorkflow({\n id: \"triage-inbox\",\n name: \"Triage Inbox\",\n description: \"Scan inbox, categorize unread messages, create and apply triage plan.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n {\n id: \"triage\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/triager\" },\n prompt:\n \"Call mailbox_list to discover connected mailboxes, then scan the \" +\n \"selected mailbox for unread messages using mailbox_search with \" +\n 'query=\"is:unread\". Categorize them (urgent, needs-reply, newsletter, ' +\n \"notification, spam). Create a triage plan: label each category, archive \" +\n \"newsletters+notifications, keep urgent+needs-reply in inbox. \" +\n \"Show me the plan before applying.\",\n },\n ],\n})\n","/**\n * Mail Triage — a self-contained MCP App dashboard for `mail-triage`.\n *\n * Talks to the daemon only through the `window.McpApp.connect()` bridge\n * (never a direct `fetch`): every action round-trips through the app's own\n * `app_tool_call` gateway tool, `{ appId, tool, args }`. Mailbox access goes\n * through an imported agentpush MCP server (`imported:<alias>/<tool>` ids);\n * running the triager agent itself goes through the daemon's own\n * `app_run` / `app_status` / `agent_output` — a second path alongside the\n * deterministic plan/apply tools, so the panel can either drive the mailbox\n * tools directly or hand the whole job to the agent and watch it work.\n *\n * The agentpush alias is NOT hardcoded to one server: `app_tool_call`\n * enforces an exact-match allowlist (`ui.tools`), so every candidate alias\n * must be baked into `MAIL_TRIAGE_TOOLS` at emit time. The candidate list\n * comes from `MAIL_TRIAGE_MCP_ALIASES` (overridable via the\n * `MAIL_TRIAGE_MCP_ALIASES` env var, comma-separated, read when this module\n * is evaluated — i.e. at emit). At runtime the panel probes each candidate's\n * `mailbox_list` and activates the first alias that answers with mailboxes;\n * a selector appears in the toolbar when several respond.\n */\n\nconst ALIAS_TOOL_NAMES = [\n \"mailbox_list\",\n \"mailbox_search\",\n \"mailbox_triage_plan\",\n \"mailbox_triage_apply\",\n] as const\n\nconst DEFAULT_MCP_ALIASES = [\"agentpush-prod\", \"agentpush\"] as const\n\nfunction envAliases(): string[] | null {\n if (typeof process === \"undefined\" || !process.env) return null\n const raw = process.env[\"MAIL_TRIAGE_MCP_ALIASES\"]\n if (!raw) return null\n const list = raw\n .split(\",\")\n .map(s => s.trim())\n .filter(Boolean)\n return list.length > 0 ? list : null\n}\n\n/** Candidate imported-MCP aliases the dashboard may reach mailboxes through. */\nexport const MAIL_TRIAGE_MCP_ALIASES: readonly string[] = envAliases() ?? [...DEFAULT_MCP_ALIASES]\n\nexport const MAIL_TRIAGE_TOOLS: readonly string[] = [\n ...MAIL_TRIAGE_MCP_ALIASES.flatMap(alias => ALIAS_TOOL_NAMES.map(t => `imported:${alias}/${t}`)),\n \"app_run\",\n \"app_status\",\n \"agent_output\",\n \"app_list\",\n]\n\nconst APP_ID = \"@agentproto/mail-triage\"\nconst AGENT_ID = \"@agentproto/triager\"\nconst CATEGORIES = [\"urgent\", \"needs-reply\", \"newsletter\", \"notification\"]\n\nexport const MAIL_TRIAGE_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Mail Triage</title>\n<style>\n*{box-sizing:border-box;margin:0;padding:0}\n:root{\n --bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;\n --text:#e6edf3;--text2:#8b949e;\n --green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;\n}\nhtml,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;font-size:13px;background:var(--bg);color:var(--text)}\n#app{display:flex;flex-direction:column;height:100%;overflow-y:auto}\n#toolbar{padding:10px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;background:var(--bg2);flex-shrink:0;flex-wrap:wrap}\n#toolbar .title{font-size:13px;font-weight:600;flex:1}\nselect,input[type=text]{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 8px;border-radius:4px;font-size:12px;font-family:inherit}\n.abtn{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 10px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}\n.abtn:hover{background:var(--border)}\n.abtn:disabled{opacity:.4;cursor:default}\n.abtn.primary{background:var(--blue);border-color:var(--blue);color:#04101c}\n.abtn.primary:hover{opacity:.9}\n#caps{padding:6px 14px;display:flex;gap:6px;align-items:center;background:var(--bg2);border-bottom:1px solid var(--border);flex-shrink:0}\n.badge{font-size:11px;padding:2px 8px;border-radius:10px;border:1px solid var(--border);color:var(--text2)}\n.badge.on{color:var(--green);border-color:var(--green)}\n.badge.off{opacity:.5;text-decoration:line-through}\n#content{padding:14px;display:flex;flex-direction:column;gap:16px}\nsection{background:var(--bg2);border:1px solid var(--border);border-radius:6px;padding:12px}\nsection h3{font-size:11px;color:var(--text2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:10px;display:flex;justify-content:space-between;align-items:center}\n#summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px}\n.tile{background:var(--bg3);border:1px solid var(--border);border-radius:6px;padding:10px;text-align:center}\n.tile .count{font-size:22px;font-weight:700}\n.tile .label{font-size:11px;color:var(--text2);text-transform:capitalize}\n.tile.unread .count{color:var(--green)}\n.tile.urgent .count{color:var(--red)}\n.tile.needs-reply .count{color:var(--yellow)}\n.tile.newsletter .count{color:var(--blue)}\n.tile.notification .count{color:var(--purple)}\n#plan-list,#search-results,#log-events,#runs-list,#connect-hint-list{max-height:260px;overflow-y:auto;font-size:12px}\n.plan-item{display:flex;justify-content:space-between;gap:10px;padding:6px 0;border-bottom:1px solid var(--border)}\n.plan-item .subj{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.plan-item .tag{color:var(--text2);font-size:11px;white-space:nowrap}\n.runs-item{display:flex;gap:10px;align-items:center;padding:6px 0;border-bottom:1px solid var(--border);cursor:pointer}\n.runs-item:hover{background:var(--bg3)}\n.runs-item .rid{font-family:Menlo,Monaco,monospace;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}\n.runs-item .st{font-size:11px;white-space:nowrap}\n.runs-item .st.running{color:var(--yellow)}\n.runs-item .st.ended{color:var(--green)}\n.runs-item .meta{color:var(--text2);font-size:11px;white-space:nowrap}\n#log-tail{font-family:Menlo,Monaco,monospace;white-space:pre-wrap;word-break:break-word;background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:8px;max-height:260px;overflow-y:auto}\n.searchbar{display:flex;gap:8px;margin-bottom:8px}\n.searchbar input{flex:1}\n.actions{display:flex;gap:8px;margin-top:10px}\n#status{padding:6px 14px;font-size:11px;color:var(--text2);border-top:1px solid var(--border);background:var(--bg2);flex-shrink:0}\n.empty{color:var(--text2);padding:8px 0}\n.mini{font-size:10px;text-transform:none;letter-spacing:0;cursor:pointer;color:var(--blue);background:none;border:none;font-family:inherit}\n.caps-hint{font-size:11px;color:var(--yellow)}\n.snippet{color:var(--text2);font-size:11px;margin-top:2px}\n.mail-checkbox{accent-color:var(--blue);cursor:pointer;flex-shrink:0;margin-top:1px}\n.mail-open{font-size:10px;cursor:pointer;color:var(--blue);background:none;border:none;font-family:inherit;white-space:nowrap;padding:0;flex-shrink:0}\n.mail-open:hover{text-decoration:underline}\n#selection-bar{display:none;margin-bottom:8px;gap:8px;align-items:center}\n#selection-bar.show{display:flex}\n</style>\n</head>\n<body>\n<div id=\"app\">\n <div id=\"toolbar\">\n <span class=\"title\">Mail Triage</span>\n <select id=\"alias-select\" style=\"display:none\" title=\"MCP server\"></select>\n <select id=\"mailbox-select\"></select>\n <button class=\"abtn\" id=\"refresh-btn\">Refresh</button>\n </div>\n <div id=\"caps\" style=\"display:none\"></div>\n <div id=\"content\">\n <section id=\"connect-hint\" style=\"display:none\">\n <h3>Connect a mailbox</h3>\n <div id=\"connect-hint-list\"></div>\n <div class=\"empty\">Import/start an agentpush MCP server and connect a Gmail account in agentpush, then retry.</div>\n <div class=\"actions\">\n <button class=\"abtn primary\" id=\"connect-retry-btn\">Retry</button>\n </div>\n </section>\n\n <section>\n <h3>Inbox summary</h3>\n <div id=\"summary\"></div>\n </section>\n\n <section>\n <h3>Search</h3>\n <div class=\"searchbar\">\n <input type=\"text\" id=\"search-input\" placeholder=\"e.g. is:unread\">\n <button class=\"abtn\" id=\"search-btn\">Search</button>\n </div>\n <div id=\"selection-bar\">\n <span id=\"sel-count\" style=\"font-size:12px;color:var(--text2)\">0 selected</span>\n <button class=\"abtn primary\" data-action=\"send-selection\">Send selection to Claude</button>\n </div>\n <div id=\"search-results\"><div class=\"empty\">No search run yet.</div></div>\n </section>\n\n <section>\n <h3>Triage plan</h3>\n <div class=\"searchbar\">\n <input type=\"text\" id=\"plan-query\" placeholder=\"criteria query\" value=\"is:unread category:promotions older_than:7d\">\n <select id=\"plan-action\">\n <option value=\"markRead\">mark read</option>\n <option value=\"archive\">archive</option>\n <option value=\"label\">label&#8230;</option>\n <option value=\"trash\">trash</option>\n </select>\n <input type=\"text\" id=\"plan-label\" placeholder=\"label name\" style=\"display:none\">\n </div>\n <div class=\"actions\">\n <button class=\"abtn primary\" id=\"triage-btn\">Build plan</button>\n <button class=\"abtn\" id=\"apply-btn\" disabled>Apply</button>\n </div>\n <div id=\"plan-list\" style=\"margin-top:10px\"><div class=\"empty\">No plan yet — click Build plan.</div></div>\n </section>\n\n <section>\n <h3>Triager agent</h3>\n <div class=\"actions\">\n <button class=\"abtn\" id=\"run-agent-btn\">Run Triager Agent</button>\n </div>\n <div id=\"log-events\" style=\"margin-top:10px\"><div class=\"empty\">Agent has not run yet.</div></div>\n <div id=\"log-tail\" style=\"margin-top:6px\"></div>\n </section>\n\n <section>\n <h3><span>Past runs</span><button class=\"mini\" id=\"runs-refresh-btn\">refresh</button></h3>\n <div id=\"runs-list\"><div class=\"empty\">No runs yet.</div></div>\n </section>\n </div>\n <div id=\"status\">Connecting&#8230;</div>\n</div>\n<script>\nvar APP_ID = ${JSON.stringify(APP_ID)};\nvar AGENT_ID = ${JSON.stringify(AGENT_ID)};\nvar CATEGORIES = ${JSON.stringify(CATEGORIES)};\nvar ALIASES = ${JSON.stringify(MAIL_TRIAGE_MCP_ALIASES)};\nvar POLL_TIMEOUT_MS = 10 * 60 * 1000;\nvar callTool = null;\nvar updateModelContext = null;\nvar openLink = null;\nvar onTeardown = null;\nvar currentPlan = null;\nvar pollHandle = null;\nvar planExpiryHandle = null;\nvar activeAlias = ALIASES[0];\nvar currentMailboxes = [];\nvar categoryCounts = null;\nvar unreadCount = null;\nvar aliasStates = {};\nvar selectedMessages = {};\n\nfunction setStatus(msg) {\n document.getElementById(\"status\").textContent = msg;\n}\n\n// See media-viewer/ui.ts for why this loop exists: app_tool_call wraps its\n// dispatch result in at least one more {content:[{type:\"text\",text:...}]}\n// shell than the underlying tool returned.\nfunction unwrapText(result) {\n var cur = result;\n for (var i = 0; i < 4; i++) {\n if (typeof cur === \"string\") {\n try {\n cur = JSON.parse(cur);\n continue;\n } catch (e) {\n return cur;\n }\n }\n if (cur && Array.isArray(cur.content) && cur.content[0] && typeof cur.content[0].text === \"string\") {\n cur = cur.content[0].text;\n continue;\n }\n break;\n }\n return cur;\n}\n\nfunction callApp(tool, args) {\n if (!callTool) {\n return Promise.reject(new Error(\"Not connected to host bridge (standalone mode).\"));\n }\n return callTool(\"app_tool_call\", { appId: APP_ID, tool: tool, args: args || {} }).then(function (result) {\n var value = unwrapText(result);\n if (value && typeof value === \"object\" && !Array.isArray(value) && typeof value.error === \"string\") {\n throw new Error(value.error);\n }\n return value;\n });\n}\n\nfunction aliasTool(name) {\n return \"imported:\" + activeAlias + \"/\" + name;\n}\n\n// Disables a button and swaps its label to an ellipsis while a call is in\n// flight, so double-submit is impossible; restores the original label after.\nfunction setBusy(btn, busy) {\n if (busy) {\n if (btn.getAttribute(\"data-label\") === null) btn.setAttribute(\"data-label\", btn.textContent);\n btn.disabled = true;\n btn.textContent = \"\\\\u2026\";\n } else {\n btn.disabled = false;\n var label = btn.getAttribute(\"data-label\");\n if (label !== null) btn.textContent = label;\n }\n}\n\nfunction isSelected(id) {\n return selectedMessages[id] ? true : false;\n}\n\nfunction toggleSelection(id, item) {\n if (selectedMessages[id]) {\n delete selectedMessages[id];\n } else {\n selectedMessages[id] = item;\n }\n updateSelectionBar();\n}\n\nfunction updateSelectionBar() {\n var bar = document.getElementById(\"selection-bar\");\n var countEl = document.getElementById(\"sel-count\");\n var count = 0;\n for (var k in selectedMessages) { if (selectedMessages.hasOwnProperty(k)) count++; }\n countEl.textContent = count + \" selected\";\n bar.className = count > 0 ? \"show\" : \"\";\n // Sync checkboxes\n var cbs = document.querySelectorAll(\"#search-results .mail-checkbox\");\n for (var i = 0; i < cbs.length; i++) {\n var mid = cbs[i].getAttribute(\"data-message-id\");\n cbs[i].checked = mid ? isSelected(mid) : false;\n }\n}\n\nfunction sendSelectionToClaude() {\n if (!updateModelContext) {\n setStatus(\"Host does not support updateModelContext.\");\n return;\n }\n var items = [];\n var names = [];\n for (var k in selectedMessages) {\n if (!selectedMessages.hasOwnProperty(k)) continue;\n var msg = selectedMessages[k];\n items.push({\n id: msg.id || k,\n threadId: msg.threadId || \"\",\n from: msg.from || \"\",\n subject: msg.subject || \"\",\n timestamp: msg.internalDate || msg.date || \"\"\n });\n if (msg.subject) names.push(msg.subject);\n }\n if (items.length === 0) {\n setStatus(\"No messages selected.\");\n return;\n }\n var summary = items.length + \" emails selected for triage: \" + names.join(\", \");\n setStatus(\"Sending selection to Claude\\\\u2026\");\n updateModelContext({\n content: [{ type: \"text\", text: summary }],\n structuredContent: { selection: items }\n }).then(function () {\n setStatus(\"Selection sent to Claude (\" + items.length + \" emails).\");\n }).catch(function (err) {\n setStatus(\"Failed to send selection: \" + (err && err.message ? err.message : String(err)));\n });\n}\n\nfunction clearPlanExpiry() {\n if (planExpiryHandle) {\n clearTimeout(planExpiryHandle);\n planExpiryHandle = null;\n }\n}\n\nfunction onPlanExpired() {\n planExpiryHandle = null;\n currentPlan = null;\n document.getElementById(\"apply-btn\").disabled = true;\n setStatus(\"Plan expired \\\\u2014 rebuild it.\");\n}\n\n// Arms a timeout that invalidates the plan at its server-declared expires_at\n// (plans expire after 15 minutes) so Apply never fires against a stale plan.\nfunction armPlanExpiry(expiresAt) {\n clearPlanExpiry();\n if (!expiresAt) return;\n var ms = new Date(expiresAt).getTime() - Date.now();\n if (isNaN(ms)) return;\n if (ms <= 0) {\n onPlanExpired();\n return;\n }\n planExpiryHandle = setTimeout(onPlanExpired, ms);\n}\n\nfunction asList(value, keys) {\n if (Array.isArray(value)) return value;\n if (value && typeof value === \"object\") {\n for (var i = 0; i < keys.length; i++) {\n if (Array.isArray(value[keys[i]])) return value[keys[i]];\n }\n }\n return [];\n}\n\nfunction renderSummary() {\n var el = document.getElementById(\"summary\");\n el.innerHTML = \"\";\n var tiles = [{ key: \"unread\", count: unreadCount }].concat(\n CATEGORIES.map(function (cat) {\n return { key: cat, count: categoryCounts ? categoryCounts[cat] : undefined };\n })\n );\n tiles.forEach(function (t) {\n var tile = document.createElement(\"div\");\n tile.className = \"tile \" + t.key;\n tile.innerHTML = '<div class=\"count\"></div><div class=\"label\"></div>';\n tile.querySelector(\".count\").textContent = t.count === undefined || t.count === null ? \"\\\\u2013\" : String(t.count);\n tile.querySelector(\".label\").textContent = t.key.replace(\"-\", \" \");\n el.appendChild(tile);\n });\n}\n\nfunction mailboxId(mb) {\n if (typeof mb === \"string\") return mb;\n return mb.id || mb.name || JSON.stringify(mb);\n}\n\nfunction mailboxText(mb) {\n if (typeof mb === \"string\") return mb;\n var name = mb.address || mb.label || mb.name || mb.id || JSON.stringify(mb);\n var caps = mb.capabilities;\n if (caps && typeof caps === \"object\") {\n return name + (caps.triage ? \" \\\\u2014 triage \\\\u2713\" : \" \\\\u2014 read-only\");\n }\n return name;\n}\n\nfunction selectedMailbox() {\n var id = document.getElementById(\"mailbox-select\").value;\n for (var i = 0; i < currentMailboxes.length; i++) {\n if (mailboxId(currentMailboxes[i]) === id) return currentMailboxes[i];\n }\n return null;\n}\n\nfunction renderCaps(mb) {\n var el = document.getElementById(\"caps\");\n var caps = mb && typeof mb === \"object\" ? mb.capabilities : null;\n if (!caps || typeof caps !== \"object\") {\n el.style.display = \"none\";\n return;\n }\n el.style.display = \"flex\";\n el.innerHTML = \"\";\n [\"read\", \"send\", \"triage\", \"compose\"].forEach(function (cap) {\n var b = document.createElement(\"span\");\n b.className = \"badge \" + (caps[cap] ? \"on\" : \"off\");\n b.textContent = cap;\n el.appendChild(b);\n });\n if (!caps.triage) {\n var hint = document.createElement(\"span\");\n hint.className = \"caps-hint\";\n hint.textContent = \"triage scope missing \\\\u2014 grant it via agentpush (mailbox_request_elevation)\";\n el.appendChild(hint);\n }\n}\n\n// Gmail category labels -> the dashboard's triage buckets. IMPORTANT wins,\n// so an important promo counts as urgent, not newsletter.\nfunction categoryOf(labelIds) {\n var l = labelIds || [];\n if (l.indexOf(\"IMPORTANT\") !== -1) return \"urgent\";\n if (l.indexOf(\"CATEGORY_PROMOTIONS\") !== -1) return \"newsletter\";\n if (l.indexOf(\"CATEGORY_UPDATES\") !== -1 || l.indexOf(\"CATEGORY_SOCIAL\") !== -1 || l.indexOf(\"CATEGORY_FORUMS\") !== -1) return \"notification\";\n return \"needs-reply\";\n}\n\nfunction onMailboxChange() {\n clearPlanExpiry();\n var mb = selectedMailbox();\n renderCaps(mb);\n var caps = mb && typeof mb === \"object\" ? mb.capabilities : null;\n var noTriage = !!(caps && typeof caps === \"object\" && !caps.triage);\n var scopeHint = noTriage ? \"Mailbox lacks triage capability \\\\u2014 grant it via agentpush (mailbox_request_elevation)\" : \"\";\n document.getElementById(\"triage-btn\").disabled = noTriage;\n document.getElementById(\"triage-btn\").title = scopeHint;\n document.getElementById(\"apply-btn\").disabled = true;\n document.getElementById(\"apply-btn\").title = scopeHint;\n currentPlan = null;\n categoryCounts = null;\n unreadCount = null;\n renderSummary();\n if (!mb) return;\n callApp(aliasTool(\"mailbox_search\"), { mailbox: mailboxId(mb), query: \"is:unread\", limit: 50 })\n .then(function (result) {\n var items = asList(result, [\"items\", \"messages\", \"results\"]);\n if (result && typeof result === \"object\" && typeof result.resultSizeEstimate === \"number\") {\n unreadCount = result.resultSizeEstimate;\n } else {\n unreadCount = items.length;\n }\n // Category breakdown over the newest page of unread — a sample when\n // the mailbox has more unread than one page.\n var counts = {};\n items.forEach(function (item) {\n var cat = categoryOf(item && item.labelIds);\n counts[cat] = (counts[cat] || 0) + 1;\n });\n categoryCounts = items.length > 0 ? counts : null;\n renderSummary();\n })\n .catch(function (err) {\n // KPI is best-effort: keep the dashes, but say why instead of silence.\n setStatus(\"Inbox summary unavailable: \" + err.message);\n });\n}\n\nfunction renderMailboxes(mailboxes) {\n currentMailboxes = mailboxes;\n var select = document.getElementById(\"mailbox-select\");\n select.innerHTML = \"\";\n if (mailboxes.length === 0) {\n var opt = document.createElement(\"option\");\n opt.textContent = \"no mailboxes\";\n opt.value = \"\";\n select.appendChild(opt);\n renderCaps(null);\n return;\n }\n mailboxes.forEach(function (mb) {\n var opt = document.createElement(\"option\");\n opt.value = mailboxId(mb);\n opt.textContent = mailboxText(mb);\n select.appendChild(opt);\n });\n onMailboxChange();\n}\n\nfunction renderAliasSelect(liveAliases) {\n var select = document.getElementById(\"alias-select\");\n select.innerHTML = \"\";\n var shown = liveAliases.length > 0 ? liveAliases : ALIASES;\n shown.forEach(function (alias) {\n var opt = document.createElement(\"option\");\n opt.value = alias;\n opt.textContent = alias;\n select.appendChild(opt);\n });\n select.value = activeAlias;\n select.style.display = shown.length > 1 ? \"\" : \"none\";\n}\n\nfunction loadMailboxes() {\n setStatus(\"Loading mailboxes from \" + activeAlias + \"\\\\u2026\");\n return callApp(aliasTool(\"mailbox_list\"), {})\n .then(function (result) {\n renderMailboxes(asList(result, [\"mailboxes\", \"items\"]));\n setStatus(\"Ready \\\\u2014 \" + activeAlias);\n })\n .catch(function (err) {\n setStatus(\"Error loading mailboxes: \" + err.message);\n });\n}\n\nfunction renderConnectHint() {\n var el = document.getElementById(\"connect-hint\");\n var anyLive = false;\n for (var i = 0; i < ALIASES.length; i++) {\n if (aliasStates[ALIASES[i]] && aliasStates[ALIASES[i]].state === \"live\") anyLive = true;\n }\n if (anyLive) {\n el.style.display = \"none\";\n return;\n }\n el.style.display = \"\";\n var list = document.getElementById(\"connect-hint-list\");\n list.innerHTML = \"\";\n ALIASES.forEach(function (alias) {\n var st = aliasStates[alias];\n var row = document.createElement(\"div\");\n row.className = \"plan-item\";\n var text;\n if (!st) text = alias + \" \\\\u2014 not probed yet\";\n else if (st.state === \"unreachable\") text = alias + \" \\\\u2014 unreachable: \" + st.message;\n else if (st.state === \"empty\") text = alias + \" \\\\u2014 connected, no mailboxes\";\n else text = alias + \" \\\\u2014 live\";\n row.textContent = text;\n list.appendChild(row);\n });\n}\n\nfunction probeAliases() {\n setStatus(\"Detecting mail servers\\\\u2026\");\n return Promise.all(\n ALIASES.map(function (alias) {\n return callApp(\"imported:\" + alias + \"/mailbox_list\", {})\n .then(function (result) {\n var mailboxes = asList(result, [\"mailboxes\", \"items\"]);\n aliasStates[alias] = { state: mailboxes.length > 0 ? \"live\" : \"empty\" };\n return { alias: alias, mailboxes: mailboxes };\n })\n .catch(function (err) {\n aliasStates[alias] = { state: \"unreachable\", message: err && err.message ? err.message : String(err) };\n return { alias: alias, mailboxes: [] };\n });\n })\n ).then(function (results) {\n var live = results.filter(function (r) { return r.mailboxes.length > 0; });\n if (live.length > 0) {\n activeAlias = live[0].alias;\n renderAliasSelect(live.map(function (r) { return r.alias; }));\n renderMailboxes(live[0].mailboxes);\n renderConnectHint();\n setStatus(\"Ready \\\\u2014 \" + activeAlias + (live.length > 1 ? \" (\" + live.length + \" servers available)\" : \"\"));\n } else {\n renderAliasSelect([]);\n renderMailboxes([]);\n renderConnectHint();\n setStatus(\"No mailboxes reachable \\\\u2014 see connection panel below.\");\n }\n });\n}\n\nfunction renderSearchResults(items, raw) {\n var el = document.getElementById(\"search-results\");\n el.innerHTML = \"\";\n if (raw && typeof raw === \"object\" && typeof raw.resultSizeEstimate === \"number\") {\n var count = document.createElement(\"div\");\n count.className = \"empty\";\n count.textContent = items.length + \" shown \\\\u00b7 ~\" + raw.resultSizeEstimate + \" total\";\n el.appendChild(count);\n }\n if (items.length === 0) {\n var none = document.createElement(\"div\");\n none.className = \"empty\";\n none.textContent = \"No results.\";\n el.appendChild(none);\n return;\n }\n items.forEach(function (item) {\n var row = document.createElement(\"div\");\n row.className = \"plan-item\";\n row.style.flexDirection = \"column\";\n row.style.alignItems = \"stretch\";\n var msgId = item && (item.id || item.messageId || \"\");\n var threadId = item && (item.threadId || \"\");\n var subj = typeof item === \"string\" ? item : item.subject || item.title || JSON.stringify(item);\n var from = item && item.from ? item.from : \"\";\n var snippet = item && item.snippet ? item.snippet : \"\";\n var top = document.createElement(\"div\");\n top.style.display = \"flex\";\n top.style.alignItems = \"flex-start\";\n top.style.gap = \"6px\";\n var cb = document.createElement(\"input\");\n cb.type = \"checkbox\";\n cb.className = \"mail-checkbox\";\n cb.setAttribute(\"data-action\", \"toggle-select\");\n if (msgId) cb.setAttribute(\"data-message-id\", msgId);\n if (isSelected(msgId)) cb.checked = true;\n top.appendChild(cb);\n var subjSpan = document.createElement(\"span\");\n subjSpan.className = \"subj\";\n subjSpan.style.flex = \"1\";\n subjSpan.textContent = subj;\n top.appendChild(subjSpan);\n var tagSpan = document.createElement(\"span\");\n tagSpan.className = \"tag\";\n tagSpan.textContent = from;\n top.appendChild(tagSpan);\n if (threadId) {\n var openBtn = document.createElement(\"button\");\n openBtn.className = \"mail-open\";\n openBtn.textContent = \"Open\";\n openBtn.setAttribute(\"data-action\", \"open-gmail\");\n openBtn.setAttribute(\"data-thread-id\", threadId);\n top.appendChild(openBtn);\n }\n row.appendChild(top);\n if (snippet) {\n var sn = document.createElement(\"div\");\n sn.className = \"snippet\";\n sn.textContent = snippet;\n row.appendChild(sn);\n }\n el.appendChild(row);\n });\n}\n\n// Renders a mailbox_triage_plan result: { plan_id, action, count, sample,\n// truncated, expires_at }. The sample rows are search-shaped messages.\nfunction renderPlan(plan) {\n var el = document.getElementById(\"plan-list\");\n el.innerHTML = \"\";\n if (!plan || typeof plan !== \"object\" || !plan.plan_id) {\n el.innerHTML = '<div class=\"empty\">Plan failed \\\\u2014 unexpected response.</div>';\n return;\n }\n var head = document.createElement(\"div\");\n head.className = \"plan-item\";\n head.innerHTML = '<span class=\"subj\"></span><span class=\"tag\"></span>';\n head.querySelector(\".subj\").textContent =\n plan.count + \" message\" + (plan.count === 1 ? \"\" : \"s\") + \" \\\\u2192 \" + (plan.action && plan.action.type);\n head.querySelector(\".tag\").textContent = plan.expires_at ? \"expires \" + fmtDate(plan.expires_at) : \"\";\n el.appendChild(head);\n var sample = asList(plan, [\"sample\"]);\n sample.forEach(function (item) {\n var row = document.createElement(\"div\");\n row.className = \"plan-item\";\n row.innerHTML = '<span class=\"subj\"></span><span class=\"tag\"></span>';\n row.querySelector(\".subj\").textContent = (item && (item.subject || item.title)) || JSON.stringify(item);\n row.querySelector(\".tag\").textContent = (item && item.from) || \"\";\n el.appendChild(row);\n });\n if (plan.truncated) {\n var more = document.createElement(\"div\");\n more.className = \"empty\";\n more.textContent = \"\\\\u2026sample truncated \\\\u2014 \" + plan.count + \" total.\";\n el.appendChild(more);\n }\n}\n\nfunction fmtDate(iso) {\n if (!iso) return \"\";\n var d = new Date(iso);\n return isNaN(d.getTime()) ? String(iso) : d.toLocaleString();\n}\n\nfunction renderRuns(runs) {\n var el = document.getElementById(\"runs-list\");\n el.innerHTML = \"\";\n if (runs.length === 0) {\n el.innerHTML = '<div class=\"empty\">No runs yet.</div>';\n return;\n }\n var shown = runs.slice(0, 20);\n shown.forEach(function (run) {\n var row = document.createElement(\"div\");\n row.className = \"runs-item\";\n row.title = \"Show sessions + output for \" + run.appRunId;\n row.innerHTML = '<span class=\"st\"></span><span class=\"rid\"></span><span class=\"meta\"></span>';\n var st = row.querySelector(\".st\");\n st.textContent = run.status || \"?\";\n st.className = \"st \" + (run.status || \"\");\n row.querySelector(\".rid\").textContent = run.appRunId;\n row.querySelector(\".meta\").textContent =\n fmtDate(run.startedAt) + (run.endedAt ? \" \\\\u2192 \" + fmtDate(run.endedAt) : \"\") + \" \\\\u00b7 \" + (run.sessions || 0) + \" session\" + (run.sessions === 1 ? \"\" : \"s\");\n row.addEventListener(\"click\", function () { viewRun(run.appRunId); });\n el.appendChild(row);\n });\n if (runs.length > 20) {\n var more = document.createElement(\"div\");\n more.className = \"empty\";\n more.textContent = \"showing 20 of \" + runs.length;\n el.appendChild(more);\n }\n}\n\nfunction loadRuns() {\n return callApp(\"app_list\", {})\n .then(function (result) {\n var apps = Array.isArray(result) ? result : [];\n var mine = null;\n for (var i = 0; i < apps.length; i++) {\n if (apps[i] && apps[i].appId === APP_ID) { mine = apps[i]; break; }\n }\n var runs = (mine && Array.isArray(mine.runs) ? mine.runs.slice() : []).sort(function (a, b) {\n return String(b.startedAt || \"\").localeCompare(String(a.startedAt || \"\"));\n });\n renderRuns(runs);\n })\n .catch(function (err) {\n setStatus(\"Could not load runs: \" + err.message);\n });\n}\n\nfunction viewRun(appRunId) {\n setStatus(\"Loading run \" + appRunId + \"\\\\u2026\");\n callApp(\"app_status\", { appRunId: appRunId })\n .then(function (status) {\n var sessions = (status && status.sessions) || [];\n appendLog(\"\\\\u2500\\\\u2500 run \" + appRunId + \" (\" + (status && status.status) + \") \\\\u2500\\\\u2500\");\n if (sessions.length === 0) {\n appendLog(\"(no sessions)\");\n setStatus(\"Ready \\\\u2014 \" + activeAlias);\n return;\n }\n sessions.forEach(function (s) {\n appendLog(\"session \" + s.sessionId + \" (\" + (s.agentId || \"?\") + \")\");\n });\n return callApp(\"agent_output\", { sessionId: sessions[0].sessionId, lastN: 200, clean: true })\n .then(function (output) {\n if (output) appendLog(typeof output === \"string\" ? output : JSON.stringify(output, null, 2));\n setStatus(\"Ready \\\\u2014 \" + activeAlias);\n });\n })\n .catch(function (err) {\n setStatus(\"Run status failed: \" + err.message);\n });\n}\n\n// Event delegation: search-results (checkboxes + open buttons)\ndocument.getElementById(\"search-results\").addEventListener(\"change\", function (e) {\n var target = e.target;\n if (target.getAttribute(\"data-action\") === \"toggle-select\") {\n var mid = target.getAttribute(\"data-message-id\");\n if (mid) {\n var item = null;\n var el = target.parentNode;\n while (el && el.parentNode && el !== document) {\n if (el.tagName === \"DIV\" && el.className.indexOf(\"plan-item\") !== -1) {\n var subjEl = el.querySelector(\".subj\");\n var tagEl = el.querySelector(\".tag\");\n var subjText = subjEl ? subjEl.textContent : \"\";\n var tagText = tagEl ? tagEl.textContent : \"\";\n // Reconstruct a minimal item from the DOM\n item = { id: mid, threadId: \"\", subject: subjText, from: tagText };\n break;\n }\n el = el.parentNode;\n }\n toggleSelection(mid, item || { id: mid });\n }\n }\n});\ndocument.getElementById(\"search-results\").addEventListener(\"click\", function (e) {\n var target = e.target;\n if (target.getAttribute(\"data-action\") === \"open-gmail\") {\n var tid = target.getAttribute(\"data-thread-id\");\n if (tid && openLink) {\n openLink(\"https://mail.google.com/mail/u/0/#inbox/\" + tid).catch(function (err) {\n setStatus(\"Failed to open link: \" + (err && err.message ? err.message : String(err)));\n });\n } else if (tid) {\n setStatus(\"Host does not support openLink.\");\n }\n }\n});\n// Event delegation: selection-bar (send-selection)\ndocument.getElementById(\"selection-bar\").addEventListener(\"click\", function (e) {\n if (e.target.getAttribute(\"data-action\") === \"send-selection\") {\n sendSelectionToClaude();\n }\n});\n\ndocument.getElementById(\"refresh-btn\").addEventListener(\"click\", function () {\n probeAliases();\n loadRuns();\n});\n\ndocument.getElementById(\"connect-retry-btn\").addEventListener(\"click\", function () {\n probeAliases();\n loadRuns();\n});\n\ndocument.getElementById(\"alias-select\").addEventListener(\"change\", function () {\n activeAlias = this.value;\n clearPlanExpiry();\n currentPlan = null;\n document.getElementById(\"apply-btn\").disabled = true;\n loadMailboxes();\n});\n\ndocument.getElementById(\"mailbox-select\").addEventListener(\"change\", onMailboxChange);\n\ndocument.getElementById(\"runs-refresh-btn\").addEventListener(\"click\", loadRuns);\n\ndocument.getElementById(\"search-btn\").addEventListener(\"click\", function () {\n var btn = this;\n var query = document.getElementById(\"search-input\").value.trim() || \"is:unread\";\n var mailbox = document.getElementById(\"mailbox-select\").value;\n setBusy(btn, true);\n setStatus(\"Searching\\\\u2026\");\n callApp(aliasTool(\"mailbox_search\"), { mailbox: mailbox, query: query })\n .then(function (result) {\n renderSearchResults(asList(result, [\"items\", \"messages\", \"results\"]), result);\n setBusy(btn, false);\n setStatus(\"Ready \\\\u2014 \" + activeAlias);\n })\n .catch(function (err) {\n setBusy(btn, false);\n setStatus(\"Search failed: \" + err.message);\n });\n});\n\ndocument.getElementById(\"plan-action\").addEventListener(\"change\", function () {\n document.getElementById(\"plan-label\").style.display = this.value === \"label\" ? \"\" : \"none\";\n});\n\ndocument.getElementById(\"triage-btn\").addEventListener(\"click\", function () {\n var btn = this;\n var mailbox = document.getElementById(\"mailbox-select\").value;\n var query = document.getElementById(\"plan-query\").value.trim() || \"is:unread\";\n var actionType = document.getElementById(\"plan-action\").value;\n var action = { type: actionType };\n if (actionType === \"label\") {\n var label = document.getElementById(\"plan-label\").value.trim();\n if (!label) {\n setStatus(\"Enter a label name for the label action.\");\n return;\n }\n action.addLabelIds = [label];\n }\n setBusy(btn, true);\n setStatus(\"Building triage plan\\\\u2026\");\n document.getElementById(\"apply-btn\").disabled = true;\n clearPlanExpiry();\n currentPlan = null;\n callApp(aliasTool(\"mailbox_triage_plan\"), { mailbox: mailbox, criteria: { query: query }, action: action })\n .then(function (plan) {\n setBusy(btn, false);\n currentPlan = plan && plan.plan_id ? plan : null;\n renderPlan(plan);\n if (currentPlan && plan.count > 0) {\n document.getElementById(\"apply-btn\").disabled = false;\n armPlanExpiry(plan.expires_at);\n setStatus(\"Plan ready (\" + plan.count + \" messages) \\\\u2014 review, then Apply.\");\n } else if (currentPlan) {\n setStatus(\"Plan matches no messages \\\\u2014 nothing to apply.\");\n } else {\n setStatus(\"Triage plan failed \\\\u2014 see plan panel.\");\n }\n })\n .catch(function (err) {\n setBusy(btn, false);\n var msg = err && err.message ? err.message : String(err);\n if (msg.indexOf(\"mailbox_scope_missing\") !== -1) {\n setStatus(\"Triage scope missing \\\\u2014 grant it via agentpush (mailbox_request_elevation).\");\n } else {\n setStatus(\"Triage plan failed: \" + msg);\n }\n });\n});\n\ndocument.getElementById(\"apply-btn\").addEventListener(\"click\", function () {\n var btn = this;\n if (!currentPlan || !currentPlan.plan_id) return;\n setBusy(btn, true);\n setStatus(\"Applying triage plan\\\\u2026\");\n callApp(aliasTool(\"mailbox_triage_apply\"), { plan_id: currentPlan.plan_id, confirm: true })\n .then(function (result) {\n setBusy(btn, false);\n var count = currentPlan.count;\n var extra = [];\n if (result && typeof result === \"object\") {\n [\"applied\", \"modified\", \"count\"].forEach(function (key) {\n if (typeof result[key] === \"number\") extra.push(key + \": \" + result[key]);\n });\n }\n setStatus(\n extra.length > 0\n ? \"Plan applied \\\\u2014 \" + extra.join(\", \") + \".\"\n : \"Plan applied (\" + count + \" message\" + (count === 1 ? \"\" : \"s\") + \").\"\n );\n document.getElementById(\"apply-btn\").disabled = true;\n clearPlanExpiry();\n currentPlan = null;\n onMailboxChange();\n })\n .catch(function (err) {\n setBusy(btn, false);\n setStatus(\"Apply failed: \" + err.message);\n });\n});\n\nfunction appendLog(text) {\n var el = document.getElementById(\"log-events\");\n if (el.querySelector(\".empty\")) el.innerHTML = \"\";\n var line = document.createElement(\"div\");\n line.textContent = text;\n el.appendChild(line);\n el.scrollTop = el.scrollHeight;\n}\n\n// Replaces the whole tail on every poll tick (the daemon returns the last\n// N lines, not a delta) — appending it instead would duplicate the log.\nfunction renderLogTail(text) {\n var el = document.getElementById(\"log-tail\");\n el.textContent = text || \"\";\n el.scrollTop = el.scrollHeight;\n}\n\nfunction pollAgentRun(appRunId, startedAt) {\n if (pollHandle) {\n clearTimeout(pollHandle);\n pollHandle = null;\n }\n if (Date.now() - startedAt > POLL_TIMEOUT_MS) {\n setStatus(\"Still running \\\\u2014 check Past runs later.\");\n return;\n }\n callApp(\"app_status\", { appRunId: appRunId })\n .then(function (status) {\n var sessions = (status && status.sessions) || [];\n var sessionId = sessions[0] && sessions[0].sessionId;\n var tail = sessionId\n ? callApp(\"agent_output\", { sessionId: sessionId, lastN: 200, clean: true })\n : Promise.resolve(\"\");\n return tail.then(function (output) {\n renderLogTail(typeof output === \"string\" ? output : output ? JSON.stringify(output, null, 2) : \"\");\n var done = status && (status.status === \"ended\" || status.status === \"done\" || status.endedAt);\n if (!done) {\n pollHandle = setTimeout(function () { pollAgentRun(appRunId, startedAt); }, 2000);\n } else {\n pollHandle = null;\n setStatus(\"Agent run finished.\");\n loadRuns();\n }\n });\n })\n .catch(function (err) {\n setStatus(\"Agent status failed: \" + err.message);\n });\n}\n\ndocument.getElementById(\"run-agent-btn\").addEventListener(\"click\", function () {\n var btn = this;\n if (pollHandle) {\n clearTimeout(pollHandle);\n pollHandle = null;\n }\n document.getElementById(\"log-events\").innerHTML = \"\";\n document.getElementById(\"log-tail\").textContent = \"\";\n setBusy(btn, true);\n setStatus(\"Starting triager agent\\\\u2026\");\n callApp(\"app_run\", { appId: APP_ID, agents: [AGENT_ID], prompt: \"Triage my inbox.\" })\n .then(function (run) {\n setBusy(btn, false);\n var sessions = (run && run.sessions) || [];\n var errors = (run && run.errors) || [];\n if (sessions.length === 0 && errors.length > 0) {\n appendLog(\n \"Run failed to start: \" +\n errors.map(function (e) { return (e.agentId || \"?\") + \": \" + e.error; }).join(\"; \")\n );\n setStatus(\"Run failed to start.\");\n return;\n }\n appendLog(\"Started app run \" + (run && run.appRunId));\n loadRuns();\n pollAgentRun(run.appRunId, Date.now());\n })\n .catch(function (err) {\n setBusy(btn, false);\n setStatus(\"Run failed: \" + err.message);\n });\n});\n\n// window.McpApp is normally injected by the serving layer (postMessage bridge\n// on the MCP resource path, REST bridge on GET /apps/:id/ui). Guard anyway:\n// without it a missing bridge throws here and the panel dies silently on\n// \"Connecting…\" instead of reporting itself.\n(window.McpApp ? window.McpApp.connect() : Promise.reject(new Error(\"window.McpApp bridge missing\")))\n .then(function (bridge) {\n callTool = bridge.callTool;\n updateModelContext = bridge.updateModelContext;\n openLink = bridge.openLink;\n onTeardown = bridge.onTeardown;\n if (onTeardown) {\n onTeardown(function () {\n if (pollHandle) { clearTimeout(pollHandle); pollHandle = null; }\n if (planExpiryHandle) { clearTimeout(planExpiryHandle); planExpiryHandle = null; }\n });\n }\n return Promise.all([probeAliases(), loadRuns()]);\n })\n .catch(function (err) {\n setStatus(\"Standalone mode \\\\u2014 no host bridge (\" + err.message + \")\");\n renderSummary();\n });\n\nrenderSummary();\n</script>\n</body>\n</html>`\n","/**\n * `mail-triage` — a single-agent agentproto app for email triage.\n *\n * One agent (triager) scans inbox, categorizes unread mail, and applies\n * triage actions (label, archive). Proves the APP primitive works end-to-end\n * with a simple, useful real-world task.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { triager } from \"./agents/triager.js\"\nimport { triageInbox } from \"./workflows/triage-inbox.js\"\nimport { MAIL_TRIAGE_HTML, MAIL_TRIAGE_TOOLS } from \"./ui.js\"\n\nexport const mailTriage: AppHandle = defineApp({\n id: \"@agentproto/mail-triage\",\n name: \"Mail Triage\",\n version: \"0.1.0\",\n description: \"Triage your inbox — categorize, label, archive.\",\n agents: [triager],\n workflows: [triageInbox],\n ui: {\n html: MAIL_TRIAGE_HTML,\n title: \"Mail Triage\",\n tools: [...MAIL_TRIAGE_TOOLS],\n },\n})\n\nexport { triager, triageInbox }\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\nexport const cataloger: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/media-cataloger\",\n description:\n \"Scans a folder for media files (images, video, audio), collects metadata, and produces a structured catalog.\",\n model: \"claude-sonnet-5\",\n boundaries: [\n \"Read-only — never modify, rename, move, or delete files\",\n \"Only catalog files with recognized media extensions\",\n \"Report exact file sizes and paths, never estimate\",\n ],\n tools: [\"list_dir\", \"read_file\", \"file_info\"],\n workflows: [{ ref: \"scan-media\" }],\n }),\n body: [\n \"You scan a workspace folder for media files and produce a structured JSON catalog.\",\n \"\",\n \"Walk the target directory recursively using list_dir. For each file, check the\",\n \"extension against known media types:\",\n \"- Images: .jpg, .jpeg, .png, .gif, .webp, .svg, .bmp, .ico, .avif\",\n \"- Video: .mp4, .webm, .mov, .avi, .mkv\",\n \"- Audio: .mp3, .wav, .ogg, .flac, .aac, .m4a\",\n \"\",\n \"For each media file found, use file_info to get size and modification date.\",\n \"\",\n \"Output a JSON object with this shape:\",\n \"```json\",\n '{',\n ' \"folder\": \"<scanned path>\",',\n ' \"scannedAt\": \"<ISO timestamp>\",',\n ' \"summary\": { \"total\": N, \"images\": N, \"video\": N, \"audio\": N, \"totalSizeBytes\": N },',\n ' \"files\": [',\n ' {',\n ' \"path\": \"relative/path.jpg\",',\n ' \"name\": \"file.jpg\",',\n ' \"type\": \"image\",',\n ' \"extension\": \".jpg\",',\n ' \"sizeBytes\": 123456,',\n ' \"modifiedAt\": \"2026-08-01T12:00:00Z\"',\n \" }\",\n \" ]\",\n \"}\",\n \"```\",\n \"\",\n \"Sort files by type (images first, then video, then audio), then by name.\",\n ].join(\"\\n\"),\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\nexport const scanMedia = defineWorkflow({\n id: \"scan-media\",\n name: \"Scan Media\",\n description:\n \"Scan a folder for media files, collect metadata, and produce a structured catalog.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n {\n id: \"catalog\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/media-cataloger\" },\n prompt:\n \"Scan the workspace root folder for media files (images, video, audio). \" +\n \"Walk all subdirectories recursively. For each media file, collect its path, \" +\n \"name, type, extension, size, and modification date. Output the full catalog \" +\n \"as a single JSON object.\",\n },\n ],\n})\n","/**\n * Media Viewer — a self-contained MCP App gallery widget for `media-viewer`.\n *\n * Talks to the daemon only through the `window.McpApp.connect()` bridge\n * (never a direct `fetch`): every action round-trips through the app's own\n * `app_tool_call` gateway tool, `{ appId, tool, args }`. `app_tool_call`\n * double-wraps its dispatch result (it returns a text-result whose `text` is\n * the JSON-stringified inner tool result, itself an MCP content array) — see\n * `unwrapText` below, which peels that apart regardless of how many layers\n * the host's bridge already stripped.\n */\n\nexport const MEDIA_VIEWER_TOOLS = [\"directory_list\", \"file_info\", \"file_read\"] as const\n\nconst APP_ID = \"@agentproto/media-viewer\"\n\nexport const MEDIA_VIEWER_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Media Viewer</title>\n<style>\n*{box-sizing:border-box;margin:0;padding:0}\n:root{\n --bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;\n --text:#e6edf3;--text2:#8b949e;\n --green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;\n}\nhtml,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;font-size:13px;background:var(--bg);color:var(--text)}\n#app{display:flex;flex-direction:column;height:100%}\n#toolbar{padding:10px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;background:var(--bg2);flex-shrink:0}\n#toolbar .title{font-size:13px;font-weight:600;flex:1}\n#breadcrumb{font-size:12px;color:var(--text2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.abtn{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:5px 10px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}\n.abtn:hover{background:var(--border)}\n#body{flex:1;display:flex;overflow:hidden}\n#grid-wrap{flex:1;overflow-y:auto;padding:14px}\n#grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}\n.cell{display:flex;flex-direction:column;align-items:center;gap:6px;padding:10px 6px;border:1px solid var(--border);border-radius:6px;background:var(--bg2);cursor:pointer;text-align:center}\n.cell:hover{background:var(--bg3);border-color:var(--blue)}\n.cell.selected{border-color:var(--blue);background:var(--bg3)}\n.icon{font-size:28px;line-height:1}\n.name{font-size:11px;color:var(--text);word-break:break-word;max-width:100%;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}\n#empty{color:var(--text2);padding:24px;text-align:center}\n#details{width:280px;border-left:1px solid var(--border);background:var(--bg2);padding:14px;overflow-y:auto;flex-shrink:0}\n#details h3{font-size:12px;color:var(--text2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px}\n#details .row{display:flex;justify-content:space-between;gap:8px;padding:4px 0;border-bottom:1px solid var(--border);font-size:12px}\n#details .row span:first-child{color:var(--text2)}\n#preview{margin-top:10px;padding:8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-family:Menlo,Monaco,monospace;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:240px;overflow-y:auto}\n#status{padding:6px 14px;font-size:11px;color:var(--text2);border-top:1px solid var(--border);background:var(--bg2);flex-shrink:0}\n</style>\n</head>\n<body>\n<div id=\"app\">\n <div id=\"toolbar\">\n <button class=\"abtn\" id=\"up-btn\" title=\"Up one level\">&uarr;</button>\n <span class=\"title\">Media Viewer</span>\n <span id=\"breadcrumb\">.</span>\n <button class=\"abtn\" id=\"refresh-btn\">Refresh</button>\n </div>\n <div id=\"body\">\n <div id=\"grid-wrap\">\n <div id=\"grid\"></div>\n <div id=\"empty\" style=\"display:none\">No files in this folder.</div>\n </div>\n <div id=\"details\" style=\"display:none\"></div>\n </div>\n <div id=\"status\">Connecting&#8230;</div>\n</div>\n<script>\nvar APP_ID = ${JSON.stringify(APP_ID)};\nvar currentPath = \".\";\nvar entries = [];\nvar callTool = null;\n\nfunction setStatus(msg) {\n document.getElementById(\"status\").textContent = msg;\n}\n\n// app_tool_call wraps its dispatch result at least once (its own\n// text-result body is the JSON-stringified inner MCP tool result); peel\n// back through nested {content:[{type:\"text\",text:...}]} shells until the\n// payload stops looking like one, so this works whichever layer the host's\n// own bridge already unwrapped.\nfunction unwrapText(result) {\n var cur = result;\n for (var i = 0; i < 4; i++) {\n if (typeof cur === \"string\") {\n try {\n cur = JSON.parse(cur);\n continue;\n } catch (e) {\n return cur;\n }\n }\n if (cur && Array.isArray(cur.content) && cur.content[0] && typeof cur.content[0].text === \"string\") {\n cur = cur.content[0].text;\n continue;\n }\n break;\n }\n return cur;\n}\n\nfunction callApp(tool, args) {\n return callTool(\"app_tool_call\", { appId: APP_ID, tool: tool, args: args || {} }).then(unwrapText);\n}\n\nfunction parseDirectoryListing(raw) {\n var text = typeof raw === \"string\" ? raw : \"\";\n return text\n .split(\"\\\\n\")\n .map(function (line) { return line.trim(); })\n .filter(function (line) { return line.length > 0; })\n .map(function (line) {\n var isDir = line.indexOf(\"[DIR]\") === 0;\n var name = line.replace(/^\\\\[(FILE|DIR)\\\\]\\\\s*/, \"\");\n return { name: name, isDir: isDir };\n });\n}\n\nfunction iconFor(entry) {\n if (entry.isDir) return \"\\\\uD83D\\\\uDCC1\";\n var ext = (entry.name.split(\".\").pop() || \"\").toLowerCase();\n if ([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\", \"bmp\", \"ico\", \"avif\"].indexOf(ext) !== -1) return \"\\\\uD83D\\\\uDDBC\\\\uFE0F\";\n if ([\"mp4\", \"webm\", \"mov\", \"avi\", \"mkv\"].indexOf(ext) !== -1) return \"\\\\uD83C\\\\uDFA5\";\n if ([\"mp3\", \"wav\", \"ogg\", \"flac\", \"aac\", \"m4a\"].indexOf(ext) !== -1) return \"\\\\uD83C\\\\uDFB5\";\n return \"\\\\uD83D\\\\uDCC4\";\n}\n\nfunction joinPath(base, name) {\n if (base === \".\" || base === \"\") return name;\n return base.replace(/\\\\/+$/, \"\") + \"/\" + name;\n}\n\nfunction parentPath(path) {\n if (path === \".\" || path.indexOf(\"/\") === -1) return \".\";\n return path.slice(0, path.lastIndexOf(\"/\")) || \".\";\n}\n\nfunction renderGrid() {\n document.getElementById(\"breadcrumb\").textContent = currentPath;\n var grid = document.getElementById(\"grid\");\n var empty = document.getElementById(\"empty\");\n grid.innerHTML = \"\";\n if (entries.length === 0) {\n empty.style.display = \"block\";\n return;\n }\n empty.style.display = \"none\";\n entries.forEach(function (entry, idx) {\n var cell = document.createElement(\"div\");\n cell.className = \"cell\";\n cell.dataset.idx = String(idx);\n cell.innerHTML = '<div class=\"icon\">' + iconFor(entry) + '</div><div class=\"name\"></div>';\n cell.querySelector(\".name\").textContent = entry.name;\n cell.addEventListener(\"click\", function () { onSelect(entry, cell); });\n grid.appendChild(cell);\n });\n}\n\nfunction onSelect(entry, cell) {\n Array.prototype.forEach.call(document.querySelectorAll(\".cell\"), function (c) {\n c.classList.remove(\"selected\");\n });\n cell.classList.add(\"selected\");\n if (entry.isDir) {\n loadDirectory(joinPath(currentPath, entry.name));\n return;\n }\n showFileDetails(joinPath(currentPath, entry.name));\n}\n\nfunction showFileDetails(path) {\n var details = document.getElementById(\"details\");\n details.style.display = \"block\";\n details.innerHTML = \"<h3>Loading&#8230;</h3>\";\n setStatus(\"Reading \" + path + \"\\\\u2026\");\n callApp(\"file_info\", { path: path })\n .then(function (info) {\n var rows = \"\";\n if (info && typeof info === \"object\") {\n [\"name\", \"type\", \"size\", \"modified\"].forEach(function (key) {\n if (info[key] !== undefined) {\n rows += '<div class=\"row\"><span>' + key + '</span><span></span></div>';\n }\n });\n }\n details.innerHTML = \"<h3>Details</h3>\" + rows + '<div id=\"preview\">Loading preview&#8230;</div>';\n var rowEls = details.querySelectorAll(\".row span:last-child\");\n var keys = [\"name\", \"type\", \"size\", \"modified\"].filter(function (key) {\n return info && info[key] !== undefined;\n });\n keys.forEach(function (key, i) {\n rowEls[i].textContent = String(info[key]);\n });\n return callApp(\"file_read\", { path: path });\n })\n .then(function (content) {\n var pre = document.getElementById(\"preview\");\n if (!pre) return;\n var text = typeof content === \"string\" ? content : JSON.stringify(content);\n pre.textContent = text.length > 2000 ? text.slice(0, 2000) + \"\\\\u2026 (truncated)\" : (text || \"(empty file)\");\n setStatus(\"Ready \\\\u00b7 \" + currentPath);\n })\n .catch(function (err) {\n var pre = document.getElementById(\"preview\");\n if (pre) pre.textContent = \"Preview unavailable (\" + err.message + \")\";\n setStatus(\"Ready \\\\u00b7 \" + currentPath);\n });\n}\n\nfunction loadDirectory(path) {\n document.getElementById(\"details\").style.display = \"none\";\n setStatus(\"Loading \" + path + \"\\\\u2026\");\n return callApp(\"directory_list\", { path: path })\n .then(function (raw) {\n currentPath = path;\n entries = parseDirectoryListing(raw).sort(function (a, b) {\n if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n renderGrid();\n setStatus(\"Ready \\\\u00b7 \" + entries.length + \" entries \\\\u00b7 \" + path);\n })\n .catch(function (err) {\n entries = [];\n renderGrid();\n setStatus(\"Error: \" + err.message);\n });\n}\n\ndocument.getElementById(\"up-btn\").addEventListener(\"click\", function () {\n loadDirectory(parentPath(currentPath));\n});\ndocument.getElementById(\"refresh-btn\").addEventListener(\"click\", function () {\n loadDirectory(currentPath);\n});\n\nwindow.McpApp.connect()\n .then(function (bridge) {\n callTool = bridge.callTool;\n return loadDirectory(\".\");\n })\n .catch(function (err) {\n setStatus(\"Standalone mode \\\\u2014 no host bridge (\" + err.message + \")\");\n entries = [];\n renderGrid();\n });\n</script>\n</body>\n</html>`\n","/**\n * `media-viewer` — a single-agent agentproto app for media cataloging.\n *\n * One agent (cataloger) scans a workspace folder for images, video, and audio\n * files, collects metadata, and produces a structured JSON catalog. Paired\n * with a Cowork McpApp gallery widget for visual browsing.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { cataloger } from \"./agents/cataloger.js\"\nimport { scanMedia } from \"./workflows/scan-media.js\"\nimport { MEDIA_VIEWER_HTML, MEDIA_VIEWER_TOOLS } from \"./ui.js\"\n\nexport const mediaViewer: AppHandle = defineApp({\n id: \"@agentproto/media-viewer\",\n name: \"Media Viewer\",\n version: \"0.1.0\",\n description: \"Scan a folder for images, video, and audio — gallery view.\",\n agents: [cataloger],\n workflows: [scanMedia],\n ui: {\n html: MEDIA_VIEWER_HTML,\n title: \"Media Viewer\",\n tools: [...MEDIA_VIEWER_TOOLS],\n },\n})\n\nexport { cataloger, scanMedia }\n","import { defineAgent } from \"@agentproto/agent\"\nimport type { AgentEntry } from \"@agentproto/app-kit\"\n\nexport const narrator: AgentEntry = {\n agent: defineAgent({\n schema: \"agent/v1\",\n id: \"@agentproto/session-narrator\",\n description:\n \"Reads a daemon session's transcript and narrates it in plain English — what happened, what was decided, what's left.\",\n model: \"claude-sonnet-5\",\n boundaries: [\n \"Read-only — never spawn, kill, resume, or write to any session\",\n \"Narrate only what the transcript actually shows, never speculate about intent\",\n ],\n tools: [\"conversation_read\", \"session_list\"],\n workflows: [{ ref: \"narrate-session\" }],\n }),\n body: [\n \"You narrate a daemon session's conversation for a human who doesn't want to read the raw transcript.\",\n \"\",\n \"Given a session id or name, call `conversation_read` with `format: \\\"json\\\"` to get the\",\n \"structured turn list (`meta` + `messages`, each `{ role, text, toolCalls?, ts }`). If the\",\n \"session id is ambiguous or unknown, call `session_list` first to resolve it by label.\",\n \"\",\n \"Write a short plain-English narration:\",\n \"- What the user asked for.\",\n \"- What the assistant did, grouped into a few natural beats (not a line-by-line tool log).\",\n \"- Any open question, decision point, or unfinished step at the end of the transcript.\",\n \"\",\n \"Skip tool-call plumbing details unless they're the point (e.g. a failing command, a real\",\n \"decision). If `conversation.conversation` comes back null, say plainly that this session has\",\n \"no readable conversation (e.g. a bare shell PTY) instead of guessing.\",\n ].join(\"\\n\"),\n}\n","import { defineWorkflow } from \"@agentproto/workflow\"\n\nexport const narrateSession = defineWorkflow({\n id: \"narrate-session\",\n name: \"Narrate Session\",\n description: \"Read a daemon session's transcript and narrate it in plain English.\",\n version: \"0.1.0\",\n inputs: {},\n outputs: {},\n steps: [\n {\n id: \"narrate\",\n kind: \"agent\",\n agent: { ref: \"@agentproto/session-narrator\" },\n prompt:\n \"Narrate the session named in the run input (or the most recently started session if \" +\n \"none was given): what the user asked for, what the assistant did, and what — if \" +\n \"anything — is still open at the end of the transcript.\",\n },\n ],\n})\n","/**\n * Session Viewer — a self-contained MCP App panel for `session-viewer`.\n *\n * Talks to the daemon only through the `window.McpApp.connect()` bridge\n * (never a direct `fetch`): every action round-trips through the app's own\n * `app_tool_call` gateway tool, `{ appId, tool, args }` — same convention as\n * `media-viewer`/`mail-triage`. Read-only by design: `session_list` picks a\n * session (live or finished), `conversation_read` renders it as a clean\n * conversation — no spawn/kill/resume action lives here.\n *\n * Live sessions are polled (`conversation_read` has no push channel over\n * this bridge — see the PR's feasibility note on a real event/PTY stream);\n * a finished session is fetched once and left alone.\n */\n\nexport const SESSION_VIEWER_TOOLS = [\"session_list\", \"conversation_read\"] as const\n\nconst APP_ID = \"@agentproto/session-viewer\"\n\nexport const SESSION_VIEWER_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Session Viewer</title>\n<style>\n*{box-sizing:border-box;margin:0;padding:0}\n:root{\n --bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;\n --text:#e6edf3;--text2:#8b949e;--text3:#6e7681;\n --green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;\n}\nhtml,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;font-size:13px;background:var(--bg);color:var(--text)}\n#app{display:flex;height:100%;overflow:hidden}\n\n/* sidebar */\n#sidebar{width:250px;min-width:190px;background:var(--bg2);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}\n#sidebar-hdr{padding:10px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px}\n#sidebar-hdr h1{font-size:11px;font-weight:700;color:var(--text2);text-transform:uppercase;letter-spacing:.06em;flex:1}\n#archived-toggle{display:flex;align-items:center;gap:5px;font-size:10.5px;color:var(--text2);padding:6px 12px;border-bottom:1px solid var(--border);cursor:pointer;user-select:none}\n#archived-toggle input{cursor:pointer}\n#refresh-btn{background:none;border:none;cursor:pointer;color:var(--text2);font-size:14px;line-height:1;padding:2px 4px;border-radius:4px;font-family:inherit}\n#refresh-btn:hover{color:var(--text);background:var(--bg3)}\n#session-list{flex:1;overflow-y:auto;padding:4px 0}\n.si{padding:8px 12px;cursor:pointer;border-left:2px solid transparent}\n.si:hover{background:var(--bg3)}\n.si.active{background:var(--bg3);border-left-color:var(--blue)}\n.si .sn{font-size:12px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.si .sm{font-size:10.5px;color:var(--text2);margin-top:3px;display:flex;gap:5px;align-items:center;flex-wrap:wrap}\n.badge{display:inline-block;padding:1px 6px;border-radius:10px;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.02em}\n.b-run{background:rgba(63,185,80,.15);color:var(--green)}\n.b-wait{background:rgba(88,166,255,.15);color:var(--blue)}\n.b-idle{background:rgba(139,148,158,.12);color:var(--text2)}\n.b-end{background:rgba(139,148,158,.1);color:var(--text3)}\n.b-err{background:rgba(248,81,73,.15);color:var(--red)}\n#empty-sidebar{padding:14px 12px;color:var(--text2);font-size:11.5px}\n\n/* main */\n#main{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}\n#header{padding:12px 18px;border-bottom:1px solid var(--border);background:var(--bg2);flex-shrink:0}\n#header .htitle{font-size:14px;font-weight:700;display:flex;align-items:center;gap:8px}\n.pulse{width:7px;height:7px;border-radius:50%;background:var(--green);flex:none;box-shadow:0 0 0 0 rgba(63,185,80,.4);animation:pulse 2s infinite}\n@keyframes pulse{70%{box-shadow:0 0 0 6px rgba(63,185,80,0)}100%{box-shadow:0 0 0 0 rgba(63,185,80,0)}}\n#header .hfacts{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}\n.fact{font-size:10.5px;font-weight:600;background:var(--bg3);border:1px solid var(--border);color:var(--text2);padding:3px 9px;border-radius:999px}\n#empty-main,#loading-main,#error-main{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text2);gap:8px;text-align:center;padding:24px}\n#empty-main .ico,#loading-main .ico{font-size:30px}\n#error-main{color:var(--red)}\n\n/* transcript */\n#transcript{flex:1;overflow-y:auto;padding:14px 18px;display:flex;flex-direction:column;gap:10px}\n.msg{display:flex;flex-direction:column;max-width:78%}\n.msg.role-user{align-self:flex-end;align-items:flex-end}\n.msg.role-assistant,.msg.role-tool{align-self:flex-start;align-items:flex-start}\n.msg.role-system{align-self:center;max-width:90%}\n.bubble{border-radius:10px;padding:9px 12px;font-size:13px;line-height:1.55;white-space:pre-wrap;word-break:break-word}\n.role-user .bubble{background:var(--blue);color:#04101c;border-bottom-right-radius:3px}\n.role-assistant .bubble{background:var(--bg2);border:1px solid var(--border);border-bottom-left-radius:3px}\n.role-system .bubble{background:transparent;border:1px dashed var(--border);color:var(--text2);font-size:11.5px;text-align:center}\n.mrow{display:flex;align-items:center;gap:6px;margin-bottom:3px}\n.mrole{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--text3)}\n.mtime{font-size:10px;color:var(--text3);font-variant-numeric:tabular-nums}\n\n.chip{margin-top:6px;border:1px solid var(--border);border-radius:8px;background:var(--bg3);overflow:hidden;cursor:pointer;width:100%}\n.chip .ch{padding:6px 10px;font-size:11.5px;display:flex;align-items:center;gap:7px;color:var(--text2)}\n.chip .ch .ci{color:var(--yellow);flex:none}\n.chip.tool-result .ch .ci{color:var(--purple)}\n.chip .ch .cname{font-weight:600;color:var(--text)}\n.chip .ch .cprev{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text3)}\n.chip .ch .cchev{flex:none;transition:transform .12s;color:var(--text3);font-size:10px}\n.chip.open .ch .cchev{transform:rotate(90deg)}\n.chip pre{display:none;margin:0;border-top:1px solid var(--border);padding:9px 11px;font-family:Menlo,Monaco,'Courier New',monospace;font-size:11px;line-height:1.5;color:var(--text2);white-space:pre-wrap;word-break:break-word;max-height:320px;overflow:auto}\n.chip.open pre{display:block}\n.reasoning{margin-bottom:6px;font-size:11.5px;font-style:italic;color:var(--text2);border-left:2px solid var(--border);padding-left:8px}\n\n#statusbar{padding:5px 18px;font-size:10.5px;color:var(--text2);border-top:1px solid var(--border);background:var(--bg2);flex-shrink:0;display:flex;justify-content:space-between}\n</style>\n</head>\n<body>\n<div id=\"app\">\n <div id=\"sidebar\">\n <div id=\"sidebar-hdr\">\n <h1>Sessions</h1>\n <button id=\"refresh-btn\" title=\"Refresh\" type=\"button\">&#8635;</button>\n </div>\n <label id=\"archived-toggle\"><input type=\"checkbox\" id=\"archived-check\"> show archived</label>\n <div id=\"session-list\"></div>\n </div>\n <div id=\"main\">\n <div id=\"header\" style=\"display:none\"></div>\n <div id=\"empty-main\"><div class=\"ico\">&#128172;</div><div>Pick a session to read its conversation</div></div>\n <div id=\"transcript\" style=\"display:none\"></div>\n <div id=\"statusbar\" style=\"display:none\">\n <span id=\"status-left\"></span>\n <span id=\"status-right\"></span>\n </div>\n </div>\n</div>\n<script>\nvar callTool = null;\nvar APP_ID = ${JSON.stringify(APP_ID)};\n\n// app_tool_call wraps its dispatch result at least once (its own text-result\n// body is the JSON-stringified inner MCP tool result); peel back through\n// nested {content:[{type:\"text\",text:...}]} shells until the payload stops\n// looking like one — works whichever layer the host's bridge already\n// unwrapped. With conversation_read's format:\"json\" this resolves straight\n// to the { meta, messages } object; with session_list, straight to\n// { sessions: [...] }.\nfunction unwrapText(result) {\n var cur = result;\n for (var i = 0; i < 4; i++) {\n if (typeof cur === \"string\") {\n try { cur = JSON.parse(cur); continue; } catch (e) { return cur; }\n }\n if (cur && Array.isArray(cur.content) && cur.content[0] && typeof cur.content[0].text === \"string\") {\n cur = cur.content[0].text;\n continue;\n }\n break;\n }\n return cur;\n}\n\nfunction callApp(tool, args) {\n return callTool(\"app_tool_call\", { appId: APP_ID, tool: tool, args: args || {} }).then(unwrapText);\n}\n\nfunction escHtml(s) {\n return String(s == null ? \"\" : s).replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\");\n}\n\nfunction fmtTime(ts) {\n if (!ts) return \"\";\n try { return new Date(ts).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\", second: \"2-digit\" }); }\n catch (e) { return \"\"; }\n}\n\nfunction fmtClock(iso) {\n if (!iso) return \"\";\n try { return new Date(iso).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" }); }\n catch (e) { return \"\"; }\n}\n\nfunction fmtRelative(iso) {\n if (!iso) return \"\";\n var ms = Date.now() - new Date(iso).getTime();\n if (!isFinite(ms)) return \"\";\n var s = Math.round(ms / 1000);\n if (s < 5) return \"just now\";\n if (s < 60) return s + \"s ago\";\n var m = Math.round(s / 60);\n if (m < 60) return m + \"m ago\";\n var h = Math.round(m / 60);\n if (h < 24) return h + \"h ago\";\n return Math.round(h / 24) + \"d ago\";\n}\n\nfunction fmtDuration(startIso, endIso) {\n var end = endIso ? new Date(endIso).getTime() : Date.now();\n var start = new Date(startIso).getTime();\n if (!isFinite(start) || !isFinite(end)) return \"\";\n var s = Math.max(0, Math.round((end - start) / 1000));\n if (s < 60) return s + \"s\";\n var m = Math.floor(s / 60);\n if (m < 60) return m + \"m \" + (s % 60) + \"s\";\n var h = Math.floor(m / 60);\n return h + \"h \" + (m % 60) + \"m\";\n}\n\nfunction prettyArgs(raw) {\n if (raw == null) return \"\";\n if (typeof raw !== \"string\") return JSON.stringify(raw, null, 2);\n try { return JSON.stringify(JSON.parse(raw), null, 2); } catch (e) { return raw; }\n}\n\nfunction firstLine(s, max) {\n var t = String(s == null ? \"\" : s).split(\"\\\\n\")[0];\n return t.length > max ? t.slice(0, max) + \"\\\\u2026\" : t;\n}\n\n// ============================================================\n// state\n// ============================================================\n\nvar sessions = [];\nvar activeId = null;\nvar includeArchived = false;\nvar currentMeta = null;\nvar lastMessageCount = -1;\nvar sidebarTimer = null;\nvar convoTimer = null;\n\nfunction findSession(id) {\n for (var i = 0; i < sessions.length; i++) if (sessions[i].id === id) return sessions[i];\n return null;\n}\n\nfunction isLive(s) {\n return !!s && (s.status === \"running\" || s.status === \"starting\");\n}\n\n// ============================================================\n// sidebar\n// ============================================================\n\nfunction displayBadge(s) {\n if (s.status === \"running\") {\n if (s.busy) return { label: \"working\", cls: \"b-run\" };\n if (s.awaitingInput) return { label: \"waiting\", cls: \"b-wait\" };\n return { label: \"idle\", cls: \"b-idle\" };\n }\n if (s.status === \"starting\") return { label: \"starting\", cls: \"b-wait\" };\n if (s.status === \"error\" || s.status === \"killed\") return { label: s.status, cls: \"b-err\" };\n return { label: s.status || \"exited\", cls: \"b-end\" };\n}\n\nfunction renderSidebar() {\n var el = document.getElementById(\"session-list\");\n if (sessions.length === 0) {\n el.innerHTML = '<div id=\"empty-sidebar\">No sessions yet.</div>';\n return;\n }\n var html = \"\";\n for (var i = 0; i < sessions.length; i++) {\n var s = sessions[i];\n var label = s.label || s.title || s.name || (s.command ? String(s.command).split(\"/\").pop() : null) || s.id.slice(0, 10);\n var db = displayBadge(s);\n var active = s.id === activeId ? \" active\" : \"\";\n html += '<div class=\"si' + active + '\" data-id=\"' + escHtml(s.id) + '\">'\n + '<div class=\"sn\">' + escHtml(label) + \"</div>\"\n + '<div class=\"sm\">'\n + '<span class=\"badge ' + db.cls + '\">' + escHtml(db.label) + \"</span>\"\n + \"<span>\" + escHtml(s.kind || \"\") + \"</span>\"\n + \"<span>\" + escHtml(fmtRelative(s.startedAt)) + \"</span>\"\n + \"</div></div>\";\n }\n el.innerHTML = html;\n var rows = el.querySelectorAll(\".si\");\n for (var j = 0; j < rows.length; j++) {\n rows[j].addEventListener(\"click\", (function (id) { return function () { selectSession(id); }; })(rows[j].getAttribute(\"data-id\")));\n }\n}\n\nfunction loadSessions() {\n return callApp(\"session_list\", { kind: \"all\", includeArchived: includeArchived }).then(function (data) {\n sessions = (data && data.sessions) || [];\n renderSidebar();\n if (activeId) {\n var hdr = document.getElementById(\"header\");\n if (hdr.style.display !== \"none\") renderHeader();\n }\n }).catch(function (e) {\n document.getElementById(\"status-left\").textContent = \"sessions: \" + e.message;\n });\n}\n\n// ============================================================\n// header\n// ============================================================\n\nfunction renderHeader() {\n var s = findSession(activeId);\n var meta = currentMeta || {};\n var el = document.getElementById(\"header\");\n el.style.display = \"block\";\n\n var title = meta.title || (s && (s.label || s.title)) || activeId;\n var live = s && isLive(s);\n var pulse = live ? '<span class=\"pulse\"></span>' : \"\";\n\n var facts = [];\n if (meta.model || (s && s.model)) facts.push(escHtml(meta.model || (s && s.model)));\n if (s && s.adapterSlug) facts.push(escHtml(s.adapterSlug));\n if (s && s.kind) facts.push(escHtml(s.kind));\n if (s) { var db = displayBadge(s); facts.push(escHtml(db.label)); }\n if (s && s.cwd) facts.push('<span title=\"' + escHtml(s.cwd) + '\">' + escHtml(s.cwd.length > 46 ? \"\\\\u2026\" + s.cwd.slice(-44) : s.cwd) + \"</span>\");\n var started = (meta.startedAt || (s && s.startedAt));\n if (started) facts.push(\"started \" + escHtml(fmtClock(started)) + \" (\" + escHtml(fmtRelative(started)) + \")\");\n var ended = meta.endedAt || (s && s.endedAt);\n if (ended) facts.push(\"ran \" + escHtml(fmtDuration(started, ended)));\n else if (started) facts.push(\"running \" + escHtml(fmtDuration(started)));\n if (meta.messageCount != null) facts.push(meta.messageCount + \" messages\");\n if (meta.toolCallCount != null) facts.push(meta.toolCallCount + \" tool calls\");\n if (meta.tokens && (meta.tokens.input != null || meta.tokens.output != null)) {\n facts.push(\"tokens \" + (meta.tokens.input || 0) + \"\\\\u2192\" + (meta.tokens.output || 0));\n }\n if (meta.costUsd != null) facts.push(\"$\" + Number(meta.costUsd).toFixed(4));\n\n el.innerHTML = '<div class=\"htitle\">' + pulse + escHtml(title) + \"</div>\"\n + '<div class=\"hfacts\">' + facts.map(function (f) { return '<span class=\"fact\">' + f + \"</span>\"; }).join(\"\") + \"</div>\";\n}\n\n// ============================================================\n// transcript\n// ============================================================\n\nfunction renderToolCallChip(tc) {\n var args = prettyArgs(tc.args);\n return '<div class=\"chip tool-call\"><div class=\"ch\"><span class=\"ci\">&#9656;</span>'\n + '<span class=\"cname\">' + escHtml(tc.name || \"tool\") + \"</span>\"\n + '<span class=\"cprev\">' + escHtml(firstLine(args, 64)) + \"</span>\"\n + '<span class=\"cchev\">&#9656;</span></div><pre>' + escHtml(args) + \"</pre></div>\";\n}\n\nfunction renderToolResultChip(m) {\n var text = m.text || \"\";\n return '<div class=\"chip tool-result\"><div class=\"ch\"><span class=\"ci\">&#128295;</span>'\n + '<span class=\"cname\">' + escHtml(m.toolName || \"result\") + \"</span>\"\n + '<span class=\"cprev\">' + escHtml(firstLine(text, 64)) + \"</span>\"\n + '<span class=\"cchev\">&#9656;</span></div><pre>' + escHtml(text) + \"</pre></div>\";\n}\n\nfunction renderMessage(m) {\n var time = m.ts ? '<span class=\"mtime\">' + fmtTime(m.ts) + \"</span>\" : \"\";\n if (m.role === \"system\") {\n return '<div class=\"msg role-system\"><div class=\"bubble\">' + escHtml(m.text || \"\") + \"</div></div>\";\n }\n if (m.role === \"tool\") {\n return '<div class=\"msg role-tool\">' + renderToolResultChip(m) + \"</div>\";\n }\n if (m.role === \"user\") {\n return '<div class=\"msg role-user\"><div class=\"mrow\"><span class=\"mrole\">You</span>' + time + \"</div>\"\n + '<div class=\"bubble\">' + escHtml(m.text || \"\") + \"</div></div>\";\n }\n // assistant\n var inner = \"\";\n if (m.reasoning) inner += '<div class=\"reasoning\">' + escHtml(m.reasoning) + \"</div>\";\n if (m.text) inner += escHtml(m.text);\n var body = inner ? '<div class=\"bubble\">' + inner + \"</div>\" : \"\";\n var chips = (m.toolCalls || []).map(renderToolCallChip).join(\"\");\n return '<div class=\"msg role-assistant\"><div class=\"mrow\"><span class=\"mrole\">Assistant</span>' + time + \"</div>\"\n + body + chips + \"</div>\";\n}\n\nfunction renderTranscript(messages) {\n var el = document.getElementById(\"transcript\");\n var atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 80;\n el.style.display = \"flex\";\n document.getElementById(\"empty-main\").style.display = \"none\";\n var errEl = document.getElementById(\"error-main\");\n if (errEl) errEl.style.display = \"none\";\n if (messages.length === 0) {\n el.innerHTML = '<div style=\"color:var(--text2);text-align:center;padding:20px\">(empty conversation)</div>';\n return;\n }\n el.innerHTML = messages.map(renderMessage).join(\"\");\n if (atBottom) el.scrollTop = el.scrollHeight;\n\n var chips = el.querySelectorAll(\".chip\");\n for (var i = 0; i < chips.length; i++) {\n chips[i].querySelector(\".ch\").addEventListener(\"click\", (function (chip) {\n return function () { chip.classList.toggle(\"open\"); };\n })(chips[i]));\n }\n}\n\nfunction renderNoConversation(reason) {\n document.getElementById(\"transcript\").style.display = \"none\";\n document.getElementById(\"empty-main\").style.display = \"none\";\n var el = document.getElementById(\"error-main\");\n el.style.display = \"flex\";\n el.innerHTML = '<div class=\"ico\">&#128683;</div><div>No readable conversation for this session'\n + (reason ? '<br><span style=\"font-size:11px;color:var(--text3)\">' + escHtml(reason) + \"</span>\" : \"\")\n + \"</div>\";\n}\n\n// ============================================================\n// selection + polling\n// ============================================================\n\nfunction clearConvoPoll() {\n if (convoTimer) { clearTimeout(convoTimer); convoTimer = null; }\n}\n\nfunction scheduleConvoPoll() {\n clearConvoPoll();\n var s = findSession(activeId);\n if (!isLive(s)) return;\n convoTimer = setTimeout(function () {\n loadConversation(activeId, { silent: true });\n }, 3500);\n}\n\nfunction loadConversation(id, opts) {\n opts = opts || {};\n var requestedId = id;\n if (!opts.silent) {\n document.getElementById(\"transcript\").style.display = \"none\";\n document.getElementById(\"empty-main\").style.display = \"none\";\n var errEl = document.getElementById(\"error-main\");\n errEl.style.display = \"flex\";\n errEl.innerHTML = '<div class=\"ico\">&#8987;</div><div>Loading\\\\u2026</div>';\n }\n document.getElementById(\"status-left\").textContent = \"loading\\\\u2026\";\n return callApp(\"conversation_read\", { idOrName: id, format: \"json\" }).then(function (data) {\n if (activeId !== requestedId) return; // switched away meanwhile\n if (!data || !Array.isArray(data.messages)) {\n currentMeta = null;\n renderHeader();\n renderNoConversation(data && data.reason);\n clearConvoPoll();\n document.getElementById(\"status-left\").textContent = \"no conversation\";\n return;\n }\n currentMeta = data.meta || {};\n lastMessageCount = data.messages.length;\n renderHeader();\n renderTranscript(data.messages);\n document.getElementById(\"status-left\").textContent = data.messages.length + \" messages\";\n document.getElementById(\"status-right\").textContent = \"updated \" + fmtTime(Date.now());\n scheduleConvoPoll();\n }).catch(function (e) {\n if (activeId !== requestedId) return;\n document.getElementById(\"status-left\").textContent = \"error: \" + e.message;\n if (!opts.silent) renderNoConversation(e.message);\n });\n}\n\nfunction selectSession(id) {\n if (activeId === id) return;\n activeId = id;\n currentMeta = null;\n lastMessageCount = -1;\n clearConvoPoll();\n renderSidebar();\n document.getElementById(\"statusbar\").style.display = \"flex\";\n renderHeader();\n loadConversation(id, {});\n}\n\n// ============================================================\n// boot\n// ============================================================\n\ndocument.getElementById(\"refresh-btn\").addEventListener(\"click\", function () {\n loadSessions();\n if (activeId) loadConversation(activeId, {});\n});\ndocument.getElementById(\"archived-check\").addEventListener(\"change\", function (e) {\n includeArchived = !!e.target.checked;\n loadSessions();\n});\n\nwindow.McpApp.connect()\n .then(function (bridge) {\n callTool = bridge.callTool;\n return loadSessions();\n })\n .then(function () {\n sidebarTimer = setInterval(loadSessions, 5000);\n if (!activeId && sessions.length > 0) selectSession(sessions[0].id);\n })\n .catch(function (err) {\n document.getElementById(\"empty-main\").innerHTML =\n '<div class=\"ico\">&#9888;</div><div>Standalone mode \\\\u2014 no host bridge (' + escHtml(err.message) + \")</div>\";\n });\n</script>\n</body>\n</html>`\n","/**\n * `session-viewer` — a single-agent agentproto app for reading daemon\n * sessions as a clean conversation.\n *\n * The panel (`ui.ts`) picks a session — live or finished — from\n * `session_list` and renders its transcript via `conversation_read`: user/\n * assistant turns, collapsible tool calls, timestamps, a metadata header.\n * Read-only, no spawn/kill/resume action. The bundled `session-narrator`\n * agent is a complementary plain-English narrator over the same\n * `conversation_read` data — usable standalone via `app_run`, independent\n * of the panel.\n */\n\nimport { defineApp, type AppHandle } from \"@agentproto/app-kit\"\nimport { narrator } from \"./agents/narrator.js\"\nimport { narrateSession } from \"./workflows/narrate-session.js\"\nimport { SESSION_VIEWER_HTML, SESSION_VIEWER_TOOLS } from \"./ui.js\"\n\nexport const sessionViewer: AppHandle = defineApp({\n id: \"@agentproto/session-viewer\",\n name: \"Session Viewer\",\n version: \"0.1.0\",\n description: \"Read a daemon session as a clean conversation — turns, tool calls, timestamps.\",\n agents: [narrator],\n workflows: [narrateSession],\n ui: {\n html: SESSION_VIEWER_HTML,\n title: \"Session Viewer\",\n tools: [...SESSION_VIEWER_TOOLS],\n },\n})\n\nexport { narrator, narrateSession }\n","#!/usr/bin/env node\n/**\n * `agentproto-apps-sync` — emit every bundled `@agentproto/apps` app to disk\n * and write a catalog of what landed where.\n *\n * Each app's `AGENT.md` / `WORKFLOW.md` / `APP.md` manifests are written\n * under `<baseDir>/<slug>` via `AppHandle.emit` (see `@agentproto/app-kit`'s\n * `emitApp`); the catalog is a flat index a host can read without walking\n * every app's directory (`app_install`'s own discovery path already reads\n * each `APP.md` individually — this is a cheaper \"what's on disk\" summary\n * for the sync bin's own log, not a replacement for that).\n */\n\nimport { mkdir, writeFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { join, dirname } from \"node:path\"\nimport { parseArgs } from \"node:util\"\nimport type { AppHandle } from \"@agentproto/app-kit\"\nimport { codeTeam } from \"../code-team/index.js\"\nimport { contentTeam } from \"../content-team/index.js\"\nimport { mailTriage } from \"../mail-triage/index.js\"\nimport { mediaViewer } from \"../media-viewer/index.js\"\nimport { sessionViewer } from \"../session-viewer/index.js\"\n\ninterface CatalogEntry {\n readonly appId: string\n readonly name: string\n readonly description: string\n readonly dir: string\n readonly category: string\n}\n\nconst APPS: readonly { readonly slug: string; readonly category: string; readonly handle: AppHandle }[] = [\n { slug: \"code-team\", category: \"team\", handle: codeTeam },\n { slug: \"content-team\", category: \"team\", handle: contentTeam },\n { slug: \"mail-triage\", category: \"app\", handle: mailTriage },\n { slug: \"media-viewer\", category: \"app\", handle: mediaViewer },\n { slug: \"session-viewer\", category: \"app\", handle: sessionViewer },\n]\n\nexport async function syncApps(baseDir: string): Promise<CatalogEntry[]> {\n const catalog: CatalogEntry[] = []\n for (const { slug, category, handle } of APPS) {\n const dir = join(baseDir, slug)\n await handle.emit(dir)\n catalog.push({\n appId: handle.id ?? slug,\n name: handle.name ?? slug,\n description: handle.description ?? \"\",\n dir,\n category,\n })\n console.log(`emitted ${handle.id ?? slug} -> ${dir}`)\n }\n return catalog\n}\n\nexport async function writeCatalog(baseDir: string, catalog: readonly CatalogEntry[]): Promise<string> {\n const catalogPath = join(baseDir, \"..\", \"app-catalog.json\")\n await mkdir(dirname(catalogPath), { recursive: true })\n await writeFile(catalogPath, JSON.stringify({ apps: catalog }, null, 2) + \"\\n\", \"utf8\")\n return catalogPath\n}\n\nasync function main(argv: readonly string[]): Promise<void> {\n const { values } = parseArgs({\n args: [...argv],\n allowPositionals: false,\n strict: true,\n options: { \"base-dir\": { type: \"string\" } },\n })\n const baseDir = values[\"base-dir\"] ?? join(homedir(), \".agentproto\", \"apps\")\n\n await mkdir(baseDir, { recursive: true })\n const catalog = await syncApps(baseDir)\n const catalogPath = await writeCatalog(baseDir, catalog)\n console.log(`wrote catalog (${catalog.length} apps) -> ${catalogPath}`)\n}\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n main(process.argv.slice(2)).catch(err => {\n console.error(err instanceof Error ? err.message : String(err))\n process.exitCode = 1\n })\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -20,4 +20,5 @@ export { codeTeam } from "./code-team/index.js";
20
20
  export { contentTeam } from "./content-team/index.js";
21
21
  export { mailTriage } from "./mail-triage/index.js";
22
22
  export { mediaViewer } from "./media-viewer/index.js";
23
+ export { sessionViewer } from "./session-viewer/index.js";
23
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA"}