* fix(acpx): migrate codex adapter to maintained @agentclientprotocol/codex-acp
The bundled codex ACP adapter pinned the npm-deprecated
@zed-industries/codex-acp@0.16.0, which embeds codex crates from the
rust-v0.137.0 era: GPT-5.6-era models are unknown to it and sessions fail
with 'requires a newer version of Codex'. Migrate to the maintained
@agentclientprotocol/codex-acp@1.1.4 (Node CLI, depends on @openai/codex
^0.144.4 matching the codex plugin pin):
- Wrapper generation resolves the adapter bin from its package manifest and
delivers startup config via CODEX_CONFIG; OpenClaw-owned wrapper args are
stripped before launch. Explicit new-adapter commands keep their own
-c/--config args untouched; only retired Zed commands migrate, with their
-c TOML overrides (dotted/quoted keys, attached forms, MCP config)
faithfully converted.
- Model overrides emit the bare model id plus a separate
model_reasoning_effort; composite model/effort ids are accepted as parse
input but never emitted.
- Process reaper recognizes the maintained adapter tree and packaged codex
app-server children without claiming native codex plugin processes.
Live proof: direct adapter smoke with local subscription auth initialized
1.1.4, advertised model gpt-5.6-sol with separate medium effort, and
completed a turn. Fixes#108664
* fix(acpx): pin codex-acp 1.1.2 to satisfy the lockfile release-age gate
1.1.4 is under the 48-hour minimumReleaseAge and fails lockfile
verification repo-wide. 1.1.2 (2026-07-09) passes the gate, carries the
same CODEX_CONFIG startup contract, and its ^0.144.0 codex dependency
resolves to the same 0.144.4 the codex plugin pins. Lockfile re-resolved
from main without any release-age bypass; shrinkwrap regenerated.
* chore(acpx): split oversized auth-bridge test file, register adapter as CLI-resolved dep
codex-auth-bridge.test.ts crossed the max-lines gate after the migration
regressions; the command-migration cluster moves to
codex-auth-bridge.migration.test.ts. knip's CLI-resolved dependency list
swaps the retired zed adapter for @agentclientprotocol/codex-acp (invoked
via generated wrapper/npx, never imported).
* fix(anthropic-vertex): shim native fetch so Google auth avoids gaxios's node-fetch import
@anthropic-ai/vertex-sdk's bundled gaxios only uses native fetch when a
global `window.fetch` exists; otherwise it dynamically imports
`node-fetch`, which can fail to resolve depending on how the plugin's
dependencies are installed. That failure surfaces deep inside gaxios's
token-exchange path as "Cannot convert undefined or null to object",
breaking every Vertex auth request (#107341). The same gaxios root
cause hit a different provider in #41380, where a global window.fetch
shim was the confirmed workaround.
Fixes#107341
* fix(anthropic-vertex): compare window directly against undefined
oxlint's unicorn/no-typeof-undefined flagged the typeof check; a
direct comparison is safe here since target.window is a property
access, not a possibly-undeclared identifier.
* fix(anthropic-vertex): keep ADC fetch provider-local
Co-authored-by: Ian Alloway <ian@allowayllc.com>
* test(anthropic-vertex): inject auth in API fixtures
Co-authored-by: Ian Alloway <ian@allowayllc.com>
* refactor(anthropic-vertex): clarify auth transport contract
Co-authored-by: Ian Alloway <ian@allowayllc.com>
* fix(anthropic-vertex): preserve ADC proxy routing
* chore: leave changelog to release workflow
---------
Co-authored-by: Ian Alloway <ian@allowayllc.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(daemon): write Windows gateway launchers in encodings wscript/cmd can decode
gateway.vbs and gateway.cmd were written as UTF-8 without BOM, but
wscript.exe only reads .vbs as ANSI or UTF-16 LE with BOM and cmd.exe
reads .cmd in the console OEM code page, so installs under CJK profile
paths failed with "file not found" (#107416).
Write .vbs as UTF-16 LE with BOM, write non-ASCII .cmd content in the
system code page when it matches the console page (CJK/Thai locales),
and BOM-sniff plus code-page-fallback on read so launchers from older
installs keep parsing and migrate on refresh. The hidden .vbs launch
path originates from #95480, which addressed console visibility only.
* refactor(daemon): drop unused WindowsLauncherScriptFormat export
The type is only referenced by encodeWindowsLauncherScript's format
parameter within the module, so the export tripped check-deadcode-exports.
Keep it module-local.
* fix(daemon): mark code-page cmd launchers with their encoding for deterministic readback
Prepend an ASCII '@rem openclaw-launcher-encoding=<label>' line to code-page
.cmd launchers and decode by that marker instead of sniffing UTF-8. Some GBK
byte sequences are valid UTF-8 (隆 = C2 A1 reads as ¡), so the old sniff
silently corrupted readback and rejected valid paths; the marker makes decode
deterministic and drops the code-page probe (a PowerShell spawn) from the
frequent readScheduledTaskCommand poll path.
Also fix the representability guard for euc-kr: Node ICU decodes euc-kr as
KS X 1001 only, but Windows code page 949 is cp949/UHC, so the TextDecoder
cross-check false-rejected ~8,800 UHC extension syllables (똠 = 8C 63) that
iconv encodes and cmd.exe reads fine. Verify euc-kr via iconv's own cp949
round-trip; keep TextDecoder for the other five labels.
* fix(infra): write Windows restart helper scripts through the launcher encoder
The update-time restart helper wrote its temp .cmd as raw UTF-8 while
embedding the restart-log path, task name, and task script path, so a CJK
profile path or task name broke the same way as the gateway launchers
(#107416). Route the write through encodeWindowsLauncherScript: ASCII content
stays byte-identical UTF-8, CJK content gets the marked code-page encoding, and
an unrepresentable task name now fails the restart attempt cleanly instead of
writing a script cmd.exe would misread.
* chore(deps): minimize pnpm-lock delta for the iconv-lite promotion
Reset pnpm-lock.yaml to origin/main and re-add only the iconv-lite root
importer entry, dropping the unrelated @types/node peer-context flips and
audio-decode deprecation metadata that a mismatched-toolchain regeneration had
pulled in. The diff vs main is now the three-line importer entry only; the
version already resolves in main's tree via express -> body-parser/raw-body.
* refactor(windows): centralize launcher encoding
Co-authored-by: Jason Yao <wsyjh8@gmail.com>
* style(windows): format launcher encoding test
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
Adds a Discord Activities integration so an agent can show a self-contained
HTML widget to Discord users, opened as a sandboxed Activity inside the client.
Off by default: routes, the discord_widget tool, and the launch handler register
only when channels.discord.activities is configured. OAuth identifies the user
and gates on the account allowlist; widget lookup is capability- or
instance-validated; token exchange is rate-limited; widget HTML runs in a
no-network sandboxed iframe.
* feat(linux): canvas via CLI-node + Tauri app IPC bridge
* refactor: extract gateway helper modules
* build(linux-canvas): register plugin package in lockfile
* fix(linux-canvas): move canvas advertise test out of core, regen docs/protocol/deadcode
* fix(gateway): break node-catalog/registry import cycle via leaf normalize module; add canvas glossary term
* style: oxfmt invoke.ts and runtime.ts after buildNodeEventParams extraction
* fix(linux): load Canvas WebView via dedicated data_directory context
Wry's Linux/WebKitGTK incognito mode discards Tauri's registered
WebContext (wry webkitgtk/mod.rs), so the Canvas window got a fresh
ephemeral context without the openclaw-canvas:// scheme handler — the
bundled A2UI page never committed (stayed about:blank) and every A2UI
command timed out. Use an isolated cache-backed data_directory instead,
which keeps the protocol handler while still isolating Canvas storage
from the dashboard window.
* fix(linux): keep Canvas WebView ephemeral via incognito + data_directory
Autoreview flagged that a dedicated data_directory alone persists Canvas
browser state (cookies, localStorage, IndexedDB, service workers) across
restarts, so an agent that navigates Canvas to a site could leak an
authenticated session into a later session. iOS uses a non-persistent
store; Linux should match.
Add .incognito(true) alongside .data_directory(): the distinct directory
gives Tauri a fresh WebContext key so it still attaches the
openclaw-canvas:// protocol closure, and incognito makes Wry swap in a
fresh *ephemeral* context carrying those protocols. Live-verified on a
Wayland/WebKitGTK box: the bundled page still loads
(location.href=openclaw-canvas://localhost/index.html, openclawA2UI
present, A2UI renders) and the canvas-webview dir holds no persistent
cookie/storage files.
* fix(feishu): upgrade single newlines to paragraph breaks in post md messages
* fix(feishu): move post-md normalization out of adapter chunker, add card-mode regression test
* fix(feishu): normalize post-md newlines before chunk/limit decisions
* fix(feishu): remove post-md normalization from outbound chunker
Move newline normalization out of feishuOutbound.chunker so card and
document-comment text paths are not affected. Post-md normalization
remains scoped to sendOutboundText, buildFeishuPostMessagePayload, and
editMessageFeishu where the render mode is known.
Add regression test verifying the chunker preserves single newlines
unchanged.
Ref: https://github.com/openclaw/openclaw/pull/99394#issuecomment-4873230477
* fix(feishu): route card on raw text, add post-md expansion length guards
* fix(feishu): scope reply target to first subchunk in expanded post fanout
* fix(feishu): skip redundant post-md normalization for pre-chunked outbound and reply text
ClawSweeper review identified that subchunks from already-normalized
post-md text re-enter sendMessageFeishu which normalizes again. If a
chunk boundary falls inside a fenced code block the second chunk lacks
the opening fence, so findCodeRegions treats code newlines as prose and
expands them to paragraph breaks.
Add alreadyNormalized flag to buildFeishuPostMessagePayload and
sendMessageFeishu so pre-normalized callers (sendOutboundText overflow
path and reply-dispatcher sendChunkedTextReply) can skip the redundant
normalization pass.
* fix(feishu): convert markdown tables before post-md newline normalization in outbound path
ClawSweeper identified that sendOutboundText normalizes newlines before
sendMessageFeishu runs convertMarkdownTables. The blank lines inserted by
normalization break markdown table row contiguity, so raw-mode Feishu
accounts can lose table rendering while fixing paragraph spacing.
Convert tables before the newline upgrader so the converter sees the
original table structure. sendMessageFeishu runs convertMarkdownTables
again internally, but that is a no-op on already-converted text.
This matches the reply-dispatcher path which already converts tables
before normalization.
* chore(feishu): avoid exporting internal card builders, test through public send API
The card-mode newline preservation tests now exercise sendMarkdownCardFeishu
and sendStructuredCardFeishu instead of calling buildMarkdownCard and
buildStructuredCard directly. This keeps the SDK surface unchanged from
upstream/main.
The schema-2.0 width config and buildStructuredCard header-fallback tests
are removed because upstream/main already covers them under the sends
cards with schema-2.0 width config describe.
* fix(feishu): preserve markdown newlines in posts
* fix(feishu): preserve multiline markdown structure
* fix(feishu): preserve markdown structure while chunking
* fix(feishu): satisfy package and type gates
* fix(feishu): chunk posts within byte envelope
* fix(feishu): restore channel chunker contract
* chore(feishu): leave release notes release-owned
* chore(feishu): shrink max-lines baseline
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(linux): add node device capabilities
* fix(linux-node): actionable pending-approval error + node-host advertise integration test
* fix(linux-node): map geoclue access-denied to LOCATION_DISABLED; floor camera maxWidth to avoid zero-height scale
* fix(linux-node): clamp small camera maxWidth to 2 instead of default
* docs(linux-node): clarify where-am-i -t is a process timeout, not update throttle
* refactor(gateway): extract legacy-node filter + rejection hint to fit LOC ratchet; docs-map + deadcode baseline
* fix(gateway): drop now-unused DEFAULT_DANGEROUS_NODE_COMMANDS import after hint extraction
* test(node-host): drop imports orphaned by removed error-code test
* refactor(ui): add Web Awesome foundation
* refactor(ui): migrate shared menus and chat controls
* refactor(ui): migrate page controls to Web Awesome
* refactor(ui): migrate workboard controls to Web Awesome
* fix(ui): harden Web Awesome interactions
* test(ui): type Web Awesome dropdown state
* fix(ui): preserve menu focus and selection
* fix(ui): restore agent picker focus
* fix(ui): track keyboard menu dismissal
* fix(ui): ignore stale session menu closes
* fix(ui): complete Web Awesome keyboard migration
* fix(ui): repair Web Awesome lockfile graph
* docs(ui): keep release note in PR body
* fix(ui): satisfy Web Awesome migration gates
* perf(ui): keep Web Awesome theme in startup stylesheet
* fix(ui): replace reopened Web Awesome session menus
* fix(ui): keep replacement session menus open