Skip to content

Simplify session creation, migrate Codex to app-server, and improve resume + notifications#898

Merged
ex3ndr merged 101 commits intomainfrom
beta
Mar 22, 2026
Merged

Simplify session creation, migrate Codex to app-server, and improve resume + notifications#898
ex3ndr merged 101 commits intomainfrom
beta

Conversation

@bra1nDump
Copy link
Contributor

@bra1nDump bra1nDump commented Mar 22, 2026

Summary

  • replace the old profile + wizard-based session setup with a simpler composer-driven new-session flow
  • migrate Codex from the legacy MCP integration to the codex app-server runtime
  • add machine/session resume plumbing, push notification routing, and a standalone app log receiver
  • add local environment + integration-test scaffolding, plus OpenClaw support
  • includes a large asset filename cleanup (420 brutalist image renames), which inflates file count but is not the main product change. This was needed after the mono repo migration for dev builds to resolve assets correctly.
  • Server: unchanged in this PR

Testing

  • Web: run yarn web locally and exercise the new session flow in Expo web.
  • Mobile: preview OTA is already pushed, so just open the preview build and test the same flows there.
  • CLI: install the beta package and restart the daemon so you are not testing against an old background process.
happy daemon stop
npm install -g happy@beta
happy daemon start
happy --version

Major additions

  • +5,580/-233, net +5,347: Resume, machine RPC, notifications, and logs. Adds happy-agent machine/spawn/resume flows, app resume commands and quick actions, push registration/routing, and the standalone app log receiver.
  • +5,210/-12, net +5,198: Providers, OpenClaw, and provider-envelope work. Adds OpenClaw support, provider/protocol groundwork, and expanded provider integration coverage.
  • +3,871/-23, net +3,848: Environments, fixtures, testing, and product docs. Adds the environments/ system, the lab-rat fixture app, integration test setup, and supporting product docs.
  • +3,795/-953, net +2,842: Codex redesign. Migrates Codex to app-server and updates related app rendering and tests.

Neutral

  • +5,499/-5, net +5,494: Plans, research, and product docs. Mostly non-runtime docs: migration/design plans, session protocol docs, Claude/Codex/OpenCode protocol and competitive research, plus internal writeups on agent teams getting stuck in non-interactive mode and unsupervised development guidelines.
  • 420 files, 0 text lines: Brutalist asset renames. File-count inflation only, not meaningful code churn.
  • +2,619/-2,675, net -56: App polish and shared plumbing. File links, markdown/link handling, session UI polish, translations, and shared app plumbing; large touch surface, roughly neutral in size.

Deletions

  • +2,560/-7,437, net -4,877: Session creation simplification and profiles deprecation. Replaces the wizard/profile flow with a simpler composer-style /new screen and removes profiles, profile sync, CLI detection/env-var setup cards, and the separate profile editor. We want the product simpler for now, and the profile UX was not consistent enough yet.

Changes By Area

1. Simpler session creation and profiles deprecation

  • replace the multi-screen new-session wizard with a single inline composer at /new
  • select machine, project path, worktree, agent, permission mode, model, and effort inline
  • support custom project paths, offline machine visibility, existing worktree selection, and client-side worktree creation
  • persist new-session drafts and keep permission/model defaults aligned with the first spawned session
  • remove profiles, profile syncing, wizard-specific CLI/env-var plumbing, and the old profile edit flow

Why remove profiles for now?

  • We want the product simpler at this stage.
  • The current profile implementation added a lot of configuration surface area before the core session flow felt solid.
  • The UX was not very consistent with the rest of the app: users were bouncing between wizard steps, profiles, env-var resolution, and agent settings just to start a session.
  • This PR moves back to direct per-session choices. If profiles return later, they should come back with a clearer product story and a more coherent UX.

2. Codex architecture redesign

  • migrate happy-cli Codex integration from the legacy MCP client to codex app-server
  • add thread-based session handling, raw notification support, interrupt fallback/restart handling, and resume-after-restart support
  • add Codex thread resume plumbing across CLI and app metadata
  • improve Codex tool rendering in the app: patch diffs, unified diff parsing, bash command formatting, and patch approval rendering
  • add Codex default model / GPT-5.4 fallback handling

3. Session resume, machine RPC, and quick actions

  • add happy-agent machine listing, spawn, resume, and send-with-yolo support
  • add machine RPC for spawning and resuming sessions remotely
  • expose happy-agent in sourced envs and gate the in-app resume UX behind expResumeSession
  • add session quick actions, session metadata copy, CLI resume commands, and resume UI on session info/detail screens
  • always show delete session button, remove archive confirmations, and stop active sessions before delete

4. Notifications, routing, and logging

  • improve push token lifecycle management: sync, unregister stale tokens, fetch/delete token management UI in account settings
  • improve session push notifications and route notification taps back into the correct session
  • add Claude question notifications and cleaner ready notifications for Codex/Claude sessions
  • add standalone happy-app-logs plus explicit API/log server URLs in dev settings
  • clarify local log retention and dev log behavior

5. Agent/backend coverage and new providers

  • add OpenClaw backend integration and frontend selection
  • add realistic Claude integration coverage, Codex app-server coverage, OpenClaw coverage, and happy-agent integration tests
  • add machine spawn integration coverage and update CLI smoke coverage

6. Dev environments and fixtures

  • add the local dev environment system under environments/
  • add env up/down workflow, auth seeding helpers, and real integration-environment bootstrapping
  • add the lab-rat fixture project and exercise flow for agent testing
  • add OpenCode tracing harness and provider-envelope redesign groundwork / protocol research docs

7. App polish and bug fixes

  • fix iOS session list freeze / invisible sessions
  • fix new session picker scrolling and composer send-button alignment
  • only open external markdown http(s) links
  • normalize bash tool command rendering
  • show explicit environment URLs and CLI availability on machine detail screens
  • improve agent markdown rendering and session quick-action behavior

bra1nDump and others added 30 commits February 26, 2026 02:58
Integrate OpenClaw as a first-class AgentBackend in the CLI daemon,
following the same pattern as Claude/Codex/Gemini. The daemon spawns an
OpenClaw session process that connects to the gateway via WebSocket and
translates the protocol to Happy's AgentMessage format.

- OpenClawSocket: WebSocket client with request/response correlation,
  reconnection, and Ed25519 device auth
- OpenClawBackend: Custom AgentBackend mapping OpenClaw chat events to
  Happy's message types (delta, thinking, tool-call, status)
- runOpenClaw: Session entry point with zero-config auto-detection from
  ~/.openclaw/openclaw.json, abort handling with inTurn guard
- controlServer: Fix /spawn-session missing agent field in Zod schema
- daemon/run: Add openclaw to agent resolution and tmux spawning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add OpenClaw to the agent selection flow, CLI detection, profile
compatibility, model/permission modes, translations, and avatar icons.

- Agent cycling: claude -> codex -> openclaw -> gemini -> claude
- CLI detection via `command -v openclaw` on remote machine
- Profile compatibility with openclaw: false default
- OpenClaw permission/model mode options
- Avatar flavor icon badge (icon-openclaw @1x/@2x/@3x)
- Translations for all 11 languages
- Session info flavor display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eading config file

Use `openclaw status --json` for the gateway URL (cross-platform, respects
remote/tailscale configs). For the token, resolve config file path via
OPENCLAW_CONFIG_PATH env var, falling back to ~/.openclaw/openclaw.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…olution

Config file path resolution now follows the same priority as the openclaw
binary: OPENCLAW_CONFIG_PATH > OPENCLAW_STATE_DIR/openclaw.json > ~/.openclaw/openclaw.json.
Works cross-platform via os.homedir().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds `yarn env:new` to create isolated, fully-wired dev environments
with auto-allocated ports, PGlite database, and CLI support.
No Docker or external databases needed.

Commands: env:new, env:list, env:use, env:remove, env:server, env:web, env:cli

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Spaces in asset filenames cause images to silently fail to load in
monorepo setups due to a double-encoding bug in the Expo/Metro/iOS
URL chain.

Root cause:
1. Expo's ensureOtaAssetHashesAsync (getAssets.js) applies
   encodeURIComponent() to the directory path in httpServerLocation
   when ?unstable_path= is present (monorepo mode), encoding / to %2F
2. React Native's AssetSourceResolver.assetServerURL() concatenates
   this encoded directory path with the unencoded asset name (which
   contains a literal space for files like "Abstract 1.png")
3. iOS URL(string:encodingInvalidCharacters:true) sees the space and
   re-encodes the URL — but in doing so, it also re-encodes the %
   in existing %2F sequences, producing %252F (double-encoding)
4. Metro server receives %252F, URLSearchParams decodes one level to
   %2F (not /), so the resolved path contains literal "%2F" directory
   segments that don't exist on disk → 404

Why images without spaces work: when the URL has %2F but no invalid
characters (no spaces), iOS URL(string:) recognizes the URL is already
valid and preserves %2F as-is. The server then decodes %2F → / and
resolves the correct file path.

This is monorepo-specific because only the unstable_serverRoot config
triggers the ?unstable_path= publicPath format, which in turn triggers
Expo's encodeURIComponent encoding of the directory path.

Workaround: rename all brutalist asset files to use hyphens instead of
spaces (e.g. "Abstract 1.png" → "Abstract-1.png"). A proper fix should
be upstreamed to Expo — ensureOtaAssetHashesAsync should not apply
encodeURIComponent to the directory path for ?unstable_path= (dev mode)
URLs, only for ?export_path= (production export) URLs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the ENABLE_SESSION_PROTOCOL_SEND feature flag that created a
mutual exclusion gate between legacy (role:'user') and session protocol
(role:'session' envelope) user messages. The flag was never enabled
anywhere, and its either-or design meant CLI-sent user messages were
silently dropped by normalizeRawMessage() since the CLI always sends
session protocol envelopes while the flag defaults to off.

Both formats are now always accepted and rendered.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the HAPPY_VARIANT/HAPPY_HOME_DIR mismatch warning from CLI
startup and always hide thinking messages in the app (previously
gated behind experiments flag).

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Add real CLI model versions: opus 4.6, sonnet 4.6, haiku 4.5
- Add per-model effort levels and defaults (claude 3, codex 4)
- Add dontAsk permission mode, OpenClaw permission/model modes
- Add worktree support flag per agent flavor
- Update AgentInput: hide permission when default, show colored icon
  (play-forward/pause) for non-default modes, remove model mode display
- Fix modeHacks capitalizing plan/build to lowercase
- Lowercase all permission labels in translations
- Add gemini auto_edit/plan permission modes across all languages

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
New session composer with real agent configurations:
- Config box: machine, path, agent+model+effort, permission, worktree
- Auto-collapses config to path row when typing, expands when cleared
- Generic picker modal for machine/path/worktree selection
- Per-mode icons: play-forward for acceptEdits/bypass, pause for plan
- Monochrome agent icons, no text selection on web
- Send button sticky to bottom-right, input grows to 240px max
- Native bottom sheet (formSheet iOS, slide-up Android), web popover

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Add cross-platform CLI detection utility (detectCLI.ts) that runs at
  daemon boot and re-checks every 20s on keep-alive
- Add cliAvailability field to MachineMetadata schema (CLI + app side)
- Remove all profile-related code from happy-cli (persistence, run.ts,
  spawn options)
- Delete profile/wizard files from happy-app (15 files including
  ProfileEditForm, NewSessionWizard, useCLIDetection hook, etc.)
- Remove environmentVariables from SpawnSessionOptions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd translations

Clean up all remaining profile and wizard references:
- Remove profile schemas, fields, and functions from settings.ts
- Remove environmentVariables from SpawnSessionOptions in ops.ts
- Remove profile props and UI from AgentInput.tsx
- Remove profile settings item from SettingsView.tsx
- Remove wizard pick screen routes from _layout.tsx
- Remove enhanced session wizard toggle from features.tsx
- Delete unused SessionTypeSelector component
- Clean up settings.spec.ts to remove profile test cases
- Remove profiles and newSession translation sections from all 10 languages
- Add placeholder new session screen at /new route

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace placeholder with the session composer screen wired to:
- Real machines from useAllMachines() with online/offline status
- Real paths from session history per selected machine
- CLI availability filtering from machine.metadata.cliAvailability
- Session spawning via machineSpawnNewSession() with agent type
- Auto-select first online machine on mount
- Persist lastUsedAgent to settings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…reen

Wire up onKeyPress handler to MultiTextInput so Enter sends on web
when agentInputEnterToSend setting is enabled (Shift+Enter for newline).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ng response format

Three bugs in the Codex MCP elicitation flow:

1. MCP SDK's ElicitRequestSchema uses strict Zod objects that strip
   codex_call_id, codex_command, codex_cwd from params. Fixed by
   rebuilding the schema with z4mini.looseObject() to preserve them.

2. Client.setRequestHandler checks def.value for method literal but
   z4mini.literal() uses def.values. Fixed by reusing the original
   method schema from ElicitRequestSchema.

3. Response format was { decision: 'approved' } but MCP expects
   action: 'accept'|'decline'|'cancel' and Codex additionally reads
   decision. Fixed by returning both fields.

Also adds experiments/codex.ts standalone test script that confirms
the full permission flow works end-to-end.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oute

Prevents stacking multiple composer screens when pressing "new session"
button repeatedly. router.navigate reuses the existing screen if already
focused.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…spacing

- Add ActivityIndicator to send button while session is spawning
- Hide CodexReasoning, GeminiReasoning, think, and change_title tools
- Reduce desktop bottom padding and horizontal padding on wide screens

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a "CLI Availability" section showing installation status of Claude,
Codex, Gemini, and OpenClaw CLIs with last detection timestamp. Data
comes from daemon-side detection via machine metadata.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The new session screen tracked the user's selected permission mode and
model but never wrote them to the session record before sending the
initial message. sync.sendMessage() reads these from storage, so the
first message always arrived at the daemon with permissionMode: "default".

Now sets updateSessionPermissionMode/updateSessionModelMode on the
session immediately after creation, before sendMessage is called.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Persist the new-session form (prompt, machine, path, agent, model,
permissions) across navigation via a Zustand store backed by MMKV.
Show offline machines in the machine picker with "last seen" timestamps,
gray out config options when the selected machine is offline, and display
an offline help banner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When "new worktree" is selected, create a git worktree via
machineBash before spawning the CLI session in that directory.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Add listWorktrees() that queries `git worktree list --porcelain` via
machineBash and populates the worktree picker with existing worktrees.
Users can now pick an existing worktree instead of always creating new ones.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…ession delete

Merge createWorktree.ts and generateWorktreeName.ts into a single
worktree.ts module. Export shared WORKTREE_PATH_MARKER constant and
isWorktreePath() helper. Add removeWorktree() and call it (best-effort)
when deleting a session that was in a worktree.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
… call

When Claude Code automatically calls EnterPlanMode, detect the tool call
in the message stream and switch the session's permission mode to plan,
so the input bar reflects the mode change without manual intervention.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
bra1nDump and others added 29 commits March 21, 2026 03:12
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Replace DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING env var
approach with a dedicated local log receiver server + app setting.
The app can now point at prod sync server for data while sending
console logs to a separate local server for debugging.

- New package: happy-app-logs — minimal HTTP server (~90 lines)
  that writes to ~/.happy/app-logs/ and stdout
- App: new Log Server URL setting on dev screen (MMKV-persisted)
- App: remoteLogger.ts reads from MMKV/EXPO_PUBLIC_LOG_SERVER_URL
- Environments: replaced EXPO_PUBLIC_DANGEROUSLY_* with
  EXPO_PUBLIC_LOG_SERVER_URL pointing to localhost:8787

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove reconnect handler that fired fetchMessages for all 150 sessions
  simultaneously, causing JS thread to block for seconds. Session metadata
  and permissions are still refreshed via sessionsSync; messages are fetched
  lazily per-session when viewed (SessionView already re-fetches on
  realtimeStatus change).

- Remove SwiftUI ContextMenu wrapper (Host/ContextMenu from @expo/ui) from
  session list FlatList items — 148 native SwiftUI views were causing layout
  measurement failures (items rendered at 0 height) and severe performance
  degradation. Long-press now opens the same PopoverMenu used on web.
  ActiveSessionsGroup still uses native menus (only ~5 items, not virtualized).

- Add FlatList performance props (windowSize, maxToRenderPerBatch,
  initialNumToRender, removeClippedSubviews).

- Reduce message validation error logging from 3x console.error with full
  JSON dump per unrecognized message type to a single console.warn line.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SwiftUI <Host matchContents> from @expo/ui causes height measurement
instability in FlatList - views report different heights on different
measurement passes, corrupting layout when scrolling back to previously
seen items. Remove SessionActionsNativeMenu (SwiftUI ContextMenu) from
all list components: SessionsList, ActiveSessionsGroup, and
ActiveSessionsGroupCompact. ChatHeaderView keeps it (single static
instance, not in a list).

Also remove long-press/context menu handling from session list items
for now - will re-add with a non-SwiftUI approach later.

NOTE: @expo/ui SwiftUI Host components should never be used inside
virtualized lists (FlatList/FlashList).

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Resume session is experimental and not ready for all users. Add
expResumeSession setting (default: false) with toggle in Features
settings screen. When disabled, resume button is hidden from session
view, session info, and context menus.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code)

via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>

Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code)

via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>

Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@ex3ndr ex3ndr merged commit 00fd1b7 into main Mar 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants