From 725f3beddb869bdbcb84a22d4ad50eae33913b7a Mon Sep 17 00:00:00 2001 From: Vikas Singhal Date: Fri, 31 Jul 2026 20:06:34 +0530 Subject: [PATCH] =?UTF-8?q?fix(cockpit):=20hide=20System-agent=20runs=20(c?= =?UTF-8?q?oncierge/operator/=E2=80=A6)=20from=20the=20Chat=20+=20Sessions?= =?UTF-8?q?=20lists=20(v0.283.2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Cockpit concierge/operator runs are spawned run-as the member (provenance chat:), so these category:'System' machinery runs cluttered the member's Chat list and the Sessions list as if they were conversations they started. listSessions now derives a `system` flag from the agent's category; the console filters it out of the Chat list, the Sessions list, and the running-count badge. The rows still exist — openable by id (Cockpit's "Open full session") and in Audit — just out of the everyday lists. Verified: typecheck + web build clean; test:governance 137/137 + tier-A 18/18. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 11 +++++++++++ package-lock.json | 4 ++-- package.json | 2 +- src/terminal.ts | 6 ++++++ web/src/App.tsx | 5 +++-- web/src/lib/api.ts | 3 +++ 6 files changed, 26 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e5d94d..902fe09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,17 @@ new version heading in the same commit. ## [Unreleased] +## [0.283.2] — 2026-07-31 +### Changed +- **System-agent runs (the Cockpit concierge/operator, consolidator, …) are hidden from the Chat + + Sessions lists.** These `category:'System'` machinery runs are spawned run-as the member (provenance + `chat:`), so they were cluttering the member's Chat list and the Sessions list as if they were + conversations the member started. `listSessions` now derives a `system` flag from the agent's category; + the console filters it out of the Chat list, the Sessions list, and the running-count badge. The rows + still exist — openable by id (Cockpit's "Open full session" still works) and in the Audit log — they're + just out of the everyday lists. `src/terminal.ts` (`Session.system`), `web/src/App.tsx`, + `web/src/lib/api.ts`. + ## [0.283.1] — 2026-07-31 ### Fixed - **Every allowed Codex tool call logged `PreToolUse hook (failed)`.** Codex's parser acts on `deny` but diff --git a/package-lock.json b/package-lock.json index fd96d62..2cb7929 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "agent-os", - "version": "0.283.1", + "version": "0.283.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "agent-os", - "version": "0.283.1", + "version": "0.283.2", "license": "MIT", "bin": { "agent-os": "bin/agent-os" diff --git a/package.json b/package.json index 70f7ae9..478c241 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-os", - "version": "0.283.1", + "version": "0.283.2", "description": "A generic, governed operating system for running autonomous agents safely across brands. Ships with a local web console.", "license": "MIT", "type": "commonjs", diff --git a/src/terminal.ts b/src/terminal.ts index 545e069..9ca56e5 100644 --- a/src/terminal.ts +++ b/src/terminal.ts @@ -221,6 +221,11 @@ export interface Session { * trigger; `task`/`chat` = the dispatcher/chat-router; `system` = an internal principal (e.g. the * consolidation gardener). */ sourceKind?: SessionSourceKind; + /** True when this run belongs to a `category:'System'` agent — the OS machinery (concierge/operator + * answering Cockpit, consolidator, …), not a user teammate. The console hides these from the Chat + + * Sessions lists to keep them uncluttered; the row still exists for by-id opens (Cockpit's "Open full + * session") + Audit. */ + system?: boolean; /** True when the run launched unattended (an automation/cron/task run). These now run as an attachable * interactive TUI (not `claude -p`) that a human can take over live; the console badges them as * unattended vs. a member's own interactive session. */ @@ -713,6 +718,7 @@ export class TerminalManager { blocked: r.status === 'running' && blocked.has(r.id), resumable: resumable.has(r.id), forkable: !!r.claude_session_id && runtimeSupports(this.os.agents.get(r.agent)?.runtime, 'fork'), + system: this.os.agents.get(r.agent)?.category === 'System', spawnedByLabel: this.spawnedByLabel(r.spawned_by, r.run_as), sourceKind: this.sourceKind(r.spawned_by), runAsLabel: this.runAsLabel(r.run_as), diff --git a/web/src/App.tsx b/web/src/App.tsx index f7612e7..aae3a36 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1290,7 +1290,7 @@ function Console({ me }: { me: Member }) { // reattachable), which would inflate this badge with finished sessions whose panes were never reaped. // A running row with a confirmed-dead pane (alive === false) is mid-reap → excluded; alive undefined // (poll failed / launcher backend) falls back to the stored 'running' status. - const runningSessions = sessions.filter((s) => s.status === 'running' && s.alive !== false).length + const runningSessions = sessions.filter((s) => s.status === 'running' && s.alive !== false && !s.system).length // The sidebar is a switcher over the sessions *I'm accountable for* — ones I started directly // (spawnedBy is my member id) OR ones a trigger spawned that run AS me (runAs): a Task I own that // auto-dispatched, a chat message I sent. Those have a `task:`/`automation:` provenance, so keying @@ -3245,6 +3245,7 @@ function SessionsPage({ const filtered = useMemo(() => { const needle = query.trim().toLowerCase() return sessions.filter((s) => + !s.system && // OS machinery (Cockpit concierge/operator, …) stays out of the sessions list matchesStatus(s, statusFilter) && (agentFilter === 'all' || s.agent === agentFilter) && (sourceFilter === 'all' || sessionSource(s) === sourceFilter) && @@ -4158,7 +4159,7 @@ function ChatPage({ agents, sessions, messages, selected, onSelect, onOpenTermin // surface (or the chat router) spawned, newest first. const chatAgents = useMemo(() => agents.filter((a) => a.runtime === 'claude-code'), [agents]) const chats = useMemo( - () => sessions.filter((s) => s.sourceKind === 'chat').sort((a, b) => b.updatedAt - a.updatedAt), + () => sessions.filter((s) => s.sourceKind === 'chat' && !s.system).sort((a, b) => b.updatedAt - a.updatedAt), [sessions], ) const active = selected ? sessions.find((s) => s.id === selected) : undefined diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index 5f3fb45..63c4fbe 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -232,6 +232,9 @@ export interface Session { * `task` = the Tasks dispatcher; `chat` = the `/agent` chat router; `system` = an internal principal. * Server-resolved (the automation sub-type needs a join the raw `spawnedBy` can't give). */ sourceKind?: 'manual' | 'cron' | 'webhook' | 'slack' | 'discord' | 'composio' | 'scheduled' | 'task' | 'chat' | 'system' + /** True for a `category:'System'` machinery agent (the Cockpit concierge/operator, consolidator, …). + * Hidden from the Chat + Sessions lists to reduce clutter; still openable by id + in Audit. */ + system?: boolean /** True when the run launched unattended (an automation/cron/task run). These now run as an attachable * interactive TUI a human can take over live; the list badges them as unattended vs. a member session. */ headless?: boolean