@agentproto/apps 0.6.0 → 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.
package/dist/index.mjs CHANGED
@@ -1561,7 +1561,533 @@ var mediaViewer = defineApp({
1561
1561
  tools: [...MEDIA_VIEWER_TOOLS]
1562
1562
  }
1563
1563
  });
1564
+ var narrator = {
1565
+ agent: defineAgent({
1566
+ schema: "agent/v1",
1567
+ id: "@agentproto/session-narrator",
1568
+ description: "Reads a daemon session's transcript and narrates it in plain English \u2014 what happened, what was decided, what's left.",
1569
+ model: "claude-sonnet-5",
1570
+ boundaries: [
1571
+ "Read-only \u2014 never spawn, kill, resume, or write to any session",
1572
+ "Narrate only what the transcript actually shows, never speculate about intent"
1573
+ ],
1574
+ tools: ["conversation_read", "session_list"],
1575
+ workflows: [{ ref: "narrate-session" }]
1576
+ }),
1577
+ body: [
1578
+ "You narrate a daemon session's conversation for a human who doesn't want to read the raw transcript.",
1579
+ "",
1580
+ 'Given a session id or name, call `conversation_read` with `format: "json"` to get the',
1581
+ "structured turn list (`meta` + `messages`, each `{ role, text, toolCalls?, ts }`). If the",
1582
+ "session id is ambiguous or unknown, call `session_list` first to resolve it by label.",
1583
+ "",
1584
+ "Write a short plain-English narration:",
1585
+ "- What the user asked for.",
1586
+ "- What the assistant did, grouped into a few natural beats (not a line-by-line tool log).",
1587
+ "- Any open question, decision point, or unfinished step at the end of the transcript.",
1588
+ "",
1589
+ "Skip tool-call plumbing details unless they're the point (e.g. a failing command, a real",
1590
+ "decision). If `conversation.conversation` comes back null, say plainly that this session has",
1591
+ "no readable conversation (e.g. a bare shell PTY) instead of guessing."
1592
+ ].join("\n")
1593
+ };
1594
+ var narrateSession = defineWorkflow({
1595
+ id: "narrate-session",
1596
+ name: "Narrate Session",
1597
+ description: "Read a daemon session's transcript and narrate it in plain English.",
1598
+ version: "0.1.0",
1599
+ inputs: {},
1600
+ outputs: {},
1601
+ steps: [
1602
+ {
1603
+ id: "narrate",
1604
+ kind: "agent",
1605
+ agent: { ref: "@agentproto/session-narrator" },
1606
+ prompt: "Narrate the session named in the run input (or the most recently started session if none was given): what the user asked for, what the assistant did, and what \u2014 if anything \u2014 is still open at the end of the transcript."
1607
+ }
1608
+ ]
1609
+ });
1610
+
1611
+ // src/session-viewer/ui.ts
1612
+ var SESSION_VIEWER_TOOLS = ["session_list", "conversation_read"];
1613
+ var APP_ID3 = "@agentproto/session-viewer";
1614
+ var SESSION_VIEWER_HTML = `<!DOCTYPE html>
1615
+ <html lang="en">
1616
+ <head>
1617
+ <meta charset="UTF-8">
1618
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1619
+ <title>Session Viewer</title>
1620
+ <style>
1621
+ *{box-sizing:border-box;margin:0;padding:0}
1622
+ :root{
1623
+ --bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;
1624
+ --text:#e6edf3;--text2:#8b949e;--text3:#6e7681;
1625
+ --green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;
1626
+ }
1627
+ html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-size:13px;background:var(--bg);color:var(--text)}
1628
+ #app{display:flex;height:100%;overflow:hidden}
1629
+
1630
+ /* sidebar */
1631
+ #sidebar{width:250px;min-width:190px;background:var(--bg2);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}
1632
+ #sidebar-hdr{padding:10px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px}
1633
+ #sidebar-hdr h1{font-size:11px;font-weight:700;color:var(--text2);text-transform:uppercase;letter-spacing:.06em;flex:1}
1634
+ #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}
1635
+ #archived-toggle input{cursor:pointer}
1636
+ #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}
1637
+ #refresh-btn:hover{color:var(--text);background:var(--bg3)}
1638
+ #session-list{flex:1;overflow-y:auto;padding:4px 0}
1639
+ .si{padding:8px 12px;cursor:pointer;border-left:2px solid transparent}
1640
+ .si:hover{background:var(--bg3)}
1641
+ .si.active{background:var(--bg3);border-left-color:var(--blue)}
1642
+ .si .sn{font-size:12px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
1643
+ .si .sm{font-size:10.5px;color:var(--text2);margin-top:3px;display:flex;gap:5px;align-items:center;flex-wrap:wrap}
1644
+ .badge{display:inline-block;padding:1px 6px;border-radius:10px;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.02em}
1645
+ .b-run{background:rgba(63,185,80,.15);color:var(--green)}
1646
+ .b-wait{background:rgba(88,166,255,.15);color:var(--blue)}
1647
+ .b-idle{background:rgba(139,148,158,.12);color:var(--text2)}
1648
+ .b-end{background:rgba(139,148,158,.1);color:var(--text3)}
1649
+ .b-err{background:rgba(248,81,73,.15);color:var(--red)}
1650
+ #empty-sidebar{padding:14px 12px;color:var(--text2);font-size:11.5px}
1651
+
1652
+ /* main */
1653
+ #main{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}
1654
+ #header{padding:12px 18px;border-bottom:1px solid var(--border);background:var(--bg2);flex-shrink:0}
1655
+ #header .htitle{font-size:14px;font-weight:700;display:flex;align-items:center;gap:8px}
1656
+ .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}
1657
+ @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)}}
1658
+ #header .hfacts{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
1659
+ .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}
1660
+ #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}
1661
+ #empty-main .ico,#loading-main .ico{font-size:30px}
1662
+ #error-main{color:var(--red)}
1663
+
1664
+ /* transcript */
1665
+ #transcript{flex:1;overflow-y:auto;padding:14px 18px;display:flex;flex-direction:column;gap:10px}
1666
+ .msg{display:flex;flex-direction:column;max-width:78%}
1667
+ .msg.role-user{align-self:flex-end;align-items:flex-end}
1668
+ .msg.role-assistant,.msg.role-tool{align-self:flex-start;align-items:flex-start}
1669
+ .msg.role-system{align-self:center;max-width:90%}
1670
+ .bubble{border-radius:10px;padding:9px 12px;font-size:13px;line-height:1.55;white-space:pre-wrap;word-break:break-word}
1671
+ .role-user .bubble{background:var(--blue);color:#04101c;border-bottom-right-radius:3px}
1672
+ .role-assistant .bubble{background:var(--bg2);border:1px solid var(--border);border-bottom-left-radius:3px}
1673
+ .role-system .bubble{background:transparent;border:1px dashed var(--border);color:var(--text2);font-size:11.5px;text-align:center}
1674
+ .mrow{display:flex;align-items:center;gap:6px;margin-bottom:3px}
1675
+ .mrole{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--text3)}
1676
+ .mtime{font-size:10px;color:var(--text3);font-variant-numeric:tabular-nums}
1677
+
1678
+ .chip{margin-top:6px;border:1px solid var(--border);border-radius:8px;background:var(--bg3);overflow:hidden;cursor:pointer;width:100%}
1679
+ .chip .ch{padding:6px 10px;font-size:11.5px;display:flex;align-items:center;gap:7px;color:var(--text2)}
1680
+ .chip .ch .ci{color:var(--yellow);flex:none}
1681
+ .chip.tool-result .ch .ci{color:var(--purple)}
1682
+ .chip .ch .cname{font-weight:600;color:var(--text)}
1683
+ .chip .ch .cprev{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text3)}
1684
+ .chip .ch .cchev{flex:none;transition:transform .12s;color:var(--text3);font-size:10px}
1685
+ .chip.open .ch .cchev{transform:rotate(90deg)}
1686
+ .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}
1687
+ .chip.open pre{display:block}
1688
+ .reasoning{margin-bottom:6px;font-size:11.5px;font-style:italic;color:var(--text2);border-left:2px solid var(--border);padding-left:8px}
1689
+
1690
+ #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}
1691
+ </style>
1692
+ </head>
1693
+ <body>
1694
+ <div id="app">
1695
+ <div id="sidebar">
1696
+ <div id="sidebar-hdr">
1697
+ <h1>Sessions</h1>
1698
+ <button id="refresh-btn" title="Refresh" type="button">&#8635;</button>
1699
+ </div>
1700
+ <label id="archived-toggle"><input type="checkbox" id="archived-check"> show archived</label>
1701
+ <div id="session-list"></div>
1702
+ </div>
1703
+ <div id="main">
1704
+ <div id="header" style="display:none"></div>
1705
+ <div id="empty-main"><div class="ico">&#128172;</div><div>Pick a session to read its conversation</div></div>
1706
+ <div id="transcript" style="display:none"></div>
1707
+ <div id="statusbar" style="display:none">
1708
+ <span id="status-left"></span>
1709
+ <span id="status-right"></span>
1710
+ </div>
1711
+ </div>
1712
+ </div>
1713
+ <script>
1714
+ var callTool = null;
1715
+ var APP_ID = ${JSON.stringify(APP_ID3)};
1716
+
1717
+ // app_tool_call wraps its dispatch result at least once (its own text-result
1718
+ // body is the JSON-stringified inner MCP tool result); peel back through
1719
+ // nested {content:[{type:"text",text:...}]} shells until the payload stops
1720
+ // looking like one \u2014 works whichever layer the host's bridge already
1721
+ // unwrapped. With conversation_read's format:"json" this resolves straight
1722
+ // to the { meta, messages } object; with session_list, straight to
1723
+ // { sessions: [...] }.
1724
+ function unwrapText(result) {
1725
+ var cur = result;
1726
+ for (var i = 0; i < 4; i++) {
1727
+ if (typeof cur === "string") {
1728
+ try { cur = JSON.parse(cur); continue; } catch (e) { return cur; }
1729
+ }
1730
+ if (cur && Array.isArray(cur.content) && cur.content[0] && typeof cur.content[0].text === "string") {
1731
+ cur = cur.content[0].text;
1732
+ continue;
1733
+ }
1734
+ break;
1735
+ }
1736
+ return cur;
1737
+ }
1738
+
1739
+ function callApp(tool, args) {
1740
+ return callTool("app_tool_call", { appId: APP_ID, tool: tool, args: args || {} }).then(unwrapText);
1741
+ }
1742
+
1743
+ function escHtml(s) {
1744
+ return String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1745
+ }
1746
+
1747
+ function fmtTime(ts) {
1748
+ if (!ts) return "";
1749
+ try { return new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }); }
1750
+ catch (e) { return ""; }
1751
+ }
1752
+
1753
+ function fmtClock(iso) {
1754
+ if (!iso) return "";
1755
+ try { return new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); }
1756
+ catch (e) { return ""; }
1757
+ }
1758
+
1759
+ function fmtRelative(iso) {
1760
+ if (!iso) return "";
1761
+ var ms = Date.now() - new Date(iso).getTime();
1762
+ if (!isFinite(ms)) return "";
1763
+ var s = Math.round(ms / 1000);
1764
+ if (s < 5) return "just now";
1765
+ if (s < 60) return s + "s ago";
1766
+ var m = Math.round(s / 60);
1767
+ if (m < 60) return m + "m ago";
1768
+ var h = Math.round(m / 60);
1769
+ if (h < 24) return h + "h ago";
1770
+ return Math.round(h / 24) + "d ago";
1771
+ }
1772
+
1773
+ function fmtDuration(startIso, endIso) {
1774
+ var end = endIso ? new Date(endIso).getTime() : Date.now();
1775
+ var start = new Date(startIso).getTime();
1776
+ if (!isFinite(start) || !isFinite(end)) return "";
1777
+ var s = Math.max(0, Math.round((end - start) / 1000));
1778
+ if (s < 60) return s + "s";
1779
+ var m = Math.floor(s / 60);
1780
+ if (m < 60) return m + "m " + (s % 60) + "s";
1781
+ var h = Math.floor(m / 60);
1782
+ return h + "h " + (m % 60) + "m";
1783
+ }
1784
+
1785
+ function prettyArgs(raw) {
1786
+ if (raw == null) return "";
1787
+ if (typeof raw !== "string") return JSON.stringify(raw, null, 2);
1788
+ try { return JSON.stringify(JSON.parse(raw), null, 2); } catch (e) { return raw; }
1789
+ }
1790
+
1791
+ function firstLine(s, max) {
1792
+ var t = String(s == null ? "" : s).split("\\n")[0];
1793
+ return t.length > max ? t.slice(0, max) + "\\u2026" : t;
1794
+ }
1795
+
1796
+ // ============================================================
1797
+ // state
1798
+ // ============================================================
1799
+
1800
+ var sessions = [];
1801
+ var activeId = null;
1802
+ var includeArchived = false;
1803
+ var currentMeta = null;
1804
+ var lastMessageCount = -1;
1805
+ var sidebarTimer = null;
1806
+ var convoTimer = null;
1807
+
1808
+ function findSession(id) {
1809
+ for (var i = 0; i < sessions.length; i++) if (sessions[i].id === id) return sessions[i];
1810
+ return null;
1811
+ }
1812
+
1813
+ function isLive(s) {
1814
+ return !!s && (s.status === "running" || s.status === "starting");
1815
+ }
1816
+
1817
+ // ============================================================
1818
+ // sidebar
1819
+ // ============================================================
1820
+
1821
+ function displayBadge(s) {
1822
+ if (s.status === "running") {
1823
+ if (s.busy) return { label: "working", cls: "b-run" };
1824
+ if (s.awaitingInput) return { label: "waiting", cls: "b-wait" };
1825
+ return { label: "idle", cls: "b-idle" };
1826
+ }
1827
+ if (s.status === "starting") return { label: "starting", cls: "b-wait" };
1828
+ if (s.status === "error" || s.status === "killed") return { label: s.status, cls: "b-err" };
1829
+ return { label: s.status || "exited", cls: "b-end" };
1830
+ }
1831
+
1832
+ function renderSidebar() {
1833
+ var el = document.getElementById("session-list");
1834
+ if (sessions.length === 0) {
1835
+ el.innerHTML = '<div id="empty-sidebar">No sessions yet.</div>';
1836
+ return;
1837
+ }
1838
+ var html = "";
1839
+ for (var i = 0; i < sessions.length; i++) {
1840
+ var s = sessions[i];
1841
+ var label = s.label || s.title || s.name || (s.command ? String(s.command).split("/").pop() : null) || s.id.slice(0, 10);
1842
+ var db = displayBadge(s);
1843
+ var active = s.id === activeId ? " active" : "";
1844
+ html += '<div class="si' + active + '" data-id="' + escHtml(s.id) + '">'
1845
+ + '<div class="sn">' + escHtml(label) + "</div>"
1846
+ + '<div class="sm">'
1847
+ + '<span class="badge ' + db.cls + '">' + escHtml(db.label) + "</span>"
1848
+ + "<span>" + escHtml(s.kind || "") + "</span>"
1849
+ + "<span>" + escHtml(fmtRelative(s.startedAt)) + "</span>"
1850
+ + "</div></div>";
1851
+ }
1852
+ el.innerHTML = html;
1853
+ var rows = el.querySelectorAll(".si");
1854
+ for (var j = 0; j < rows.length; j++) {
1855
+ rows[j].addEventListener("click", (function (id) { return function () { selectSession(id); }; })(rows[j].getAttribute("data-id")));
1856
+ }
1857
+ }
1858
+
1859
+ function loadSessions() {
1860
+ return callApp("session_list", { kind: "all", includeArchived: includeArchived }).then(function (data) {
1861
+ sessions = (data && data.sessions) || [];
1862
+ renderSidebar();
1863
+ if (activeId) {
1864
+ var hdr = document.getElementById("header");
1865
+ if (hdr.style.display !== "none") renderHeader();
1866
+ }
1867
+ }).catch(function (e) {
1868
+ document.getElementById("status-left").textContent = "sessions: " + e.message;
1869
+ });
1870
+ }
1871
+
1872
+ // ============================================================
1873
+ // header
1874
+ // ============================================================
1875
+
1876
+ function renderHeader() {
1877
+ var s = findSession(activeId);
1878
+ var meta = currentMeta || {};
1879
+ var el = document.getElementById("header");
1880
+ el.style.display = "block";
1881
+
1882
+ var title = meta.title || (s && (s.label || s.title)) || activeId;
1883
+ var live = s && isLive(s);
1884
+ var pulse = live ? '<span class="pulse"></span>' : "";
1885
+
1886
+ var facts = [];
1887
+ if (meta.model || (s && s.model)) facts.push(escHtml(meta.model || (s && s.model)));
1888
+ if (s && s.adapterSlug) facts.push(escHtml(s.adapterSlug));
1889
+ if (s && s.kind) facts.push(escHtml(s.kind));
1890
+ if (s) { var db = displayBadge(s); facts.push(escHtml(db.label)); }
1891
+ if (s && s.cwd) facts.push('<span title="' + escHtml(s.cwd) + '">' + escHtml(s.cwd.length > 46 ? "\\u2026" + s.cwd.slice(-44) : s.cwd) + "</span>");
1892
+ var started = (meta.startedAt || (s && s.startedAt));
1893
+ if (started) facts.push("started " + escHtml(fmtClock(started)) + " (" + escHtml(fmtRelative(started)) + ")");
1894
+ var ended = meta.endedAt || (s && s.endedAt);
1895
+ if (ended) facts.push("ran " + escHtml(fmtDuration(started, ended)));
1896
+ else if (started) facts.push("running " + escHtml(fmtDuration(started)));
1897
+ if (meta.messageCount != null) facts.push(meta.messageCount + " messages");
1898
+ if (meta.toolCallCount != null) facts.push(meta.toolCallCount + " tool calls");
1899
+ if (meta.tokens && (meta.tokens.input != null || meta.tokens.output != null)) {
1900
+ facts.push("tokens " + (meta.tokens.input || 0) + "\\u2192" + (meta.tokens.output || 0));
1901
+ }
1902
+ if (meta.costUsd != null) facts.push("$" + Number(meta.costUsd).toFixed(4));
1903
+
1904
+ el.innerHTML = '<div class="htitle">' + pulse + escHtml(title) + "</div>"
1905
+ + '<div class="hfacts">' + facts.map(function (f) { return '<span class="fact">' + f + "</span>"; }).join("") + "</div>";
1906
+ }
1907
+
1908
+ // ============================================================
1909
+ // transcript
1910
+ // ============================================================
1911
+
1912
+ function renderToolCallChip(tc) {
1913
+ var args = prettyArgs(tc.args);
1914
+ return '<div class="chip tool-call"><div class="ch"><span class="ci">&#9656;</span>'
1915
+ + '<span class="cname">' + escHtml(tc.name || "tool") + "</span>"
1916
+ + '<span class="cprev">' + escHtml(firstLine(args, 64)) + "</span>"
1917
+ + '<span class="cchev">&#9656;</span></div><pre>' + escHtml(args) + "</pre></div>";
1918
+ }
1919
+
1920
+ function renderToolResultChip(m) {
1921
+ var text = m.text || "";
1922
+ return '<div class="chip tool-result"><div class="ch"><span class="ci">&#128295;</span>'
1923
+ + '<span class="cname">' + escHtml(m.toolName || "result") + "</span>"
1924
+ + '<span class="cprev">' + escHtml(firstLine(text, 64)) + "</span>"
1925
+ + '<span class="cchev">&#9656;</span></div><pre>' + escHtml(text) + "</pre></div>";
1926
+ }
1927
+
1928
+ function renderMessage(m) {
1929
+ var time = m.ts ? '<span class="mtime">' + fmtTime(m.ts) + "</span>" : "";
1930
+ if (m.role === "system") {
1931
+ return '<div class="msg role-system"><div class="bubble">' + escHtml(m.text || "") + "</div></div>";
1932
+ }
1933
+ if (m.role === "tool") {
1934
+ return '<div class="msg role-tool">' + renderToolResultChip(m) + "</div>";
1935
+ }
1936
+ if (m.role === "user") {
1937
+ return '<div class="msg role-user"><div class="mrow"><span class="mrole">You</span>' + time + "</div>"
1938
+ + '<div class="bubble">' + escHtml(m.text || "") + "</div></div>";
1939
+ }
1940
+ // assistant
1941
+ var inner = "";
1942
+ if (m.reasoning) inner += '<div class="reasoning">' + escHtml(m.reasoning) + "</div>";
1943
+ if (m.text) inner += escHtml(m.text);
1944
+ var body = inner ? '<div class="bubble">' + inner + "</div>" : "";
1945
+ var chips = (m.toolCalls || []).map(renderToolCallChip).join("");
1946
+ return '<div class="msg role-assistant"><div class="mrow"><span class="mrole">Assistant</span>' + time + "</div>"
1947
+ + body + chips + "</div>";
1948
+ }
1949
+
1950
+ function renderTranscript(messages) {
1951
+ var el = document.getElementById("transcript");
1952
+ var atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
1953
+ el.style.display = "flex";
1954
+ document.getElementById("empty-main").style.display = "none";
1955
+ var errEl = document.getElementById("error-main");
1956
+ if (errEl) errEl.style.display = "none";
1957
+ if (messages.length === 0) {
1958
+ el.innerHTML = '<div style="color:var(--text2);text-align:center;padding:20px">(empty conversation)</div>';
1959
+ return;
1960
+ }
1961
+ el.innerHTML = messages.map(renderMessage).join("");
1962
+ if (atBottom) el.scrollTop = el.scrollHeight;
1963
+
1964
+ var chips = el.querySelectorAll(".chip");
1965
+ for (var i = 0; i < chips.length; i++) {
1966
+ chips[i].querySelector(".ch").addEventListener("click", (function (chip) {
1967
+ return function () { chip.classList.toggle("open"); };
1968
+ })(chips[i]));
1969
+ }
1970
+ }
1971
+
1972
+ function renderNoConversation(reason) {
1973
+ document.getElementById("transcript").style.display = "none";
1974
+ document.getElementById("empty-main").style.display = "none";
1975
+ var el = document.getElementById("error-main");
1976
+ el.style.display = "flex";
1977
+ el.innerHTML = '<div class="ico">&#128683;</div><div>No readable conversation for this session'
1978
+ + (reason ? '<br><span style="font-size:11px;color:var(--text3)">' + escHtml(reason) + "</span>" : "")
1979
+ + "</div>";
1980
+ }
1981
+
1982
+ // ============================================================
1983
+ // selection + polling
1984
+ // ============================================================
1985
+
1986
+ function clearConvoPoll() {
1987
+ if (convoTimer) { clearTimeout(convoTimer); convoTimer = null; }
1988
+ }
1989
+
1990
+ function scheduleConvoPoll() {
1991
+ clearConvoPoll();
1992
+ var s = findSession(activeId);
1993
+ if (!isLive(s)) return;
1994
+ convoTimer = setTimeout(function () {
1995
+ loadConversation(activeId, { silent: true });
1996
+ }, 3500);
1997
+ }
1998
+
1999
+ function loadConversation(id, opts) {
2000
+ opts = opts || {};
2001
+ var requestedId = id;
2002
+ if (!opts.silent) {
2003
+ document.getElementById("transcript").style.display = "none";
2004
+ document.getElementById("empty-main").style.display = "none";
2005
+ var errEl = document.getElementById("error-main");
2006
+ errEl.style.display = "flex";
2007
+ errEl.innerHTML = '<div class="ico">&#8987;</div><div>Loading\\u2026</div>';
2008
+ }
2009
+ document.getElementById("status-left").textContent = "loading\\u2026";
2010
+ return callApp("conversation_read", { idOrName: id, format: "json" }).then(function (data) {
2011
+ if (activeId !== requestedId) return; // switched away meanwhile
2012
+ if (!data || !Array.isArray(data.messages)) {
2013
+ currentMeta = null;
2014
+ renderHeader();
2015
+ renderNoConversation(data && data.reason);
2016
+ clearConvoPoll();
2017
+ document.getElementById("status-left").textContent = "no conversation";
2018
+ return;
2019
+ }
2020
+ currentMeta = data.meta || {};
2021
+ lastMessageCount = data.messages.length;
2022
+ renderHeader();
2023
+ renderTranscript(data.messages);
2024
+ document.getElementById("status-left").textContent = data.messages.length + " messages";
2025
+ document.getElementById("status-right").textContent = "updated " + fmtTime(Date.now());
2026
+ scheduleConvoPoll();
2027
+ }).catch(function (e) {
2028
+ if (activeId !== requestedId) return;
2029
+ document.getElementById("status-left").textContent = "error: " + e.message;
2030
+ if (!opts.silent) renderNoConversation(e.message);
2031
+ });
2032
+ }
2033
+
2034
+ function selectSession(id) {
2035
+ if (activeId === id) return;
2036
+ activeId = id;
2037
+ currentMeta = null;
2038
+ lastMessageCount = -1;
2039
+ clearConvoPoll();
2040
+ renderSidebar();
2041
+ document.getElementById("statusbar").style.display = "flex";
2042
+ renderHeader();
2043
+ loadConversation(id, {});
2044
+ }
2045
+
2046
+ // ============================================================
2047
+ // boot
2048
+ // ============================================================
2049
+
2050
+ document.getElementById("refresh-btn").addEventListener("click", function () {
2051
+ loadSessions();
2052
+ if (activeId) loadConversation(activeId, {});
2053
+ });
2054
+ document.getElementById("archived-check").addEventListener("change", function (e) {
2055
+ includeArchived = !!e.target.checked;
2056
+ loadSessions();
2057
+ });
2058
+
2059
+ window.McpApp.connect()
2060
+ .then(function (bridge) {
2061
+ callTool = bridge.callTool;
2062
+ return loadSessions();
2063
+ })
2064
+ .then(function () {
2065
+ sidebarTimer = setInterval(loadSessions, 5000);
2066
+ if (!activeId && sessions.length > 0) selectSession(sessions[0].id);
2067
+ })
2068
+ .catch(function (err) {
2069
+ document.getElementById("empty-main").innerHTML =
2070
+ '<div class="ico">&#9888;</div><div>Standalone mode \\u2014 no host bridge (' + escHtml(err.message) + ")</div>";
2071
+ });
2072
+ </script>
2073
+ </body>
2074
+ </html>`;
2075
+
2076
+ // src/session-viewer/index.ts
2077
+ var sessionViewer = defineApp({
2078
+ id: "@agentproto/session-viewer",
2079
+ name: "Session Viewer",
2080
+ version: "0.1.0",
2081
+ description: "Read a daemon session as a clean conversation \u2014 turns, tool calls, timestamps.",
2082
+ agents: [narrator],
2083
+ workflows: [narrateSession],
2084
+ ui: {
2085
+ html: SESSION_VIEWER_HTML,
2086
+ title: "Session Viewer",
2087
+ tools: [...SESSION_VIEWER_TOOLS]
2088
+ }
2089
+ });
1564
2090
 
1565
- export { codeTeam, contentTeam, mailTriage, mediaViewer };
2091
+ export { codeTeam, contentTeam, mailTriage, mediaViewer, sessionViewer };
1566
2092
  //# sourceMappingURL=index.mjs.map
1567
2093
  //# sourceMappingURL=index.mjs.map