Skip to content

Keep Cloud agent chat recoverable when browser storage fails - #15968

Merged
teamleaderleo merged 2 commits into
mainfrom
fix/cloud-chat-storage-recovery
Sep 30, 2026
Merged

teamleaderleo merged 2 commits into
mainfrom
fix/cloud-chat-storage-recovery

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

When browser storage rejects access or writes, Agent Chat can fail to mount, throw during option changes, or interrupt failed-start cleanup before returning to the composer. The chat now uses one guarded storage facade for provider preferences, working directories, and draft handoffs. A failed write preserves the latest value in memory, and a failed removal preserves a tombstone so a consumed draft does not reappear.

Healthy storage keeps its existing behavior, including observing changes from other views. The fallback lasts for the current page; it cannot persist across a reload when browser storage remains unavailable.

Changelog

  • Fixed: Keep Cloud agent chat and failed-start draft recovery usable when browser storage rejects access or writes.

Validation

  • bun run test/options-store.test.ts: fails with SecurityError at test commit 9f9b116d23d4; passes at 6b29ab82ab5a.
  • bun run check: TypeScript check, 23 assertion scripts, and 50 Bun tests passed.
  • bun run build: passed.
  • python3 scripts/verify-local.py --affected upstream/main: all 15 selected checks passed.
  • Headless Chrome with the production bundle and an isolated fake WebSocket: denied-storage and quota-error scenarios both mounted the composer, changed permission mode, submitted a prompt, and recovered the exact prompt after an injected start failure. Both completed with no page errors; no real agent or Cloud VM was started.
  • git diff upstream/main --check: passed. No user-facing copy changed.

Summary by cubic

Fixes Cloud agent chat so the composer stays usable when browser storage rejects access or writes. All storage access—provider preferences, working directories, and draft handoffs—now goes through a guarded facade that keeps the latest value in memory when writes fail and keeps a tombstone on failed removal so a consumed draft doesn't reappear. Healthy storage keeps existing behavior, including observing changes from other views; the in-memory fallback lasts for the current page only and can't survive a reload.

Bug Fixes

  • Added regression coverage for denied access (SecurityError), quota errors, and failed draft removal.

Written for commit 6b29ab8. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Sep 30, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 19 seconds.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 9dfc4596-8aaa-4b07-a43b-a5eb2a9eefe8

📥 Commits

Reviewing files that changed from the base of the PR and between 40a636e and 6b29ab8.

📒 Files selected for processing (9)
  • agent-chat/src/browser-storage.ts
  • agent-chat/src/components/Chat.tsx
  • agent-chat/src/components/Composer.tsx
  • agent-chat/src/hooks/useCatalogs.ts
  • agent-chat/src/options-store.ts
  • agent-chat/src/session.ts
  • agent-chat/test/browser-storage.test.ts
  • agent-chat/test/options-store.test.ts
  • agent-chat/test/session.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review: land. Resolving the storage object lazily inside each operation's try is the right shape, and three of the four failure modes are covered: a throwing property accessor (SecurityError), storage absent, and a quota error on write.

The fourth is handled but untested: a storage object that is returned while its getItem throws (Firefox with blocked cookies). I probed it, and it behaves correctly. Reads fall back to the cache, the written value is served back, and the tombstone works.

Verified the app renders with storage entirely unavailable: with throwing getters on both globalThis.localStorage and globalThis.sessionStorage, importing browser-storage and options-store succeeds and all nine operations return sane values. The migration is also complete, zero raw localStorage/sessionStorage references remain outside browser-storage.ts, so nothing is left to throw during first render.

11 mutants, 2 survived:

  • The headline case has no regression test at its one production call site. src/session.ts:407: reverting restoreComposerDraft(draftStorage, ...) to sessionStorage passes the full 50-test suite, session.test.ts included. "Do not lose the user's draft when storage fails" is the point of the PR.
  • The module-load hoist the file's own comment warns about is unguarded. src/browser-storage.ts:53-56 says evaluating a default like storage = localStorage can throw before the try/catch. Hoisting the resolution to module scope passes all six tests, because bun's globalThis.localStorage does not throw, so the test environment structurally cannot reproduce it. The note is right and the code follows it; the guard is aspirational.

Nit: a stray 4-space over-indent in the added block of test/session.test.ts.
— Raindrop g2 🫧 / Run: run_worker_20260930_3fc64ba6

@teamleaderleo
teamleaderleo enabled auto-merge (squash) September 30, 2026 20:54
@teamleaderleo
teamleaderleo merged commit e6e6982 into main Sep 30, 2026
58 checks passed
@teamleaderleo
teamleaderleo deleted the fix/cloud-chat-storage-recovery branch September 30, 2026 20:54
@github-actions

Copy link
Copy Markdown
Contributor

Merge receipt for 6b29ab82ab: every check was green at merge (12 verified; 16 skipped by policy). Full suite runs on main after merge.

rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 30, 2026
5e83d80 Keep agent mode controls reachable and respect disabled choices (manaflow-ai#15971)
24f1ee0 fix(codex): arm the transcript monitor's watch before it reads (manaflow-ai#15913)
17f370e fix: pass the action reference for untrusted setting tab-bar buttons (manaflow-ai#16223)
5e33b84 Agent messages that never land in a human's draft: cmux agent message (manaflow-ai#15279)
522ba05 fix(sidebar): replay agent runtime changes for late observers (manaflow-ai#15829)
3016cf3 Fix browser state helper package convention (manaflow-ai#16205)
b1fd787 Preserve agent Stop completion before session teardown (manaflow-ai#16122)
7ba9740 Prevent duplicate pool VMs after lost create responses (manaflow-ai#15946)
e6e6982 Keep Cloud agent chat recoverable when browser storage fails (manaflow-ai#15968)
d8f62dc fix(ci): production-secret jobs run only from protected refs (manaflow-ai#16171)
8aa9b5c fix(agents): isolate OpenCode workspace auto-naming (manaflow-ai#16210)
7bce471 Add cmux agent hibernate and wake (manaflow-ai#15308)
90d2fb9 fix(agent-chat): surface a rejected send on the transcript branch (manaflow-ai#16216)
d01e8ce fix: list setting actions in Actions discovery so main compiles (manaflow-ai#16222)
b3ca418 Serialize Pi Agent Chat startup before prompts (manaflow-ai#16121)
75650a8 fix: end CodeRouter sessions on team removal; fresh auth for presence mutations (manaflow-ai#16169)
1831681 fix(web): refuse to publish the Cloud VM daemon port (manaflow-ai#16144)
258c2ee Let remote workspaces use cmux agent message through the SSH relay (manaflow-ai#15863)
3b196d0 Merge pull request manaflow-ai#16160 from manaflow-ai/ci/failfast
f02bdec Fix browser state restoration ordering (manaflow-ai#16204)
2fdf7d0 fix(coderouter): pin the OpenCode provider address per request (manaflow-ai#16165)
aaebb18 Fix Cmd+I notifications popover anchor (manaflow-ai#14582)
ef3e658 Preserve valid Claude hook sessions after decode drift (manaflow-ai#16196)
a0660ce test: avoid fixed cancellation delay
6e997e2 Fix narrow pane tab close UX (manaflow-ai#15957)
a018381 ci: run process tree regression in guard preflight
723bbe6 fix(ci): bound artifact fallback at workflow call sites
7cbc73e test: require caller bounded artifact downloads
6120003 fix(ci): retain artifact download action
c801205 test: keep artifact fallback action wired
c1f0509 docs: record overstay evidence and bounded transfers
e91d51b fix(ci): bound artifact download fallback
a2679ce test(ci): require bounded artifact fallback transfer
ef447e2 ci: bound process tree reaping after kill
8f342fc test: bound process tree reaping
5d7af99 test: update cancellation guard expectations
984bf0c Merge remote-tracking branch 'mf/main' into ci/failfast
2c47268 Merge commit '57fd5ac4df7641c05eb73df76fe3554a2a604264' into ci/failfast
83998ac ci: skip cancelled iOS status rollup
bd5692e ci: stop leaking cancelled test processes
55a1003 ci: reap detached processes on cancellation
0351680 test: bound cancellation cleanup for stubborn CI children
bfe79f1 test: cover CI cancellation process cleanup
f20c7d3 ci: cancel useless downstream work
fd0a123 test: require job-scoped CI fail-fast cancellation

# Conflicts:
#	.github/workflows/ci-guards.yml
#	.github/workflows/ci-macos.yml
#	.github/workflows/ci-web.yml
#	.github/workflows/ci.yml
#	.github/workflows/cmux-tui-artifacts.yml
#	.github/workflows/ios-app-store.yml
#	.github/workflows/ios-appstore-upload.yml
#	.github/workflows/ios-testflight.yml
#	.github/workflows/iroh-release-gate.yml
#	.github/workflows/nightly.yml
#	.github/workflows/release.yml
#	.github/workflows/repair-nightly-appcast-content-types.yml
#	.github/workflows/repair-v0-64-25-helper-rpaths.yml
#	.github/workflows/test-e2e.yml
#	.github/workflows/test-ios.yml
#	.github/workflows/update-homebrew.yml
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.

1 participant