feat: move chat into Experimental settings, default automations to chat

Closed
HA
2d ago
+227-227

Summary

  • Chat leaves PostHog. Desktop and mobile each get a device-local Settings → Experimental → Chat switch, off by default. With it on, ACP agents (Claude, Codex, OpenCode, Pi) open as a chat; every other agent opens a terminal. With it off, everything opens a terminal.
  • Automations default to chat. Server-side automation runs ask for a chat whenever the agent supports ACP, with no flag or setting; the host still answers with a terminal for an agent it can't chat with.
  • Presets bar is back on chat tabs. Agent presets already pick chat or terminal from the setting and ACP support.

Why / Context

Chat was gated by the acp-chat PostHog flag, so the audience was chosen per user in PostHog and people could not opt in themselves. Moving it to an Experimental switch makes it opt-in per device. Automations have no device to read a setting from, so they open as chat by default whenever the agent supports it.

How It Works

SurfaceDecides chat vs terminal from
DesktopuseAcpChatEnabled(): the persisted acp-chat store, default false (a stored null from before migrates to off)
Mobilenew acpChatStore (AsyncStorage, acp-chat-v1), default false
Automations (relay + cloud)always chat: true; launchAgentSession checks sessions.capabilities and falls back to a terminal
cloudWorkspace.createnew optional input surface: "chat" | "terminal"; when omitted, the old flag lookup (acpChatEnabled(userId))

In every path, chat needs both the gate and ACP support (acpHarnessForPreset / supportsCloudAgentChat / host capabilities).

Also:

  • "Fork into chat" on terminal headers (gated by agent-surface-fork) is hidden while Chat is off. Before this PR it was offered to people who had no chat at all.
  • "Track file changes per turn" shows only while Chat is on.
  • useIsChatTabActive is deleted; it existed only to hide the presets bar.

Manual QA Checklist

Desktop dev app driven over CDP with real clicks. For "before", the same dev server ran main (changes stashed).

Before (main)After
Settings → ExperimentalNo Chat switch (account not in the flag)Chat switch shown, off, nothing stored
Chat off: Claude presetTerminalTerminal ✅
Chat off: terminal "CLI mode" menuOffers Fork into chatOnly Fork session / Continue with another agent ✅
Chat on: Claude preset—Chat pane ✅
Chat on: Copilot preset (no ACP)—Terminal ✅
Presets bar on a chat tabHiddenShown ✅
  • Turning Chat on reveals "Track file changes per turn"; the setting persists as {enabled: true} (v1)
  • No console errors during the after runs
  • Not run: mobile on a simulator (Experimental switch, chat gates)
  • Not run: a real automation run end to end (covered by hostSession.test.ts, including the unsupported-agent fallback)

Testing

  • bun run typecheck in apps/desktop and packages/trpc: clean
  • apps/mobile typecheck: same 188 errors as main, none new
  • bun test in packages/trpc automation + cloud-workspace (86), desktop settings / tasks / persisted-keys / chat adoption (148+44), all of apps/mobile (248): pass
  • bun run check:i18n: clean (one new mobile string, translated in all 16 locales)
  • biome check: clean

Design Decisions

  • Per-device switch, not an account setting. An account column would need a migration in its own PR first (db-migrations / trpc-compat rules). The device switch matches desktop's existing experimental toggles; automations sidestep the problem by defaulting to chat.
  • Omitted surface keeps the flag lookup instead of meaning terminal: trpc-compat says a changed default must be opt-in, so released desktop/mobile builds, CLI, MCP and SDK behave as they did.

Risks / Rollout

  • Behaviour change for current flag users: desktop and mobile users in acp-chat lose chat until they turn the switch on. Intended: it's opt-in now.
  • Automations for everyone: owners who were not in the flag now get a chat for Claude/Codex/OpenCode/Pi automations. Other agents are unchanged.
  • Client before API: a new desktop or mobile build calling an API without this change sends surface; zod strips the unknown key and the server falls back to the flag. Harmless.
  • Rollback: revert the PR. No data or schema changes; the persisted acp-chat store reads back as before (enabled: true stays on).

Follow-ups

  • Remove the acp-chat flag and packages/trpc/src/lib/acp-chat.ts once no released build omits surface.

Summary by cubic

Moves ACP chat for Claude, Codex, OpenCode, and Pi agents out of the acp-chat PostHog flag into a per-device Settings → Experimental → Chat switch, off by default, on desktop and mobile. Server-side automation runs now default to chat whenever the agent supports ACP and fall back to a terminal otherwise, without consulting the flag.

Behavior changes

  • Users who had chat via the acp-chat flag lose it until they turn the switch on.
  • "Fork into chat" on terminal headers is hidden while Chat is off; "Track file changes per turn" shows only while it's on.
  • The presets bar is back on chat tabs.

API

  • cloudWorkspace.create accepts an optional surface: "chat" | "terminal"; callers that omit it (released builds, CLI, MCP, SDK) keep the old flag lookup, so the input is additive.

Summary by CodeRabbit

  • New Features
    • Added an Experimental Settings option on mobile to open Claude and Codex sessions as chats, with translations across supported languages.
    • Cloud workspace creation now supports choosing a chat or terminal session surface.
  • Improvements
    • Chat availability is managed through a saved setting across desktop and mobile, and workspace launches use that setting to select a session type.
    • Automation-launched sessions now open as chats when the selected agent supports chat.