-
Notifications
You must be signed in to change notification settings - Fork 458
feat: New Session Page w/ one touch Profiles, adds z.ai / deepseek / microsoft AI Backend Support #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ahundt
wants to merge
161
commits into
slopus:main
Choose a base branch
from
ahundt:feature/new-session-wizard-ux-improvements
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
feat: New Session Page w/ one touch Profiles, adds z.ai / deepseek / microsoft AI Backend Support #272
ahundt
wants to merge
161
commits into
slopus:main
from
ahundt:feature/new-session-wizard-ux-improvements
Conversation
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
- Fix YOLO mode not persisting across app restarts and agent type changes - Add comprehensive profile management system for environment variables - Support ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_MODEL, and TMUX_SESSION_NAME - Add profile selection UI in settings and session creation flows - Extend session spawning to accept environment variables from profiles - Add translations for profiles feature in English, Spanish, Russian, and Polish - Include proper mode mapping between Claude and Codex permission systems Resolves: - GitHub issue slopus#206: YOLO mode persistence bug - GitHub issue slopus#204: Profile management and environment variable support
…riables Based on tmux best practices research, add comprehensive tmux environment variable support: - Add TMUX_TMPDIR support for custom socket directories - Add tmuxUpdateEnvironment toggle for automatic environment updates - Enhance profile schema with additional tmux-specific options - Update session spawning to properly pass TMUX_TMPDIR environment variable - Add comprehensive UI for configuring tmux options in profile management - Include translations for new tmux options in all supported languages - Improve profile display to show tmux directory configuration Technical improvements: - Follow tmux native environment variable patterns - Support both custom session names and socket directories - Enable automatic environment variable updates per session - Maintain backward compatibility with existing profiles This provides users with full control over tmux session configuration as requested in GitHub issue slopus#204.
…agement - Fix TypeScript configuration with proper expo/tsconfig.base.json reference - Resolve Modal.alert TypeScript conflicts by using Alert directly - Update settings parsing to preserve unknown fields while maintaining validation - Fix all settings tests to accommodate new profile management fields - Add comprehensive type interfaces for Modal class to prevent conflicts - Ensure complete TypeScript compliance with zero errors Previous behavior: TypeScript errors prevented compilation due to Modal type conflicts What changed: Fixed type definitions, updated configuration, resolved test expectations Why: To ensure production-ready code with full type safety and no regressions Files affected: - sources/app/(app)/settings/profiles.tsx: Fixed Modal.alert usage - sources/modal/*: Added explicit type interfaces - sources/sync/settings.ts: Enhanced parsing logic - sources/sync/settings.spec.ts: Updated test expectations - sources/text/translations/*: Added missing profile keys - tsconfig.json: Fixed base configuration Testable: All TypeScript checks pass, settings tests pass, profile management complete
- Remove TODO comments from profiles.tsx (replaced with proper validation) - Add comprehensive comments for tmux environment variables in ops.ts - Add detailed architecture documentation for translation file structure - Implement profile validation (empty names and duplicate name prevention) - Improve inline documentation for maintainability Previous behavior: TODO comments and incomplete validation What changed: Added proper validation, comprehensive documentation, cleaner code Why: To meet maintainer standards and improve long-term maintainability Files affected: - sources/app/(app)/settings/profiles.tsx: Added validation and removed TODOs - sources/sync/ops.ts: Added detailed environment variable documentation - sources/text/translations/en.ts: Added architecture documentation Testable: Profile validation works, no TypeScript errors, functionality preserved
- Add useProfileMap hook for O(1) profile lookup using Map instead of array.find() - Create transformProfileToEnvironmentVars helper to eliminate repetitive code - Replace linear profile search with optimized Map-based lookup in session creation - Improve performance for users with many profiles while maintaining functionality Previous behavior: O(n) profile lookup with repetitive environment variable mapping What changed: O(1) profile lookup with clean transformation helper Why: Performance optimization for scalability and code maintainability Files affected: - sources/app/(app)/new/index.tsx: Added optimized lookup and transformation utilities Testable: All settings tests pass, TypeScript compliance verified, no regressions
- Add built-in profiles (Anthropic, DeepSeek, Z.AI) with pre-configured settings - Implement profile selector in AgentInput settings overlay - Add comprehensive profile management in settings screen - Support custom environment variables beyond predefined fields - Enable built-in profile editing with configurable names - Add O(1) profile lookup optimization using Map - Integrate profile environment variables into session creation - Support arbitrary custom environment variables with add/remove UI Previous behavior: No profile system existed, users had to manually configure environment variables each session What changed: Complete profile management system with built-in configurations and custom profile support Why: Streamlines user workflow by saving common configurations and reducing repetitive setup Files affected: - sources/app/(app)/new/index.tsx: Add profile interface, built-in profiles, and session creation integration - sources/components/AgentInput.tsx: Integrate profile selector into settings overlay with optimized lookup - sources/app/(app)/settings/profiles.tsx: Complete profile management UI with custom environment variables Note: WIP implementation - needs CLI synchronization and persistence validation
…riendly tmux integration Summary: Add comprehensive AI backend profile system with agent compatibility validation, environment variable management, and seamless tmux session integration across happy app and happy-cli. Previous behavior: - Flat profile configuration with basic Anthropic settings only - Manual environment variable filtering in daemon - Limited TypeScript support in tmux utilities - No profile versioning or compatibility validation What changed: - sources/sync/settings.ts: Replaced simple schema with comprehensive Zod-validated AIBackendProfileSchema supporting multiple AI providers (Anthropic, OpenAI, Azure, Together AI) with nested configurations, compatibility flags, and versioning - sources/app/(app)/new/index.tsx: Updated built-in profiles to use new schema structure, added agent compatibility validation with user-friendly warnings, integrated profile-based environment variable filtering - sources/app/(app)/settings/profiles.tsx: Migrated profile management UI to use new schema, updated form handling for nested configuration objects - sources/components/AgentInput.tsx: Removed legacy profile reference in favor of unified schema Why: - Enable support for multiple AI providers beyond just Anthropic - Provide type-safe profile management across both happy app and happy-cli - Add automatic environment variable filtering based on agent compatibility - Implement proper versioning for future profile schema migrations - Improve tmux integration with strong typing and validation Files affected: - sources/sync/settings.ts: Core profile schema and helper functions - sources/app/(app)/new/index.tsx: New session creation with profile selection - sources/app/(app)/settings/profiles.tsx: Profile management interface - sources/components/AgentInput.tsx: Removed legacy profile reference Testable: - Create new sessions with different AI provider profiles (anthropic, deepseek, zai, openai, azure-openai, together) - Verify profile compatibility warnings appear when using incompatible profiles - Confirm environment variables are properly filtered by agent type - Test profile management in settings with new nested configuration structure
…nt fallbacks Summary: Replace reactive error handling with proactive profile filtering that prevents incompatible selections by showing only compatible profiles for the current agent type and automatically selecting compatible defaults when switching agents. Previous behavior: - Users could select incompatible profiles for their chosen agent type - Agent switching showed warning dialogs after making bad selections - Reactive error handling with automatic profile switching as fallbacks - Profile picker showed all profiles regardless of agent compatibility What changed: - sources/app/(app)/new/index.tsx: Added compatibleProfiles useMemo that filters profiles by agent type using validateProfileForAgent, added isCurrentProfileCompatible validation for UI feedback, enhanced handleAgentClick to proactively select compatible built-in profiles when current profile is incompatible, added compatibleProfiles and isCurrentProfileCompatible props to AgentInput component - sources/components/AgentInput.tsx: Extended AgentInputProps interface with compatibleProfiles and isCurrentProfileCompatible fields for UI filtering support Why: Prevents user errors by design rather than handling them after the fact. The previous approach relied on fallbacks and warnings, indicating poor UX design. The new approach prevents incompatible selections at the UI level, eliminating the need for complex error recovery logic. Files affected: - sources/app/(app)/new/index.tsx: Profile filtering logic and agent switching improvements - sources/components/AgentInput.tsx: Props extension for compatibility filtering support Testable: - Switch between Claude and Codex agents - incompatible profiles should be filtered from picker - Select "OpenAI (GPT-5)" profile with Claude agent - should automatically switch to "Anthropic (Default)" - All profiles in picker should be compatible with current agent type - No warning dialogs should appear when switching agents (prevention vs. recovery)
… system Replaces the AgentInput-based new session interface with a multi-step wizard that guides users through session creation with proper profile management and AI backend selection. Previous behavior: Single input interface with minimal configuration options and reactive error handling with fallbacks. What changed: - Implemented multi-step wizard (welcome → ai-backend → tmux-config → session-details → creating) - Added built-in AI profiles for Anthropic, DeepSeek, Z.AI, OpenAI, Azure OpenAI, and Together AI - Added profile creation workflow with custom profile support - Integrated AI backend compatibility validation (Claude vs Codex) - Added profile filtering to prevent incompatible selections - Preserved yolo and permission mode persistence from original implementation - Enhanced environment variable handling with agent-specific filtering Why: Provides a guided, user-friendly session creation experience while maintaining the robust profile system and yolo persistence fixes. The wizard prevents configuration errors through proactive filtering rather than reactive fallbacks. Files affected: - sources/app/(app)/new/index.tsx: Complete wizard implementation replacing AgentInput interface - sources/sync/settings.ts: Added profile schema, validation, and environment variable helpers Testable: Navigate to /new and verify wizard guides through profile selection, AI backend choice, and session creation with proper error handling and settings persistence.
Adds API schemas for profile synchronization across clients and fixes RevenueCat type exports to properly separate runtime values from types. Changes: - ApiUpdateProfileSchema: Profile update events for cross-client sync - ApiDeleteProfileSchema: Profile deletion events - ApiActiveProfileSchema: Active profile change events - Fixed RevenueCat exports to separate types from runtime enums Files affected: - sources/sync/apiTypes.ts: Added profile synchronization schemas - sources/sync/revenueCat/index.ts: Fixed type vs value exports
Previous behavior: Wizard only handled basic session configuration without profile support. Users could not configure AI backend providers or API keys through the interface. What changed: - sources/components/NewSessionWizard.tsx: Added complete profile-centric wizard flow with 6 built-in profiles (Anthropic, DeepSeek, OpenAI, Azure OpenAI, Z.ai, Microsoft Azure) - sources/sync/settings.ts: Added comprehensive profile schema with validation, environment variable mapping, and compatibility checking - sources/app/(app)/new/index.tsx: Updated session creation to accept profileId and environmentVariables from wizard - sources/sync/ops.ts: Added environmentVariables to SpawnSessionOptions interface and RPC calls - sources/text/_default.ts, sources/text/translations/en.ts: Added complete translation support for profile management Why: Enable users to configure AI backend providers through a user-friendly wizard interface while maintaining seamless CLI integration and proper environment variable management. Files affected: - sources/components/NewSessionWizard.tsx: Complete profile wizard with conditional configuration steps - sources/sync/settings.ts: Profile schema, validation, and environment variable mapping functions - sources/app/(app)/new/index.tsx: Session creation integration with profile data - sources/sync/ops.ts: RPC interface updates for environment variable support - sources/text/_default.ts: Profile management translations - sources/text/translations/en.ts: Dedicated English translation file Testable: Wizard profile selection → API key configuration → session creation with proper environment variable flow
…ization WIP - Added complete profile CRUD operations with bidirectional sync between GUI and CLI Major changes: - Enhanced NewSessionWizard with dual-action profile cards (Use As-Is, Edit) - Implemented profile management operations (Create, Duplicate, Delete) - Added ProfileSyncService for bidirectional GUI-CLI synchronization - Profiles sync to ~/.happy/settings.json with proper conflict resolution - Added manual configuration option with CLI environment variable support - Enhanced profile compatibility checking for claude/codex agents - Added progressive disclosure for management actions (custom profiles only) Profile Management Features: - Profile creation with auto-generated UUIDs - Profile duplication with customizable naming - Profile deletion with confirmation dialogs - Built-in profile protection (delete/edit restrictions) - Active profile synchronization between GUI and CLI GUI-CLI Integration: - Direct file-based sync to CLI settings file - Graceful fallback when CLI unavailable - Profile schema consistency across systems - Environment variable precedence handling Next: Implement actual CLI RPC integration and test end-to-end workflow
SECURITY CRITICAL FIX - Removed all direct file access operations that bypassed Happy's encryption system Major security fixes: - Removed direct file access to ~/.happy/settings.json (SECURITY VIOLATION) - Replaced file operations with proper Happy settings infrastructure usage - Fixed storage import to use official Happy storage system - Added security warnings to prevent future direct file access - Maintained all ProfileSyncService API compatibility Fixed Methods: - syncGuiToCli(): Now uses Happy settings instead of direct file writes - syncCliToGui(): Now reads from Happy settings instead of direct file reads - setActiveProfile(): Now uses sync.applySettings() instead of file manipulation - getActiveProfile(): Now uses storage.getState() instead of file access Security Impact: - ELIMINATED risk of corrupting CLI settings during daemon operations - RESTORED proper encryption channel usage for all profile data - FIXED potential race conditions with CLI daemon file access - ENSURED all profile operations use Happy's security infrastructure Technical Details: - Profiles continue to work through existing Happy settings sync system - CLI daemon accesses profiles through established channels - No breaking changes to ProfileSyncService API - All functionality preserved while eliminating security risks This addresses a critical security vulnerability where profile management was bypassing Happy's established encryption and sync infrastructure.
BREAKING FIX: Profile ID Generation - Replace Date.now() timestamps with proper UUIDs using expo-crypto - Fixes schema validation errors that would crash on profile creation - Affected locations: * sources/app/(app)/new/index.tsx:628 * sources/app/(app)/settings/profiles.tsx:113 * sources/app/(app)/settings/profiles.tsx:180 UX FIX: Remove Empty Tmux Wizard Step - Remove placeholder tmux-config step from wizard flow - Simplify navigation: welcome → ai-backend → session-details - Users configure tmux in profile settings instead - Update step numbering accordingly Files changed: - sources/app/(app)/new/index.tsx - sources/app/(app)/settings/profiles.tsx Related to profile management and yolo mode persistence feature. Addresses critical bugs identified in cross-repository validation. Co-authored-by: Claude (Anthropic AI Assistant)
SETTINGS SYNC: - Add schema version detection (SUPPORTED_SCHEMA_VERSION = 2) - Implement update-account settings handler in sync.ts - Add version mismatch warnings for user awareness - Settings now sync in real-time across GUI instances CONFIRMATION DIALOGS: - Add confirmation dialog before profile deletion - Prevents accidental data loss from quick taps TRANSLATIONS: - Add profiles.delete translations for 7 languages - Support: English, Spanish, Catalan, Polish, Portuguese, Russian, Chinese BACKWARDS COMPATIBILITY: - Old clients (v1) ignore new profile fields gracefully - New clients handle old settings with defaults - Zero breaking changes to existing functionality - Graceful degradation for all scenarios FILES MODIFIED: - sources/sync/settings.ts (schema version constant and defaults) - sources/sync/sync.ts (update-account handler with decryption) - sources/app/(app)/settings/profiles.tsx (Alert confirmation dialog) - sources/text/translations/*.ts (7 language files) TECHNICAL DETAILS: - Schema version v1 → v2 migration automatic - Settings decrypted, parsed, and validated on sync - Console warnings for schema version mismatches - Error handling prevents crashes on sync failures Tested scenarios: - ✅ Settings sync across multiple GUI instances - ✅ Confirmation dialog before profile deletion - ✅ Schema version mismatch warnings - ✅ Backwards compatibility with v1 settings Related to yolo-mode-persistence and profile management feature Builds on previous UUID and wizard fixes (commit 6807055)
…ith cross-env Previous behavior: - Single build configuration determined by APP_ENV environment variable - Manual environment variable setting required before running builds - No npm scripts for variant-specific builds - Developers had to remember to set APP_ENV=development/preview/production What changed: - package.json: Added 9 new variant-specific build scripts - ios:dev/preview/production for iOS builds - android:dev/preview/production for Android builds - start:dev/preview/production for development server - Added cross-env dependency for cross-platform environment variable support - USAGE.md (new): Complete 250-line guide covering all three variants Why: - Enables easy switching between development, preview, and production builds - All three variants can be installed simultaneously (different bundle IDs) - Cross-platform support via cross-env (Windows/macOS/Linux) - Discoverable commands in package.json reduce errors - Aligns with CLI variant system for consistent development workflow - Prevents accidentally building wrong variant Files affected: - package.json: Added 9 variant build scripts, added cross-env@^10.1.0 devDependency - yarn.lock: Updated with cross-env and @epic-web/invariant dependencies - USAGE.md (new): Comprehensive guide with workflows, troubleshooting, and examples Testable: - npm run ios:dev builds "Happy (dev)" with bundle ID com.slopus.happy.dev - npm run ios:preview builds "Happy (preview)" with bundle ID com.slopus.happy.preview - npm run ios:production builds "Happy" with bundle ID com.ex3ndr.happy - All three apps can be installed on same device simultaneously - Each variant connects to different CLI daemon instances
… documentation Previous behavior: - No macOS desktop app variant build system - Single tauri.conf.json for all builds - Development documentation mixed in user-facing files - No CONTRIBUTING.md file following GitHub conventions What changed: - src-tauri/tauri.dev.conf.json (new): Partial config overriding productName to "Happy (dev)" and identifier to com.slopus.happy.dev - src-tauri/tauri.preview.conf.json (new): Partial config overriding productName to "Happy (preview)" and identifier to com.slopus.happy.preview - src-tauri/tauri.conf.json (unchanged): Remains production base config - package.json: Added 4 Tauri variant build scripts using official --config flag - tauri:dev uses --config src-tauri/tauri.dev.conf.json - tauri:build:dev/preview/production for variant builds - USAGE.md → CONTRIBUTING.md: Renamed and expanded with Tauri documentation - CONTRIBUTING.md: Added macOS Tauri variant section explaining JSON Merge Patch approach - CONTRIBUTING.md: Added preview vs dev vs production use case explanations - README.md: Added CONTRIBUTING.md link in Documentation section Why: - Enables installing all three macOS desktop app variants simultaneously in /Applications - Uses official Tauri v2 --config flag and JSON Merge Patch (RFC 7396) - Partial configs follow DRY principle - only specify differences from base - Aligns macOS variant system with existing iOS/Android mobile variants - Follows GitHub convention with CONTRIBUTING.md for developer documentation - No breaking changes - existing tauri dev and tauri build commands still work - Backward compatible - base tauri.conf.json remains unchanged Files affected: - src-tauri/tauri.dev.conf.json (new, 12 lines): Dev variant partial config - src-tauri/tauri.preview.conf.json (new, 12 lines): Preview variant partial config - package.json: Added 4 Tauri build scripts with --config flag - USAGE.md → CONTRIBUTING.md: Renamed with 41 additional lines for macOS/Tauri - README.md: Added CONTRIBUTING.md link (1 line) Testable: - tauri dev (direct command) still uses base tauri.conf.json with com.slopus.happy - npm run tauri:dev launches "Happy (dev)" with identifier com.slopus.happy.dev - npm run tauri:build:preview builds "Happy (preview)" with com.slopus.happy.preview - npm run tauri:build:production builds "Happy" with com.slopus.happy - All three .app bundles can be installed in /Applications simultaneously - Existing developer workflows using direct tauri commands remain unchanged
Previous behavior: - apiTypes.ts defined ApiUpdateProfileSchema, ApiDeleteProfileSchema, ApiActiveProfileSchema (lines 151-169) - These schemas added to ApiUpdateSchema discriminated union (lines 184-186) - Exported types ApiUpdateProfile, ApiDeleteProfile, ApiActiveProfile (lines 192-194) - Zero usage across entire codebase - profiles sync via Account.settings blob instead - Dead code causing potential confusion about profile sync architecture What changed: - sources/sync/apiTypes.ts: Removed unused profile-specific update schemas - Removed ApiUpdateProfileSchema (lines 151-158) - Removed ApiDeleteProfileSchema (lines 160-163) - Removed ApiActiveProfileSchema (lines 165-169) - Removed from ApiUpdateSchema union (lines 184-186) - Removed type exports (lines 192-194) - Net change: -26 lines of dead code Why: - Profiles actually sync via Account.settings encrypted blob using ApiUpdateAccountSchema - Per-profile update events were designed but never implemented - Settings-based sync is simpler and already working - Removing dead code prevents future confusion about sync architecture - Reduces bundle size minimally Files affected: - sources/sync/apiTypes.ts: Removed 26 lines of unused profile schemas (lines 151-169, 184-186, 192-194) Testable: - yarn typecheck passes (no TypeScript errors) - No references to ApiUpdateProfileSchema anywhere in sources/ - Profile creation and sync still works via Account.settings - No case 'update-profile' handlers exist in sync.ts
Previous behavior: - sources/app/(app)/_layout.tsx line 319: presentation: 'modal' - New session wizard appeared as slide-up modal overlay from bottom - Wizard did not appear in main content area where sessions display - Inconsistent with user expectation of inline workflow What changed: - sources/app/(app)/_layout.tsx: Removed presentation: 'modal' from new/index screen (line 319) - Wizard now uses default stack navigation (card presentation) - Appears in same main panel as session message interface Why: - User expects wizard in main content area, not as overlay - Consistent with session display pattern - Standard stack navigation provides better UX for multi-step wizards - Matches pattern of other screens in app (settings, session details, etc.) Files affected: - sources/app/(app)/_layout.tsx: Removed presentation: 'modal' from new/index screen Testable: - Tap "New Session" button → wizard appears in main content area (not slide-up overlay) - Wizard navigation feels consistent with rest of app - Back button navigates correctly within wizard and to previous screen
Integrate wizard infrastructure by Denys Vitali while preserving complete inline wizard implementation from fix branch. **Primary Contributors:** - Denys Vitali: Initial wizard implementation (commit 36ad094) - Andrew Hundt: Profile management and CLI synchronization **Merged from Feature Branch:** - sources/components/NewSessionWizard.tsx: Reference wizard component - sources/sync/profileSync.ts: Profile CLI sync service - sources/components/AgentInput.tsx: Cleaner version without duplicate profile code **Preserved from Fix Branch:** - sources/app/(app)/new/index.tsx: Complete inline wizard (1048 lines) - sources/app/(app)/new/pick/path.tsx: Path selection UI with recent paths - sources/app/(app)/new/pick/machine.tsx: Machine selection UI - sources/sync/ops.ts: Detailed environment variable type definitions - sources/sync/settings.ts: Complete profile schema - sources/text/translations/en.ts: All translations **Conflict Resolution:** - new/index.tsx: Kept fix/yolo complete inline implementation - AgentInput.tsx: Used feature/yolo cleaner version - ops.ts, settings.ts, en.ts: Kept fix/yolo complete implementations - path.tsx: Restored (was mistakenly deleted by feature branch) **Next Step:** Convert multi-step wizard to single-page panel design per user requirements.
Document complete design requirements for single-page wizard: - Settings panel style for configuration - Session panel style for prompt + create button - Profile selection first with create/edit/delete - All features visible on one page - Create button greyed until valid See notes/2025-11-16-wizard-merge-and-refactor-plan.md for complete specification.
Add actionable specifications with file paths, line numbers, and code snippets: - AgentInput component interface and props (SessionView.tsx:276 usage reference) - Exact lines to delete (WizardStep type, navigation functions, renderStepContent) - Content extraction mapping (which step content becomes which section) - Profile utilities extraction plan (DRY - remove duplication) - Validation logic with code examples - Picker screen integration strategy (keep valuable recent paths UI) All details CLAUDE.md concrete: specific files, functions, classes, line numbers.
…rkflows Corrections: - Lines 30-40, 647-671, 673-681: KEEP callbacks and picker handlers (not delete) - AgentInput is controlled component (needs value prop, not self-managing) - Add missing profile edit modal requirements (full editor with env vars, tmux) - Add complete ProfileEditForm reference (settings/profiles.tsx:481-989) Added end-to-end workflows: - Profile creation/edit workflow with modal and settings sync - Session creation workflow with env var transformation - Picker integration workflow with callbacks All logic errors fixed, workflows documented, ready for implementation.
Document support for variable substitution in profile env vars:
- Literal values: API_TIMEOUT_MS='600000'
- Variable references: ANTHROPIC_AUTH_TOKEN='${Z_AI_AUTH_TOKEN}'
- References resolve on target machine (daemon/CLI side)
- GUI stores templates, daemon performs substitution
Examples from user:
- Anthropic: unset env vars, use defaults
- Z.AI: Reference Z_AI_* variables
- DeepSeek: Reference DEEPSEEK_* variables with multiple mappings
ProfileEditForm (settings/profiles.tsx:786-943) already implements env var
editor with key-value pairs. Template strings passed to daemon which resolves
variables on target machine before spawning Claude/Codex process.
Summary: Eliminate code duplication by moving built-in profile definitions to shared module
Previous behavior (based on git diff):
- new/index.tsx:43-187 defined getBuiltInProfile() with 6 provider configs (Anthropic, DeepSeek, Z.AI, OpenAI, Azure, Together)
- new/index.tsx:156-187 defined DEFAULT_PROFILES constant with profile metadata
- settings/profiles.tsx:27-43 defined duplicate DEFAULT_PROFILES (only 3 providers)
- settings/profiles.tsx:46-100 defined duplicate getBuiltInProfile() (only 3 providers)
- Total duplication: 221 lines across 2 files
What changed:
- Created sources/sync/profileUtils.ts (+157 lines)
- Exported getBuiltInProfile() function with all 6 providers
- Exported DEFAULT_PROFILES constant with all 6 providers
- Added JSDoc comments documenting parameters and return values
- Updated sources/app/(app)/new/index.tsx (-148 lines)
- Line 23: Added import { getBuiltInProfile, DEFAULT_PROFILES } from '@/sync/profileUtils'
- Lines 43-187: Deleted local getBuiltInProfile() and DEFAULT_PROFILES definitions
- Replaced with comment "// Profile utilities now imported from @/sync/profileUtils"
- Updated sources/app/(app)/settings/profiles.tsx (-77 lines)
- Line 13: Added import { getBuiltInProfile, DEFAULT_PROFILES } from '@/sync/profileUtils'
- Lines 27-100: Deleted local DEFAULT_PROFILES and getBuiltInProfile() definitions
- Now has all 6 providers (was missing OpenAI, Azure, Together)
Why:
- DRY principle: Single source of truth for profile configurations
- Consistency: Both wizard and settings now use identical profile sets
- Maintainability: Changes to built-in profiles only need updating in one location
- Preparation: Sets up shared utilities needed for upcoming single-page wizard refactor
Files affected:
- sources/sync/profileUtils.ts (new file, 157 lines)
- sources/app/(app)/new/index.tsx (removed 148 duplicate lines)
- sources/app/(app)/settings/profiles.tsx (removed 77 duplicate lines, gained 3 providers)
Net change: -221 lines of duplication, +161 lines of shared code (-60 lines total)
Testable: Build compiles, profile grid shows all 6 built-in profiles in both new session wizard and settings panel
… with integrated AgentInput
Summary: Replace 4-step wizard navigation with single scrollable page using session panel's AgentInput component
Previous behavior (based on git diff):
- Multi-step wizard with 4 steps: welcome → ai-backend → session-details → creating (904 lines)
- Step navigation with Back/Next buttons controlled by currentStep state
- Separate prompt TextInput (lines 791-801) with Create Session button (lines 837-848)
- Step-by-step flow requiring multiple button clicks to create session
- Loading step showing "Creating Session" spinner
- Module-level navigation functions goToNextStep() and goToPreviousStep()
What changed:
- Deleted multi-step infrastructure (-262 lines total, now 642 lines):
- Line 28: Deleted WizardStep type definition
- Line 337: Deleted currentStep state and setCurrentStep
- Lines 425-457: Deleted goToNextStep() and goToPreviousStep() navigation functions
- Lines 640-878: Deleted renderStepContent() step rendering switch statement
- Lines 472-501: Deleted createNewProfile() (profile creation moved to settings panel)
- Line 409: Removed sessionPrompt.trim() validation (prompt now optional)
- Line 555: Removed setCurrentStep('creating') (no loading step)
- Added AgentInput integration:
- Line 24: Added import { AgentInput } from '@/components/AgentInput'
- Lines 619-634: Integrated AgentInput at bottom with full props:
- isSendDisabled={!canCreate} wires validation to arrow button state
- isSending={isCreating} shows loading state during session creation
- machineName and currentPath props show context in input area
- agentType, permissionMode, modelMode props configure agent display
- Created single-page layout:
- Lines 521-615: Single scrollable view with 4 numbered sections
- Section 1 (lines 523-563): Profile grid with selection cards
- Section 2 (lines 566-575): Machine selector button (opens picker)
- Section 3 (lines 578-588): Path selector button (opens picker)
- Section 4 (lines 591-614): Collapsible advanced options (experimental features)
- Updated validation logic:
- Lines 351-357: canCreate useMemo checks profile, machine, path (prompt optional)
- Prompt no longer required - sessions can be created without initial message
- Kept picker integration (CRITICAL - maintains UX):
- Lines 29-39: Module-level callbacks for picker screens
- Lines 373-406: useEffect hooks wiring onMachineSelected and onPathSelected
- Lines 398-406: handleMachineClick and handlePathClick navigation functions
- Picker screens (machine.tsx, path.tsx) unchanged and functional
- Updated styles:
- Lines 139-232: Replaced wizard step styles with single-page section styles
- Removed: stepHeader, stepNumber, buttonContainer, creatingContainer
- Added: wizardContainer, sectionHeader, selectorButton, advancedHeader
- Session creation improvements:
- Line 475: Made initial prompt optional (if (sessionPrompt.trim()) check)
- Line 419: Removed "creating" step transition (setIsCreating only)
- Direct session creation without intermediate loading UI
Why:
- User request: "wizard to appear in the same main panel as the message interface"
- Simplification: Single page instead of multi-step navigation reduces clicks
- Consistency: Reuse AgentInput component from session panel (same UX)
- Efficiency: All options visible at once - no need to navigate between steps
- Flexibility: Prompt now optional - users can create session and start typing after
- Maintainability: -262 lines of navigation code, simpler state management
Files affected:
- sources/app/(app)/new/index.tsx (-262 lines: 904 → 642 lines)
Technical implementation:
- AgentInput controlled component: value={sessionPrompt}, onChangeText={setSessionPrompt}
- Validation: canCreate=true enables arrow button via isSendDisabled={!canCreate}
- Picker callbacks: Module-level callbacks maintained for picker screen integration
- State preserved: All session configuration state (profile, machine, path, modes) unchanged
- Environment variables: Profile-to-env transformation logic unchanged (lines 50-89)
Testable:
- Build compiles without TypeScript errors
- New session button navigates to wizard
- Single-page wizard appears (no step transitions)
- All 6 built-in profiles render in grid
- Machine/path picker buttons open respective pickers
- AgentInput arrow button greyed when fields invalid
- AgentInput arrow button active when profile+machine+path selected
- Session creation works with profile environment variables
- Optional prompt: Can create session without typing initial message
Path picker screen exists at sources/app/(app)/new/pick/path.tsx but was missing route definition in _layout.tsx, causing navigation error when clicking path selector button in wizard. Files affected: - sources/app/(app)/_layout.tsx: Added Stack.Screen for new/pick/path route (lines 307-313) Testable: Click path selector button in new session wizard → path picker opens
…Form
**Summary:** Fixed environment variable display in profile edit form showing actual values from daemon environment (e.g., "glm-4.6") instead of "Not set on remote".
**Previous behavior:**
- Profile subtitles showed evaluated variables correctly: "Z_AI_MODEL: glm-4.6"
- ProfileEditForm showed "Not set on remote" for all environment variables
- Two different query patterns caused inconsistent results between components
**Root causes (two bugs):**
1. **Bash command prevented variable expansion** (useEnvironmentVariables.ts:73)
- Used `\${${name}:-}` which bash interprets literally as "${VAR}" instead of expanding
- Should use `$${name}` in template literal to produce `$VAR` for bash expansion
2. **ProfileEditForm queried wrong variable names** (ProfileEditForm.tsx:64-67)
- Queried documentation variable names directly (e.g., `Z_AI_MODEL`)
- Should extract variable names from INSIDE `${...}` patterns in profile.environmentVariables
- index.tsx used `extractEnvVarReferences()` correctly, ProfileEditForm did not
**What changed:**
useEnvironmentVariables.ts:73:
- Changed bash command from `.map(name => echo "${name}=\${${name}:-}")` to `.map(name => echo "${name}=$${name}")`
- Removed backslash escape that prevented shell variable expansion
- Matches working pattern from commits 6ec68b0 and 950a08f
ProfileEditForm.tsx:14,64-67:
- Import `extractEnvVarReferences` helper function
- Changed from querying `profileDocs.environmentVariables[].name` to `extractEnvVarReferences(profile.environmentVariables)`
- Now matches index.tsx pattern exactly for DRY consistency
**Why:**
- Bash command `echo "VAR=\${VAR}"` outputs literal string `VAR=${VAR}` (backslash prevents expansion)
- Correct command `echo "VAR=$VAR"` outputs `VAR=glm-4.6` (shell expands variable)
- ProfileEditForm needs to query variables referenced INSIDE `${...}` patterns (e.g., `Z_AI_MODEL` from `${Z_AI_MODEL}`)
- Both components must use same extraction logic for consistent behavior
**Files affected:**
- sources/hooks/useEnvironmentVariables.ts: Fixed bash command to enable variable expansion (line 73)
- sources/components/ProfileEditForm.tsx: Use extractEnvVarReferences to match index.tsx pattern (lines 14, 64-67)
**Testable:**
- Open Z.AI profile edit form with machine selected
- Environment variables section shows: "Z_AI_MODEL" → "Expected: GLM-4.6" → "Actual: ✓ glm-4.6"
- Matches behavior in profile selection subtitle: "Z_AI_MODEL: glm-4.6"
- Run `echo ${Z_AI_MODEL}` on remote machine outputs same value
Summary: Design specification for improving environment variable configuration UI in ProfileEditForm with checkbox-based remote variable copying and inline edit/add/delete operations.
Previous behavior:
- Environment variables shown as read-only documentation in Z.AI/DeepSeek profiles
- No clear distinction between reading from daemon environment (${VAR}) vs literal values
- No ability to add/edit/delete variables from built-in profile templates
- Users couldn't see actual remote machine values while configuring
What changed:
- Created comprehensive design specification in notes/2025-11-21-environment-variable-configuration-ux-design.md
- Checkbox pattern: "First try copying variable from remote machine" with fallback
- Status indicators: ✓ Value found, ✗ Value not found, ⏳ Checking, ℹ️ No machine
- Warning system: Muted gray for "Differs from documented value" and "Overriding documented default"
- CRUD operations: Delete/Duplicate/Edit buttons matching profile list pattern (index.tsx:1189-1215)
- Inline expansion: Cards expand in place when editing, not modal
- Color scheme: Uses existing theme.colors.* variables (success, warning, textSecondary, textDestructive)
- Typography: Matches ProfileEditForm existing sizes (16, 14, 12, 11)
Why:
- Users need clear control over read-from-remote vs literal value distinction
- Contractor scenario: Support multiple accounts with different variables (Z_AI_MODEL_ACCOUNT2)
- Bash fallback syntax ${VAR:-default} provides graceful degradation
- Inline editing maintains context and reduces cognitive load
- Consistent styling with profile list creates familiar UX patterns
Files affected:
- notes/2025-11-21-environment-variable-configuration-ux-design.md: Complete design specification with 8 states, CRUD operations, color scheme, implementation details
Security:
- Secrets (TOKEN, KEY, SECRET) never queried from remote
- Variable name validation: /^[A-Z_][A-Z0-9_]*$/ prevents bash injection
- Expected values guide users without exposing actual credentials
Testable:
- Design includes 8 visual states covering all scenarios
- Includes test cases TC1-TC8 for implementation validation
- References exact line numbers from existing code patterns
…config design
Summary: Added theme.colors.deleteAction variable, replaced all hardcoded #FF6B6B, fixed URL template validation, and finalized environment variable configuration UX design with DRY component architecture.
Previous behavior:
- Delete buttons used hardcoded #FF6B6B color across multiple files
- No theme variable for delete action color
- Could not save profiles with ${VAR} template strings in URL fields
- Environment variable UI design had collapse/expand complexity
What changed:
theme.ts:15,222:
- Added deleteAction: '#FF6B6B' to both light and dark themes
- Provides single source of truth for delete button color
index.tsx:1196,1303-1304, profiles.tsx:362, ProfileEditForm.tsx:1079:
- Changed all trash-outline/remove-circle icons from "#FF6B6B" to theme.colors.deleteAction
- Eliminates hardcoded colors, follows DRY principle
settings.ts:10-64:
- Added Zod refinement to anthropicConfig.baseUrl, openaiConfig.baseUrl, azureOpenAIConfig.endpoint
- Accepts ${VAR} template strings OR valid URLs
- Regex: /^\$\{[A-Z_][A-Z0-9_]*\}$/
- Fixes "cannot be parsed as a URL" error when saving Z.AI/DeepSeek profiles
notes/2025-11-21-environment-variable-configuration-ux-design.md:
- Complete design specification with DRY component architecture
- Two new components: EnvironmentVariablesList.tsx (~200 lines), EnvironmentVariableCard.tsx (~300 lines)
- All-editable design (no collapse/expand - user already in Edit Profile mode)
- Checkbox pattern: "First try copying variable from remote machine" with fallback
- Status indicators: ✓ Value found, ✗ Value not found, ⚠️ warnings (muted gray)
- Button layout: [Delete] [Duplicate] matching profile list (index.tsx:1189-1215)
- Color scheme documented: Uses theme.colors.* variables (success, warning, textSecondary, deleteAction)
- Implementation guidance: Exact line references, component structure, state management
Why:
- DRY: theme.colors.deleteAction used everywhere, change once affects all
- Template support: Users can save ${Z_AI_BASE_URL} in profiles
- Maintainability: Delete color defined in theme, not scattered
- Component reuse: EnvironmentVariablesList can be used in other contexts
- Simpler UX: All editable by default, no hidden edit mode
- Clear architecture: Matches profile list pattern (simple .map(), not SearchableListSelector)
Files affected:
- sources/theme.ts: Added deleteAction color (lines 15, 222)
- sources/app/(app)/new/index.tsx: Use theme variable (lines 1196, 1303-1304)
- sources/app/(app)/settings/profiles.tsx: Use theme variable (line 362)
- sources/components/ProfileEditForm.tsx: Use theme variable (line 1079)
- sources/sync/settings.ts: URL template validation (lines 10-64)
- notes/2025-11-21-environment-variable-configuration-ux-design.md: Complete design spec with DRY architecture
Testable:
- All delete buttons show same #FF6B6B color via theme variable
- Z.AI profile saves successfully with ${Z_AI_BASE_URL} template
- Design spec includes 8 visual states, CRUD operations, component structure
- References exact code patterns (profile list at index.tsx:1163-1217)
- Typecheck passes with no errors
…mentVariables Summary: Converted OpenAI and Azure OpenAI profiles from using config objects to unified environmentVariables array, providing consistent configuration interface across all profile types. Previous behavior: - OpenAI profile used openaiConfig.baseUrl and openaiConfig.model fields - Azure OpenAI used azureOpenAIConfig.apiVersion and azureOpenAIConfig.deploymentName fields - Anthropic/DeepSeek/Z.AI used environmentVariables array - Inconsistent configuration approaches across profile types - ProfileEditForm would need separate UI for each config type What changed: profileUtils.ts:236-250 (OpenAI profile): - Emptied openaiConfig object - Moved baseUrl → OPENAI_BASE_URL environment variable - Moved model → OPENAI_MODEL environment variable - Now all 6 variables in environmentVariables array profileUtils.ts:255-267 (Azure OpenAI profile): - Emptied azureOpenAIConfig object - Moved apiVersion → AZURE_OPENAI_API_VERSION environment variable - Moved deploymentName → AZURE_OPENAI_DEPLOYMENT_NAME environment variable - Now all 4 variables in environmentVariables array profileUtils.ts:143-178 (OpenAI documentation): - Added setupGuideUrl: https://platform.openai.com/docs/api-reference - Added environmentVariables documentation for OPENAI_BASE_URL, OPENAI_API_KEY, OPENAI_MODEL, OPENAI_SMALL_FAST_MODEL - Added shell configuration example - Expected values guide users what to set in ~/.zshrc profileUtils.ts:179-214 (Azure documentation): - Added setupGuideUrl: https://learn.microsoft.com/en-us/azure/ai-services/openai/ - Added environmentVariables documentation for AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_OPENAI_API_VERSION, AZURE_OPENAI_DEPLOYMENT_NAME - Added shell configuration example - Expected values include placeholder YOUR_RESOURCE for user customization Why: - DRY: Single configuration approach (environmentVariables) for ALL profiles - Consistency: OpenAI/Azure now match Anthropic/DeepSeek/Z.AI pattern - Simplifies UI: ProfileEditForm can use single EnvironmentVariablesList component for everything - No priority confusion: environmentVariables is only source (no config vs env vars conflict) - getProfileEnvironmentVariables() already converts both formats (lines 182-202), so functionally equivalent Files affected: - sources/sync/profileUtils.ts: Converted OpenAI and Azure profiles (lines 143-267) Security: - OPENAI_API_KEY and AZURE_OPENAI_API_KEY marked as isSecret: true - Never displayed or queried from remote machine Testable: - OpenAI profile spawns Codex session with OPENAI_BASE_URL and OPENAI_MODEL set - Azure profile spawns with AZURE_OPENAI_* variables set - Setup Instructions box shows documentation for all profiles - Expected values guide users for shell configuration - Profile version remains '1.0.0' for future versioning
Summary: Added comprehensive OODA analysis verifying unified environment variables approach works correctly across all profile types (Anthropic, DeepSeek, Z.AI, OpenAI, Azure). What changed: - Added "OODA Analysis: Cross-Profile Verification" section - Documented all 5 profile types and their migration paths - Verified CLI compatibility (Claude reads ANTHROPIC_*, Codex reads OPENAI_*/AZURE_OPENAI_*) - Confirmed getProfileEnvironmentVariables() functionally equivalent after migration - Documented implementation status (profiles migrated in commit 3f367c1) Why: - Ensures unified approach works for both Claude and Codex CLIs - Verifies no breaking changes to CLI environment variable expectations - Documents migration safety (no users exist yet) - Provides implementation confidence (all profile types tested) Files affected: - notes/2025-11-21-environment-variable-configuration-ux-design.md: Added OODA section
…nvironmentVariableCard/List components Summary: Added theme.margins, theme.borderRadius, theme.iconSize based on actual codebase usage patterns, and created new DRY components for environment variable configuration UI. **STATUS: WORK IN PROGRESS - Components created but not yet integrated into ProfileEditForm** Previous behavior: - Spacing, border radii, icon sizes hardcoded throughout codebase - No centralized design tokens for sizing - No reusable components for environment variable editing What changed: theme.ts:3-31: - Added sharedSpacing constant with margins, borderRadius, iconSize - Based on actual usage analysis (grep search of codebase) - margins: xs(4), sm(8), md(12), lg(16), xl(20), xxl(24) - borderRadius: sm(4), md(8), lg(10), xl(12), xxl(16) - iconSize: small(12), medium(16), large(20), xlarge(24) - DRY: Single definition, spread into both light and dark themes EnvironmentVariableCard.tsx (317 lines): - Single variable card component matching profile list pattern - Checkbox: "First try copying variable from remote machine" - Two input fields: remote variable name, default value - Status indicators: ✓ Value found, ✗ Value not found, ⏳ Checking - Warnings:⚠️ Differs,⚠️ Overriding (muted gray - theme.colors.textSecondary) - Action buttons: [Delete] [Duplicate] using theme.colors.deleteAction - Uses theme variables: theme.margins.*, theme.borderRadius.*, theme.iconSize.* - Queries remote via useEnvironmentVariables hook EnvironmentVariablesList.tsx (255 lines): - Complete section component with title and add button - Maps over variables rendering EnvironmentVariableCard for each - [+] Add Variable button matching profile list style - Inline add form with name/value inputs - Handles add/update/delete/duplicate operations - Gets expectedValue and description from profileDocs Why: - DRY: Centralized sizing tokens prevent inconsistency - Maintainability: Change spacing in one place, affects all components - Component reuse: EnvironmentVariablesList can be used anywhere - Consistency: All theme properties accessible via theme.* (colors, margins, borderRadius, iconSize) - Matches existing patterns: Profile list structure (index.tsx:1163-1217) Files affected: - sources/theme.ts: Added sharedSpacing with margins, borderRadius, iconSize - sources/components/EnvironmentVariableCard.tsx: New component (317 lines) - sources/components/EnvironmentVariablesList.tsx: New component (255 lines) Next steps: - Integrate EnvironmentVariablesList into ProfileEditForm - Remove Base URL, Model, Auth Token individual fields - Replace both env var sections with unified component Testable: - Theme provides theme.margins.md, theme.borderRadius.xl, theme.iconSize.large - Components use theme variables exclusively - Typecheck passes with no errors - Components NOT yet integrated (ProfileEditForm still uses old UI)
Previous behavior: CLAUDE.md listed yarn commands for iOS/Android/web but omitted macOS desktop (Tauri) build commands, making them undiscoverable What changed: - Added "macOS Desktop (Tauri)" section with 4 yarn commands - yarn tauri:dev - Run macOS app with hot reload - yarn tauri:build:dev/preview/production - Build variants - Fixed trailing whitespace on android command line Why: Ensures consistent use of yarn (not npm) and makes Tauri commands easily discoverable when working on macOS desktop development Files affected: - CLAUDE.md: Added macOS Desktop section after Development section Testable: Commands documented match package.json scripts and CONTRIBUTING.md
…List component
Previous behavior: ProfileEditForm had fragmented UI with separate sections for Base URL, Auth Token, Model fields (using anthropicConfig) plus a separate Custom Environment Variables section. This created confusion about priority (anthropicConfig vs environmentVariables) and required maintaining two parallel systems.
What changed:
- ProfileEditForm.tsx (-800 lines, +24 lines):
- Removed individual config field sections: Base URL, Auth Token, Model, Model Mappings
- Removed Custom Environment Variables section with checkbox toggle
- Added EnvironmentVariablesList component integration
- Removed obsolete state: baseUrl, authToken, useAuthToken, model, useModel, customEnvVars, useCustomEnvVars
- Removed obsolete helpers: extractedBaseUrl, extractedModel, modelMappings, getEnvVarValue, evaluateEnvVar
- Updated handleSave to clear all config objects (anthropicConfig, openaiConfig, azureOpenAIConfig)
- Simplified Setup Instructions box to show only description + docs link
- Added environmentVariables state as single source of truth
- profileUtils.ts:
- Updated DeepSeek profile: added ${VAR:-default} fallback values for checkbox + default value field population
- Updated Z.AI profile: added ${VAR:-default} fallback values for checkbox + default value field population
- Secrets (DEEPSEEK_AUTH_TOKEN, Z_AI_AUTH_TOKEN) use ${VAR} without fallback for security
- Non-secrets include fallbacks from expectedValue (e.g., ${DEEPSEEK_BASE_URL:-https://api.deepseek.com/anthropic})
- settings.ts:
- Updated URL validation regex in AnthropicConfigSchema, OpenAIConfigSchema, AzureOpenAIConfigSchema
- Now accepts ${VAR:-default} format: /^\$\{[A-Z_][A-Z0-9_]*(:-[^}]*)?\}$/
- Updated error messages to reflect new format support
- new/index.tsx:
- Fixed getProfileSubtitle to check environmentVariables first (not anthropicConfig.baseUrl)
- Removed legacy anthropicConfig fallback path (no users exist, no backward compatibility needed)
Why: Implements unified environment variable configuration UX design. Single interface for ALL configuration (Base URL, Model, Auth Token, custom vars) eliminates confusion and duplication. EnvironmentVariableCard's checkbox shows ${VAR:-default} format correctly with checkbox checked and default value pre-populated. Follows DRY, OODA, and "easy to use correctly, hard to use incorrectly" principles.
Files affected:
- ProfileEditForm.tsx: Massive simplification, removed 800 lines of duplicate config UI
- profileUtils.ts: Added fallback values to DeepSeek/Z.AI profiles for UI initialization
- settings.ts: Extended schema validation to support ${VAR:-default} bash parameter expansion
- new/index.tsx: Fixed profile subtitle to prioritize environmentVariables over config objects
Testable: Launch macOS app (yarn tauri:dev), edit DeepSeek profile, verify checkbox checked and default values populated from ${VAR:-default} format. Verify old Base URL/Auth Token/Model fields completely removed.
…w resize Previous behavior: Session header hidden when window resized to medium widths on macOS/desktop because landscape phone detection (!(isLandscape && deviceType === 'phone')) treated resizable desktop windows at <9" diagonal as phones, hiding header in landscape orientation. What changed: - SessionView.tsx:108,124: Added Platform.OS !== 'web' check to landscape phone header hiding condition - Condition changed from !(isLandscape && deviceType === 'phone') to !(isLandscape && deviceType === 'phone' && Platform.OS !== 'web') - Applied to both header visibility (line 108) and content paddingTop (line 124) - Added bug investigation documentation Why: Desktop/Mac windows are resizable and shouldn't hide UI based on calculated diagonal size. The landscape phone optimization (hiding header for more screen space) should only apply to actual iOS/Android phones, not web/desktop platforms. This creates consistent desktop behavior where header always shows regardless of window dimensions. Files affected: - sources/-session/SessionView.tsx: Modified header conditional rendering (lines 108, 124) - notes/2025-11-21-session-header-responsive-breakpoint-bug.md: Complete bug investigation with root cause analysis Testable: Resize macOS app window from narrow to wide - header remains visible at all widths. iOS/Android phones still hide header in landscape for more space.
…ty messages for secret variables
Previous behavior: isSecret only set from profileDocs, defaulting to false for undocumented or custom variables. Variables with TOKEN/KEY/SECRET/AUTH in name were queried from remote daemon, exposing secrets. No security warning message displayed.
What changed:
- EnvironmentVariablesList.tsx:240: Added auto-detection regex /TOKEN|KEY|SECRET|AUTH/i for variable name and remote variable name
- Checks both envVar.name and extracted remote variable name from ${VAR} template
- isSecret = docs.isSecret || auto-detected from name pattern
- EnvironmentVariableCard.tsx:262-272: Added security message "🔒 Secret value - not retrieved for security"
- Message shows when isSecret is true, preventing confusion about why remote status is missing
Why: Secrets must not be queried from remote for security. Auto-detection ensures custom variables with TOKEN/KEY/SECRET/AUTH are protected even without explicit documentation. Matches behavior from old ProfileEditForm (commit b0825b7). Follows "easy to use correctly, hard to use incorrectly" principle.
Files affected:
- EnvironmentVariablesList.tsx: Auto-detect secrets from variable name (line 240)
- EnvironmentVariableCard.tsx: Display security message for secrets (lines 262-272)
Security: Variables with TOKEN, KEY, SECRET, or AUTH (case-insensitive) never queried from remote daemon, show lock icon, use secureTextEntry, display security warning.
Testable: Edit DeepSeek profile - ANTHROPIC_AUTH_TOKEN shows 🔒 icon, security message, secureTextEntry input, no remote query. ANTHROPIC_BASE_URL shows remote status (not a secret).
…hile showing substitution logic
Previous behavior: "Session will receive" preview showed actual secret values or "(empty)" for secrets, exposing sensitive data in GUI or providing inaccurate information about variable substitution.
What changed:
- EnvironmentVariableCard.tsx:324-332: Updated session preview logic to handle secrets
- For secrets with remote variable (${DEEPSEEK_AUTH_TOKEN}): Shows "(from DEEPSEEK_AUTH_TOKEN - hidden for security)"
- For secrets with literal value: Shows "***hidden***" instead of actual value
- For non-secrets: Shows actual resolved value (unchanged)
Why: Session WILL receive the expanded secret value (daemon expands ${VAR} from its environment), but GUI must not display actual secret values for security. Preview now accurately describes what happens ("from REMOTE_VAR") without exposing the actual key. Follows principle: accurate description of behavior without compromising security.
Files affected:
- EnvironmentVariableCard.tsx: Conditional session preview for secrets (lines 324-332)
Security: Secret values never displayed in preview text. Shows substitution source (remote variable name) to explain behavior without exposing credentials.
Testable: Edit DeepSeek profile ANTHROPIC_AUTH_TOKEN card - preview shows "(from DEEPSEEK_AUTH_TOKEN - hidden for security)" not actual key value. ANTHROPIC_BASE_URL shows actual hostname (not a secret).
…review instead of descriptive text
Previous behavior: Secret preview showed descriptive text "(from DEEPSEEK_AUTH_TOKEN - hidden for security)" which doesn't match the actual variable format sent to daemon.
What changed:
- EnvironmentVariableCard.tsx:327: Changed secret preview to show actual bash variable syntax
- With remote var: Shows ${Z_AI_AUTH_TOKEN} - hidden for security
- With fallback: Shows ${Z_AI_AUTH_TOKEN:-***} - hidden for security (masks fallback value)
- Literal value: Shows ***hidden*** (no variable syntax)
Why: Daemon receives the actual ${VAR} or ${VAR:-default} syntax which it expands from its environment. Preview should show the exact syntax sent to daemon (not a description), while still hiding the actual secret value. This accurately represents what the session receives without compromising security.
Files affected:
- EnvironmentVariableCard.tsx: Updated secret preview format (line 327)
Testable: Edit DeepSeek ANTHROPIC_AUTH_TOKEN card - preview shows "${DEEPSEEK_AUTH_TOKEN} - hidden for security" matching the actual variable format sent.
… prevent selection outline clipping
Fixed visual bug where selected item's 2px border was clipped by ItemGroup container's overflow:hidden, causing the white selection outline to be cut off at the edges.
Previous behavior:
- ITEM_BORDER_RADIUS was hardcoded to 8px (SearchableListSelector.tsx:102)
- ItemGroup container has borderRadius: 10px (iOS) or 16px (Android/web) with overflow:hidden
- Selected item border (2px) extended outside the 8px radius but got clipped by container's 10px/16px radius
- Selection outline appeared cut off, especially noticeable on machine selection
What changed:
- SearchableListSelector.tsx:102-104: Changed ITEM_BORDER_RADIUS to Platform.select({ ios: 10, default: 16 })
- Now matches ItemGroup's contentContainer borderRadius exactly
- Added comment explaining why radius values must match
Why:
- ItemGroup uses overflow:hidden with platform-specific borderRadius (10px iOS, 16px Android/web)
- When selected item border radius (8px) < container radius (10/16px), border gets clipped by overflow
- Matching radii ensures 2px selection border stays within visible bounds
Files affected:
- sources/components/SearchableListSelector.tsx: Updated ITEM_BORDER_RADIUS constant to match platform-specific ItemGroup radius
Testable:
- Select a machine in new session wizard - white selection outline now fully visible
- Select a path in new session wizard - no clipping on selection border
- Works correctly on iOS (10px radius) and Android/web (16px radius)
Summary: Built-in profiles now display "Save As" button instead of "Save" to make it clear that editing a built-in profile creates a new custom copy rather than modifying the original. Previous behavior: When editing a built-in profile (like Z.AI), the button showed "Save". Clicking it created a new custom profile with a new UUID (index.tsx:776-786 logic), but users expected it to modify the built-in profile. This caused confusion when the built-in profile "converted" to a custom profile. What changed: - ProfileEditForm now conditionally renders button based on profile.isBuiltIn - Built-in profiles: Shows "Save As" button (creates custom copy) - Custom profiles: Shows "Save" button (updates existing profile) - Added "saveAs" translation to all 7 language files: - English: "Save As" - Russian: "Сохранить как" - Polish: "Zapisz jako" - Spanish: "Guardar como" - Catalan: "Desa com a" - Portuguese: "Salvar como" - Simplified Chinese: "另存为" Why: The backend intentionally creates a new custom profile when editing built-ins (to preserve the original), but the UI didn't communicate this. Users thought they were modifying the built-in profile when they were actually creating a copy. The "Save As" label makes the behavior explicit and matches user expectations from other software (File → Save As creates a copy). Files affected: - sources/components/ProfileEditForm.tsx: Conditional button rendering - sources/text/_default.ts: Added saveAs to type definition - sources/text/translations/*.ts: Added saveAs translation to 7 languages Testable: Open Z.AI built-in profile → should see "Save As" button. Open a custom profile → should see "Save" button. Clicking "Save As" on built-in creates new custom profile. Clicking "Save" on custom updates that profile.
… empty Summary: Tmux checkbox now correctly shows as checked when reopening a profile with tmux enabled but no specific session name configured. Previous behavior: When user enabled tmux checkbox without specifying a session name (meaning "use current/most recent tmux session"), the profile saved `tmuxConfig.sessionName: ''` (empty string). On reload, the initialization logic `!!profile.tmuxConfig?.sessionName` evaluated the empty string as falsy, causing the checkbox to appear unchecked even though tmux was actually enabled in the saved profile. What changed: - ProfileEditForm.tsx line 54: Changed from `!!profile.tmuxConfig?.sessionName` to `profile.tmuxConfig?.sessionName !== undefined` - Now correctly distinguishes between: - `sessionName: ''` → tmux enabled, use current session → checkbox checked ✓ - `sessionName: 'my-session'` → tmux enabled, specific session → checkbox checked ✓ - `sessionName: undefined` → tmux disabled → checkbox unchecked ✓ Why: Empty string is a valid value for tmuxConfig.sessionName (documented as "use current/most recent session" in daemon/run.ts:359-360). The initialization logic must check for undefined, not falsiness, to correctly reflect the saved state. This is a classic JavaScript falsy value bug where `'' !== false` but `!!'' === false`. Files affected: - sources/components/ProfileEditForm.tsx: Fixed useTmux state initialization Testable: 1. Open any profile 2. Check "Enable tmux" without specifying session name 3. Click Save/Save As 4. Close and reopen the same profile 5. Expected: Checkbox still checked ✓ 6. Actual before fix: Checkbox unchecked ✗
…ation to match MessageMetaSchema Previous behavior (based on git diff): - settings.ts:116 used z.string().optional() for defaultPermissionMode allowing any string value - typesRaw.ts:55 used z.string().optional() for permissions.mode allowing any string value - Invalid permission modes could be stored without runtime validation errors - Schema inconsistency: MessageMetaSchema correctly validated enum but these schemas didn't What changed: - settings.ts:116 - changed from z.string().optional() to z.enum(['default', 'acceptEdits', 'bypassPermissions', 'plan', 'read-only', 'safe-yolo', 'yolo']).optional() - typesRaw.ts:55 - changed from z.string().optional() to z.enum(['default', 'acceptEdits', 'bypassPermissions', 'plan', 'read-only', 'safe-yolo', 'yolo']).optional() Why: - Ensures runtime validation matches type system expectations across all schema definitions - Prevents invalid permission modes from being stored in profiles and tool result permissions - Matches MessageMetaSchema (sources/sync/typesMessageMeta.ts:6) which already correctly validates the enum - Maintains consistency between happy app and happy-cli permission mode validation Files affected: - sources/sync/settings.ts:116 - strengthened AIBackendProfile.defaultPermissionMode schema validation - sources/sync/typesRaw.ts:55 - strengthened RawToolResultContent.permissions.mode schema validation Testable: - Verify TypeScript compilation passes (yarn typecheck) - Attempt to set invalid permission mode in profile - should fail zod validation - All 7 valid modes correctly validate: default, acceptEdits, bypassPermissions, plan, read-only, safe-yolo, yolo
…bility analysis Previous behavior (based on git diff): - No documentation of branch vs main changes existed - No backwards compatibility analysis for permission mode changes - No complete assessment of breaking changes across 171 commits in both repositories - No PR readiness evaluation for feature branches What changed: - notes/2025-11-22-permission-mode-backwards-compatibility-analysis.md - detailed analysis proving permission mode enum validation is safe (no custom modes ever existed) - notes/2025-11-22-full-branch-backwards-compatibility-analysis.md - analysis of permission mode changes plus initial breaking change assessment - notes/2025-11-22-complete-branch-readiness-report.md - comprehensive analysis of all 171 commits across happy and happy-cli branches including breaking changes, required fixes, testing strategy, and PR recommendations Why: - Permission mode investigation revealed stricter enum validation changes from z.string() to z.enum([7 modes]) - Code analysis proved no custom permission modes ever existed in codebase (GUI uses hardcoded arrays, CLI validates at runtime) - Complete branch contains major features: profile system, tmux integration, wizard rewrite, env var expansion - Identified 4 actual breaking changes requiring fixes before merge (profile data loss, tmux behavior, RPC coordination, settings logging) - Documents recommended split into 5 smaller PRs for safer incremental rollout Files affected: - notes/2025-11-22-permission-mode-backwards-compatibility-analysis.md - permission mode specific analysis with code flow tracing - notes/2025-11-22-full-branch-backwards-compatibility-analysis.md - breaking changes in both branches with evidence - notes/2025-11-22-complete-branch-readiness-report.md - complete PR readiness assessment with testing strategy and fix requirements Testable: - Analysis documents contain file paths and line numbers for all findings - Breaking changes identified with severity ratings and required fixes - Testing matrix provided for cross-version compatibility verification - Specific code changes recommended with effort estimates
…d auto-reset on agent change Previous behavior: New session wizard showed only Claude permission modes (default, acceptEdits, plan, bypassPermissions) regardless of selected agent, causing incompatibility when Codex selected What changed: - lines 1487-1500: Make permission mode options conditional on agentType - Codex: show 'default', 'read-only', 'safe-yolo', 'yolo' - Claude: show 'default', 'acceptEdits', 'plan', 'bypassPermissions' - lines 576-588: Add useEffect to validate permission mode when agentType changes - Checks if current permissionMode is valid for new agentType - Resets to 'default' if invalid (e.g., user had 'bypassPermissions' then switched to Codex) Why: Wizard displayed Claude-specific modes even when Codex agent selected. If user selected 'bypassPermissions' with Codex agent, the mode would be sent to Codex which doesn't recognize it (only handles yolo/safe-yolo/read-only/default). This caused undefined approvalPolicy and sandbox values in Codex session config. Semantic correctness: - Codex yolo ≈ Claude bypassPermissions (both skip permissions, full access) - Codex safe-yolo ≠ Claude acceptEdits (different approval triggers) - Codex read-only has no Claude equivalent - Claude plan mode has no Codex equivalent Each agent now shows only its supported modes to prevent confusion Files affected: - sources/app/(app)/new/index.tsx: Conditional permission options + validation effect Testable: 1. Select Codex agent in wizard → should see Default, Read Only, Safe YOLO, YOLO options 2. Select Claude agent in wizard → should see Default, Accept Edits, Plan, Bypass Permissions options 3. Select Claude + Bypass Permissions, then switch to Codex → permission mode should reset to 'default'
- Fix reducer.spec.ts tests (lines 1639, 1654, 1691): access .messages property on ReducerResult - Implement Escape key to clear autocomplete suggestions using setTextAndSelection - Wrap console.log statements in authQRStart.ts with EXPO_PUBLIC_DEBUG guards - Remove sources/trash/ directory (duplicate/dev-only files) Test expectations now correctly access the .messages property since reducer() returns a ReducerResult object, not an array directly. Escape key handler uses same imperative API (setTextAndSelection) as handleSuggestionSelect for consistency. Canonical translation tools remain at sources/scripts/compareTranslations.ts
Previous behavior: notes/ directory contained 7 development planning/analysis documents (~200KB total) that were specific to this branch's development process. What changed: - .gitignore: added notes/ to prevent future planning docs from being committed - notes/*.md: removed 7 dated planning documents from tracking Why: These internal development artifacts don't belong in the main repo: - 2025-11-16-wizard-merge-and-refactor-plan.md - 2025-11-20-cli-detection-and-profile-availability-plan.md - 2025-11-21-environment-variable-configuration-ux-design.md - 2025-11-21-session-header-responsive-breakpoint-bug.md - 2025-11-22-complete-branch-readiness-report.md - 2025-11-22-full-branch-backwards-compatibility-analysis.md - 2025-11-22-permission-mode-backwards-compatibility-analysis.md Files moved to macOS trash for local reference if needed.
Previous behavior: 27 tests were failing due to test expectations that didn't match the actual implementation return values and API signatures. What changed: - findActiveWord.test.ts: updated 24 test expectations to include all 6 ActiveWord properties (word, activeWord, offset, length, activeLength, endOffset) instead of just the original 3 properties - settings.spec.ts: updated 2 test expectations to include new settings fields (schemaVersion, favoriteDirectories, favoriteMachines, dismissedCLIWarnings) that were added to settingsDefaults - apiGithub.spec.ts: removed incorrect 'Content-Type' header expectation from DELETE request test (implementation correctly omits it since there's no request body) Why: Tests were written before the implementation was updated with: 1. Richer ActiveWord return type for autocomplete functionality 2. New settings schema fields for favorites and CLI warnings 3. Streamlined API headers for bodyless requests All 316 tests now pass.
This was referenced Dec 7, 2025
…izard-ux-improvements
…lidation tests
Summary: Fixes env var parsing for ${VAR:-default} syntax and adds comprehensive
profile validation tests. Follows TDD by writing tests first.
Previous behavior:
- Regex /^\$\{(.+)\}$/ extracted entire substitution including defaults
(e.g., 'DEEPSEEK_BASE_URL:-https://api.deepseek.com' instead of 'DEEPSEEK_BASE_URL')
- Profile schema required UUID for id field, rejecting built-in profiles
that use descriptive IDs like 'anthropic', 'deepseek'
- Missing translations for markdown features (copy, mermaid errors)
What changed:
- sources/hooks/envVarUtils.ts: NEW - Pure utility functions extracted
for testability (resolveEnvVarSubstitution, extractEnvVarReferences)
- sources/hooks/useEnvironmentVariables.ts: Re-exports from envVarUtils,
removed duplicate implementations (~70 lines removed)
- sources/hooks/useEnvironmentVariables.test.ts: NEW - 21 tests covering
${VAR}, ${VAR:-default}, ${VAR:=default}, DeepSeek/Z.AI patterns
- sources/sync/settings.ts: Profile id field now accepts any non-empty
string (line 95), not just UUIDs. isBuiltIn flag distinguishes types.
- sources/sync/settings.spec.ts: Added AIBackendProfile validation tests
(built-in profiles, 7 permission modes, env var name validation)
- sources/text/translations/en.ts: Added copy, markdown section
Why: DeepSeek/Z.AI profiles use ${VAR:-default} bash syntax for fallback
values. The original regex captured the default value as part of the
variable name, causing GUI to fail validation and show "unconfigured"
status for valid environment variables.
Files affected:
- envVarUtils.ts: extractEnvVarReferences(), resolveEnvVarSubstitution()
- useEnvironmentVariables.ts: Re-exports only
- settings.ts: AIBackendProfileSchema.id validation relaxed
- settings.spec.ts: +85 lines of profile validation tests
Testable: yarn test (346 pass), yarn typecheck (pass)
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.
feat: New Session Page with Profile System and Alternative AI Backend Support
companion to slopus/happy-cli#86
Motivation
Users want to use Happy with alternative AI backends (OpenAI, DeepSeek, Anthropic direct API) and configure environment variables from their mobile device. Previously, all AI configuration required manual CLI setup. This PR enables:
New Session Wizard
One tap profiles to select different cli tools and ai backends, tap once then type your message and hit send!
Editable Profiles
Key Features (Priority Order)
1. Profile System with Alternative AI Backend Support
2. Environment Variable Configuration
EnvironmentVariableCardandEnvironmentVariablesListcomponents${VAR}syntax without exposing secrets${VAR:-default}syntax3. tmux Session Integration (when tmux is available)
4. Agent-Specific Permission Modes
5. Working Directory Selection
6. CLI Detection & Availability Warnings
7. Internationalization
8. UI/UX Polish
Screenshots
Stats
Related