Added the --isolated flag to the Playwright MCP configuration to ensure
browser instances run in isolated contexts, preventing state leakage
between different agent sessions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, only standard tool_use blocks had their input parameters
captured in upsertToolUseEntry. MCP tools (mcp_tool_use) and server
tools (server_tool_use) were being handled for execution but their
inputs were never stored in the tool cache, resulting in missing input
data in the UI.
Now all three tool types have their inputs properly captured and
normalized, ensuring consistent tool call display across all tool types.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove ReadonlyPendingPermissionsMap class and related deep cloning/freezing
methods that were causing "ManagedAgent views returned by AgentManager are
read-only" errors. The bug occurred because Map constructor internally calls
.set() which was overridden to throw.
Replace with simple shallow clones - sufficient because all consumers either
read 1-2 fields or immediately serialize via toAgentPayload() which already
creates safe copies.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhance the get_agent_activity tool to provide better context about the activity list:
- Add activity count headers showing "X of Y activities"
- Remove raw format option, always use curated format for consistency
- Simplify implementation by removing unnecessary format branching
- Respect limit parameter to show most recent N activities
This makes it clearer to users when they're viewing a subset of activities versus the full list.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement hard-coded 60-second timeout for agent wait operations in three tools:
- create_agent (when background=false and initialPrompt provided)
- send_agent_prompt (when background=false)
- wait_for_agent (always)
When timeout occurs, returns gracefully with message "Awaiting the agent timed out, await again" instead of throwing an error. Agent continues running after timeout.
Added AGENT_WAIT_TIMEOUT_MS constant and waitForAgentWithTimeout helper function to handle timeout logic with proper cleanup and signal forwarding.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Move mode validation from AgentManager into the individual AgentSession
implementations (ClaudeAgentSession and CodexAgentSession) where it belongs.
Each session validates modes in:
- Constructor (when config.modeId is provided)
- setMode() method (when mode is changed)
This follows the principle that the AgentManager is a coordinator while
the AgentSession is the authority on what modes it supports.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Prevents server crashes when invalid modes are passed to agents by
validating mode IDs before attempting to set them on the SDK.
- Export VALID_CLAUDE_MODES and VALID_CODEX_MODES from provider files
- Add validateMode() function in agent-manager.ts
- Apply validation in createAgent(), resumeAgent(), and setAgentMode()
- Throws clear error listing valid modes when validation fails
This fixes the crash when 'read-only' (Codex mode) was passed to
a Claude agent.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two fire-and-forget promise patterns that could cause unhandled rejections:
- interruptActiveTurn() in requestCancel closure - now catches errors and logs warning
- forwardPromptEvents() in stream method - now catches errors and logs to console
Both now properly handle promise rejections to prevent Node.js process crashes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add top-level agentLastActivity Map to store (prevents cascade rerenders)
- Remove lastActivityAt updates from agent_stream handler (only status changes update timestamps)
- useAggregatedAgents now uses agent.lastActivityAt directly
- Remove investigation logging from previous debug session
The root cause was that every stream event updated agent.lastActivityAt,
causing useAggregatedAgents to recalculate, which caused HomeScreen to
rerender even when not visible. Now timestamps only update on meaningful
events (agent status changes), not individual stream tokens.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, drafts were nested inside each session object, which caused
the entire session to be recreated on every keystroke. This triggered
rerenders in all components subscribed to the session.
Now drafts are stored at the top level of the Zustand store, keyed by
agentId (not serverId:agentId since agent IDs are globally unique).
This means draft updates only trigger rerenders in components that
subscribe to drafts specifically.
Changes:
- Move drafts from SessionState to SessionStoreState (top-level)
- Add createModalDraft for future use by the create agent modal
- Update getDraftInput/saveDraftInput to work with agentId only
- Update session-context to clear drafts using new API
- Remove investigation console.log statements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend fixes:
- Fix server crash when logging large session_state messages (186 agents)
- Use safe logging that extracts only message/stack properties
- Prevents TypeError in util.inspect on circular refs or deep objects
Frontend loading state fixes:
- Add reactive WebSocket update to prevent stale session.ws references
- Add timeout/retry wrapper for session_state loading (3 retries × 15s)
- Add hasEverReceivedSessionState flag to track cached data across reconnects
- Implement data-aware loading (isInitialLoad vs isRevalidating)
- Show reconnecting badge instead of hiding agents on reconnect
Debug improvements:
- Add detailed connection state logging in useAggregatedAgents
- Add connection status logging in AgentInputArea
- Track connecting reasons for easier debugging
Fixes:
- Agent screen no longer shows false "Offline" toast on reconnect
- Home screen shows loading immediately (no empty state flash)
- Home screen keeps agents visible during reconnect with badge
- Stuck loading has timeout protection with graceful degradation
- Server no longer crashes when sending session_state with many agents
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Finalizes the pure Zustand refactor by ensuring all components properly access both session state and imperative methods through useDaemonSession. Moves CLAUDE.md to root and ignores local overrides.
Changes:
- Fix useDaemonSession to return stable combined state + methods object
- Update components to use useDaemonSession instead of direct context
- Fix realtime context to work with session state only
- Move CLAUDE.md to root, ignore CLAUDE.local.md for local config
- Add proper null checks and type safety throughout
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This function was never called after implementing lazy-loading.
Agents are now loaded on-demand via ensureAgentLoaded when the UI
needs them, not eagerly at startup.
Keeping the unused function was confusing - it suggested eager
restoration was still the intended pattern.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The previous agent claimed it removed the test skipping logic but
actually didn't - it hallucinated the fix. The file still had:
- const claudeIntegrationEnabled check
- describeClaudeIntegration conditional
- Warning message about skipping tests
This commit ACTUALLY removes all of it. Tests now run unconditionally.
All 14 tests pass ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The test was expecting the old 'files' array format, but after the refactor
to structured tool results (commit 4001b7d), Write tools now return:
{ type: 'file_write', filePath, oldContent, newContent }
Updated the test predicate to check for the new structure instead of the
old files array format.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add stripAnsiSequences function to remove ANSI escape codes from terminal
output when includeColors is false. This ensures test assertions can match
against clean text without terminal formatting codes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The test was failing because it was checking for tool data in a `raw` field
that no longer exists in the AgentToolCallData interface. The interface now
uses a `result` field instead of `raw`.
Updated all references from `snapshot.data.raw` to `snapshot.data.result` to
match the current AgentToolCallData structure defined in packages/app/src/types/stream.ts:
- Changed editTool search to use rawContainsText(snapshot.data.result, ...)
- Changed readTool search to use rawContainsText(snapshot.data.result, ...)
- Updated assertHydratedReplica callbacks to check data.result instead of data.raw
- Removed fallback checks for data.raw since that field doesn't exist
This appears to be a result of a refactoring where the `raw` field was renamed
to `result` in the AgentToolCallData interface, but the test wasn't updated to
match the new structure.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The test was failing because it only checked for file changes in a
structured `output.files` array, but the actual implementation returns
file changes in different formats depending on the tool type.
The test now checks for file changes in two ways:
1. Structured `output.files` array (original check)
2. Structured tool outputs with `type: "file_write"` or `type: "file_edit"`
which include a `filePath` field
This matches how the claude-agent implementation structures tool results
in the `buildStructuredToolResult` method (lines 1081-1162), which creates
different output structures for file write/edit tools that include the
filePath directly in the output object rather than in a files array.
The test now properly detects when Claude creates files using either:
- Legacy file change tracking via output.files array
- Modern structured outputs for write_file/edit_file tools
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add .agents.json.tmp-* pattern to gitignore and remove 82 temporary
agent files that were accidentally committed. These files are runtime
artifacts that should not be tracked in version control.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The test was failing because terminal output wrapping caused "No such file"
to be split across lines with a newline between "No" and "such file". The
actual output was:
"No\n such file or directory"
Changed the assertion to check for more reliable parts of the error message:
- "cannot access" - always present in ls errors
- "nonexistent-directory-test" - the directory name we're testing with
This is a test assertion fix, not a code bug. The terminal output is correct,
but the test expectation needed to account for line wrapping.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The 'raw' field was removed from timeline entries in commit e80989e
(Nov 28) to reduce payload sizes by 64-85%. This test was never updated
and has been broken since then. Remove the stale expectation to align
with the current implementation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove AgentSnapshot from persistence layer. AgentRegistry now accepts
ManagedAgent and uses toStoredAgentRecord for atomic config + lifecycle
persistence. Deleted obsolete recordConfig method.
- Update applySnapshot to accept ManagedAgent and use toStoredAgentRecord
- Remove recordConfig method entirely (no longer needed)
- Remove sanitizeConfig helper (handled by projection)
- Update session to stop calling deleted recordConfig
- Add ManagedAgent test fixtures to registry and persistence-hook tests
- Test config persistence, title retention, and subscription forwarding
Registry and persistence-hooks now typecheck cleanly. Expected failures
in session/mcp-server/messages (still reference AgentSnapshot).
Task 4 of 8 in agent architecture refactor.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace ManagedAgent with discriminated union keyed on lifecycle state
to make impossible states unrepresentable. Remove AgentSnapshot type
and toSnapshot method entirely - consumers now receive ManagedAgent
directly.
- Define ManagedAgent as discriminated union with 5 lifecycle states
- Add comprehensive immutability protection (deep clone + freeze)
- Update getAgent/listAgents to return immutable ManagedAgent views
- Remove AgentSnapshot type and toSnapshot method
- Update event emissions to use ManagedAgent
Breaking change: All consumers must now use ManagedAgent or projection
functions instead of AgentSnapshot.
Tasks 2 & 3 of 8 in agent architecture refactor.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Introduce toStoredAgentRecord and toAgentPayload as deterministic
pure functions to project ManagedAgent state to persistence and
client payload formats. This eliminates the need for AgentSnapshot
as an intermediate representation.
- Add toStoredAgentRecord for persistence projection
- Add toAgentPayload for client communication projection
- Add comprehensive test suite covering all lifecycle states
- Handle optionality at boundaries with proper null/undefined semantics
Task 1 of 8 in agent architecture refactor.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- recordConfig no longer writes lastStatus (handled by applySnapshot only)
- Add warning if recordConfig called before snapshot exists
- Update tests to reflect new separation of concerns
- Add AGENT_REFACTOR_PROPOSAL.md with comprehensive design plan
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
1. Lazy agent loading - fix race condition where prompts sent before initialization completes:
- Add ensureAgentLoaded() helper that deduplicates initialization requests
- Update handleSendAgentMessage/Audio to await agent initialization before streaming
- Fix status updates being sent to client after initialization
2. Type safety - remove unsafe 'as any' casts for agent status:
- Create AGENT_LIFECYCLE_STATUSES constant as single source of truth
- Export AgentStatusSchema from messages.ts for reuse
- Update registry schema to validate lastStatus against AgentStatusSchema
- Add .default("closed") to handle missing status values from legacy files
- Remove (record.lastStatus as any) cast in buildStoredAgentPayload
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove blocking restorePersistedAgents() call that was resuming all 87
agents before opening the HTTP socket. The architecture already supports
lazy loading through handleInitializeAgentRequest() - agents are shown
in the UI from the registry and only initialized when clients request them.
This eliminates:
- Sequential thread resumes with network handshakes to Codex/Claude
- Synchronous history file reads from disk
- DNS lookups and model catalog fetches
- 87 awaited operations before socket binding
Server now starts immediately by just loading the lightweight agents.json
registry. Agents resume on-demand when requested by clients.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously shared one McpServer instance across all sessions/transports,
which caused Protocol._transport to be overwritten when new sessions
connected. This broke message routing for requests on previous transports
after long-running SSE streams.
Now creates a new McpServer instance per session, following the stateful
session pattern from the MCP SDK documentation. Each session gets its own
server+transport pair, preventing transport reference conflicts.
Fixes issue where get_agent_status would fail after wait_for_agent completed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This fixes two root causes of MCP tool calls hanging indefinitely:
1. Transform ensureValidJson from validator to transformer
- Previously threw errors on undefined values, causing handlers to crash
- Now converts undefined→null, Date→ISO string, bigint→string, etc.
- Ensures all MCP handlers always return valid JSON responses
2. Cancel waitTracker when interrupting agents
- Added waitTracker.cancel() in cancel_agent handler
- Added waitTracker.cancel() in kill_agent handler
- Resolves waiting wait_for_agent promises when agents are interrupted
- Prevents indefinite hangs requiring server restart
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add structured output matching StructuredToolResult type for Codex
apply_patch operations when parsing rollout files. This enables the
frontend to render file edit diffs for restored Codex sessions, matching
the behavior of Claude agent file edits.
Note: For live streaming, the Codex SDK only provides file_change events
with {path, kind} without actual patch content. The structured diffs are
only available for restored sessions via rollout file parsing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Codex SDK uses aggregated_output and exit_code fields for command
execution results (not output). Build structured output matching
StructuredToolResult type so frontend renders Codex commands the
same way as Claude commands.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove all heuristic parsing code from message.tsx (hasCommandDetails,
commandSection, editSections, readSections, hasStructuredContent, etc.)
- Replace with simple raw JSON fallback for tools without structured results
- Fix server-side support for Claude SDK's old_string/new_string params
(in addition to old_str/new_str)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Server-side:
- Add StructuredToolResult discriminated union type with command, file_write,
file_edit, file_read, and generic variants
- Implement buildStructuredToolResult in claude-agent.ts to detect tool types
and emit properly structured results
- Update codex-agent.ts to emit structured command results
Client-side:
- Add type guard and extraction functions for structured results
- Render tool calls based on result.type when available:
- command: show command, output, exit code
- file_write/file_edit: show diff viewer with proper +/- format
- file_read: show file content
- generic: show raw JSON
- Fall back to heuristic parsing for backwards compatibility
This fixes file writes showing as "Command: success message" - they now
properly show as diffs with +line additions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>