Problem: Agent directory was still being synced from SessionProvider to
Zustand store, violating the "SessionProvider is just a message handler"
principle and creating unnecessary state duplication.
Solution: Make agent directory pure derived state:
- Removed agentDirectory from SessionStoreState
- Removed setAgentDirectory/clearAgentDirectory actions
- Changed getAgentDirectory to compute on-demand from session.agents
- Removed buildAgentDirectoryEntries and syncing from SessionProvider
- Updated useAggregatedAgents to derive from sessions directly
How it works now:
- Agent directory is computed on-demand from session.agents Map
- lastActivityAt automatically updates when agents update via WebSocket
- No syncing, no stale state, no overhead
- Single source of truth: session.agents
Benefits:
- Eliminated redundant state (agentDirectory was duplicate of agents)
- No sync overhead or complexity
- Always fresh data (derived on read)
- Simpler mental model (agents is the only source)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem: SessionContext used useSyncedSessionState which synced every state
update from React useState to Zustand, creating double overhead and complexity.
Solution: Complete migration to pure Zustand architecture:
- session-store.ts is now the single source of truth
- Moved ALL state directly into Zustand (no React useState)
- Added per-session state: messages, agents, commands, permissions, etc.
- Added Zustand actions for all state updates (setMessages, setAgents, etc.)
- Exported all types (MessageEntry, Agent, Command, etc.)
- session-context.tsx is now a pure WebSocket message handler
- Removed useSyncedSessionState hook entirely
- Removed all useState calls for session state
- Removed sync machinery (syncSessionField, syncSessionPartial)
- WebSocket handlers now call Zustand setters directly
- Context only provides imperative APIs (ws, audioPlayer, actions)
- No state values in context
Architecture change:
Before: WebSocket → React useState → sync → Zustand (double overhead)
After: WebSocket → Zustand directly (single source of truth)
Benefits:
- Eliminated double overhead from React → Zustand syncing
- Single state update per WebSocket message
- Better re-render optimization via Zustand selectors
- Cleaner separation: SessionProvider = message handler, SessionStore = state
- Reduced complexity: removed 400+ lines of hybrid sync code
Backward compatibility:
- useDaemonSession() already reads from Zustand, no changes needed
- Type exports re-exported from session-store for compatibility
- Components work exactly the same
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit implements a critical performance optimization by removing
unused orchestrator logic that was causing thousands of unnecessary
store updates per minute:
1. Removed orchestratorFocusedAgentId state and auto-selection logic
- The orchestrator auto-selected "most recently active agent"
- Nothing in the codebase actually reads focusedAgentId
- Simplified focusedAgentId to just focusedAgentOverride (user selection only)
2. Stopped updating timestamps on every stream event
- Previously updated lastActivityAt/updatedAt on every agent_stream event
- These events occur 15+ times per second during streaming
- Created 2700+ store updates per minute from a single agent
3. Removed focusedAgentId from store sync
- No longer syncs focusedAgentId changes to the store
- Reduces cascading updates throughout the app
Expected impact:
- 95%+ reduction in store updates during agent streaming
- Home screen no longer re-renders during agent activity
- Improved UI responsiveness and reduced battery usage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove the `raw` field that was duplicating provider data in timeline
items, reducing WebSocket payload sizes by 64-85%:
- session_state: ~320KB → ~48KB
- agent_stream_snapshot: ~320KB → ~114KB
Changes:
- Remove raw from AgentTimelineItem, AgentStreamEvent, AgentPermissionRequest
- Remove raw assignments from claude-agent.ts and codex-agent.ts
- Remove provider_event handling from stream.ts (only used for Codex raw)
- Update tests to reflect new behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Skip dispatching individual agent_stream events when replaying history
(the snapshot is sent after priming anyway, so individual events were wasted)
- Add WebSocket message logging on client (type, size, id) for debugging
- Change default daemon URL from dev to localhost
- Clean up unused imports in session.ts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace subscription-based session accessor pattern with direct snapshot updates in daemon connections context. Add sessionReady flag to connection state to track when initial agent list has been received. Auto-select first ready host in create agent modal. Migrate from app.json to app.config.js. Add android:release script and Playwright testing docs.
Introduce DaemonRegistry and DaemonConnections contexts for managing multiple server connections. Add session directory for aggregating agents across daemons, with automatic routing based on agent/server context.
Key changes:
- Add daemon profile management (add/edit/remove/set default)
- Implement connection state tracking with reconnection and exponential backoff
- Create useAggregatedAgents hook for unified agent list across daemons
- Add useDaemonSession and useDaemonRequest hooks for daemon-scoped operations
- Update agent routes to include serverId for proper session routing
- Add connection health indicators on home screen and settings
- Persist session snapshots per daemon for faster hydration on reconnect
- Guard screens gracefully when target daemon is offline
- Add React Query for consistent loading/error state management
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add cancel button during agent runs, real-time mode toggle in all states, and agent deletion via long-press action sheet. Server now handles cancel_agent_request and delete_agent_request messages, cleaning up agent state and notifying clients.
Replace separate ACP and agent-types modules with unified AgentManager using provider pattern. Migrate Claude and Codex implementations to provider-specific clients. Streamline session-agent integration, remove old ACP notification types, and introduce agent timeline events. Delete obsolete documentation and test files. Add AgentRegistry for persistence and agent-stream-based communication pattern.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add git diff viewer screen accessible via agent dropdown menu
- Implement git_diff_request/response WebSocket messages
- Add dropdown menu to agent screen header with "View Changes" option
- Clear pending permissions when starting new agent turn to prevent conflicts
- Treat agent refusals as completed rather than failed state
- Add test coverage for permission handling edge cases
- Configure vitest to run in single-threaded mode for stability
- Remove redundant wrapper in agent stream view
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>