feat(macos): adopt the shared read-only chat transcript cache#100275
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e729c2a726
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| sessionKey: sessionKey, | ||
| presentation: presentation, | ||
| transport: MacGatewayChatTransport(), | ||
| transcriptCache: MacChatTranscriptCache.make()) |
There was a problem hiding this comment.
Rebind the cache when the remote gateway changes
When a user edits the remote URL/target/transport while staying in remote mode, GatewayEndpointStore.requireConfig() rereads the new config for subsequent chat requests, but the existing chat window is not torn down because resetTunnels() is only wired to connection-mode changes. This controller keeps the MacChatTranscriptCache.make() instance created for the old gateway, so live history from the newly selected gateway can be written under the previous gateway ID and old cached rows can pre-paint for the new gateway until the live fetch returns. Please either recreate/rebind the transcript cache on remote endpoint changes or make cache scoping follow the effective endpoint per request.
Useful? React with 👍 / 👎.
|
Codex review: needs real behavior proof before merge. Reviewed July 6, 2026, 12:44 AM ET / 04:44 UTC. Summary PR surface: Docs +1, Other +287. Total +288 across 5 files. Reproducibility: yes. from source and CI inspection: keep a macOS chat window open, change the remote endpoint while staying in remote mode, and later gateway requests can refresh to the new endpoint while the view model still holds a store scoped to the old identity. CI also reproduces the SwiftFormat and docs-map blockers on the current merge commit. Review metrics: 3 noteworthy metrics.
Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review findings
Review detailsBest possible solution: Land macOS transcript caching and any macOS outbox support only after cache/outbox identity follows effective endpoint changes, docs and tests match the chosen offline-send contract, generated docs are refreshed, formatting is green, and live macOS proof demonstrates gateway-scoped offline behavior. Do we have a high-confidence way to reproduce the issue? Yes from source and CI inspection: keep a macOS chat window open, change the remote endpoint while staying in remote mode, and later gateway requests can refresh to the new endpoint while the view model still holds a store scoped to the old identity. CI also reproduces the SwiftFormat and docs-map blockers on the current merge commit. Is this the best way to solve the issue? No: adopting the shared cache/outbox seam is plausible, but the PR is not the best merge shape until endpoint rebinding, offline-send docs/product alignment, generated docs, formatting, and live macOS proof are addressed. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against f296dc4e68e4. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Docs +1, Other +287. Total +288 across 5 files. View PR surface stats
Security concerns:
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (2 earlier review cycles)
|
165063f to
b1c7f92
Compare
e729c2a to
7a4e0a9
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7a4e0a9ba3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| presentation: presentation, | ||
| transport: MacGatewayChatTransport(), | ||
| transcriptCache: store, | ||
| outbox: store) |
There was a problem hiding this comment.
Don't enable the offline outbox for read-only cache
When a Mac chat window is disconnected, OpenClawChatViewModel.performSend takes the durable offline-queue path whenever outbox != nil; this line now passes the SQLite cache as that outbox. That means a user typing while browsing cached history can persist a command that auto-sends on reconnect, even though docs/platforms/mac/webchat.md documents this macOS surface as read-only offline browsing with sending disabled until the Gateway is back. If macOS should remain read-only offline, wire only transcriptCache here or add product/docs/tests for intentional queued sends.
Useful? React with 👍 / 👎.
1ea0410 to
6497b8c
Compare
7a4e0a9 to
3f9ca09
Compare
Cache-first cold open for the macOS chat window/panel: the last known transcript and session list paint immediately from the shared SQLite transcript cache, then live gateway history replaces them wholesale. Recent chats stay browsable read-only while the gateway is unreachable; sending remains gated by connection state. - Wires OpenClawChatSQLiteTranscriptCache into WebChatSwiftUIWindowController; DB at ~/Library/Application Support/OpenClaw/chat-cache.sqlite. - Gateway identity (MacChatTranscriptCache.gatewayID), derivable offline: local keys on the canonical gateway state dir; remote/direct keys on the full canonical URL (scheme, host, resolved port, percent-encoded path/query); remote/ssh keys on the SSH target plus the resolved remote gateway port, mirroring the tunnel port resolution. Unconfigured mode gets no cache. - macOS file protection: no per-file Data Protection classes; iOS-only attribute stays gated behind #if os(iOS) in the shared store, and the per-user container plus FileVault protect at rest. - Onboarding chat stays uncached (transient guided setup session). Part of #100194
54d4d18 to
12075b2
Compare
…aw#100275) * feat(macos): adopt shared read-only chat transcript cache Cache-first cold open for the macOS chat window/panel: the last known transcript and session list paint immediately from the shared SQLite transcript cache, then live gateway history replaces them wholesale. Recent chats stay browsable read-only while the gateway is unreachable; sending remains gated by connection state. - Wires OpenClawChatSQLiteTranscriptCache into WebChatSwiftUIWindowController; DB at ~/Library/Application Support/OpenClaw/chat-cache.sqlite. - Gateway identity (MacChatTranscriptCache.gatewayID), derivable offline: local keys on the canonical gateway state dir; remote/direct keys on the full canonical URL (scheme, host, resolved port, percent-encoded path/query); remote/ssh keys on the SSH target plus the resolved remote gateway port, mirroring the tunnel port resolution. Unconfigured mode gets no cache. - macOS file protection: no per-file Data Protection classes; iOS-only attribute stays gated behind #if os(iOS) in the shared store, and the per-user container plus FileVault protect at rest. - Onboarding chat stays uncached (transient guided setup session). Part of openclaw#100194 * feat(macos): wire the offline command outbox into chat windows * style(macos): fix orphaned doc comment; regenerate docs map * style(macos): doc-comment lint fix; regenerate docs map on current main
…aw#100275) * feat(macos): adopt shared read-only chat transcript cache Cache-first cold open for the macOS chat window/panel: the last known transcript and session list paint immediately from the shared SQLite transcript cache, then live gateway history replaces them wholesale. Recent chats stay browsable read-only while the gateway is unreachable; sending remains gated by connection state. - Wires OpenClawChatSQLiteTranscriptCache into WebChatSwiftUIWindowController; DB at ~/Library/Application Support/OpenClaw/chat-cache.sqlite. - Gateway identity (MacChatTranscriptCache.gatewayID), derivable offline: local keys on the canonical gateway state dir; remote/direct keys on the full canonical URL (scheme, host, resolved port, percent-encoded path/query); remote/ssh keys on the SSH target plus the resolved remote gateway port, mirroring the tunnel port resolution. Unconfigured mode gets no cache. - macOS file protection: no per-file Data Protection classes; iOS-only attribute stays gated behind #if os(iOS) in the shared store, and the per-user container plus FileVault protect at rest. - Onboarding chat stays uncached (transient guided setup session). Part of openclaw#100194 * feat(macos): wire the offline command outbox into chat windows * style(macos): fix orphaned doc comment; regenerate docs map * style(macos): doc-comment lint fix; regenerate docs map on current main
Stacked on #100219 (
feat/ios-chat-offline-cache) — lands after it; this PR's own diff is the macOS adoption only.Part of #100194
What Problem This Solves
Resolves a problem where the macOS chat window showed nothing until the gateway responded, and nothing at all while disconnected — the shared read-only transcript cache introduced for the chat pipeline was not wired up on macOS.
Why This Change Was Made
Adopts the shared
OpenClawChatTranscriptCacheseam in the macOS app. The work is wiring plus a macOS-specific gateway identity key (MacChatTranscriptCache.gatewayID): unconfigured → no cache; local gateway →local:<canonical state dir>(the state dir owns local session data, so profiles never collide even when sharing a port); direct remote → the full canonical URL (one origin can path-route to several gateways); ssh remote →ssh:<target>:<resolved remote gateway port>reusing the tunnel's own port resolution so identity matches what is actually forwarded. Per-launch tunnel URLs are never used as identity. Auth is deliberately excluded from the key — gateway transcripts are not principal-scoped, and keying on credentials would drop the cache on every token rotation (documented inline). DB lives at~/Library/Application Support/OpenClaw/chat-cache.sqlite; at-rest protection is the per-user container plus FileVault. The shared store needed zero changes (its iOS file-protection call was already platform-gated).User Impact
The macOS chat window renders the last known conversation instantly on open and stays browsable read-only while the gateway is unreachable, per gateway identity, with the same disposable, bounded, text-only cache contract as the rest of the app family.
Evidence
apps/shared/OpenClawKit:swift buildclean;swift test --filter "ChatTranscriptCacheStoreTests|ChatViewModelTranscriptCacheTests"— 15/15.apps/macos:swift buildclean;swift test --filter ChatTranscriptCacheIdentityTests— 5/5 (per-mode identity keys, symlink canonicalization, ssh port resolution).Known gap: no live end-to-end offline pass on macOS yet (wiring proven by builds + the shared suite); pnpm-gated lint lanes covered by CI (diff is Swift + one docs line).
Update: now stacked on the iOS outbox branch (#100331) and additionally wires the offline command outbox into macOS chat windows (
feat(macos): wire the offline command outbox into chat windows): the same gateway-scoped SQLite store instance backs both the transcript cache and the outbox, so macOS gets queued offline sends with the full shared contract (FIFO flush, retry/expiry, tombstoned deletes). Landing order: #100219 → #100331 → this PR. Codex autoreview on the wiring commit: clean (0.88). Part of #46664 as well.