* fix(discord): bound webhook send body reads
Rebase onto latest main to clear unrelated CI flake. No code changes.
Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>
* fix(discord): bound webhook send body reads
Keep the Discord REST deadline active through webhook response-body reads while preserving malformed-body fallbacks. Avoid logging token-bearing webhook URLs.
Co-authored-by: nebulacoder-v8.0 <noreply@zte.com.cn>
---------
Co-authored-by: nebulacoder-v8.0 <noreply@zte.com.cn>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(feishu): guard Object.assign against prototype pollution in webhook payload merge
Object.assign merges JSON.parse webhook payload into an Object.create target
without filtering `__proto__`, `constructor`, or `prototype` keys. A
crafted webhook body like `{"__proto__": {"polluted": true}}` would set
the `__proto__` property on the envelope object, enabling prototype
pollution on the target.
Replace Object.assign with explicit iteration that skips blocked keys, and
use Object.create(null) so the envelope itself has no prototype to pollute.
Matches the isBlockedObjectKey pattern used in core config paths.
* fix(feishu): preserve webhook envelope when filtering payloads
* fix(feishu): preserve webhook envelope when filtering payloads
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(telegram): isolate webhook/ingress-worker shutdown errors and guard all cleanup
- Wrap entire shutdown() body in outer try/catch so sync throws never
produce unhandled rejections from fire-and-forget abort listeners.
- bot.stop() rejection is caught and logged; finally block ensures
closeTransportOnce(), noteWebhookStop(), and stopDiagnosticHeartbeat()
always execute regardless of bot.stop() outcome.
- closeTransportOnce() rejection is caught independently in the finally
block so noteWebhookStop() and stopDiagnosticHeartbeat() are not skipped.
- Startup failure path: bot.stop() and closeTransportOnce() get
.catch(() => undefined) so cleanup rejections never mask the original
startup error.
- Both abort-listener call sites use consistent void shutdown() since
the never-reject contract is now internal.
- Ingress worker stop() shares an in-flight stopPromise for idempotency;
worker.terminate() rejections are observed via .catch(() => undefined).
Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>
* fix(telegram): continue webhook cleanup after sync server.close throw
Extract independently guarded shutdown phases so a synchronous
server.close() failure still runs bot.stop, transport close, status,
and diagnostic heartbeat cleanup. Add sync-throw regression coverage
and a loopback real-behavior proof (negative/positive/valid).
* fix(telegram): clear webhook CI lint, knip, and test-types
Restore curly-safe drain timer clear, stop exporting the unused
shutdown phases interface, and narrow the transport close spy for
tsgo test types.
* chore(telegram): drop committed webhook shutdown proof script
* test(telegram): inline webhook shutdown proof cases without proof script
* fix(telegram): drop stale reply-fence import after main rebase
telegram-reply-fence.ts was deleted on main (f7786a16cf) during the
core drain refactor. The rebased webhook.ts no longer calls these
functions; remove the dangling import so the module resolves.
Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>
* fix(telegram): continue webhook shutdown after phase failures
Keep each fallible Telegram-owned teardown phase independent so an early failure cannot skip transport, ingress, status, or diagnostic cleanup.
Co-authored-by: Pick-cat <huang.ting3@xydigit.com>
---------
Co-authored-by: nebulacoder-v8.0 <noreply@zte.com.cn>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(voice-call): pin Twilio webhook verification to the configured public path
buildTwilioVerificationUrl overwrote the configured publicUrl path with the
incoming request path, so behind a path-rewriting reverse proxy the
reconstructed verification URL no longer matched the URL Twilio signed and
valid webhooks were rejected. Use the configured public path (keep the request
query), mirroring the Plivo sibling fixed in #112559. Restores callback-path
binding and adds proxy-prefix accept + local-path reject regression tests.
* fix(voice-call): pin Twilio verification to public URL path
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(ui): rename the Memory Palace browser to Memory Wiki and document it
* refactor(memory-wiki): drop the old memory-palace module files
* docs: refresh generated docs map
* test(ui): align dream-diary hub-tab assertion with the wiki sub-tab id
* docs: describe memory wiki clustering behavior accurately
* docs(memory-wiki): record the wiki.overview rename decision at the registration site
* test(ui): query the Agents channels hub tab by tab role after the hub-tabs refactor
* fix(tlon): stop reconnecting after SSE client close
* fix(tlon): cancel SSE reconnect timers when monitoring stops
Preserve the original Tlon SSE reconnect fix while canceling both retry timers at stopReceiving(), the real monitor shutdown boundary.
Verified against an authenticated, SSRF-guarded loopback SSE server; both reconnect waits settle immediately without changing replay, close cleanup, or uninterrupted reconnection.
Co-authored-by: wahaha1223 <0668001153@xydigit.com>
* fix(tlon): prepare conflict-free reconnect refresh
Prepare a conflict-free refresh of the original Tlon SSE shutdown correction without changing its ownership or losing the contributor commit.
Co-authored-by: wahaha1223 <0668001153@xydigit.com>
* fix(tlon): cancel reconnect timers when monitoring stops
Cancel both Tlon SSE reconnect waits at stopReceiving(), the monitor-owned shutdown boundary, while preserving guarded HTTP, durable same-channel replay, and the original contributor's fix.
Verified with authenticated loopback SSE, both real retry delays, normal reconnection, all 252 Tlon tests, extension type checks, lint, and focused owner regressions.
Co-authored-by: wahaha1223 <0668001153@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(google): cancel unread response body on video download error
* fix(google): safely cancel failed streaming video downloads
---------
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>
* fix(feishu): log message content JSON parse failures instead of silently swallowing
Replace formatErrorMessage(err) with safe metadata-only logging in
parseFeishuMessageContent to prevent potential message content leaks
through V8 JSON.parse error messages.
Changes:
- Remove formatErrorMessage import (security: V8 JSON.parse errors can
include input content in the message)
- Log only msgType and optional messageId (safe metadata) when parse
fails, never the exception message or raw content
- Add assertion that raw content is NOT present in the log output
- Pass messageId through to enable richer diagnostics
The raw content is still preserved as the function return value (existing
fallback behavior).
* fix(feishu): move parse-failure test into getMessageFeishu suite
The test 'logs a safe diagnostic (not raw content) when message content
is not valid JSON' was declared after the closing brace of
describe('getMessageFeishu'), so it did not inherit that suite's fixture
setup and reset hooks (beforeEach/afterAll). Move it inside the suite
so it benefits from the shared mock reset and cleanup.
Fixes ClawSweeper P2: 'Keep the parse-failure test inside the fetch suite'
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(synology-chat): stop fabricating send message ids
The incoming-webhook send contract acks with a bare boolean and carries
no platform message id, but the adapter returned a synthetic
`sc-${Date.now()}` id and stamped it into the delivery receipt. Return
the established empty-id sentinel and an empty receipt instead, matching
the honest no-platform-id shape used by sibling channels (qqbot,
googlechat) and the plugin-sdk empty-id contract.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(synology-chat): verify truthful webhook receipts
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The react message-action handler dispatched straight to the sender without
checking the resolved account, so a disabled Nextcloud Talk account
(`enabled:false`) that still had a baseUrl/botSecret in config could keep
emitting reactions. `describeMessageTool` already hides the tool for
unconfigured accounts, but an explicit accountId can reach `handleAction`
directly. Enforce the same enabled+configured gate at dispatch, mirroring
the Signal reaction fix (#112607).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>