Hide model fallback and recovery notices in group and channel conversations while preserving direct-chat notices, persisted state, and lifecycle events.
Co-authored-by: NehoraiHadad <nehorai.hadad.projects@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
resolveHeartbeatRunPrompt can no longer return prompt: null — the only
null producer (inferred commitments follow-ups) was deleted in
4b0151682e — so the two not-due short-circuits in
heartbeat-runner-execution.ts were dead branches. Narrow the resolution
type to string and delete both consumers.
HEARTBEAT_SKIP_LANES_BUSY had no remaining producer and
HeartbeatDeps.getCommandLaneSnapshots no prod reader (the per-lane busy
check was consolidated into requests-in-flight admission). Delete the
constant, the dep, and the tests that only exercised the dead seam;
retarget the retry-simulation tests at live retryable reasons and drop
the stale lanes-busy mention from the troubleshooting doc.
* docs(plan): mark wave-5 rows landed and record Linux as partial
* docs(plan): record trust-model, tool-split, ref-convergence, and rig root cause
* docs(plan): carry wave-4 rows forward, define partial status, order rows by wave
* Control UI: unify chat rails in a tabbed panel
* fix(ui): compact chat side panel navigation
* Polish rail separators around the active tab
* Soften rail tab separators
* Inset the rail tab strip from the resize handle
* Remove the rail tab strip bottom rule
* Add compact close controls to rail tabs
* Redistribute rail tabs after closing a surface
* Restore bottom docking from the rail terminal
* Fade clipped rail tab labels at the edge
* Add drag reordering to rail tabs
* Polish terminal tab chrome
* Polish rail tab close controls
* Regularize rail tab separators
* Space rail tabs evenly
* Add feedback to inactive rail tabs
* Smooth rail tab selection
* Hide unavailable rail menu items
* Preserve browser multi-tab entry
* Support bottom-docked rail tabs
* Polish rail tab sizing
* Refine side panel actions
* Neutralize rail empty-state icons
* Separate dark rail surfaces
* Fade only clipped rail tab labels
* Compact rail header controls
* Refine the surface picker
* Raise and tighten the surface empty state
* Keep Browser and Tasks in the chat topbar
* Unify surface list cards
* Keep embedded rails inside their dock
* Reveal rail tab close actions on demand
* Show full rail labels only when clipped
* Let rail tabs use available space
* Align chat topbar surface controls
* Unify rail surface empty states
* Center rail tab glyphs
* Fade clipped rail labels smoothly
* Keep rail tab labels visible
* Tighten rail tab separator and label fade
* Keep the new-tab control beside the rail tabs
* Keep rail tab separators from reflowing on activation
* Anchor the new-tab control outside the rail tab scroll
* Scroll rail tabs at the floor instead of clipping them
* Tighten spacing between rail tabs
* Report an unusable terminal open response as a readable failure
* Keep the terminal session menu inside the panel
* Share one dock destination cluster across rails, terminal and browser
* Align the terminal mini rail with its management icons
* Tidy panel pickers, browser chrome and task detail retry
* Type the sidebar callbacks object so tsgo:ui passes
* Space out the panel picker rows
* Measure tab strip edges by rect and cancel superseded installs
* Validate every terminal session field and release unusable sessions
* Fold the terminal rail stretch rules into one
* test(ui): match rail tab checks to the tabbed panel header
Anchoring the new-tab control outside the scroll area wrapped the strip in
.side-panel__header-tabs, and every query written as a direct-child chain
started matching nothing. The assertions did not fail loudly: they compared
an empty label list, so three side-panel journeys and the background-tasks
rail went red on a selector detail rather than on behaviour.
Give the label query one named owner scoped to the panel's own header, so
Terminal and Browser strips in the panel body stay excluded while header
layout can keep moving. Freeing that width also means two short tabs now
fit where they used to be squeezed, so the closing step asserts the honest
invariant - a strip that fits again releases the fade - while the overflow
and tooltip regime stays proven earlier in the same journey.
Settle the shared side-panel helper on whichever surface renders: an empty
panel offers its type list and a populated one offers the header menu, and
probing before either existed left it waiting for a control that never
comes. Also drop a shadowed seed binding and an unused locator that oxlint
flagged in the same file.
* fix(ui): adapt session discussion slot to the unified panel layout
* test(ui): capture the bottom dock evidence with a resting divider
* fix(ui): drop the dead previousDock toggle path
* test(ui): align sibling suites with the tabbed panel decisions
* fix(ui): keep narrow-pane hides above the icon-button styling cascade
* fix(ui): create tooltip descriptions via ownerDocument
* fix(lint): merge duplicate terminal header styles and drop dead branches
* refactor(ui): extract embedded panel templates from the pane render
* refactor(ui): delete rail-era dead exports
* test(ui): repair type drift after the rebase
* fix(ui): keep the workspace toggle module-local and settle test caps
* fix(ui): give side-panel tabs their runtime and their own header actions
The tabbed side panel dropped two things the old rails owned. The discussion
tab lost its only runtime registration, so `openclaw-session-discussion` was
never defined and the tab rendered as a permanently blank box; it now loads
through the per-slot lazy runtime map like the other panel types.
Panels also have no header of their own anymore, so any action on the active
panel's content had to reach the shared header. The header now takes panel
contributed actions instead of a discussion-only open-url special case, which
brings back the side-chat "Clear thread" overflow menu — its gateway reset had
no reachable entry point at all in the embedded rail.
* test(ui): anchor the sidebar scroll proof to the panel host
The bounding contract moved from `.sidebar-column__panel` to
`.side-panel__panel`; mounting the detail panel under the removed class let it
grow instead of scroll, so the test failed for its harness rather than for the
behavior it guards.
* refactor(ui): move side-panel wiring out of the chat render monolith
`chat-pane-render.ts` crossed its line budget. The region callbacks are layout
policy, so they resolve in the layout module with the pane injecting only what
it owns, and the two panel-action producers collapse into the single header
contribution the region consumes.
* fix(ui): keep one terminal intent queue per document
A session route mounts the side-panel terminal beside the shell instance kept
for the bottom dock. Each owned a private action array over one sessionStorage
key, so their whole-array writes erased each other's intents and a freshly
mounted panel drained through a reconnect fence it never saw.
The queue, its persisted record, and the fence now belong to the document;
panels bind as executors while connected. The chat pane records a toggle intent
where it observes it, instead of parking the raw event in memory and handing it
to a panel that may not be mounted yet — a service-worker reload in that window
dropped the request, thread id and all, with nothing explaining why. That makes
the deferred-restore handshake dead weight, so it goes.
* fix(ui): let the panel that starts a terminal intent finish it
Opening a catalog terminal from a chat route mounts the side-panel terminal
right after the request, which swapped the queue's executor mid-flight and
stranded the in-flight action; the panel's own mount restore then opened a
plain session instead. The executor that began an action stays responsible for
it as long as it can still run.
* test(ui): cover closed legacy side docks
* refactor(ui): split side panel controllers
* fix(ui): stabilize embedded panel handoffs
* style(ui): format browser panel refresh
* test(ui): settle catalog prepend paint incrementally
* docs(ui): describe the unified chat side panel
* test(ui): settle catalog clock transitions
---------
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
* fix: keep claude-cli prompt-cache prefix stable across turns
Claude CLI has no cache_control breakpoint, so mashed per-turn system
prompts rewrote the native prefix and burned cache hits on follow-ups.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(cli): scope Claude prompt cache suffix
* docs: scope Claude CLI cache guidance
* test(cli): cover Claude cache prompt modes
* fix: gate Claude CLI prompt cache flag
* docs: clarify Claude CLI cache flag gate
* fix: preserve Claude CLI system prompt roles
* fix(claude-cli): close cache flag compatibility gaps
* fix(claude-cli): reject prerelease cache flag versions
---------
Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(workers): run device sessions from Gateway bundles
Install the current Gateway bundle before a device environment becomes ready, verify it at attach and tunnel boundaries, launch only from the immutable namespaced bundle directory, and retire stale environments for idempotent reprovisioning. Remove the local execution mode and preserve the node-local build claim only as temporary inventory metadata for the final projection/cleanup slice.
* docs(runners): record Gateway bundle cutover
* test(ci): repair runner validation fixtures
# Conflicts:
# src/scripts/test-projects.test.ts
* fix(workers): surface outdated node recovery
Keep legacy runner inventory diagnostic-only while exposing the update-and-reconnect action through node, environment, provider, placement, and Control UI surfaces.
* fix(workers): reject legacy inventory with recovery
* fix(workers): bundle worker deploy closure
* test(workers): close bundle cutover gates
* fix(workers): compose browser runtime at build
* fix(workers): satisfy bundle cutover gates
* fix(workers): route temp runtime through infra
* docs(workers): align bundle host guidance
* fix(ui): fence outdated session destinations
* fix(cua-computer): prove Linux X11 live vertical
* test(computer-use): authenticate isolated Linux rig
* fix(gateway): refresh computer use after node approval
* refactor(cua-computer): resolve the plugin manifest by static import
* fix(gateway): break plugin runtime import cycle
* fix(computer-use): bind live rig to committed helpers
Measured on run 31865243804: the 3-part split on
blacksmith-8vcpu-windows-2025 got exactly 2 runners; part 2 queued 211s
and only started when part 1 finished, pushing the wall to 6:45 despite
133-163s job bodies (vs 342s hosted). The runner class admits ~2
concurrent jobs, so any profile that can land on Blacksmith now uses the
single lane; the 3-part split stays for guaranteed-hosted executions
(github mode and dispatches, whose runner expression forces hosted in
every mode). A hybrid retry reruns the single lane hosted - slower but
bounded, and Windows-scoped retries are rare.
Three measured cuts toward the sub-4-minute wall:
- checks-ui-e2e hosted-planner matrix 10 -> 12 rows (11 Vitest shards +
browser-extension). Shard 1 measured 230s with ~123s test body; Vitest
hash sharding leaves count as the only balance lever.
- checks-windows hybrid attempt 1 now rides blacksmith-8vcpu-windows-2025
(retries stay hosted windows-2025). The 3-part hosted split measured a
342s pole on full-graph runs.
- build-artifacts folds the Doctor plugin-index proof (12s), singleton
smoke, and startup-memory check (7s) into one step that overlaps them
on Blacksmith and stays serial on hosted 4-core so the RSS measurement
is unperturbed; any missing startup asset rebuild completes before the
fork so no verifier reads dist mid-write.
* feat(cua-computer): add browser action family
* refactor(cua-computer): split browser action files
* refactor(cua-computer): move the shared act-params type to the leaf module
* fix(cua-computer): carry the contract params import with the moved type
* feat(workers): install Gateway bundles on nodes
Add a private paired-channel install command backed by a one-use Gateway HTTP capability, bounded archive validation, script-disabled dependency materialization, and atomic gateway-namespaced publication. Existing device provisioning remains on the local-build path until the next cutover slice.
* docs(runners): record node bundle installer
Sub-agent caching audit tranche, all measured on runs 31856622489 and
31857193681:
- QA Smoke parts rebuilt the runtime twice and packed a docker tgz that no
scenario in the smoke coverage set consumes (verified against the resolved
plan: zero docker-lane, zero Control UI scenarios). One private overlay
build replaces the public-build/ui:build/pack/private-rebuild chain
(~35-50s per part); the run step fails closed if a docker-lane scenario
ever returns, and the guard now pins never-pack-after-private-build.
- report-plugin-sdk-api-diff is pure reporting (nothing passes
--require-acknowledgement); it now runs on manual/release dispatches only.
- check-npm-lock resolved ~94 npm graphs against the registry on every push;
push/PR runs now use the reviewed --changed scoping (2.4s on dep-free
diffs) with a fail-open full sweep on base-fetch failure, and dispatches
keep --all for registry-drift coverage.
- Six restore-test-caches gates said 'github' only while the writer already
runs in hybrid; hybrid attempt-1 lanes now restore the transform seed.
- QA smoke lane reads the protected build compile cache; test-type stripes
widen to five.
Green tip run 31855637027 measured the remaining poles: ui-e2e shards up to
282s (imbalanced eight-way split), report-plugin-sdk-api-diff 246s on 4 vCPU,
and 220s test-type stripes (tsgo saturates a machine per graph, so stripe
count is the lever, not cores). Widen hosted-profile ui-e2e to ten rows,
test-type stripes to four, and lift the SDK diff to the 8 vCPU class.
The Windows/Linux cross-reference still pointed at the pre-rename anchor
("experimental, via CUA Driver SDK"); the heading became "experimental,
direct SDK" in 19ace6830b, so the link landed at the top of the node
computer-use page instead of the fulfiller section.
First hybrid main cycles (runs 31850815388-31851455918) landed at 5:47-6:32
walls with every remaining pole a hosted lane: the three test-type stripes
(232-269s), check-additional-runtime-topology (274s), check-dependencies
(260s), and report-plugin-sdk-api-diff (209s). Route them to their Blacksmith
labels on hybrid attempt 1 (retries stay hosted) and lift runtime topology to
the 8 vCPU class.