From 4738cecc232263c22334e40c183993e4d16e4a89 Mon Sep 17 00:00:00 2001 From: mintaka Date: Sat, 12 Sep 2026 10:32:39 -0400 Subject: [PATCH 1/2] feat(ui): mark each agent's runtime tier and egress posture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The board showed lifecycle state only, so a host-tier agent — which shares the host's network namespace and cannot be firewalled — looked exactly like a contained one. Each agent now carries a marker naming its tier, and an unenforced posture reads as uncontained on both colour and shape. The session-status payload was subscribed but dropped, so this also lands the decode: statuses key a runtime map by account id, which the roster join folds in beside presence. A status with no resolvable account is skipped rather than keyed under an empty id, and the map clears on resync — a stale posture surviving a reset could show a torn-down session as still contained. An absent marker stays absent rather than defaulting, because guessing a posture is the one error this must not make. --- apps/ui/src/App.tsx | 4 + apps/ui/src/components/AgentView.tsx | 4 + apps/ui/src/components/Bridge.tsx | 7 ++ apps/ui/src/components/LeftSidebar.tsx | 2 + apps/ui/src/components/RightSidebar.tsx | 10 ++- apps/ui/src/components/RuntimeMarker.tsx | 39 +++++++++ .../src/design/components/runtime-marker.css | 29 +++++++ apps/ui/src/live/adapt.test.ts | 42 ++++++++++ apps/ui/src/live/adapt.ts | 39 +++++++++ apps/ui/src/live/events.test.ts | 84 ++++++++++++++++++- apps/ui/src/live/events.ts | 28 ++++++- apps/ui/src/roster.test.ts | 32 ++++++- apps/ui/src/roster.ts | 12 ++- apps/ui/src/store.ts | 11 ++- apps/ui/src/stub-data.ts | 29 +++++++ packages/compass-client/src/index.ts | 3 + 16 files changed, 366 insertions(+), 9 deletions(-) create mode 100644 apps/ui/src/components/RuntimeMarker.tsx create mode 100644 apps/ui/src/design/components/runtime-marker.css diff --git a/apps/ui/src/App.tsx b/apps/ui/src/App.tsx index fdc23d6da..972796f11 100644 --- a/apps/ui/src/App.tsx +++ b/apps/ui/src/App.tsx @@ -17,6 +17,7 @@ import { import { LeftSidebar } from "./components/LeftSidebar"; import { Palette } from "./components/Palette"; import { RightSidebar } from "./components/RightSidebar"; +import { RuntimeMarker } from "./components/RuntimeMarker"; import { ShortcutsOverlay } from "./components/ShortcutsOverlay"; import { StateDot } from "./components/StateDot"; import { UsageBar } from "./components/UsageBar"; @@ -111,6 +112,9 @@ const App: Component = (props) => { onClick={() => store.openAgent(agent().account.id)} > + + {(m) => } + {agent().account.displayName} )} diff --git a/apps/ui/src/components/AgentView.tsx b/apps/ui/src/components/AgentView.tsx index e4ef67709..617aa61ad 100644 --- a/apps/ui/src/components/AgentView.tsx +++ b/apps/ui/src/components/AgentView.tsx @@ -10,6 +10,7 @@ import { import type { Agent, Terminal } from "../stub-data"; import { ChannelView } from "./ChannelView"; import { LogPanel } from "./LogPanel"; +import { RuntimeMarker } from "./RuntimeMarker"; import { StateDot } from "./StateDot"; /** A terminal pane: the fixture scrollback for the terminal a tab references. A @@ -215,6 +216,9 @@ export const AgentView: Component = () => {
+ + {(m) => } + {agent().account.handle} {agent().model} diff --git a/apps/ui/src/components/Bridge.tsx b/apps/ui/src/components/Bridge.tsx index 95746831d..b7651d240 100644 --- a/apps/ui/src/components/Bridge.tsx +++ b/apps/ui/src/components/Bridge.tsx @@ -41,6 +41,7 @@ import { createRovingGroup, type Stop } from "../keyboard/roving"; import type { IssueState } from "../stub-data"; import { BadgeGlyph } from "./BadgeGlyph"; import { IssueCard } from "./IssueCard"; +import { RuntimeMarker } from "./RuntimeMarker"; import { StateDot } from "./StateDot"; /** How the board groups rows. Swimlane = one row per agent (default); status = @@ -665,6 +666,9 @@ export const Bridge: Component = () => { onClick={() => store.openAgent(agent.account.id)} > + + {(m) => } + {agent.account.handle}
@@ -758,6 +762,9 @@ export const Bridge: Component = () => { onClick={() => store.openAgent(agent().account.id)} > + + {(m) => } + {agent().account.handle}