Skip to content

feat(desktop): shell overhaul, agent daemon rework, and forensics expansion#77

Open
bb-connor wants to merge 2 commits intomainfrom
feat/desktop-shell-overhaul
Open

feat(desktop): shell overhaul, agent daemon rework, and forensics expansion#77
bb-connor wants to merge 2 commits intomainfrom
feat/desktop-shell-overhaul

Conversation

@bb-connor
Copy link
Collaborator

@bb-connor bb-connor commented Feb 13, 2026

Summary

Cherry-picked desktop/agent changes from feat/policy-editor (PR #76) that were not yet on main. Excludes all adapter package refactors to avoid clobbering the current clawdstrike-claude / clawdstrike-openai packages.

  • Shell overhaul: NavRail refactor, new ProfileMenu, SessionRail major expansion (+840 lines), DockSystem shelf system, hotCommands keyboard shortcuts, plugin registry updates
  • Forensics expansion: ForensicsRiverView massive rework (+1200 lines), AgentGlyphOverlay, AgentOrbHud, useAgentCognitionState hook, ChronicleWorkbenchShelf, PolicyWorkbenchPanel follow-up fixes
  • Agent app: daemon.rs rework (+450 lines), notifications overhaul, events refactor, crimson/gold icon retheme
  • New feature: OperationsHubView
  • New tests: NavRail, SessionRail, DockSystem shelf, hotCommands, ProfileMenu, ShellApp routing, NexusControlStrip, sessionStore, plugin registry, policyDraftGuard

Stats

62 files changed, 6055 insertions, 877 deletions

Test plan

  • Verify desktop app builds (cargo tauri build in apps/desktop)
  • Verify agent app builds (cargo tauri build in apps/agent)
  • Run desktop TS tests
  • Smoke test shell navigation, dock, session rail
  • Verify policy workbench loads and evaluates
  • Confirm no adapter package changes leaked in

🤖 Generated with Claude Code


Note

High Risk
Touches core agent daemon lifecycle management (spawn/attach/restart) and substantially rewires the desktop’s primary navigation and forensics telemetry flow, so regressions could affect connectivity, daemon availability, and key UI entrypoints.

Overview
Agent (Tauri/Rust): Reworks DaemonManager to attach to an already-running hushd on the configured port ("external mode"), automatically fall back to spawning a managed child if the external daemon disappears, and write a generated hushd.runtime.toml config (with legacy policy guard detection that falls back to a default ruleset). Also expands find_hushd_binary search paths and updates restart/ready logic accordingly.

Agent notifications & packaging: Adds toml + macOS-only mac-notification-sys, switches notifications to a branded title + resolved app icon, and uses a macOS-specific notification sender to avoid OS dialogs/attribution issues. Updates tray/menu behavior and Tauri bundle resources/icons configuration.

Desktop (React/Tauri): Adds an OperationsHubView with query-param tabs that scopes SettingsView to connection vs preferences, improves Tauri detection, and adds OpenClaw command fallbacks (env token overrides + CLI probe fallback when agent is unavailable). Major UX rebrand/routing updates from Cyber Nexus to /nexus, enhances NexusControlStrip and NexusAppRail, and heavily expands ForensicsRiverView into a Nexus-style station scene with probe/runtime-derived telemetry, agent glyph overlays, and method-unavailable fallbacks.

Tests: Adds targeted Vitest coverage for routing (ShellApp), NexusControlStrip, and OperationsHubView.

Written by Cursor Bugbot for commit aaaa541. This will update automatically on new commits. Configure here.

…ansion

Cherry-picked desktop/agent changes from feat/policy-editor (PR #76)
that were not yet on main. Excludes all adapter package refactors.

Includes:
- Shell: NavRail refactor, ProfileMenu, SessionRail overhaul, DockSystem
  shelf, hotCommands, plugin registry updates
- Forensics: ForensicsRiverView expansion, AgentGlyphOverlay, AgentOrbHud,
  useAgentCognitionState, ChronicleWorkbenchShelf, PolicyWorkbenchPanel fixes
- Agent: daemon.rs rework, notifications, events, crimson/gold icon retheme
- Operations: new OperationsHubView
- Tests: NavRail, SessionRail, DockSystem, hotCommands, ProfileMenu,
  ShellApp routing, NexusControlStrip, sessionStore, registry, policyDraftGuard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: baf05288be

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let mut request = self
.http_client
.get(&url)
.query(&[("limit", "50"), ("offset", "0")]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Track audit cursor in polling fallback

The fallback poll now always requests only limit=50, offset=0, so when SSE is down and more than 50 new audit events arrive between polls, older unseen events are never fetched and are permanently dropped from the UI stream; this is a data-loss regression under moderate/high event throughput and should keep a cursor (or paginate) to guarantee continuity.

Useful? React with 👍 / 👎.

Comment on lines +291 to +292
if !external_mode.load(Ordering::SeqCst) {
if let Some(reason) = check_process_exit(&child).await {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recover from external hushd loss by respawning managed daemon

All restart logic is guarded by !external_mode, so after attaching to an already-running external hushd, if that external process later exits, the monitor only marks the state unhealthy and never attempts to spawn a managed child; this leaves the agent offline until manual intervention instead of self-healing.

Useful? React with 👍 / 👎.

Comment on lines 56 to 58
function asAppId(value: unknown): AppId | null {
if (typeof value !== "string") return null;
return KNOWN_APP_IDS.has(value as AppId) ? (value as AppId) : null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve legacy session app IDs when loading localStorage

Session deserialization now rejects any appId not in the new KNOWN_APP_IDS set, so existing persisted sessions from prior versions (for example cyber-nexus, settings, or forensics-river) are dropped during upgrade, causing avoidable user data loss unless those legacy IDs are mapped to current equivalents.

Useful? React with 👍 / 👎.

- events.rs: restore cursor-based pagination in poll fallback so events
  beyond the latest 50 are not silently dropped when SSE is unavailable
- daemon.rs: remove redundant --port/--bind CLI args that duplicate the
  runtime config file; add recovery path from external_mode when the
  external hushd becomes unhealthy (fall back to spawning a managed child)
- sessionStore.ts: map legacy persisted app IDs (cyber-nexus, settings,
  forensics, forensics-river, policy-workbench, strikecell) to current
  equivalents so existing sessions survive upgrades
- OpenClawAgentProvider.tsx: guard auto-connect effect with in-flight ref
  to prevent rapid-fire reconnection loops when connectGateway fails fast

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

history: sortedActions.slice(0, 200),
} satisfies AgentGlyphState;
});
}, [actions, focusedAgentId, sessionRows]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side effects inside useMemo corrupt controller state

Medium Severity

The useMemo callback mutates external refs (controllersRef, lastProcessedRunRef) and calls stateful methods like controller.handleEvent() and controller.tick(). React requires useMemo computations to be pure. In React strict mode (development), the component body is double-invoked, causing controllers to receive duplicate events and lastProcessedRunRef to be mutated twice per render cycle. The second invocation sees already-set ref values and takes different code branches, leaving controllers in an inconsistent emotional state. This logic belongs in useEffect or useRef-based manual memoization, not useMemo.

Fix in Cursor Fix in Web

.await
.with_context(|| "Failed to parse health response")?;

Ok(health)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

health_check method duplicates standalone health_check_with_client function

Low Severity

DaemonManager::health_check was changed from delegating to health_check_with_client to inlining identical logic. The free function health_check_with_client still exists and is used extensively in the health monitor, start, and spawn_and_wait_ready. This duplication means a future bug fix in one copy won't be reflected in the other. The previous one-liner delegation was cleaner and safer.

Additional Locations (1)

Fix in Cursor Fix in Web

const selectedRows = selectedKeys
.map((key) => rowByKey.get(key))
.filter((row): row is ProbeSessionRow => Boolean(row));
const sourceRows = selectedRows.length > 0 ? selectedRows : args.rows.slice(0, 8);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probe fallback uses top-8 but label says top-3

Low Severity

The session dropdown labels the "all" option as "ALL (top 3)", and the live polling path correctly uses sessionRows.slice(0, 3). However, deriveActionsFromProbeRows (the probe fallback path) uses args.rows.slice(0, 8) for "__all__", showing data from up to 8 sessions. Users see more sessions than the label promises when the probe fallback is active.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant