* fix(doctor): follow symlinked launcher when locating sandbox setup scripts
* refactor(doctor): reuse shared package-root resolver for sandbox scripts
resolveSandboxScript hand-rolled its own cwd/argv1/realpath candidate
scan, duplicating openclaw-root discovery. Reuse resolveOpenClawPackageRootSync,
which already follows the symlinked launcher via realpath and handles
node_modules/.bin and version-manager links, then look for scripts/ under
the resolved package root. Resolves the duplicate-discovery review finding.
* fix(doctor): keep searching cwd for sandbox scripts when first package root lacks them
resolveSandboxScript stopped at the first openclaw package root the shared
resolver returned. argv1 candidates resolve before cwd, so an installed/published
root (package.json present, scripts/ dropped by the npm files allowlist) shadowed
a valid source-checkout cwd and made doctor --fix return null.
- add resolveOpenClawPackageRootsSync: all distinct roots in candidate order
- resolveSandboxScript returns the first root that actually contains the script
- resolveOpenClawPackageRootSync now delegates to the plural (behavior unchanged)
- test: source-checkout cwd shadowed by an installed argv1 root without scripts/
* feat(ui): hide chat avatars in direct sessions
* fix(ui): classify direct threads from full history, not the render window
* fix(ui): classify direct threads by canonical session kind
* fix(ui): match session metadata across equivalent alias keys in the chat thread
* fix(ui): classify unmatched chat sessions by canonical key shape
* fix(ui): keep avatars in global sessions that can aggregate group senders
* docs(ui): note global-alias limitation on chat avatar classification
* feat(ui): add gateway browser panel with annotate and inspect modes
* chore(i18n): translate browser panel strings
* fix(ui): address browser panel review findings (stable tab handles, stale view guard, inspect click suppression)
* fix(ui): make the browser panel reload button reload the remote page
* fix(ui): tile the browser dock against the terminal dock and re-probe evaluate per connection
* fix(ui): label page-reported prompt text as untrusted and accept host:port URL entries
* fix(ui): sanitize inspected selector fragments in the annotation prompt
* fix(i18n): retranslate annotation prompt keys so the page-reported provenance label survives localization
* docs: regenerate docs map for the browser panel section
* chore(i18n): reconcile locale bundles with latest main after rebase
* style(ui): format chat-pane imports after rebase
* chore(i18n): refresh locale metadata after rebase
restrict-plus-operands (Buffer + string) and no-promise-executor-return
tripped the lint shards on every PR since #101783 landed; scoped fixes
only, no behavior change.
Long-running Telegram senders leaked HTTP transports: the client-options cache in extensions/telegram/src/send.ts created an owned undici dispatcher per account/network entry but evicted entries without closing it. Cache entries now own their transport; eviction retires the entry and closes the transport once the operation-level lease releases, so cache pressure never aborts an in-flight, retrying, or still-preparing send. Includes a real-socket e2e regression against a local Bot API server.
Co-authored-by: zhangguiping-xydt <zhangguiping-xydt@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
The workspace rail and message detail panel keyed layout off viewport
media queries, so narrow split-view panes and compact windows got
crushed side-by-side columns, and below 1120px the rail was
display:none with no way to reach workspace files.
Chat panes now measure their own width with a ResizeObserver: under
800px the rail presents as the existing bottom-dock strip (side-dock
controls hide), and when the chat + detail split has under 680px the
detail panel stacks below the thread with a horizontal resize divider.
The phone full-screen detail takeover is unchanged. Bottom-strip path
and summary rows no longer flex-shrink into clipped text.
Closes#104023
* feat(webui): reintroduce opt-in AI purpose titles for tool calls
Restores the chat.toolTitles path removed in #103821, gated behind the new
gateway.controlUi.toolTitles opt-in (default false) so tool rendering stays
fully deterministic with no background model calls unless an operator enables
it. Disabled gateways answer { titles: {}, disabled: true } without loading
the completion runtime, and clients stop asking for the session.
When enabled, titles use canonical utility-model routing: an explicit
utilityModel (operator-chosen provider, like every utility task), else the
session provider's declared small-model default, honoring per-session model
overrides and auth profiles; utilityModel "" disables titles and malformed
refs fail closed — never the primary model. Tool inputs are redacted with the
tools-mode redactor before cache keys or prompts, caller ids are bounded and
never reach the model, and results cache in the per-agent SQLite
cache_entries so repeat views never re-bill.
Also completes two crestodian model-input mock factories that leaked into
sibling tests under shared-registry CI shards.
Fixes#103987
* fix(webui): redact tool-title inputs before truncation
* feat(browser): import Chrome-family system-profile cookies into managed profiles
Import cookies from a real Chrome/Brave/Edge/Chromium system profile (macOS)
into a fresh OpenClaw-managed browser profile so the agent can browse as the
signed-in user. Reads the source Cookies DB via a coherent VACUUM INTO snapshot,
decrypts v10 AES-128-CBC values with the Safe Storage Keychain key (one Touch ID
consent), maps rows to Playwright cookies (FILETIME expiry, SameSite, M124+
domain-hash prefix strip, CHIPS skipped), and best-effort injects them via
addCookies into a mock-keychain profile so they persist without further prompts.
Decrypted values are never logged or returned.
Exposed as agent tool action=importprofile, CLI system-profiles/import-profile,
and POST /profiles/import; action=profiles surfaces importable systemProfiles.
Listing and import are pinned host-local at every surface (gateway, browser
tool, node proxy) since they read the local Keychain and Chrome profiles.
Malformed domain filters fail closed via a shared validator. Gated by
browser.allowSystemProfileImport (default on). Imports cookies only.
* fix(browser): satisfy CI lint (OpenClaw temp dir, Unicode control-char class)
Use resolvePreferredOpenClawTmpDir() instead of os.tmpdir() for the cookie DB
snapshot (messaging/channel runtime tmpdir guard), and match control characters
via the Unicode \p{Cc} class instead of a literal control-char range so the
CLI table sanitizer passes the no-control-regex lint.
* fix(macos): drag the window from split-view pane headers
In the macOS app, split-screen pane header rows (session title strip) now
start a native window drag instead of selecting the title text. The Control
UI posts an openclawWindowDrag script message on mousedown over passive
header chrome; the dashboard window validates the trusted main-frame source
and the in-flight left-mouse press before calling NSWindow.performDrag.
Pane headers are chrome, so they are also no longer text-selectable.
* refactor(macos): move the dashboard failure page out of the window controller
SwiftLint type_body_length: the drag-message handler pushed
DashboardWindowController past 800 lines; the failure-page HTML is
presentation-only and now lives in DashboardFailurePage.
* feat(ui): hover-revealed chat message meta with relative timestamps
* fix(ui): derive relative chat timestamps from the injected clock
* fix(ui): date far-future chat timestamps instead of clamping to just now
* fix(ui): invalidate memoized chat thread on relative-time ticks
* fix(ui): keep relative-time ticks out of the chat live region
* fix(skill-workshop): reject invalid proposal list limits
* refactor(skill-workshop): validate list params before storage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>