Changelog

v0.2.39

Fixes

  • app.foxl.ai white screen fixed. A vite chunking quirk was bundling two copies of React into the relay-only build; the second copy hit the `__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE` symbol mismatch on boot and rendered nothing. The build now dedupes React across the workspace symlink and lands all of `react`, `react-dom`, and `react/jsx-runtime` in a single `vendor-react` chunk. Verified live: zero `CLIENT_INTERNALS` references in `vendor-radix` / `vendor-ui` / `index` chunks.
  • Sidebar borders no longer render black, electron no longer starts in dark mode. `index.css` was using `@import '@foxl/ui/styles.css'` after `@font-face` and `@tailwind` directives, which PostCSS silently drops. Every `--background` / `--border` / `--sidebar-*` HSL var was undefined; tailwind's `border-border` resolved to `hsl()` (invalid) and the browser fell back to black. Tokens are inlined into `index.css` again so the rule order does not matter. `@foxl/ui/styles.css` stays the canonical source for forge/web.

Cross-product

  • Foxl Code (code.foxl.ai) shipped as a separate product on the same monorepo. Foxl Desktop and Foxl Code share `relay.foxl.ai` for auth and the credit pool but version independently. See `VERSIONING.md` at the monorepo root for the full strategy. Foxl Code release tags carry the `forge-v` prefix so the two products do not interfere.
  • `packages/ui` now exposes shared layout primitives (`AppShell`, `NavSidebar`, `TopBar`, `Breadcrumbs`) consumed by Foxl Desktop and Foxl Code so the chrome stays in sync between the two products.
  • React bumped to 19. `foxl/apps/web` and `packages/ui` now agree on `react ^19.0.0` and `react-dom ^19.0.0`; recharts bumped to `^3.8.1` to keep peer-dep alignment. No user-visible UI change beyond the rendering fixes above.

Foxl Code (code.foxl.ai)

  • `/tasks` rebuilt around a bottom-pinned composer. Active tasks (queued/running/review) and recent five closed tasks render above the composer on the empty-state view, with an 8s background poll so status changes show up without a manual refresh. Pressing Enter now surfaces a clear inline error if no repo is connected or no paste is typed - it used to swallow the keystroke silently. Submit + topbar GitHub buttons flattened (no shadow / border).
  • `/repos` populates after install. The orchestrator gained a state-bound install URL handler, a manual `POST /api/installations/sync` endpoint, an `adopt`-by-owner endpoint for the legacy install path, and webhook handlers for `installation` + `installation_repositories` so future installs sync without manual action. The Repos page auto-syncs once when installations exist with no repos and exposes a "Sync repos" button. Empty-install state shows both "Sync repos" and "Configure on GitHub".
  • Workspace page added at `/workspace`. Per-user view of the shared S3 Files mount (`/mnt/repos`) - finder-style lazy tree, resizable sidebar, search across loaded files, context menu (Copy path), text / binary preview with a 1 MiB inline cap. Shape ported from Foxl Desktop's WorkspacePage, data layer swapped for the orchestrator's new `/api/files` (S3 ListObjectsV2 + GetObject via aws4fetch). Read-only - the AgentCore container owns writes. Tenant isolation is the bucket prefix `users/{user_id}/{org}/{repo}/...` enforced server-side.
  • Terraform shared S3 Files mount enabled. `forge/infra/terraform` now provisions the VPC, NAT gateways, S3 bucket + S3 Files file system, mount targets per AZ, IAM service role, and the access point. AgentCore endpoint flipped from PUBLIC to VPC mode so the shared cache is reachable. Updated CLAUDE.md to reflect that Pro/Ultra share one mount with prefix-based tenancy (was previously documented as Enterprise-only).

v0.2.38

Gateway "Enable remote access" actually works now

  • Click was a no-op when no access token existed. The `tunnel-toggle` IPC handler returned `{ok: true}` immediately and only kicked off `connectTunnel(at, rt, ...)` if `at` was truthy. If the user wasn't signed in, `at` was empty, the function silently bailed, and the button looked frozen forever.
  • Renderer polls `tunnelStatus()` for up to 15 seconds after a successful start, refreshes `fetchAll()` once the WebSocket attaches, and shows a clear timeout message if it never does.
  • Loading state on the button - spinning Power icon + localized "Connecting…" copy + disabled while awaiting, so a click no longer looks like a no-op.

Gateway page i18n

  • Gateway "No desktop" copy now resolves in every locale. v0.2.37 added new English strings (`relay_no_desktop_desc_v2`, `relay_turn_on_desktop`, `relay_desktop_not_connected`, `relay_desktop_not_connected_desc`, `relay_enable_remote_access`) but didn't add them to the other 9 locale files - users on Korean / Japanese / Chinese / etc. saw raw key names. v0.2.38 ships translations for all 10 locales (en, ko, ja, zh, es, fr, de, pt, ru, ar) plus the new connecting/error strings.

v0.2.37

Sidebar tooltips

  • Collapsed sidebar (Cmd+B) now shows the conversation title as a dark tooltip on hover. Previously the title only rendered when expanded; with the sidebar collapsed to icons there was no way to read which conversation was which. Tooltip mirrors the Cmd+B nav-item style.
  • Top-level nav items and the settings group also surface their label as a tooltip when collapsed - Chat, Workspace, Usage, Settings, Account. Same dark popover styling.
  • Truncated rows show a hover tooltip with the full text in both the sidebar conversation list and the Workspace file/folder tree. The tooltip only opens when the inner span actually overflows (`scrollWidth > clientWidth`); short titles don't trigger it. 700ms delay so it's not noisy.

Workspace polish

  • Editor header reads `MEMORY.md` instead of `WORKSPACE / MEMORY.md`. The redundant `WORKSPACE /` crumb prefix was removed - the sidebar already says WORKSPACE, the breadcrumb just doubled the label.
  • File and folder rows restored to v0.2.15-era density (`text-sm` / `py-1.5`). The earlier compaction to `text-[13px]` made the tree feel cramped against the rest of the app.

app.foxl.ai conversation pagination fix

  • Sidebar conversation list now pages past the first 50. On app.foxl.ai (relay-only mode with desktop tunnel online) the IntersectionObserver attached to the conv scroll container saw `convScrollRef.current === null` even after the list had rendered, so the "load more" sentinel never fired. Switched to a callback-ref + state pattern so the effect re-runs the moment the scroll element actually mounts, plus replaced the IntersectionObserver with a plain `scroll` event listener for reliability inside the nested hidden-scrollbar container.

Gateway page "No desktop" actions

  • Web (app.foxl.ai): replaced the lone Download button with two CTAs - "Turn on desktop" (deep-links to `foxl://open` to launch the installed app) and "Download for macOS" (fallback if the app isn't installed yet).
  • Desktop: when looking at /gateway from inside the installed app, the page no longer suggests downloading the desktop app (you're already in it). Instead it surfaces an "Enable remote access" button that flips the tunnel toggle, with copy explaining what relay connection gets you.

v0.2.36

max_tokens overhaul (Claude Code parity)

  • Every Claude path now requests the model's full output capacity. Previously the desktop chat sent `max_tokens: 4096` whenever thinking was off, the agent layer hard-coded `8192` for every provider, and the relay-only web app capped output at `64000` regardless of model. Long generations were silently truncated. Now Opus 4.7 / 4.6 ask for 128k, Sonnet 4.6 / Haiku 4.5 ask for 64k, matching what the Claude Code CLI sends.
  • Agent layer reads from the catalog instead of a hard-coded constant. `strands-agent.ts` now resolves `entry.maxOutput` per model for Bedrock, Anthropic, OpenAI, OpenAI-OAuth, Claude Code OAuth, and the foxl relay path.
  • Claude Code OAuth catalog corrected. The 32k metadata was misleading - real requests already went out at the catalog max. Updated to 128k (Opus) / 64k (Sonnet) so `/context` percentages and autocompaction triggers reflect actual behavior.

Relay billing fixes

  • Opus 4.7 unblocked on the relay. D1 `model_pricing` was missing a row for `claude-opus-4-7`, so `meter.canRequest()` rejected every request with "Unknown model" - even though the registry listed it. Migration `0011_opus_4_7_pricing.sql` adds the row.
  • Opus pricing corrected to Anthropic's published rates. Catalog and pricing previously inherited Opus 4.1's $15/$75 per MTok. Both Opus 4.7 and Opus 4.6 are $5/$25 per MTok. Updated `model-catalog.ts` and the local D1 row.

Relay, Schedules, Feed, Activities, Logs redesigned

UX polish pass

Subagent recursion guard (the real one)

Subagent result gathering matches Claude Code's Task-tool semantics

Notifications: actions, deep-links, and a working Install Now

`generate_image` tool: gpt-image-2 through ChatGPT subscription

Pixel Office — full redesign pass

Fixes

Improvements

Multi-Agent