feat(server): autonomOS-aware statusline for spawned CC sessions - #162
Merged
Conversation
Inject an adaptive statusline into every Claude Code session spawned by autonomos via the inline `--settings` JSON. Renderer auto-detects role from hierarchy (manager + direct-report count) and renders one of: [Dispatcher@autonomos · ↓3 reports] [TeamLead@autonomos · ↑Dispatcher · ↓2 reports] [Worker@autonomos · ↑TeamLead] [Agent@autonomos · standalone] Plus an activity line with model, ctx %, cost, duration, branch. Refresh every 5s so hierarchy stays current during idle. Single GET /api/sessions covers both identity lookup and report counting (the LIST endpoint enriches with persisted manager/template/project; the SINGLE endpoint does not, so we don't use it here). Settings: `statusLine.enabled` toggle in the dashboard settings panel, default ON. When disabled, falls back to the user's personal `~/.claude/settings.json` statusLine. Renderer ships as a plain `.mjs` next to claude-code.ts (no build step, no external deps — only built-in node modules + global fetch). Path is JSON.stringified into the inline statusLine.command for safe shell quoting against install paths with spaces. 30 unit tests cover formatHierarchy, formatActivity, buildBar, formatDuration, and getAutonomosMeta against mocked fetch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aterrylu
force-pushed
the
terry/statusline-config
branch
from
May 4, 2026 06:33
f40eea1 to
70ca6ad
Compare
aterrylu
marked this pull request as ready for review
May 4, 2026 06:33
nox-0x
approved these changes
May 4, 2026
nox-0x
left a comment
Collaborator
There was a problem hiding this comment.
LGTM. Well-structured feature with a solid failure-mode contract — the statusline renderer is defensive by design and the 30-unit test suite covers all edge cases. No blocking issues.
5 tasks
aterrylu
added a commit
that referenced
this pull request
May 6, 2026
The statusline renderer shipped in #162 had two bugs that would have made the hierarchy line silently non-functional in production. Both were caught by visual QA against the live prod server post-merge — unit tests with mocked fetch couldn't detect them. 1. Missing Authorization header. /api/sessions requires `Authorization: Bearer ${AUTONOMOS_TOKEN}` (the same token the channel-server uses). Without it, the renderer always got 401, getAutonomosMeta returned null, and every spawned agent's identity line was `[autonomos · offline]` instead of the proper hierarchy. 2. Wrong field match. AUTONOMOS_SESSION_ID is the autonomOS managed session id (the `id` field on /api/sessions entries), not Claude's internal session id (`claudeSessionId`). The renderer matched on the wrong field, so even if auth had been correct, find() would always have returned undefined. Verified against the live :3100 server with three real sessions: [StatuslineConfig@autonomOS · ↑TeamLead@autonomOS] [TeamLead@autonomOS · ↑Dispatcher · ↓4 reports] [Dispatcher · ↓5 reports] Added 2 tests covering the auth header behavior (sent when token provided, omitted when absent). Updated existing tests' mock data to use `id` instead of `claudeSessionId` so the field semantics are asserted correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aterrylu
added a commit
that referenced
this pull request
May 6, 2026
* fix(server): statusline auth + correct session id field The statusline renderer shipped in #162 had two bugs that would have made the hierarchy line silently non-functional in production. Both were caught by visual QA against the live prod server post-merge — unit tests with mocked fetch couldn't detect them. 1. Missing Authorization header. /api/sessions requires `Authorization: Bearer ${AUTONOMOS_TOKEN}` (the same token the channel-server uses). Without it, the renderer always got 401, getAutonomosMeta returned null, and every spawned agent's identity line was `[autonomos · offline]` instead of the proper hierarchy. 2. Wrong field match. AUTONOMOS_SESSION_ID is the autonomOS managed session id (the `id` field on /api/sessions entries), not Claude's internal session id (`claudeSessionId`). The renderer matched on the wrong field, so even if auth had been correct, find() would always have returned undefined. Verified against the live :3100 server with three real sessions: [StatuslineConfig@autonomOS · ↑TeamLead@autonomOS] [TeamLead@autonomOS · ↑Dispatcher · ↓4 reports] [Dispatcher · ↓5 reports] Added 2 tests covering the auth header behavior (sent when token provided, omitted when absent). Updated existing tests' mock data to use `id` instead of `claudeSessionId` so the field semantics are asserted correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(statusline): colors, project layout, security sanitization Visual + correctness iteration on top of the auth fix: Layout (matching Terry's personal CC statusline pattern): - Activity line now leads with project (blue) → branch (green) → cost (yellow) → model (magenta) → ctx bar (threshold green/yellow/red) → duration (dim grey). No two adjacent segments share a color. - │ vertical separators between activity segments (was double space). - Centralized ANSI palette at top of file for easy tweaking. New resolvers: - resolveProject: 3-tier fallback chain — meta.project → @-suffix on agent name → basename of CC's project_dir. Strict project_dir-first preference (current_dir is excluded so subdirs don't mislabel). - resolveBranch: synchronous git fallback when CC's stdin doesn't include a branch (common case for non-worktree sessions). Bounded by 100ms timeout and GIT_CEILING_DIRECTORIES so a misconfigured cwd can't make git stat-storm up the ancestor chain. Security: - ANSI/control characters stripped from agent name and manager name in getAutonomosMeta. Prevents a malicious peer agent named "evil\x1b[2J" from clearing the user's terminal on every 5s refresh tick. Compat with main (PR #165): - /api/sessions endpoint replaced by /api/agents. - Manager refs are now by UUID (managerId) not name. Renderer resolves the manager's display name by looking up the referenced agent in the same response. Direct-report counting now compares managerId === me.id. Tests: 42 unit tests (was 30) cover format helpers, color verification, meta resolver against the new /api/agents shape, sanitization, and manager-by-id resolution including the dangling-reference case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(statusline): biome formatter pass --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Inject an autonomOS-aware statusline into every spawned Claude Code session via inline
--settingsJSON. The renderer reads CC's stdin payload (model, ctx %, cost, branch) and queries the local autonomOS server for hierarchy, then renders an adaptive identity line that auto-detects role frommanager+directReports.What it looks like
Architecture
--settingsaccepts inline JSON, autonomos already uses this for hook relay. NewstatusLinekey piggybacks on the existing inline payload (one extra JSON property)..mjsatpackages/server/src/providers/statusline.mjs. No build step — uses only built-in node modules + globalfetch. Same source-tree-shipping pattern aschannel-server/dist.mjsbut doesn't need bundling because it has no external deps.GET /api/sessions) covers identity lookup AND report counting. The LIST endpoint enriches each entry withmanager/template/projectfromPersistedSession; the SINGLE endpoint (/api/sessions/:id) does NOT, so we don't use it.set_managerreorgs without requiring an assistant message).--settingsoverrides~/.claude/settings.jsonfor object-typed fields). Toggle off → falls back to personal config.Failure-mode contract
The script must NEVER crash CC's terminal (per CC docs: non-zero exit or no output → blank statusline). All boundaries degrade gracefully:
[autonomos · offline]+ activity-line-only[autonomos]+ activity[autonomos]fallbackFiles
packages/server/src/settings.tsstatusLine?: { enabled: boolean }toAppSettingspackages/server/src/routes/settings.tsstatusLinein REST handlerpackages/server/src/providers/claude-code.tsstatusLineinto existing inline--settingsJSON when enabled (default on)packages/server/src/providers/statusline.mjs(NEW)packages/server/src/providers/statusline.d.mts(NEW).mjs(test-only)packages/dashboard/src/plugins/settings/SettingsStatusBarItem.tsxpackages/server/src/__tests__/statusline.test.ts(NEW)Test plan
formatHierarchyacross all 4 hierarchy states (standalone / worker / top / middle)buildBarboundary clamping (0%, 100%, >100, <0, null/undefined)formatDurationminute/second padding + null handlingformatActivitywith all fields, empty stdin, missing branch, null nested fieldsgetAutonomosMetaagainst mocked fetch: errors, non-array response, session-not-found, manager + reports counting, exited-session exclusion, missing-name fallback, null-vs-undefined managerNotes
formatHierarchy({manager: null, directReports: -1})was emitting[X]instead of[X · standalone]because the standalone check was=== 0instead of<= 0. Test caught it on first run.command: \node ${PATH}`was unquoted. Fixed byJSON.stringify(STATUSLINE_SCRIPT)for proper shell quoting against install paths with spaces/quotes/$`/backticks./api/orgreturns a flat array withmanagerfields and/api/sessions/:idreturns enriched session — both wrong. Fixed by switching to the LIST endpoint which has the correct enriched shape.showStatusInTitle(terminal-title icons), no command-script statusline./statuslineslash command but no custom-command support — see openai/codex#17827.🤖 Generated with Claude Code