From 4cee50a3bed3ccec5e78ec09f60152928c01e4a3 Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 31 May 2026 15:23:08 +0200 Subject: [PATCH] =?UTF-8?q?agent/app.js:=20final=20cookie=20scrub=20?= =?UTF-8?q?=E2=80=94=200=20cookies=20(#713=20batch=203=20final)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All 19 remaining cookies in app.js were attribution refs to closed issues / PRs / past mara/argus reviews. Substance is already in docs from earlier batches (PR #780). This batch collapses the cookie attributions to brief inline notes pointing at docs where appropriate. - #360 side-panel duplication note → drop closed-issue framing - #394 vibec0re header redesign (×5) → drop cookies, prose stays - #589 hive-qualified label glyphic-title note → drop cookie - #576/#542/#584 logout chain rationale → drop cookies, prose preserved (the WHY + .credentials.json + mcp-needs-auth- cache.json detail stays inline) - #14 relative paths convention → docs/web-ui.md::Per-agent relative paths pointer - #233 marked autolinks new-tab → drop cookie - #559/#566 mark-all-read flyout (×3) → docs/web-ui.md::Per-agent page (Loose-ends flyout) pointer + drop attribution - #666 ask→operator inline-answer slot (×2) → docs/web-ui.md:: Per-agent page (Ask → operator inline-answer binding) pointer app.js: 19 → 0 cookies (100% reduction). Combined with PR #780 + batch 1 + 2, app.js is fully migrated for #713 scope. Closes #713 — all agent UI files (app.js, agent.css, screen.html, index.html, stats.js, stats.html) at 0 issue-ref cookies. --- frontend/packages/agent/src/app.js | 113 +++++++++++++---------------- 1 file changed, 49 insertions(+), 64 deletions(-) diff --git a/frontend/packages/agent/src/app.js b/frontend/packages/agent/src/app.js index b181d248..87b0b37c 100644 --- a/frontend/packages/agent/src/app.js +++ b/frontend/packages/agent/src/app.js @@ -79,8 +79,7 @@ window.marked = marked; // ─── side panel (singleton drawer for inbox + loose-ends flyouts) ────── // Shared shape with the dashboard's panel. Candidate for extraction - // into @hive/shared in a follow-up — keeping the duplication for - // now to land #360 without simultaneously refactoring the dashboard. + // into @hive/shared once both surfaces stabilise. const Panel = (() => { const root = $('side-panel'); const titleEl = $('side-panel-title'); @@ -141,13 +140,12 @@ window.marked = marked; // ─── state rendering ──────────────────────────────────────────────────── function setHeader(label, qualifiedLabel, dashboardPort) { const title = $('title'); - // Title is now just the glowing identity glyph — DASHB04RD, - // R3BU1LD, NEW SESSION all live in the overflow `⋯` menu now - // (#394). Glow + uppercase styling from h2 / .agent-header-title-row. - // The glyphic title stays short (no @hive suffix) — the hive - // qualifier lives on the second row's `qualified` chip + the - // browser tab title so the cinematic header reads cleanly at a - // glance (#589). + // Title is just the glowing identity glyph — DASHB04RD, R3BU1LD, + // NEW SESSION live in the overflow `⋯` menu. Glow + uppercase + // styling from h2 / .agent-header-title-row. The glyphic title + // stays short (no @hive suffix) — the hive qualifier lives on + // the second row's `qualified` chip + the browser tab title so + // the cinematic header reads cleanly at a glance. title.textContent = `◆ ${label} ◆`; // Document title carries the qualified name so the browser's tab // bar disambiguates between same-named agents on different hives @@ -160,20 +158,17 @@ window.marked = marked; populateOverflowMenu(label, dashUrl); } - // Overflow popover: dashboard back-link + rebuild + new-session. - // Per #394 mara's spec — rebuild + new-session both moved off the - // header strip into the `⋯` menu (rare actions, both destructive - // enough to warrant one extra click; the operator rebuilds from - // the host dashboard normally). Dashboard link also slotted in so - // every "leave this page" action lives in one menu. + // Overflow popover: dashboard back-link + rebuild + new-session + + // logout. Rare + destructive actions live here behind one extra + // click (the operator rebuilds from the host dashboard normally). + // See docs/web-ui.md::Per-agent page (Overflow button). let overflowMenuPopulated = false; function populateOverflowMenu(label, dashUrl) { const menu = $('overflow-menu'); if (!menu) return; menu.replaceChildren(); - // ↑ dashboard — host dashboard back-link (was `.btn-dashlink` - // beside the title pre-#394). + // ↑ dashboard — host dashboard back-link. menu.append(el('a', { class: 'overflow-item overflow-item-dashboard', href: dashUrl, @@ -227,15 +222,14 @@ window.marked = marked; }); menu.append(newSessBtn); - // 🔓 logout (#576) — SIGINTs claude, wipes the credentials dir, - // flips the harness LoginState to NeedsLogin. The turn loop's - // next iteration parks in wait_for_login; a fresh `claude auth - // login` from the dashboard re-arms it (#542 mtime resumption). - // Operator has to re-paste OAuth creds on the login screen after, - // but the --continue session history is preserved (#584 narrowed - // the backend wipe to just .credentials.json + mcp-needs-auth- - // cache.json) — so the agent picks up where it left off on the - // next turn after re-login. + // 🔓 logout — SIGINTs claude, wipes the credentials dir, flips + // the harness LoginState to NeedsLogin. The turn loop's next + // iteration parks in wait_for_login; a fresh `claude auth login` + // from the dashboard re-arms it. Operator has to re-paste OAuth + // creds on the login screen after, but the --continue session + // history is preserved (the backend wipe is narrowed to just + // .credentials.json + mcp-needs-auth-cache.json) — so the agent + // picks up where it left off on the next turn after re-login. const logoutBtn = el('button', { type: 'button', class: 'overflow-item overflow-item-logout', @@ -464,8 +458,9 @@ window.marked = marked; if (termAPI) termAPI.row('turn-end-fail', '✗ ' + label + ' failed: ' + err); } } - // First arg is the URL path (relative to document base, #14); - // second is the slash-command label rendered in the local note. + // First arg is the URL path (relative to document base — see + // docs/web-ui.md::Per-agent relative paths); second is the + // slash-command label rendered in the local note. const postCancelTurn = () => postSimple('api/cancel', '/cancel'); const postCompact = () => postSimple('api/compact', '/compact'); const postNewSession = () => postSimple('api/new-session', '/new-session'); @@ -485,7 +480,7 @@ window.marked = marked; return true; case '/clear': termAPI.clear(); - // #666: detached `ask → operator` rows no longer have a live + // Detached `ask → operator` rows no longer have a live // mount-point in the DOM — drop their slots so subsequent // loose-ends reconciliation doesn't walk dead references. pendingAskBinds.length = 0; @@ -837,12 +832,11 @@ window.marked = marked; return wrap; } - /** #559: "mark all read" affordance for the agent's inbox flyout. - * Returns a DOM row containing a button + an inline status pill. - * POSTs to the host dashboard's `/api/agent/{name}/mark-all-read` - * (damocles PR #566) and surfaces the `{ marked: N }` count back to - * the operator. Re-runs `onCleared` on success so the caller can - * refresh whatever state it owns. */ + /** "mark all read" affordance for the agent's inbox flyout — + * see docs/web-ui.md::Per-agent page (Loose-ends flyout) for the + * cross-origin POST + count rendering. Returns a DOM row + * containing a button + an inline status pill; re-runs + * `onCleared` on success so the caller can refresh its own state. */ function buildInboxMarkAllRow(label, onCleared) { const status = el('span', { class: 'inbox-mark-status' }); const btn = el('button', { @@ -896,12 +890,12 @@ window.marked = marked; 'inbox empty.')); return wrap; } - // #559: "mark all read" header row drains the host broker's - // pending + delivered-unacked rows for this agent (damocles PR - // #566). Visible rows here are the most-recent-N regardless of - // ack state, so the list itself doesn't visually empty on click — - // the status pill confirms the drain count, and the next - // turn_start's "unread" badge will read zero. + // "mark all read" header row drains the host broker's pending + + // delivered-unacked rows for this agent. Visible rows here are + // the most-recent-N regardless of ack state, so the list itself + // doesn't visually empty on click — the status pill confirms + // the drain count, and the next turn_start's "unread" badge + // will read zero. wrap.append(buildInboxMarkAllRow(currentLabel, () => { // Refresh state so any UI surface that DOES depend on // delivery state (eg future per-status filters) picks up @@ -1029,10 +1023,6 @@ window.marked = marked; }); })(); - // (#394) — `↻ new session` button moved into the overflow `⋯` - // menu and wired by `populateOverflowMenu()` above. Previously - // wired here as a static `#new-session-btn` in index.html. - // Track banner activity by reference-counting in-flight turns. A turn // can begin while the previous turn_end is still in the pipeline (rare // but happens on tight wake cycles), so we count rather than toggle. @@ -1074,9 +1064,8 @@ window.marked = marked; rel: 'noopener', title: lnk.label || '', }); - // Layout gap comes from `.agent-nav { gap }` (#394) — drop - // the legacy per-link inline `marginLeft`. The trailing `→` - // is the "leaves this page" affordance. + // Layout gap comes from `.agent-nav { gap }`. The trailing + // `→` is the "leaves this page" affordance. a.append(((lnk.icon || '') + ' ' + (lnk.label || '')).trim() + ' →'); metaLinks.append(a); }); @@ -1154,7 +1143,7 @@ window.marked = marked; marked.setOptions({ breaks: true, gfm: true }); div.innerHTML = marked.parse(src); // marked autolinks URLs but leaves them same-tab — open them - // externally so a click never unloads the terminal. (issue #233) + // externally so a click never unloads the terminal. div.querySelectorAll('a[href]').forEach((a) => { a.target = '_blank'; a.rel = 'noopener noreferrer'; @@ -1306,15 +1295,10 @@ window.marked = marked; const d = detailsOpenMd(api, 'tool-use', 'ask → ' + to + (lines > 1 ? ` · ${lines}L` : ''), q); - // #666: when the ask targets the operator, mount an inline - // answer slot in the live terminal so the operator doesn't - // need to open the loose-ends side panel (or jump to Y3R C4LL - // on the dashboard) to respond. The slot starts empty and - // gets populated by `reconcileAskBinds()` once the - // loose-ends fetch identifies a matching pending question - // (by asker == this agent + question text). Resolved - // questions render as a struck-through [answered ✓] tag - // instead of a form. + // When the ask targets the operator, mount an inline answer + // slot in the live terminal — see docs/web-ui.md::Per-agent + // page (Ask → operator inline-answer binding) for the slot + // registry + reconciler + [resolved] semantics. if (to === 'operator') { const slot = el('div', { class: 'ask-answer-inline-slot' }); // Stash the question text on the slot so the reconciler @@ -1356,12 +1340,13 @@ window.marked = marked; : (c.content || ''); const sourceName = c.tool_use_id ? toolNameById.get(c.tool_use_id) : null; const isMessageBearing = sourceName === 'mcp__hyperhive__recv'; - // #666: when an ask's tool_result lands the broker has just - // persisted the question with its assigned id. Refresh loose - // ends so `reconcileAskBinds` finds the new entry and mounts - // the inline answer form under the rendered ask row. Skipped - // during history replay (the question's likely long-resolved; - // turn_end refresh on cold-load already covers reconciliation). + // When an ask's tool_result lands the broker has just + // persisted the question with its assigned id. Refresh + // loose-ends so reconcileAskBinds finds the new entry and + // mounts the inline answer form under the rendered ask row. + // Skipped during history replay (the question's likely + // long-resolved; turn_end refresh on cold-load covers + // reconciliation). if (sourceName === 'mcp__hyperhive__ask' && !api.fromHistory) { refreshLooseEnds(); }