* fix: reject malformed UTF-8 in proxy gateway error response bodies
Add { fatal: true } to TextDecoder in readProxyErrorData so invalid
UTF-8 bytes throw a TypeError before JSON.parse, instead of silently
becoming U+FFFD inside the parsed error message.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(agents): prove proxy error UTF-8 boundaries
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Add { fatal: true } to TextDecoder in readHostedCatalogResponseText so
invalid UTF-8 bytes throw a TypeError before the text is JSON.parsed,
instead of silently becoming U+FFFD inside field values.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(browser): resolve upload paths on the owning browser node, not the Gateway
When a browser session is proxied to a remote node, the upload action
previously ran resolveExistingUploadPaths on the Gateway, pinning paths
to a filesystem the node cannot see and rejecting node-local files. The
node-side /hooks/file-chooser route already re-resolves paths against
its own filesystem, so skip Gateway-local resolution whenever the
request is proxied and forward the requested paths as-is.
Fixesopenclaw/openclaw#115251
* fix(browser): transfer uploads to remote browser nodes
* fix(browser): normalize upload abort errors
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(agents): stop apply_patch from silently overwriting existing files
An "*** Add File:" hunk wrote its target unconditionally. When the path
already existed, apply_patch replaced the entire file, returned Success,
and listed the path under "added", so neither the model nor the UI got
any signal that existing content had been destroyed. The "*** Move to:"
destination of an update hunk had the same gap and reported the clobbered
path as merely modified.
The add and move-to branches now check the destination through the patch
file ops before writing and fail closed when it exists. Routing the check
through fileOps keeps it correct on all three backends (workspace-scoped
fs-safe root, raw fs, sandbox bridge). The check runs per hunk in patch
order, so deleting a path earlier in the same patch and recreating it
still works.
* fix(agents): make apply_patch destination creation atomic
The previous guard checked that an add or move-to destination was absent
and then wrote it. A competing writer could create the path in that gap,
after which the write still replaced it, so the no-clobber guarantee did
not hold under contention.
Destination creation now goes through a single exclusive create-if-absent
operation on every patch backend: Root.create for the workspace-scoped
default, an O_EXCL write for the raw filesystem, and a new pinned create
operation in the sandbox mutation helper that opens the target with
O_CREAT|O_EXCL and reports a reserved exit code when it already exists.
PatchFileOps drops its separate existence check.
Resolving the host ops behind an early return removes the repeated
workspaceOnly branch inside each operation and the optional-call dance
that let a missing root silently skip a write.
* fix(agents): complete atomic apply-patch creation
* fix(agents): preserve raced create replacements
* fix(agents): handle fs-safe patch collisions
* fix(agents): publish sandbox creates atomically
* test(agents): cover exclusive create provenance rollback
* fix(agents): use typed exclusive-create signal
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* origin/main:
fix(ui): fall back to updateAvailable.latestVersion for handoff version verification (#112987)
feat(gateway): manage audio and video attachments end to end (#115842)
* fix(config): materialize snapshot mode with the same defaults as load
* test(config): prove snapshot-injected defaults stay out of persisted config
* test(cli): keep unknown-command process test hermetic
---------
Co-authored-by: Kevin Lin <kevin@dendron.so>
* feat(gateway): manage audio and video attachments
* feat(gateway): preserve generated media metadata
* fix(gateway): align seeded managed media
* fix(gateway): scope pending media trust by URL
* fix(gateway): split managed media by trust
* fix(gateway): require terminal media trust unanimity
* fix(gateway): align managed media metadata types
* fix(gateway): refresh managed media contracts
* chore(ci): model managed media test seams
* test(sessions): expect canonical media kinds
Route pattern, dependency, and active conditional schemas through the real plugin config validator while keeping standalone conditional annotations on the empty-config path.
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(cli-runner): scope bundle-MCP cleanup to the run's session, not the process-wide loopback server
On run end, runCliAgentInternal handled cleanupBundleMcpOnRunEnd by calling
closeMcpLoopbackServer(), which tears down the process-wide MCP loopback HTTP
server for the whole gateway. Any concurrent CLI turn or restart-recovered live
session that already baked that loopback port into its --mcp-config is left
pinned to a dead port ("Unable to connect"), while the gateway still reports the
session as recovered. This is the same hazard the embedded-runner (run-loop.ts)
and CLI dispatch (cli-backend-dispatch.ts) paths already avoid by retiring only
session-scoped MCP runtimes.
Retire only this run's session-scoped MCP runtime here too (by session key, with
a session-id fallback), leaving the shared loopback server up for other sessions.
Every CLI spawn/respawn already re-derives the current loopback port and rewrites
mcp.json via prepareCliRunContext, so removing this teardown keeps recovered
sessions on a live transport instead of stranding them.
Fixes#98435
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMPjCgbgHTiB8X3R9XACPW
* fix(cli): preserve rebound MCP session owners
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The spawned agent child process in createAcpClient had no error
listener. If the agent binary failed to start, the error event
would be unhandled and could crash the process.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(codex): restore connected apps for token-authenticated runs
* fix(codex): keep app inventory protocol types private
* fix(codex): align native runtime with Codex 0.146.0
Co-authored-by: Steven Lee <stevenlee@openai.com>
* fix(codex): clean up latest app-server integration
Co-authored-by: Steven Lee <stevenlee@openai.com>
* fix(codex): keep internal protocol types private
* fix(ci): repair current main Codex landing gates
* fix(ci): format inherited code mode matrix
* fix(codex): reconcile native app-server contracts with main
Prepare a verified GitHub-hosted mainline merge while preserving the reviewed Codex 0.146.0 fixes and canonical OpenAI authentication.
Co-authored-by: Steven Lee <stevenlee@openai.com>
* fix(codex): keep QA evidence in its owning plugin
Resolve the current-main Code Mode test rename without resurrecting the retired core test path.
Co-authored-by: Steven Lee <stevenlee@openai.com>
* fix(codex): enforce canonical OpenAI app-server auth
Reject retired provider aliases without runtime compatibility, direct operators to the doctor migration, and remove the redundant OpenAI API-key predicate.
Co-authored-by: Steven Lee <stevenlee@openai.com>
* chore(codex): reconcile latest main dependency graph
Preserve current main dependency changes while preparing the original Codex PR for an ancestry-preserving signed mainline merge.
Co-authored-by: Steven Lee <stevenlee@openai.com>
* fix(codex): unify bundled Codex 0.146 runtimes
Keep the ACP adapter on the same 0.146.0 Codex release as the managed runtime, remove obsolete 0.145.0 platform artifacts and unused semver compatibility, and preserve the latest main dependency upgrades.
Co-authored-by: Steven Lee <stevenlee@openai.com>
---------
Co-authored-by: Peter Steinberger <peter@steipete.me>
Co-authored-by: Peter Steinberger <steipete@gmail.com>