Extract auto-approval decision resolution and common RPC handler registration into BasePermissionHandler to eliminate duplication across Claude, Codex, and Gemini implementations. Make handlePermissionResponse async throughout the hierarchy to properly support awaitable permission completions.
- Replace .passthrough() with explicit field definitions across all schemas
- Add missing optional fields (homeDir, happyHomeDir, happyLibDir, displayName)
- Refactor RawJSONLinesSchema to use structured base schema for clarity
- Improve schema validation strictness and type safety
- Update Machine interface to reflect explicit fields instead of index signature
Move duplicate isObject, asString, asNumber, and safeStringify functions from multiple modules into a centralized shared/src/utils.ts module and update imports across cli, server, and web packages. This eliminates code duplication and improves maintainability.
CODEX_SESSION_SCANNER was incorrectly auto-resuming old sessions because
session files persist after Codex exits. Fixed by:
1. Only matching sessions created AFTER hapi startup (reject if
sessionTimestamp < referenceTimestampMs)
2. Snapshot resumeSessionId at startup so scanner callbacks don't
affect codexLocal's launch parameters
3. Updated session timestamp test to verify boundary conditions
Codex elicitation requests from MCP use the 'message' field (from
ElicitRequest schema), not 'command' or 'cwd'. Update the permission
handler to extract and pass this field, and display it as the card
subtitle with fallback to command.
Adds onRequest and onComplete callbacks to CodexPermissionHandler to track
permission request/response lifecycle. Includes helper functions to normalize
command and cwd inputs, and displays permission requests in the web UI.
Windows global installs expose codex as codex.cmd; spawning 'codex' without shell hits ENOENT.
Enable shell to align with Claude's Windows behavior and resolve PATH lookup.
Shell spawn makes cmd.exe strip TOML double quotes in -c arrays, so args become a string.
Switch mcp args to TOML literal (single-quoted) arrays to preserve types; update tests.
Extract duplicate MCP bridge initialization code from codexLocalLauncher and
codexRemoteLauncher into a new buildHapiMcpBridge utility. This ensures both
modes use identical server setup logic and provides a single source of truth
for MCP configuration.
Update codexStartConfig to deliver system prompt via developer_instructions
instead of appending to the user message. This ensures consistent behavior
between local and remote modes and properly separates system guidance from
user intent.
Implements MCP server configuration and developer instructions support in
Codex local mode, bringing it to parity with remote mode.
Key additions:
- buildMcpServerConfigArgs() and buildDeveloperInstructionsArg() utilities
to construct -c config arguments for passing MCP servers and instructions
to the Codex CLI at runtime
- TITLE_INSTRUCTION for Codex to call hapi__change_title to update chat
session titles dynamically
- Codex local mode now starts hapi MCP bridge server and passes both MCP
configuration and developer instructions to Claude, enabling full feature
parity with remote mode
Files changed:
- New: codexMcpConfig.ts (utilities), systemPrompt.ts (prompt definition),
codexMcpConfig.test.ts (comprehensive tests)
- Updated: codexLocal.ts (accepts mcpServers, builds config args),
codexLocalLauncher.ts (starts hapi server, passes config),
codexStartConfig.ts (imports TITLE_INSTRUCTION)
Create BaseSessionScanner abstract class to consolidate common session
scanning logic shared between Claude and Codex session scanners. Both
scanners now inherit from this base, reducing duplication and providing
consistent patterns for file watching, event processing, and state
management.
Extract permission and model mode validation into reusable functions utilizing
@hapi/protocol schemas. Use isPermissionModeAllowedForFlavor and
isModelModeAllowedForFlavor to validate modes based on session flavor (claude/codex).
Replace inline type definitions with shared types from @hapi/protocol.
Consolidates duplicate cleanup, signal handling, and state management logic from
runClaude and runCodex into a reusable createRunnerLifecycle factory function.
Extracts mode switching handler and controlled user state updates into utilities.
Adds runLocalRemoteSession wrapper to handle session ready callbacks in loop base.
Implements bidirectional sync of permission/model modes between CLI sessions and web app. Adds Codex-specific permission modes (read-only, safe-yolo, yolo) alongside Claude's modes. Web can now control CLI session state via RPC set-session-config handler, while CLI broadcasts state changes through keep-alive payloads. UI controls are flavor-aware, showing appropriate modes for Claude vs Codex vs Gemini. Type centralization in api/types eliminates circular dependencies.
Implement comprehensive worktree session support allowing users to spawn sessions in temporary git worktrees. Includes backend worktree management, full-stack integration, and refined UI for session type selection.
Backend:
- Add worktree creation/removal utilities with branch management
- Track worktree metadata (basePath, branch, name, path) in session metadata
- Automatic cleanup of worktrees when sessions fail or exit
- Enhanced error handling with stderr tail logging
UI improvements:
- Redesign session type toggle with improved alignment and spacing
- Move worktree description inline with label for cleaner layout
- Add branch name input field that appears when worktree mode selected
- Auto-focus on worktree input when switching modes
- Reduce gap between radio options from gap-3 to gap-1.5
- Update descriptive text and placeholders for clarity
Integration:
- Thread worktree parameters through API client, RPC handlers, and daemon
- Add worktreeEnv utility to read worktree info from environment
- Update session spawning to support both simple and worktree modes
Consolidate process lifecycle management (kill, check alive) into a new
utility module with proper Windows/Unix handling, replacing scattered
process.kill() calls with consistent async APIs.