* feat(webchat): expand truncated assistant messages inline
Replace the per-message Open in canvas button with an inline Show more /
Show less disclosure on truncated assistant messages that fetches full
content via chat.message.get on demand.
* test(webchat): stabilize inline expansion e2e
* fix(ui): satisfy lint and export gates for inline show-more
* test(ui): update shared disclosure selector
---------
Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
* fix(macos): let onboarding replace an auto-connected AI
The AI page auto-tests the best detected candidate and connects without
asking, then hides every alternative route. Add 'Choose a different AI'
to the connected banner: a re-detect pass with auto-activation
suppressed that ends at the picker (candidates, provider sign-in, API
keys). Also disable the manual key Connect button while another test
runs (submitManualKey silently dropped the tap), and isolate a test
that read the machine's real resume store.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(macos): surface real daemon errors past the Node banner
Gateway daemon failures summarized as 'Node.js v26.5.1' because the
summary takes the last non-empty line and Node fatal errors end with a
version banner. Drop trailing banner lines and prefer the last
error-shaped line above them; all other output keeps its last line.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(macos): hand onboarding off to the dashboard custodian
Native onboarding now ends once inference verifies: welcome, connection,
install (when needed), AI setup. Finish opens the dashboard at
/custodian?onboarding=1, where the custodian onboarding owns memory
import, channels, app recommendations, and the hatch (browser-first per
the onboarding redesign). The native memory-import and permissions
pages leave the first-run flow; 'Set up later' keeps the native ready
page. The native navigation bridge gains a validated optional search
field so the handoff can request onboarding chrome; the URL fallback
carries the query alongside the token fragment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(macos): delete the unreachable native memory-import module
The dashboard handoff removed the memory-import page from every flow,
leaving the module reachable only from tests. CI's dead-code scan
rightly flagged the first orphans; remove the whole path (model, page,
mascot wiring, tests) instead of trimming symbol by symbol. The
dashboard's own memory-import surface owns the feature.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(browser): relay CDP compat for Puppeteer clients
Answer Target.getBrowserContexts with the always-empty context list so
puppeteer.connect() (chrome-devtools-mcp) can drive the paired Chrome
through the extension relay without the remote-debugging prompt. Serve
DevTools-style /json/list descriptors (id + type), which also makes the
HTTP tab-list fallback see extension-profile tabs, and add
`openclaw browser extension cdp` to print the relay endpoint and auth
header for external CDP clients.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(browser): avoid map-spread in relay target descriptors
oxc(no-map-spread): build the /json/list descriptor object explicitly
instead of spreading RelayTabInfo inside map().
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* refactor(ingress): consolidate standard raw-event monitors
* fix(ingress): preserve monitor callback typing
* fix(ingress): keep standard profile within line limits
* chore(plugin-sdk): regenerate API baseline for shared ingress profile
* feat(gateway): advertise chat attachment limits on hello-ok
Clients had no way to learn the gateway attachment ceilings, so external
clients hardcoded guesses that drifted from server enforcement. Publish the
two unconditional decoded-size ceilings on hello-ok policy.attachments from
one shared resolver so advertised values cannot drift from the parser.
MIME acceptance and per-message counts stay server-side: they depend on the
entrypoint, the resolved model, and payload sniffing, so they cannot be stated
once per connection.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 17d6c355-8948-4b48-a936-e08b1c8806ef
* feat(gateway): advertise chat attachment limits on hello-ok
---------
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Copilot-Session: 17d6c355-8948-4b48-a936-e08b1c8806ef
* fix(tui): cancel buffered submissions on shutdown
* refactor(tui): preserve callable submit coalescer
* test(tui): run local PTY smoke against built CLI
* fix(channels): record account lifecycle facts
* fix(channels): bound recorded lifecycle grace
* fix(channels): preserve authored health state
* test: dedupe health snapshot type literal and fix spread order
* fix(doctor): restore the pre-migration session index when runs share a store
Restore picked the winning archive by manifest iteration order, newest first.
When a legacy writer recreated an empty sessions.json between migrations, the
newest run had archived that empty file, so restore installed it and then
refused the only archive holding real sessions as a conflict.
Resolve one winning archive per destination before writing anything, preferring
the earliest run whose archive is still restorable. Losing archives are never
deleted and stay visible as conflicts.
Closes#116163
* fix(doctor): fail closed on ambiguous session restores
* test(doctor): key restore conflicts by archive
* fix(doctor): narrow restored archive lookup
* fix(doctor): stream restore archive hashing
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* docs(gateway): document llamacpp toolSchemaProfile for custom llama.cpp endpoints
Custom openai-completions providers pointed at a llama.cpp / llama-server
endpoint bypass the GBNF-safe schema normalization that built-in llama-cpp,
ollama, and lmstudio providers apply automatically. The toolSchemaProfile:
"llamacpp" switch activates it, but the supported value was never named in
the docs, so users hit llama.cpp 400 'failed to parse grammar' on tools with
large minLength/maxLength (e.g. cron trigger.script, maxLength 65536).
- Expand the toolSchemaProfile row to name the values recognized on a model's
compat (llamacpp, gemini) and when llamacpp must be set explicitly;
cross-reference the unsupportedToolSchemaKeywords escape hatch.
- Add a 'Local models (llama.cpp / llama-server)' provider example showing
the required compat.toolSchemaProfile: "llamacpp" on a custom endpoint.
Related to #117070
Co-Authored-By: iCodeMate <noreply@anthropic.com>
* docs(gateway): describe llamacpp profile as stripping, not clamping
Address review feedback on #117120: the fallback sentence claimed the
llamacpp profile "also clamps" constraints, but cleanSchemaForLlamacppGbnf
only removes `pattern` outright and drops `maxLength` at or above the
2000-repetition threshold (LLAMACPP_GBNF_MAX_REPETITION_THRESHOLD). It
never clamps a value, and small bounds like `minLength: 1` are preserved
(confirmed by the projection test at src/plugin-sdk/provider-tools.test.ts).
Reword the escape-hatch sentence to describe the implemented stripping
behavior and contrast it with the unconditional keyword-list drops, so
operators do not expect a preserved bounded constraint.
No code or behavior changes; documentation only.
* docs(gateway): scope llamacpp profile description to its actual transformations
Address follow-up review on #117120: the toolSchemaProfile table row
described `llamacpp` as "rewriting tool schemas into the JSON Schema
subset llama.cpp can compile to GBNF," which overstates what
cleanSchemaForLlamacppGbnf does. The cleaner only removes `pattern`
outright and drops `maxLength` at or above the 2000-repetition threshold
(LLAMACPP_GBNF_MAX_REPETITION_THRESHOLD); it leaves every other keyword
and `minLength` untouched, and those are not guaranteed GBNF-safe.
Reword the row to state the exact transformation contract and explicitly
note it is a targeted compatibility cleaner, not a blanket GBNF-safety
guarantee. No code or behavior changes; documentation only.
Co-Authored-By: iCodeMate <noreply@anthropic.com>
* docs(gateway): tighten llama.cpp profile contract
* docs(gateway): order local model examples
---------
Co-authored-by: iCodeMate <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(cli): update plugins doctor clean message (#115073)
Root cause: plugins doctor reports 'No plugin issues detected.' even when a selected context-engine plugin is quarantined at runtime, because doctor is a fast static control-plane check.
Architectural owner: plugins-cli.runtime.ts
Canonical fix: Change clean status message to direct operators to 'openclaw health' for active runtime quarantine/fallback status.
Production LOC delta: +2 LOC
* fix(cli): update plugins doctor clean message wording (#115073)
Root cause: Refine the diagnostic text message to run openclaw health directly.
Architectural owner: src/cli/plugins-cli.runtime.ts
* fix(cli): clarify plugin doctor scope
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(plugin-sdk): project recorded health facts through computed account status
* fix(plugin-sdk): regenerate API baseline and pin ingressUnavailable literal in test
* fix: gateway service commands refuse a named profile or relocated OPENCLAW_HOME
- Resolve the default install identity against the canonical state directory
for the active OpenClaw home and profile instead of the unprofiled OS
account default.
- `--profile <name>` / `--dev` project `.openclaw-<profile>` state and config
paths, so every named profile was classified as isolated state and refused
`install`, `start`, `stop`, `restart`, `uninstall`, Doctor service repair,
and self-update service handling.
- `OPENCLAW_HOME` relocates all OpenClaw path defaults and is documented for
running as a dedicated service user; a relocated home is now an install
identity. `HOME` alone still is not.
- An `OPENCLAW_STATE_DIR` or `OPENCLAW_CONFIG_PATH` pointing outside those
canonical paths is still treated as isolated state.
- Recovery guidance in the refusal message now names the paths that must match.
Verified: focused vitest shards for the changed suites plus the daemon, CLI,
and doctor suites that consume the identity check; tsgo core and core-test
lanes; oxlint; docs format, MDX, link, and map checks.
* fix(gateway): keep relocated homes isolated
* fix(config): validate service profile identity
* fix(daemon): enforce named-profile service ownership
* fix(update): reject drifted service selectors before probes
* test(windows): prove scheduled task lifecycle
* test(windows): harden scheduled task proof cleanup
* test(windows): bind lifecycle proof to checkout
* test(windows): normalize cleanup exit status
* test(windows): verify effective task privilege
* test(windows): protect scheduled task proof roots
* test(windows): prove listener-owned task lifecycle
* test(windows): fix scheduled task proof contracts
* test(windows): remove redundant mock coercions
* test(windows): measure fallback before task probes
* test(windows): prove scheduled task process origin
* fix(gateway): preserve unmanaged restart fallback
* test(gateway): cover denied restart ownership
* test(gateway): keep restart helper types private
* test(gateway): classify lifecycle helpers as test code
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* docs(config-channels): replace legacy allow:true with enabled:true for Discord, Slack, and Google Chat config examples
* docs(config-channels): repair channel configuration examples
Make the complete Discord, Slack, and Google Chat setup examples valid and keep Slack channel routing on stable IDs.
Co-authored-by: 赵旺0668001248 <zhao.wang1@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>