Conversation
…142) * feat(install): pin OpenClaw + refresh plugins + IPv4-first DNS Three install.sh + updater changes that together let ClawBox control the OpenClaw runtime on customer Jetsons, instead of devices racing to whatever npm published. **OpenClaw version pin** - New file: config/openclaw-target.txt — single source of truth, one line, bumped via PR + beta→main. - install.sh::step_openclaw_install reads the pin (with OPENCLAW_PIN_VERSION env override for QA flows). Falls back to the hardcoded OPENCLAW_VERSION constant if the pin file is missing. - src/lib/updater.ts::getVersionInfo reads the same pin file for the 'Latest' column in the System Update UI. Flattened the env-override branch into plain async/await per CLAUDE.md guidelines. **Plugin refresh after core install** Parses 'openclaw plugins list --json' via inline python3 to find non- bundled plugins, then force-reinstalls each. Without this a 5.12→5.22 core bump leaves @openclaw/codex stuck at 5.12 and the in-UI updater silently reports 'Up to date'. Runs even when core is already at target because plugins can drift independently. **IPv4-first DNS drop-in for the gateway** step_gateway_setup now writes /etc/systemd/system/clawbox-gateway.service.d/dns-ipv4first.conf: [Service] Environment="NODE_OPTIONS=--dns-result-order=ipv4first" Without this, on networks where the ISP advertises an IPv6 prefix but doesn't actually route public v6 traffic (common on home/SMB networks), every Node fetch to a dual-stack host (Telegram polling, OAuth, npm registry, model providers) hangs ~2 minutes on the dead AAAA before falling back. The hung socket starves Node's event loop and makes every WS request slow — surfacing to the user as 'Failed to change effort: Request timeout' on model switches. No-op on networks where v6 works. * fix(chat): rAF scroll race + gated history refetch for deferred replies Two related fixes for the chat UX: **Double-rAF scroll** (new src/lib/scroll.ts) When a chat message is appended while another component in the header re-renders (e.g. the model-picker dropdown becoming visible as the catalog loads), the scroll target was reading the pre-reflow layout and landing above the freshly-added message — user thought their send 'disappeared' until they refreshed. The new scrollToBottomAfterLayout helper waits for two animation frames so the scroll fires after the next paint regardless of which order React batches the renders in. Shared by ChatPopup and ChatApp so they can't drift apart. **Gated 3s history refetch on deferred replies** OpenClaw can ack a turn with 'Sent.' while the real reply is generated server-side a moment later via the delivery-mirror persona pipeline. That reply is persisted to chat history but never streamed back over the WS — the client used to see only 'Sent.' until the user refreshed. After 'final' arrives we now schedule one chat.history refetch 3s later, but ONLY when the final's text was empty or 'Sent.'-shaped. Normal streamed replies (which arrive via delta+final) skip the refetch so they don't pay an extra round-trip per turn. Both ChatPopup and ChatApp get the same fallback path so the windowed chat app stops being silently broken in the same way the popup was. * feat(ui): remove standalone OpenClaw upgrade entry points Both the 'OpenClaw Update' tile in Settings and the OpenClaw ComponentCard in System Update were customer-facing standalone upgrade triggers that bypassed the ClawBox-pinned version. With the pin in place, customers should only upgrade OpenClaw as part of a full ClawBox release — that way the OpenClaw bump rides through beta → main alongside any client code changes that accompany it. - SettingsApp.tsx: dropped the blue 'OpenClaw Update' button. Current OpenClaw version is still surfaced in the version-info section. - SystemUpdateApp.tsx: dropped the OpenClaw ComponentCard; the grid-cols-1 sm:grid-cols-2 collapsed to grid-cols-1 since ClawBox is the only component card now. - SystemUpdateApp.tsx::triggerUpdate narrowed to take no parameter (only the 'full' path is exposed; the 'openclaw' branch is dead). /setup-api/update/openclaw endpoint and startOpenclawUpdate helper are kept as a server-side hook for SSH/MCP/admin triggers — they just have no UI surface anymore. - Dropped the dead outer-scope openclawAvail and the openclawAvail branch from the status useMemo. Status now flips to 'available' on ClawBox deltas only, matching what the UI actually offers. * chore(release): bump version to 3.0.5 * fix: address CodeRabbit review on #142 Three findings from CodeRabbit's first pass: - install.sh PIN_FILE parse switched from `tr -d '[:space:]'` to `awk '{print $1}'`. The old one stripped ALL whitespace and would concat tokens on a multi-field line ("2026.5.22 beta" → "2026.5.22beta"). awk matches updater.ts's `raw.trim().split(/\s+/)[0]` exactly so the two parsers stay identical if the pin format ever grows beyond a single token. - ChatPopup + ChatApp ack-only 3s history-refetch timer is now single-flight: the timer id is stored in ackOnlyHistoryTimerRef and any pending timer is cleared before scheduling a new one. Also cleared in the component unmount cleanup so a tab close during the 3s window doesn't leave a dangling timer. - updater.ts adds OPENCLAW_VERSION_FALLBACK = '2026.5.3-1' (mirrors install.sh::OPENCLAW_VERSION) so a missing pin file no longer desyncs the UI from install.sh — both report the same target version the device would actually install. * fix(chat): suppress ack-only finals from rendered transcript Follow-up to the CodeRabbit outside-diff comment for ChatApp.tsx. The previous ack-only handling scheduled a 3s chat.history refetch but still appended the 'Sent.' / NO_REPLY text as an assistant bubble. On legacy delivery-mirror configurations that produced a brief 'Sent.' flash before the real reply replaced it. Both ChatPopup and ChatApp now compute a shared isAckOnly check (empty text, /^Sent.$/, or protocol sentinel) once, use it to skip the setMessages append, AND use the same flag to drive the dedupe-guarded 3s refetch. Single source of truth in each component. On the pinned OpenClaw 5.22 this branch never fires (codex responds directly without the Sent. dance), but the defensive path is now correct for any fleet device still on legacy delivery-mirror configs.
📝 WalkthroughWalkthroughThis PR establishes OpenClaw version pinning via a config file, updates both the install script and client code to read from it rather than npm, implements chat ack-only response suppression with deferred history refetch, simplifies update UI to show only ClawBox updates, configures gateway DNS to prefer IPv4, and bumps the package version. ChangesOpenClaw pinning, chat, and update flow
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/ChatApp.tsx`:
- Around line 256-286: The ack-only detection currently sets isAckOnly using a
NO_REPLY regex which misses other protocol sentinels; update the condition that
defines isAckOnly (the variable near the setMessages/prettifyAssistantText
block) to use the shared sentinel checker isSentinel(text) instead of (or in
addition to) the /^\s*NO_REPLY\s*$/ test so the ack-only branch (including the
ackOnlyHistoryTimerRef logic and deferred loadHistory call) fires for all
sentinel finals; keep the other checks (empty text and the "Sent." regex) intact
and ensure any references to prettifyAssistantText, loadHistory, and
ackOnlyHistoryTimerRef remain unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 36ad3621-c592-4bf4-a4f4-4e4022392a99
📒 Files selected for processing (9)
config/openclaw-target.txtinstall.shpackage.jsonsrc/components/ChatApp.tsxsrc/components/ChatPopup.tsxsrc/components/SettingsApp.tsxsrc/components/SystemUpdateApp.tsxsrc/lib/scroll.tssrc/lib/updater.ts
Addresses CodeRabbit finding on #143: the local NO_REPLY regex misses other protocol sentinels (still here scuttling around, all good boss, etc. — see chat-sentinels.ts::PROTOCOL_SENTINEL_REPLIES). Without this the ack-only branch wouldn't fire for those sentinels, and the deferred chat.history refetch wouldn't run, so a real reply could be missed. - isAckOnly now uses isSentinel(text) (matches ChatPopup behaviour). - The aborted/error streaming-flush branch and the loadHistory message filter also switched from inline NO_REPLY regex to isSentinel, so every protocol sentinel is handled uniformly across the file.
Promotes the v3.0.5 release from
betato production. All commits have been live onbetaand validated end-to-end on physical Jetson hardware (simon.local). CodeRabbit + CI green on each component PR before squash-merge into beta.v3.0.5 highlights (PRs #142 + #144)
config/openclaw-target.txt. ClawBox now controls which OpenClaw release the fleet runs, instead of every device racing to whatever npm last published. Bumped in a PR → beta → main, fleet follows on next update.5.12 → 5.22core leaves@openclaw/codexstuck at 5.12 and the in-UI updater silently reports 'Up to date'.scrollIntoViewso a new message + dropdown re-render don't race the scroll target. Extracted tosrc/lib/scroll.tsso ChatPopup and ChatApp can't drift.chat.historyrefetch after ack-only finals (delivery-mirror persona pipeline) with single-flight timer ref + unmount cleanup. Ack-only text suppressed from the rendered transcript so no 'Sent.' bubble flashes.isSentinel()helper (was using a local NO_REPLY-only regex). Brings it to parity with ChatPopup so every protocol sentinel fromchat-sentinels.tsis handled uniformly — no sentinel can sneak past as a visible assistant bubble or skip the deferred history refetch.Customer-impact wins shipping with this release
Unrecognized key: "supportedReasoningEfforts"(caused by their OpenClaw being older than 4.26) auto-resolve as soon as the device runs System Update — the pin converges every device on the tested 5.22.Test plan
mainon a Jetson, run the standard update flow, verify version reports 3.0.5./proc/<gateway-MainPID>/environcontainsNODE_OPTIONS=--dns-result-order=ipv4first.Reminders on merge
betabranch — uncheck "Delete branch" on the merge dialog. The workflow depends onbetaexisting for future feature work.