Skip to content

fix for SIGTERM ignored closes #51100#51242

Merged
obviyus merged 5 commits intoopenclaw:mainfrom
juliabush:fix/sigterm-timeout
Mar 23, 2026
Merged

fix for SIGTERM ignored closes #51100#51242
obviyus merged 5 commits intoopenclaw:mainfrom
juliabush:fix/sigterm-timeout

Conversation

@juliabush
Copy link
Copy Markdown
Contributor

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: Gateway does not exit on SIGTERM when Telegram polling is stalled/recovering due to blocking async operations (fetch/backoff timers).

Why it matters: Causes zombie process which causes port binding failure which causes gateway downtime and potential session/event loss on forced SIGKILL.

What changed: Added shutdown watchdog (30s forced exit), ensured non-zero exit code, propagated AbortSignal to polling fetch to unblock shutdown.

What did NOT change (scope boundary): No changes to session storage format, polling logic semantics, or retry/backoff strategy.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

-Closes #51100
-Related #51097 #51144

User-visible / Behavior Changes

Gateway now force-exits after ~30s if shutdown hangs
Restart always exits with non-zero code to ensure supervisor restarts cleanly

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)

Repro + Verification

Environment

-Environment:

OS: macOS 15.4
Runtime/container: Node v24.14.0
Model/provider: N/A
Integration/channel: Telegram polling
Relevant config (redacted): network.autoSelectFamily=false

-Steps:

Start gateway with Telegram polling
Wait for polling stall/recovery
Send SIGTERM (launchctl kickstart -k or kill)
Observe process behavior

-Expected:

Process exits within ~30s
New process binds port successfully

-Actual:

Before: process hangs indefinitely until SIGKILL
After: process exits within timeout, no port conflict

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Before:

SIGTERM received
(no exit, process remains alive)

After:

SIGTERM received
Shutdown timed out after 30s, forcing exit
Process exited

Human Verification (required)

What you personally verified (not just CI), and how:

-Verified scenarios:
SIGTERM during normal operation → clean exit
SIGTERM during polling stall → forced exit after timeout
Restart via launchctl → no port binding conflict

-Edge cases checked:
Multiple SIGTERM signals
AbortSignal cancels in-flight fetch

  • What you did not verify:
    Long-running session write under heavy load
    Multi-gateway race conditions beyond local test

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No)
  • Config/env changes? (Yes/No)
  • Migration needed? (Yes/No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly:

Revert commit introducing shutdown watchdog and AbortSignal wiring

  • Files/config to restore:

gateway loop runner file
telegram polling runner
Known bad symptoms reviewers should watch for:
Premature forced exits during normal operation
Incomplete shutdown of active runners

Risks and Mitigations

Risk: Forced exit may interrupt session writes

Mitigation: 30s grace period before exit

Risk: AbortSignal propagation may cancel valid in-flight requests

Mitigation: Only triggered during shutdown path

Risk: Non-zero exit code may affect supervisor behavior

Mitigation: Aligns with intended restart semantics for launchd/systemd

@openclaw-barnacle openclaw-barnacle bot added channel: telegram Channel integration: telegram cli CLI command changes size: XS labels Mar 20, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 20, 2026

Greptile Summary

This PR fixes a SIGTERM-ignored bug where a Telegram polling stall could block the graceful shutdown path indefinitely, causing zombie processes and port-binding conflicts on restart. The fix has two parts: propagating the shutdown AbortSignal directly to the in-flight fetchAbortController in polling-session.ts, and raising SHUTDOWN_TIMEOUT_MS from 5 s to 30 s in run-loop.ts along with always exiting non-zero on timeout.

Key observations:

  • The core logic is sound and targets the right code path. The increased 30 s timeout correctly accommodates Telegram's existing 15 s stop grace period (POLL_STOP_GRACE_MS).
  • Logic issue in polling-session.ts: the new abort event listener on opts.abortSignal (lines 196–204) is never removed in the finally block, unlike the pre-existing stopOnAbort listener. Because #runPollingCycle is re-entered on every polling restart, listeners accumulate across cycles, creating a memory leak and risking a Node.js MaxListenersExceededWarning. This needs the same removeEventListener cleanup pattern already used for stopOnAbort.
  • Stale comment in run-loop.ts: the inline comment still reads "Stop-timeout stays at 0 (graceful)." which is now factually wrong after changing exitProcess(isRestart ? 1 : 0) to exitProcess(1). Minor but could mislead future maintainers.
  • No tests were added for the new shutdown path; the PR description acknowledges this and provides manual verification steps, which is acceptable given the difficulty of testing OS signal handling in unit tests.

Confidence Score: 3/5

  • Safe to merge after fixing the listener leak in polling-session.ts; the stale comment is minor but should also be corrected.
  • The overall approach is correct and well-scoped. However, the abort listener added in #runPollingCycle is never explicitly removed in the finally block. Given that polling restarts happen regularly (on every stall or network error), this listener accumulates on the long-lived abortSignal and will eventually trigger a Node.js MaxListenersExceededWarning. In a high-churn polling environment this could surface quickly. The stale comment is a lower-severity concern. Both issues should be addressed before merge.
  • extensions/telegram/src/polling-session.ts — the new abort listener cleanup is the primary concern.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/telegram/src/polling-session.ts
Line: 196-204

Comment:
**Listener accumulates across polling cycles**

The `abort` listener is added to `this.opts.abortSignal` on every call to `#runPollingCycle`, but it is never removed in the `finally` block (lines 295–307). Because `{ once: true }` only auto-removes after the listener fires, cycles that end without the signal firing (network restarts, stall recovery, etc.) leave a dangling listener holding a closure reference to the cycle's `fetchAbortController`. Over many restarts, these accumulate on the long-lived `abortSignal`, causing both a memory leak and eventually a Node.js `MaxListenersExceededWarning`.

The existing `stopOnAbort` listener (line 260) already handles this correctly: it is explicitly removed in the `finally` block via `removeEventListener` (line 300). The new listener should follow the same pattern:

```ts
const abortFetch = () => fetchAbortController?.abort();
if (this.opts.abortSignal && fetchAbortController) {
  this.opts.abortSignal.addEventListener("abort", abortFetch, { once: true });
}
```

and then in the `finally` block (after line 300):
```ts
this.opts.abortSignal?.removeEventListener("abort", abortFetch);
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/cli/gateway-cli/run-loop.ts
Line: 115-118

Comment:
**Stale comment contradicts new behavior**

The inline comment still says `"Stop-timeout stays at 0 (graceful)."` but that was true only for the old code (`exitProcess(isRestart ? 1 : 0)`). After this PR's change to `exitProcess(1)`, both restart timeouts and stop timeouts now exit with code `1`. The comment should be updated to reflect the new behavior:

```suggestion
      // Exit non-zero on timeout (both stop and restart) so launchd/systemd
      // treats it as a failure and triggers a clean process restart instead of
      // assuming the shutdown was intentional. (#36822, #51100)
      exitProcess(1);
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "fix(gateway): enforc..."

Comment on lines +115 to +118
// Exit non-zero on restart timeout so launchd/systemd treats it as a
// failure and triggers a clean process restart instead of assuming the
// shutdown was intentional. Stop-timeout stays at 0 (graceful). (#36822)
exitProcess(isRestart ? 1 : 0);
exitProcess(1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Stale comment contradicts new behavior

The inline comment still says "Stop-timeout stays at 0 (graceful)." but that was true only for the old code (exitProcess(isRestart ? 1 : 0)). After this PR's change to exitProcess(1), both restart timeouts and stop timeouts now exit with code 1. The comment should be updated to reflect the new behavior:

Suggested change
// Exit non-zero on restart timeout so launchd/systemd treats it as a
// failure and triggers a clean process restart instead of assuming the
// shutdown was intentional. Stop-timeout stays at 0 (graceful). (#36822)
exitProcess(isRestart ? 1 : 0);
exitProcess(1);
// Exit non-zero on timeout (both stop and restart) so launchd/systemd
// treats it as a failure and triggers a clean process restart instead of
// assuming the shutdown was intentional. (#36822, #51100)
exitProcess(1);
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cli/gateway-cli/run-loop.ts
Line: 115-118

Comment:
**Stale comment contradicts new behavior**

The inline comment still says `"Stop-timeout stays at 0 (graceful)."` but that was true only for the old code (`exitProcess(isRestart ? 1 : 0)`). After this PR's change to `exitProcess(1)`, both restart timeouts and stop timeouts now exit with code `1`. The comment should be updated to reflect the new behavior:

```suggestion
      // Exit non-zero on timeout (both stop and restart) so launchd/systemd
      // treats it as a failure and triggers a clean process restart instead of
      // assuming the shutdown was intentional. (#36822, #51100)
      exitProcess(1);
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b22c1e852

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


const DRAIN_TIMEOUT_MS = 90_000;
const SHUTDOWN_TIMEOUT_MS = 5_000;
const SHUTDOWN_TIMEOUT_MS = 30_000;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the SIGTERM watchdog below the systemd stop timeout

On Linux installs this undermines the shutdown-hang fix instead of hardening it: the generated unit already sets TimeoutStopSec=30 in src/daemon/systemd-unit.ts:58-64, so raising SHUTDOWN_TIMEOUT_MS to the same 30s creates a race where systemd can SIGKILL the process before this timer fires. In the exact server.close()-is-stuck case this code is trying to handle, the JS timeout/finally path may never run, so the gateway does not emit the timeout log or release the lock itself. Keeping the in-process watchdog comfortably below the supervisor's stop budget (or increasing the unit timeout in the same change) avoids that regression.

Useful? React with 👍 / 👎.

Comment on lines +196 to +200
if (this.opts.abortSignal && fetchAbortController) {
this.opts.abortSignal.addEventListener(
"abort",
() => {
fetchAbortController.abort();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the per-cycle abort listener after each polling restart

runUntilAbort() reuses one opts.abortSignal across every polling retry, but this new anonymous abort listener is never removed when a cycle finishes—only stopOnAbort is detached in the finally block. Any recoverable Telegram outage or getUpdates conflict therefore leaves another listener retaining an old fetchAbortController, so a long-lived bot that restarts repeatedly accumulates stale controllers for the lifetime of the process.

Useful? React with 👍 / 👎.

@obviyus obviyus force-pushed the fix/sigterm-timeout branch from 5b22c1e to 4833443 Compare March 23, 2026 09:16
@obviyus obviyus self-assigned this Mar 23, 2026
@obviyus obviyus force-pushed the fix/sigterm-timeout branch from 4833443 to 9bb9b1a Compare March 23, 2026 09:29
@obviyus obviyus merged commit e94ebfa into openclaw:main Mar 23, 2026
9 checks passed
@obviyus
Copy link
Copy Markdown
Contributor

obviyus commented Mar 23, 2026

Landed on main.

Thanks @juliabush.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9bb9b1a2ed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +203 to +205
if (this.opts.abortSignal && fetchAbortController) {
this.opts.abortSignal.addEventListener("abort", abortFetch, { once: true });
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate shutdown abort before Telegram preflight API calls

fetchAbortController is created in #createPollingBot(), but the new abortSignal bridge is only attached here after #ensureWebhookCleanup() and #confirmPersistedOffset() have already awaited Telegram API calls. If SIGTERM lands while deleteWebhook() is hanging during 409 recovery or while the offset-confirmation getUpdates() is in flight, shutdown still blocks until those requests time out, so the "stalled/recovering" fix does not actually cover those recovery windows.

Useful? React with 👍 / 👎.

Comment on lines +203 to +205
if (this.opts.abortSignal && fetchAbortController) {
this.opts.abortSignal.addEventListener("abort", abortFetch, { once: true });
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle abort-driven polling exits as graceful stops

When shutdown lands during an active long-poll getUpdates, this listener aborts the fetch immediately. #runPollingCycle() then rethrows on opts.abortSignal.aborted, and src/gateway/server-channels.ts records any provider rejection as lastError, which src/commands/channels/status.ts surfaces as error:.... So a clean Telegram stop/restart can now leave the channel looking unhealthy even though nothing actually failed.

Useful? React with 👍 / 👎.

@juliabush juliabush deleted the fix/sigterm-timeout branch March 23, 2026 10:36
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 24, 2026
)

* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
(cherry picked from commit e94ebfa)
alexey-pelykh added a commit to remoteclaw/remoteclaw that referenced this pull request Mar 24, 2026
* refactor: share telegram network test helpers

(cherry picked from commit 05a1b0c)

* fix: bypass telegram runtime proxy during health checks

(cherry picked from commit 1b31ede)

* refactor: share telegram voice send path

(cherry picked from commit 1d99401)

* refactor: share telegram named account dm fixtures

(cherry picked from commit 2cd1a4b)

* test: share telegram sticky fetch helpers

(cherry picked from commit 34a5523)

* test(telegram): cover caption and forum service helpers

(cherry picked from commit 377be13)

* test: refine telegram token coverage

(cherry picked from commit 3e8d9bc)

* refactor: share telegram reply chunk threading

(cherry picked from commit 5197171)

* fix: align telegram probe test mock

(cherry picked from commit 5f78057)

* refactor: share telegram native command auth harness

(cherry picked from commit 60dc46a)

* test: share telegram monitor startup helpers

(cherry picked from commit 66aabf5)

* test(telegram): cover normalization and status issues

(cherry picked from commit 833d0df)

* refactor: share lane delivery test flows

(cherry picked from commit 9780e99)

* refactor: share telegram channel test harnesses

(cherry picked from commit 9ecd189)

* refactor: share telegram outbound send options

(cherry picked from commit a57c590)

* test: share lane delivery final helpers

(cherry picked from commit ba1d7b2)

* refactor: share telegram payload send flow

(cherry picked from commit bc1cc2e)

* test(telegram): cover dm access and allowed updates

(cherry picked from commit bd8ca6d)

* test: share telegram draft stream helpers

(cherry picked from commit d78b7b3)

* fix: widen telegram reply progress typing

(cherry picked from commit d886ca6)

* fix(telegram): prevent duplicate messages with slow LLM providers (openclaw#41932)

Merged via squash.

Prepared head SHA: 2f50c51
Co-authored-by: hougangdev <[email protected]>
Co-authored-by: obviyus <[email protected]>
Reviewed-by: @obviyus

(cherry picked from commit e37e1ed)

* refactor: share telegram dispatch failure harness

(cherry picked from commit f201bad)

* test: share telegram account helpers

(cherry picked from commit fbdea7f)

* Telegram: split setup adapter helpers

(cherry picked from commit 0672156)

* refactor(telegram): share plugin base config

(cherry picked from commit 3cc1c7b)

* feat(telegram): auto-rename DM topics on first message (openclaw#51502)

* feat(telegram): auto-rename DM topics on first message

fix(telegram): use bot.api for topic rename to avoid SecretRef resolution

* fix(telegram): address security + test review feedback

- Fix test assertion: DEFAULT_PROMPT_SUBSTRING matches 'very short'
- Use RawBody instead of Body (no envelope metadata to LLM)
- Truncate user message to 500 chars for LLM prompt
- Remove user-derived content from verbose logs
- Remove redundant threadSpec.id null check
- Fix AutoTopicLabelParams type to match generateTopicLabel

* fix(telegram): use effective dm auto-topic config

* fix(telegram): detect direct auto-topic overrides

* fix: auto-rename Telegram DM topics on first message (openclaw#51502) (thanks @Lukavyi)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
(cherry picked from commit 466debb)

* fix(security): harden explicit-proxy SSRF pinning

(cherry picked from commit 55ad5d7)

* fix: stabilize full gate

(cherry picked from commit 5fb7a13)

* Telegram: consolidate message tool discovery

(cherry picked from commit 60104de)

* Telegram: preserve inbound debounce order

(cherry picked from commit 9a34a60)

* feat(telegram): add topic-edit action

(cherry picked from commit a516141)

* refactor: move telegram onboarding to setup wizard

(cherry picked from commit a4047bf)

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
(cherry picked from commit e94ebfa)

* fix: adapt cherry-picks for fork TS strictness

- Remove duplicate renameForumTopicTelegram function in send.ts
- Restore tts.test.ts to fork version (upstream version has gutted pi-ai deps)

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore fork versions of test files overwritten by upstream

- monitor.test.ts: upstream version references tagTelegramNetworkError not in fork
- send.test.ts: upstream version references missing test helpers and unexported functions

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: remove withPinnedLookup call not available in fork

Use direct proxyTls spread instead of upstream's withPinnedLookup helper
which was removed during fork restructuring.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: resolve type errors from cherry-pick adaptations

- Add missing GetReplyOptions/MsgContext imports to bot test
- Fix warn parameter typing in draft-stream test

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore bot test files to fork versions

Upstream versions have deeply entangled barrel import paths that don't
exist in the fork's restructured module layout.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: align test helpers with refactored exports

- Rename createNativeCommandTestParams -> createNativeCommandsHarness
- Widen matchPluginCommand mock return type for test flexibility

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore bot-native-commands.test.ts to fork version

Upstream harness refactoring changed return types incompatible with
the test's existing assertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore all bot-native-commands test files to fork versions

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore all native-command test infrastructure to fork versions

The upstream refactoring of test helpers is incompatible with the fork's
existing test patterns.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore bot-message-dispatch.test.ts to fork version

Upstream version uses telegramDeps injection pattern not in fork.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore test files with upstream-incompatible patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore media fetch network test to fork version

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore remoteclaw-root test to fork version

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore outbound test files to fork versions

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: resolve remaining type errors from cherry-pick adaptations

- Restore test files overwritten by upstream full-gate commit
- Fix OpenClawConfig -> RemoteClawConfig in extension files
- Fix barrel import paths in extension test files

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: remove incompatible upstream files and relocate test files

- Remove setup/shared files that depend on upstream barrel exports
- Move extension test files to src/telegram/ where their modules live
- Restore channel.ts to fork version

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: final type error cleanup from cherry-pick batch

- Restore types.plugin.ts to fork version (setup-wizard removed)
- Remove normalize/status-issues tests (modules at extension paths)
- Fix readonly array spread in allowed-updates test
- Add explicit types for dm-access test callback parameters

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore test-parallel.mjs to fork version

Upstream version references vitest.channel-paths.mjs which doesn't exist
in the fork.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore test files causing CI failures to fork versions

Upstream test changes reference infrastructure patterns (gateway injection,
plugin runtime seams) not available in the fork.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore outbound/telegram.ts to fork version

The upstream sendTelegramPayloadMessages refactor may have side effects
on channel registration in tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: restore message-channel.ts to fork version

Upstream's symbol-based registry lookup breaks channel resolution in
test isolation without the full gateway injection infrastructure.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

---------

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: Wayne <[email protected]>
Co-authored-by: Taras Lukavyi <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: Julia Bush <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Interstellar-code pushed a commit to Interstellar-code/operator1 that referenced this pull request Mar 24, 2026
)

* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
(cherry picked from commit e94ebfa)
furaul pushed a commit to furaul/openclaw that referenced this pull request Mar 24, 2026
)

* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
npmisantosh pushed a commit to npmisantosh/openclaw that referenced this pull request Mar 25, 2026
)

* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
PotatoParser added a commit to Lilac-Labs/openclaw that referenced this pull request Mar 26, 2026
* fix(delivery-queue): increment retryCount on deferred entries when time budget exceeded

When delivery recovery ran out of the 60s time budget, remaining pending
entries were silently deferred to the next restart with no retryCount
increment. This caused them to loop forever across restarts, never hitting
MAX_RETRIES and never moving to failed/.

Fix: call failDelivery() on each remaining entry before breaking out of
the recovery loop (both the deadline check and the backoff-exceeds-deadline
check). This increments retryCount so that entries eventually exhaust
MAX_RETRIES and are permanently skipped.

Fixes openclaw#24353

* fix(delivery-queue): break immediately on deadline instead of failing all remaining entries

P1-C: After now >= deadline, the old code would iterate all remaining queue
entries and call failDelivery() on each — O(n) work that nullified the
maxRecoveryMs wall-clock cap on large queues.

Fix: break out of the recovery loop immediately when the deadline is exceeded.
Remaining entries are picked up on next startup unchanged (retryCount not
incremented). The deadline means 'stop here', not 'fail everything remaining'.

* fix(delivery-queue): align test assertion and JSDoc with 'next startup' log message

* fix(browser): add ChildProcessWithoutNullStreams cast for @types/node compat

The stdio tuple overload resolves differently across @types/node versions
(v20 vs v24/v25). Cast the spawn() result to ChildProcessWithoutNullStreams
to ensure proc.stderr?.on/off type-checks regardless of installed @types/node.

* test(delivery-queue): align test assertion with 'next startup' log message

* fix(delivery-queue): increment retryCount on deadline-deferred entries

Codex P1: entries deferred by the recovery time budget kept retryCount=0
forever, so they could loop across restarts without ever reaching MAX_RETRIES.

After breaking on deadline, call failDelivery() for all remaining entries
so retryCount is incremented. Entries stay in queue until MAX_RETRIES is
reached and they are pruned normally.

Also updates the maxRecoveryMs test to assert retryCount=1 on deferred entries.

* fix(ci): restore delivery queue branch checks

* fix(plugins): make metadata generator formatter portable

* fix(image): deprecate legacy skill and clarify auth

* docs(image): remove duplicate typical values bullet

* fix: harden image auth env lookups (openclaw#52552) (thanks @vincentkoc)

* test(mattermost): cover directory discovery

* test(msteams): cover store and live directory helpers

* fix(mattermost): honor replyToMode off for threaded messages

* perf(reply): lazy-load runner execution and memory

* fix(plugins): remove metadata generator conflict markers

* test(nextcloud-talk): cover inbound behavior branches

* test(irc): cover inbound behavior branches

* test: slim outbound test import graphs

* perf: remove remaining unit thread pins

* perf(reply): lazy-load usage cost resolution

* test(googlechat): cover security normalization

* perf(reply): lazy-load context token lookup

* style(format): fix extension test drift

* test: centralize cli runtime capture helpers

* refactor: reuse shared cli runtime test mocks

* fix: restore bundled plugin metadata generator

* fix(telegram): add allow_sending_without_reply to prevent lost messages

When a Telegram message that OpenClaw is replying to gets deleted before
delivery, the Telegram API rejects the entire sendMessage call with
"message to be replied not found". This causes the bot's response to be
silently lost and stuck in the failed delivery queue permanently.

Setting allow_sending_without_reply: true tells Telegram to deliver the
message as a standalone message if the reply target no longer exists,
instead of failing the entire request.

Applied to all 6 locations across 4 source files where
reply_to_message_id is set:
- send.ts: buildTelegramReplyParams (both reply_parameters and plain reply)
- bot/delivery.send.ts: buildTelegramSendParams
- draft-stream.ts: draft stream reply params
- bot-handlers.runtime.ts: error reply messages (file too large, media download failed)

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix(telegram): update test expectations for allow_sending_without_reply

Update exact-match test assertions in send.test.ts to include the new
allow_sending_without_reply: true parameter. Tests using objectContaining
already pass, but several tests use exact object matching.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: finish telegram reply fallback landing (openclaw#52524) (thanks @moltbot886)

* refactor: extract single-provider plugin entry helper

* refactor: add provider onboarding preset appliers

* refactor: share parsed channel allowlist prompts

* refactor: share channel setup status helpers

* fix: export provider-entry plugin sdk subpath

* style: format plugin sdk helper updates

* fix(docs): code-verified fixes from deep reference audit

- sdk-runtime.md: add missing required params (runId, timeoutMs) to
  runEmbeddedPiAgent example
- sdk-provider-plugins.md: add missing onModelSelected hook (openclaw#22),
  clarify capabilities is data not callable, drop misleading '21' count

* test: fix googlechat security typing drift

* perf: remove stale unit isolated entry

* refactor(outbound): split delivery queue storage and recovery

* fix(docs): remaining code audit fixes

- sdk-entrypoints.md: fix mislabeled 'Channel entry options' heading
  (should be 'Options' — these are definePluginEntry options, not
  channel-specific)
- sdk-overview.md: add 4 missing API object fields (version, description,
  source, rootDir) from OpenClawPluginApi type

* perf(reply): split usage line helpers

* perf(reply): narrow queue imports

* test(msteams): cover graph helpers

* test(msteams): cover upload and webhook helpers

* refactor: harden generated-file guards and provider ids

* fix: restore main gate after type updates

* fix(plugins): accept media-understanding id hints

* test: default scoped vitest configs to no-isolate

* test(msteams): cover poll and file-card helpers

* test: fix provider config typing drift

* perf: default channel vitest lanes to threads

* perf(reply): lazy-load media path normalization

* style(format): fix msteams test drift

* refactor(plugins): move remaining channel and provider ownership out of src

* refactor(plugins): finish provider and whatsapp cleanup

* fix(gateway): pass process.env in status command probe auth to resolve SecretRef

Fixes openclaw#52360

resolveGatewayProbeAuthSafe was called from status-all.ts without an
env argument, causing the credential resolution chain to fall back to
an empty object instead of process.env. This made env-backed SecretRef
tokens (gateway.auth.token, Telegram botToken, etc.) appear unresolved
in the status command path even when the runtime was healthy.

Added process.env as default fallback in buildGatewayProbeCredentialPolicy
and passed env explicitly from status-all.ts callers.

Related: openclaw#33070, openclaw#38973, openclaw#39415, openclaw#46014, openclaw#49730

* fix(status): resolve only selected probe-auth branch and fix plain status path

Address two Codex P1/P2 issues:

1. (P1) Plain 'openclaw status' and 'openclaw status --json' still went
   through the sync resolveGatewayProbeAuthSafe path in
   status.gateway-probe.ts, which cannot expand SecretRef objects.
   Switched to async resolveGatewayProbeAuthSafeWithSecretInputs.

2. (P2) status-all.ts was eagerly resolving both local and remote probe
   auth before deciding which to use. A stale SecretRef in the unused
   branch could abort the command. Collapsed to a single resolution
   call using the correct mode upfront.

Updated status.scan.test.ts to use mockResolvedValue since
resolveGatewayProbeAuthResolution is now async.

* fix(status): await resolveGatewayProbeAuthResolution in scan.shared

Function is now async after switching to resolveGatewayProbeAuthSafeWithSecretInputs.
Missing await caused TS error: Property 'auth' does not exist on type 'Promise<...>'.

* fix: finish gateway probe auth landing (openclaw#52513) (thanks @CodeForgeNet)

* fix: finish gateway probe auth landing (openclaw#52513) (thanks @CodeForgeNet)

* test: clear msteams gate drift for gateway probe auth landing (openclaw#52513) (thanks @CodeForgeNet)

* fix(exec): return plain-text tool result on failure instead of raw JSON

When an exec command fails (e.g. timeout), the tool previously rejected
with an Error, which the tool adapter caught and wrapped in a JSON object
({ status, tool, error }). The model then received this raw JSON as the
tool result and could parrot it verbatim to the user.

Now exec failures resolve with a proper tool result containing the error
as human-readable text in content[], matching the success path structure.
The model sees plain text it can naturally incorporate into its reply.

Also fixes a pre-existing format issue in update-cli.test.ts.

Fixes openclaw#52484

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: finish exec tool failure landing (openclaw#52508) (thanks @martingarramon)

* fix(openshell): bundle upstream cli fallback

* perf(reply): lazy-load session store writes

* fix(build): restore plugin-sdk and line compat after refactor

* refactor(kilocode): route shared model constants through core seam

* refactor(plugin-sdk): route core provider and telegram seams through sdk barrels

* fix(acp): preserve hidden thought chunks from gateway chat

* fix(docs): rename 'Channel Options' to 'Options' in sdk-entrypoints

This heading labels definePluginEntry options (generic, not channel-specific).
Another agent reverted the previous fix during a merge.

* fix(line): narrow plugin-sdk seams after refactor

* test(voice-call): cover helper utilities

* test(voice-call): cover manager and api helpers

* fix(acp): preserve hidden thought replay on session load

* test(voice-call): cover twilio and reaper helpers

* docs(changelog): note ACP hidden thought replay fix

* test(voice-call): cover utility and tailscale helpers

* fix(ci): resync generated baselines and line runtime seam

* fix(docs): remove duplicate '### Options' headings (MD024)

* fix(ci): restore plugin manifests and boundary tests

* style(docs): format sdk entrypoints doc

* docs(config): refresh generated baseline

* test(voice-call): cover outbound call flow helpers

* fix(ci): repair tts and matrix refactor fallout

* fix(ci): repair voice-call typing and provider contracts

* fix(ci): satisfy voice-call typing and extension boundaries

* Remove personal references from docs (openclaw#25260)

* docs: remove personal references from AGENTS.md

* docs: remove personal reference from sag skill

* docs: note generic agent guidance cleanup

* Update CHANGELOG.md

---------

Co-authored-by: Josh Lehman <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>

* fix(plugins): route keyed queue imports through core (openclaw#52608)

* fix(matrix): preserve send aliases and voice intent

* fix(build): repair stale plugin sdk surfaces

* fix(plugin-sdk): export line runtime subpath

* fix(auth): route copilot login through sdk seam

* feat(web-search): add bundled Exa plugin (openclaw#52617)

* test(msteams): await async setup status lines

* fix(whatsapp): remove outbound runtime cycle

* fix(plugin-sdk): fast-path root diagnostic subscriptions

* fix(exa): align freshness typing and config docs

* fix(matrix): avoid touching dropped room bindings

* fix(web-search): align Exa plugin with current API

* docs(tools): add DuckDuckGo Search provider page

New page: tools/duckduckgo-search.md
- Key-free fallback provider, no API key needed
- Clear Warning about unofficial HTML-based integration
- Limitations section covering bot-challenge risk and reliability
- CardGroup showing good-for vs not-recommended-for use cases

Updated: tools/web.md with DuckDuckGo in CardGroup and comparison table
Updated: docs.json nav and redirect

* test(telegram): align webhook grammy mock

* docs(tools): add Exa Search page, align all search provider docs

New page: tools/exa-search.md
- Neural/keyword/hybrid search modes with content extraction
- Tool parameters including contents (highlights, text, summary)
- Search mode reference table

Rewritten: tools/duckduckgo-search.md
- Aligned to consistent template (Setup, Config, Tool parameters, Notes, Related)
- Simplified from previous version

Aligned across all providers:
- Every search page now ends with a consistent ## Related section
- Replaced 'See [Web tools]' with proper Related links
- Added Exa + DuckDuckGo to web.md overview CardGroup and comparison table
- Added Exa to docs.json nav and redirects

* fix(telegram): inject media loader through bot deps

* fix(ci): harden telegram seams and cap job timeouts

* docs(tools): update Exa Search notes for current API behavior

Add notes about default highlights contents, highlightScores/summary
preservation from responses, description resolution order, and
100-result cap.

* perf: add vitest test perf workflows

* fix(ci): harden changed extension diff fallback

* fix(telegram): harden grammy seams across tests

* refactor: extract exec outcome and tool result helpers

* test: isolate exec foreground failure coverage

* fix(test): allow empty extension lane

* perf: enable vitest fs module cache by default

* fix(cli): route plugin logs to stderr during --json output

* fix(cli): route deferred plugin logs to stderr in status --json

* fix: keep status --json stdout clean (openclaw#52449) (thanks @cgdusek)

* refactor(ci): collapse fast setup jobs into preflight

* fix(exec): accept runtime failure kind in formatter

* fix: include .env file vars in gateway service environment on install

When building the gateway install plan, read and parse
~/.openclaw/.env (or $OPENCLAW_STATE_DIR/.env) and merge those
key-value pairs into the service environment at the lowest
priority — below config env vars, auth-profile refs, and the
core service environment (HOME, PATH, OPENCLAW_*).

This ensures that user-defined secrets stored in .env (e.g.
BRAVE_API_KEY, OPENROUTER_API_KEY, DISCORD_BOT_TOKEN) are
embedded in the LaunchAgent plist (macOS), systemd unit (Linux),
and Scheduled Task (Windows) at install time, rather than
relying solely on the gateway process loading them via
dotenv.config() at startup.

Previously, on macOS the LaunchAgent plist never included .env
vars, which meant:
- launchctl print did not show user secrets (hard to debug)
- Child processes spawned before dotenv loaded had no access
- If the same key existed in both .env and the plist, the stale
  plist value won via dotenv override:false semantics

Dangerous host env vars (NODE_OPTIONS, LD_PRELOAD, etc.) are
filtered using the same security policy applied to config env
vars.

Fixes openclaw#37101
Relates to openclaw#22663

* fix: normalize env var keys and isolate tests from real .env

- Apply normalizeEnvVarKey({ portable: true }) before security
  filtering, matching the established pattern in env-vars.ts.
  Rejects non-portable key names (spaces, special chars) that
  would produce invalid plist/systemd syntax.

- Isolate existing tests from the developer's real ~/.openclaw/.env
  by providing a temp HOME directory, preventing flaky failures
  when the test machine has a populated .env file.

* fix: narrow exec exit failure kind typing

* fix(test): isolate flaky extension lanes

* feat(web-search): add DuckDuckGo bundled plugin (openclaw#52629)

* feat(web-search): add DuckDuckGo bundled plugin

* chore(changelog): restore main changelog

* fix(web-search): harden DuckDuckGo challenge detection

* refactor: split durable service env helpers

* refactor: extract gateway install token helpers

* fix(web-search): mark DuckDuckGo experimental

* docs(tools): update DuckDuckGo Search for landed plugin code

- Mark as experimental (not just unofficial)
- Add region and safeSearch tool parameters (from DDG schema)
- Add plugin config example for region/safeSearch defaults
- Document auto-detection order (100 = last)
- Note SafeSearch defaults to moderate
- Verified against extensions/duckduckgo/src/

* fix(agents): deny local MEDIA paths for MCP results

* Usage: include reset and deleted session archives (openclaw#43215)

Merged via squash.

Prepared head SHA: 49ed6c2
Co-authored-by: rcrick <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* docs(tools): soften DDG wording (scrapes -> pulls/gathers)

* fix(build): add stable memory-cli dist entry (openclaw#51759)

Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: Frank <[email protected]>

* refactor!: drop legacy CLAWDBOT env compatibility

* refactor!: remove moltbot state-dir migration fallback

* fix(gateway): preserve async hook ingress provenance

* fix(ci): write dist build stamp after builds

* perf: trim vitest hot imports and refresh manifests

* fix(security): unwrap time dispatch wrappers

* fix(plugin-sdk): fall back to src root alias files

* fix(ci): skip docs-only preflight pnpm audit

* docs(changelog): note time exec approval fix

* docs: refresh plugin-sdk api baseline

* fix(runtime): make dist-runtime staging idempotent

* fix(media): bound remote error-body snippet reads

* fix(gateway): gate internal command persistence mutations

* fix: restrict remote marketplace plugin sources

* fix(runtime): skip peer resolution for bundled plugin deps

* docs(agents): prefer current test model examples

* fix(exec): escape invisible approval filler chars

* test(models): refresh example model fixtures

* fix(security): unify dispatch wrapper approval hardening

* fix(security): harden explicit-proxy SSRF pinning

* fix: gate synology chat reply name matching

* docs: clarify sessions_spawn ACP vs subagent policies

* refactor(exec): split wrapper resolution modules

* refactor(exec): make dispatch wrapper semantics spec-driven

* refactor(exec): share wrapper trust planning

* refactor(exec): rename wrapper plans for trust semantics

* fix: include .npmrc in onboard docker build

* test: trim docker live auth mounts

* Docs: refresh config baseline for Synology Chat

* refactor: clarify synology delivery identity names

* refactor: centralize synology dangerous name matching

* refactor: narrow synology legacy name lookup

* refactor: audit synology dangerous name matching

* refactor: dedupe synology config schema

* fix: normalize scoped vitest filter paths

* fix(voice-call): harden webhook pre-auth guards

* fix(synology-chat): fail closed shared webhook paths

* docs: credit nexrin in synology changelog

* test: fix base vitest thread regressions

* test: finish base vitest thread fixture fixes

* test(voice-call): accept oversize webhook socket resets

* test: honor env auth in gateway live probes

* fix: harden plugin docker e2e

* Docs: align MiniMax examples with M2.7

* fix(ci): restore stale guardrails and baselines

* Test: isolate qr dashboard integration suite

* Gateway: resolve fallback plugin context lazily

* fix: bind bootstrap setup codes to node profile

* fix(tlon): unify settings reconciliation semantics

* refactor(synology-chat): type startup webhook path policy

* docs(synology-chat): clarify multi-account webhook paths

* refactor: unify minimax model and failover live policies

* docs: sync minimax m2.7 references

* fix: harden Windows Parallels smoke installs

* docs: reorder unreleased changelog by user impact

* refactor: remove embedded runner cwd mutation

* Infra: support shell carrier allow-always approvals

* refactor: centralize bootstrap profile handling

* refactor: reuse canonical setup bootstrap profile

* fix(plugin-sdk): resolve hashed diagnostic events chunks

* fix(plugin-sdk): normalize hashed diagnostic event exports

* test: fix ci env-sensitive assertions

* fix(gateway): fail closed on unresolved discovery endpoints

* feat: add slash plugin installs

* fix(media): block remote-host file URLs in loaders

* fix(media): harden secondary local path seams

* test: harden no-isolate reply teardown

* docs(changelog): add Windows media security fix

* refactor(gateway): centralize discovery target handling

* test: narrow live transcript scaffolding strip

* test: fix ci docs drift and bun qr exit handling

* fix(browser): enforce node browser proxy allowProfiles

* refactor(media): share local file access guards

* test: stabilize ci test harnesses

* test: harden no-isolate test module resets

* fix(plugins): preserve live hook registry during gateway runs

* test: fix channel summary registry setup

* test: harden isolated test mocks

* chore(plugins): remove opik investigation checkpoints

* ACPX: align pinned runtime version (openclaw#52730)

* ACPX: align pinned runtime version

* ACPX: drop version example from help text

* test: stop leaking image workspace temp dirs

* fix(android): gate canvas bridge to trusted pages (openclaw#52722)

* fix(android): gate canvas bridge to trusted pages

* fix(changelog): note android canvas bridge gating

* Update apps/android/app/src/main/java/ai/openclaw/app/node/CanvasActionTrust.kt

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(android): snapshot canvas URL on UI thread

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* test: isolate base vitest thread blockers

* fix: sync agent and autoreply e2e updates

* test: harden no-isolate mocked module resets

* docs: reorder unreleased changelog

* fix(changelog): note windows media path guardrails (openclaw#52738)

* fix: alphabetize web search provider listings

* docs: clarify unreleased breaking changes

* test: harden ci isolated mocks

* fix: align websocket stream fallback types

* test: finish no-isolate suite hardening

* style: format image-generation runtime tests

* fix(memory-core): register memory tools independently to prevent coupled failure (openclaw#52668)

Merged via admin squash because current required CI failures are inherited from base and match latest `main` failures outside this PR's `memory-core` surface.

Prepared head SHA: df7f968
Co-authored-by: artwalker <[email protected]>
Reviewed-by: @frankekn

* fix(status): recompute fallback context window (openclaw#51795)

* fix(status): recompute fallback context window

* fix(status): keep live context token caps on fallback

* fix(status): preserve fallback runtime context windows

* fix(status): preserve configured fallback context caps

* fix(status): keep provider-aware transcript context lookups

* fix(status): preserve explicit fallback context caps

* fix(status): clamp fallback configured context caps

* fix(status): keep raw runtime slash ids

* fix(status): refresh plugin-sdk api baseline

* fix(status): preserve fallback context lookup

* test(status): refresh plugin-sdk api baseline

* fix(status): keep runtime slash-id context lookup

---------

Co-authored-by: create <[email protected]>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: RichardCao <[email protected]>

* fix(telegram): make buttons schema optional in message tool

The Telegram plugin injects a `buttons` property into the message tool
schema via `createMessageToolButtonsSchema()`, but without wrapping it
in `Type.Optional()`. This causes TypeBox to include `buttons` in the
JSON Schema `required` array.

In isolated sessions (e.g. cron jobs) where no `currentChannel` is set,
all plugin schemas are merged into the message tool. When the LLM calls
the message tool without a `buttons` parameter, AJV validation fails
with: `buttons: must have required property 'buttons'`.

Wrap the buttons schema in `Type.Optional()` so it is not required.

* fix: keep message-tool buttons optional for Telegram and Mattermost (openclaw#52589) (thanks @tylerliu612)

* test: update codex test fixtures to gpt-5.4

* fix: repair runtime seams after rebase

* fix: restore Telegram topic announce delivery (openclaw#51688) (thanks @mvanhorn)

When `replyLike.text` or `replyLike.caption` is an unexpected
non-string value (edge case from some Telegram API responses),
the reply body was coerced to "[object Object]" via string
concatenation. Add a `typeof === "string"` guard to gracefully
fall back to empty string, matching the existing pattern used
for `quoteText` in the same function.

Co-authored-by: Penchan <[email protected]>

* docs: sync generated release baselines

* test: isolate pi embedded model thread fixtures

* fix: restore provider runtime lazy boundary

* fix: preserve Telegram reply context text (openclaw#50500) (thanks @p3nchan)

* fix: guard Telegram reply context text (openclaw#50500) (thanks @p3nchan)

* fix: preserve Telegram reply caption fallback (openclaw#50500) (thanks @p3nchan)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* build: prepare 2026.3.22-beta.1

* fix: restore provider runtime lazy boundary

* test: add parallels npm update smoke

* test: split pi embedded model thread fixtures

* fix: stop browser server tests from launching real chrome

* test: stabilize live provider docker probes

* fix: restart windows gateway after npm update

* test: isolate server-context browser harness imports

* test: inject model runtime hooks for thread-safe tests

* test: snapshot ci timeout investigation

* test: target gemini 3.1 flash alias

* test: stabilize trigger handling and hook e2e tests

* build: prepare 2026.3.22

---------

Co-authored-by: Stephen Schoettler <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: create <[email protected]>
Co-authored-by: moltbot886 <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: CodeForgeNet <[email protected]>
Co-authored-by: Martin Garramon <[email protected]>
Co-authored-by: François Martin <[email protected]>
Co-authored-by: Josh Lehman <[email protected]>
Co-authored-by: Charles Dusek <[email protected]>
Co-authored-by: Kevin ONeill <[email protected]>
Co-authored-by: Rick_Xu <[email protected]>
Co-authored-by: rcrick <[email protected]>
Co-authored-by: frankekn <[email protected]>
Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: Frank <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: ruochen <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: artwalker <[email protected]>
Co-authored-by: RichardCao <[email protected]>
Co-authored-by: RichardCao <[email protected]>
Co-authored-by: liuyang <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Penchan <[email protected]>
Co-authored-by: Penchan <[email protected]>
Co-authored-by: Julia Bush <[email protected]>
joe2643 added a commit to joe2643/openclaw that referenced this pull request Mar 26, 2026
…d off (#2)

* refactor(plugins): move remaining channel and provider ownership out of src

* refactor(plugins): finish provider and whatsapp cleanup

* fix(gateway): pass process.env in status command probe auth to resolve SecretRef

Fixes openclaw#52360

resolveGatewayProbeAuthSafe was called from status-all.ts without an
env argument, causing the credential resolution chain to fall back to
an empty object instead of process.env. This made env-backed SecretRef
tokens (gateway.auth.token, Telegram botToken, etc.) appear unresolved
in the status command path even when the runtime was healthy.

Added process.env as default fallback in buildGatewayProbeCredentialPolicy
and passed env explicitly from status-all.ts callers.

Related: openclaw#33070, openclaw#38973, openclaw#39415, openclaw#46014, openclaw#49730

* fix(status): resolve only selected probe-auth branch and fix plain status path

Address two Codex P1/P2 issues:

1. (P1) Plain 'openclaw status' and 'openclaw status --json' still went
   through the sync resolveGatewayProbeAuthSafe path in
   status.gateway-probe.ts, which cannot expand SecretRef objects.
   Switched to async resolveGatewayProbeAuthSafeWithSecretInputs.

2. (P2) status-all.ts was eagerly resolving both local and remote probe
   auth before deciding which to use. A stale SecretRef in the unused
   branch could abort the command. Collapsed to a single resolution
   call using the correct mode upfront.

Updated status.scan.test.ts to use mockResolvedValue since
resolveGatewayProbeAuthResolution is now async.

* fix(status): await resolveGatewayProbeAuthResolution in scan.shared

Function is now async after switching to resolveGatewayProbeAuthSafeWithSecretInputs.
Missing await caused TS error: Property 'auth' does not exist on type 'Promise<...>'.

* fix: finish gateway probe auth landing (openclaw#52513) (thanks @CodeForgeNet)

* fix: finish gateway probe auth landing (openclaw#52513) (thanks @CodeForgeNet)

* test: clear msteams gate drift for gateway probe auth landing (openclaw#52513) (thanks @CodeForgeNet)

* fix(exec): return plain-text tool result on failure instead of raw JSON

When an exec command fails (e.g. timeout), the tool previously rejected
with an Error, which the tool adapter caught and wrapped in a JSON object
({ status, tool, error }). The model then received this raw JSON as the
tool result and could parrot it verbatim to the user.

Now exec failures resolve with a proper tool result containing the error
as human-readable text in content[], matching the success path structure.
The model sees plain text it can naturally incorporate into its reply.

Also fixes a pre-existing format issue in update-cli.test.ts.

Fixes openclaw#52484

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: finish exec tool failure landing (openclaw#52508) (thanks @martingarramon)

* fix(openshell): bundle upstream cli fallback

* perf(reply): lazy-load session store writes

* fix(build): restore plugin-sdk and line compat after refactor

* refactor(kilocode): route shared model constants through core seam

* refactor(plugin-sdk): route core provider and telegram seams through sdk barrels

* fix(acp): preserve hidden thought chunks from gateway chat

* fix(docs): rename 'Channel Options' to 'Options' in sdk-entrypoints

This heading labels definePluginEntry options (generic, not channel-specific).
Another agent reverted the previous fix during a merge.

* fix(line): narrow plugin-sdk seams after refactor

* test(voice-call): cover helper utilities

* test(voice-call): cover manager and api helpers

* fix(acp): preserve hidden thought replay on session load

* test(voice-call): cover twilio and reaper helpers

* docs(changelog): note ACP hidden thought replay fix

* test(voice-call): cover utility and tailscale helpers

* fix(ci): resync generated baselines and line runtime seam

* fix(docs): remove duplicate '### Options' headings (MD024)

* fix(ci): restore plugin manifests and boundary tests

* style(docs): format sdk entrypoints doc

* docs(config): refresh generated baseline

* test(voice-call): cover outbound call flow helpers

* fix(ci): repair tts and matrix refactor fallout

* fix(ci): repair voice-call typing and provider contracts

* fix(ci): satisfy voice-call typing and extension boundaries

* Remove personal references from docs (openclaw#25260)

* docs: remove personal references from AGENTS.md

* docs: remove personal reference from sag skill

* docs: note generic agent guidance cleanup

* Update CHANGELOG.md

---------

Co-authored-by: Josh Lehman <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>

* fix(plugins): route keyed queue imports through core (openclaw#52608)

* fix(matrix): preserve send aliases and voice intent

* fix(build): repair stale plugin sdk surfaces

* fix(plugin-sdk): export line runtime subpath

* fix(auth): route copilot login through sdk seam

* feat(web-search): add bundled Exa plugin (openclaw#52617)

* test(msteams): await async setup status lines

* fix(whatsapp): remove outbound runtime cycle

* fix(plugin-sdk): fast-path root diagnostic subscriptions

* fix(exa): align freshness typing and config docs

* fix(matrix): avoid touching dropped room bindings

* fix(web-search): align Exa plugin with current API

* docs(tools): add DuckDuckGo Search provider page

New page: tools/duckduckgo-search.md
- Key-free fallback provider, no API key needed
- Clear Warning about unofficial HTML-based integration
- Limitations section covering bot-challenge risk and reliability
- CardGroup showing good-for vs not-recommended-for use cases

Updated: tools/web.md with DuckDuckGo in CardGroup and comparison table
Updated: docs.json nav and redirect

* test(telegram): align webhook grammy mock

* docs(tools): add Exa Search page, align all search provider docs

New page: tools/exa-search.md
- Neural/keyword/hybrid search modes with content extraction
- Tool parameters including contents (highlights, text, summary)
- Search mode reference table

Rewritten: tools/duckduckgo-search.md
- Aligned to consistent template (Setup, Config, Tool parameters, Notes, Related)
- Simplified from previous version

Aligned across all providers:
- Every search page now ends with a consistent ## Related section
- Replaced 'See [Web tools]' with proper Related links
- Added Exa + DuckDuckGo to web.md overview CardGroup and comparison table
- Added Exa to docs.json nav and redirects

* fix(telegram): inject media loader through bot deps

* fix(ci): harden telegram seams and cap job timeouts

* docs(tools): update Exa Search notes for current API behavior

Add notes about default highlights contents, highlightScores/summary
preservation from responses, description resolution order, and
100-result cap.

* perf: add vitest test perf workflows

* fix(ci): harden changed extension diff fallback

* fix(telegram): harden grammy seams across tests

* refactor: extract exec outcome and tool result helpers

* test: isolate exec foreground failure coverage

* fix(test): allow empty extension lane

* perf: enable vitest fs module cache by default

* fix(cli): route plugin logs to stderr during --json output

* fix(cli): route deferred plugin logs to stderr in status --json

* fix: keep status --json stdout clean (openclaw#52449) (thanks @cgdusek)

* refactor(ci): collapse fast setup jobs into preflight

* fix(exec): accept runtime failure kind in formatter

* fix: include .env file vars in gateway service environment on install

When building the gateway install plan, read and parse
~/.openclaw/.env (or $OPENCLAW_STATE_DIR/.env) and merge those
key-value pairs into the service environment at the lowest
priority — below config env vars, auth-profile refs, and the
core service environment (HOME, PATH, OPENCLAW_*).

This ensures that user-defined secrets stored in .env (e.g.
BRAVE_API_KEY, OPENROUTER_API_KEY, DISCORD_BOT_TOKEN) are
embedded in the LaunchAgent plist (macOS), systemd unit (Linux),
and Scheduled Task (Windows) at install time, rather than
relying solely on the gateway process loading them via
dotenv.config() at startup.

Previously, on macOS the LaunchAgent plist never included .env
vars, which meant:
- launchctl print did not show user secrets (hard to debug)
- Child processes spawned before dotenv loaded had no access
- If the same key existed in both .env and the plist, the stale
  plist value won via dotenv override:false semantics

Dangerous host env vars (NODE_OPTIONS, LD_PRELOAD, etc.) are
filtered using the same security policy applied to config env
vars.

Fixes openclaw#37101
Relates to openclaw#22663

* fix: normalize env var keys and isolate tests from real .env

- Apply normalizeEnvVarKey({ portable: true }) before security
  filtering, matching the established pattern in env-vars.ts.
  Rejects non-portable key names (spaces, special chars) that
  would produce invalid plist/systemd syntax.

- Isolate existing tests from the developer's real ~/.openclaw/.env
  by providing a temp HOME directory, preventing flaky failures
  when the test machine has a populated .env file.

* fix: narrow exec exit failure kind typing

* fix(test): isolate flaky extension lanes

* feat(web-search): add DuckDuckGo bundled plugin (openclaw#52629)

* feat(web-search): add DuckDuckGo bundled plugin

* chore(changelog): restore main changelog

* fix(web-search): harden DuckDuckGo challenge detection

* refactor: split durable service env helpers

* refactor: extract gateway install token helpers

* fix(web-search): mark DuckDuckGo experimental

* docs(tools): update DuckDuckGo Search for landed plugin code

- Mark as experimental (not just unofficial)
- Add region and safeSearch tool parameters (from DDG schema)
- Add plugin config example for region/safeSearch defaults
- Document auto-detection order (100 = last)
- Note SafeSearch defaults to moderate
- Verified against extensions/duckduckgo/src/

* fix(agents): deny local MEDIA paths for MCP results

* Usage: include reset and deleted session archives (openclaw#43215)

Merged via squash.

Prepared head SHA: 49ed6c2
Co-authored-by: rcrick <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* docs(tools): soften DDG wording (scrapes -> pulls/gathers)

* fix(build): add stable memory-cli dist entry (openclaw#51759)

Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: Frank <[email protected]>

* refactor!: drop legacy CLAWDBOT env compatibility

* refactor!: remove moltbot state-dir migration fallback

* fix(gateway): preserve async hook ingress provenance

* fix(ci): write dist build stamp after builds

* perf: trim vitest hot imports and refresh manifests

* fix(security): unwrap time dispatch wrappers

* fix(plugin-sdk): fall back to src root alias files

* fix(ci): skip docs-only preflight pnpm audit

* docs(changelog): note time exec approval fix

* docs: refresh plugin-sdk api baseline

* fix(runtime): make dist-runtime staging idempotent

* fix(media): bound remote error-body snippet reads

* fix(gateway): gate internal command persistence mutations

* fix: restrict remote marketplace plugin sources

* fix(runtime): skip peer resolution for bundled plugin deps

* docs(agents): prefer current test model examples

* fix(exec): escape invisible approval filler chars

* test(models): refresh example model fixtures

* fix(security): unify dispatch wrapper approval hardening

* fix(security): harden explicit-proxy SSRF pinning

* fix: gate synology chat reply name matching

* docs: clarify sessions_spawn ACP vs subagent policies

* refactor(exec): split wrapper resolution modules

* refactor(exec): make dispatch wrapper semantics spec-driven

* refactor(exec): share wrapper trust planning

* refactor(exec): rename wrapper plans for trust semantics

* fix: include .npmrc in onboard docker build

* test: trim docker live auth mounts

* Docs: refresh config baseline for Synology Chat

* refactor: clarify synology delivery identity names

* refactor: centralize synology dangerous name matching

* refactor: narrow synology legacy name lookup

* refactor: audit synology dangerous name matching

* refactor: dedupe synology config schema

* fix: normalize scoped vitest filter paths

* fix(voice-call): harden webhook pre-auth guards

* fix(synology-chat): fail closed shared webhook paths

* docs: credit nexrin in synology changelog

* test: fix base vitest thread regressions

* test: finish base vitest thread fixture fixes

* test(voice-call): accept oversize webhook socket resets

* test: honor env auth in gateway live probes

* fix: harden plugin docker e2e

* Docs: align MiniMax examples with M2.7

* fix(ci): restore stale guardrails and baselines

* Test: isolate qr dashboard integration suite

* Gateway: resolve fallback plugin context lazily

* fix: bind bootstrap setup codes to node profile

* fix(tlon): unify settings reconciliation semantics

* refactor(synology-chat): type startup webhook path policy

* docs(synology-chat): clarify multi-account webhook paths

* refactor: unify minimax model and failover live policies

* docs: sync minimax m2.7 references

* fix: harden Windows Parallels smoke installs

* docs: reorder unreleased changelog by user impact

* refactor: remove embedded runner cwd mutation

* Infra: support shell carrier allow-always approvals

* refactor: centralize bootstrap profile handling

* refactor: reuse canonical setup bootstrap profile

* fix(plugin-sdk): resolve hashed diagnostic events chunks

* fix(plugin-sdk): normalize hashed diagnostic event exports

* test: fix ci env-sensitive assertions

* fix(gateway): fail closed on unresolved discovery endpoints

* feat: add slash plugin installs

* fix(media): block remote-host file URLs in loaders

* fix(media): harden secondary local path seams

* test: harden no-isolate reply teardown

* docs(changelog): add Windows media security fix

* refactor(gateway): centralize discovery target handling

* test: narrow live transcript scaffolding strip

* test: fix ci docs drift and bun qr exit handling

* fix(browser): enforce node browser proxy allowProfiles

* refactor(media): share local file access guards

* test: stabilize ci test harnesses

* test: harden no-isolate test module resets

* fix(plugins): preserve live hook registry during gateway runs

* test: fix channel summary registry setup

* test: harden isolated test mocks

* chore(plugins): remove opik investigation checkpoints

* ACPX: align pinned runtime version (openclaw#52730)

* ACPX: align pinned runtime version

* ACPX: drop version example from help text

* test: stop leaking image workspace temp dirs

* fix(android): gate canvas bridge to trusted pages (openclaw#52722)

* fix(android): gate canvas bridge to trusted pages

* fix(changelog): note android canvas bridge gating

* Update apps/android/app/src/main/java/ai/openclaw/app/node/CanvasActionTrust.kt

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(android): snapshot canvas URL on UI thread

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* test: isolate base vitest thread blockers

* fix: sync agent and autoreply e2e updates

* test: harden no-isolate mocked module resets

* docs: reorder unreleased changelog

* fix(changelog): note windows media path guardrails (openclaw#52738)

* fix: alphabetize web search provider listings

* docs: clarify unreleased breaking changes

* test: harden ci isolated mocks

* fix: align websocket stream fallback types

* test: finish no-isolate suite hardening

* style: format image-generation runtime tests

* fix(memory-core): register memory tools independently to prevent coupled failure (openclaw#52668)

Merged via admin squash because current required CI failures are inherited from base and match latest `main` failures outside this PR's `memory-core` surface.

Prepared head SHA: df7f968
Co-authored-by: artwalker <[email protected]>
Reviewed-by: @frankekn

* fix(status): recompute fallback context window (openclaw#51795)

* fix(status): recompute fallback context window

* fix(status): keep live context token caps on fallback

* fix(status): preserve fallback runtime context windows

* fix(status): preserve configured fallback context caps

* fix(status): keep provider-aware transcript context lookups

* fix(status): preserve explicit fallback context caps

* fix(status): clamp fallback configured context caps

* fix(status): keep raw runtime slash ids

* fix(status): refresh plugin-sdk api baseline

* fix(status): preserve fallback context lookup

* test(status): refresh plugin-sdk api baseline

* fix(status): keep runtime slash-id context lookup

---------

Co-authored-by: create <[email protected]>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: RichardCao <[email protected]>

* fix(telegram): make buttons schema optional in message tool

The Telegram plugin injects a `buttons` property into the message tool
schema via `createMessageToolButtonsSchema()`, but without wrapping it
in `Type.Optional()`. This causes TypeBox to include `buttons` in the
JSON Schema `required` array.

In isolated sessions (e.g. cron jobs) where no `currentChannel` is set,
all plugin schemas are merged into the message tool. When the LLM calls
the message tool without a `buttons` parameter, AJV validation fails
with: `buttons: must have required property 'buttons'`.

Wrap the buttons schema in `Type.Optional()` so it is not required.

* fix: keep message-tool buttons optional for Telegram and Mattermost (openclaw#52589) (thanks @tylerliu612)

* test: update codex test fixtures to gpt-5.4

* fix: repair runtime seams after rebase

* fix: restore Telegram topic announce delivery (openclaw#51688) (thanks @mvanhorn)

When `replyLike.text` or `replyLike.caption` is an unexpected
non-string value (edge case from some Telegram API responses),
the reply body was coerced to "[object Object]" via string
concatenation. Add a `typeof === "string"` guard to gracefully
fall back to empty string, matching the existing pattern used
for `quoteText` in the same function.

Co-authored-by: Penchan <[email protected]>

* docs: sync generated release baselines

* test: isolate pi embedded model thread fixtures

* fix: restore provider runtime lazy boundary

* fix: preserve Telegram reply context text (openclaw#50500) (thanks @p3nchan)

* fix: guard Telegram reply context text (openclaw#50500) (thanks @p3nchan)

* fix: preserve Telegram reply caption fallback (openclaw#50500) (thanks @p3nchan)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* build: prepare 2026.3.22-beta.1

* fix: restore provider runtime lazy boundary

* test: add parallels npm update smoke

* test: split pi embedded model thread fixtures

* fix: stop browser server tests from launching real chrome

* test: stabilize live provider docker probes

* fix: restart windows gateway after npm update

* test: isolate server-context browser harness imports

* test: inject model runtime hooks for thread-safe tests

* test: snapshot ci timeout investigation

* test: target gemini 3.1 flash alias

* test: stabilize trigger handling and hook e2e tests

* build: prepare 2026.3.22

* test: harden channel suite isolation

* test: inject thread-safe deps for agent tools

* test: raise timeout for slow provider auth normalization

* ci: stabilize windows and bun unit lanes

* test: inject thread-safe gateway and ACP seams

* test: isolate pi model and reset-model thread fixtures

* build: prepare 2026.3.23

* test: inject image-tool provider deps for raw threads

* test: stabilize e2e module isolation

* test: decouple vitest config checks from ambient env

* fix: harden parallels smoke agent invocation

* test: avoid repo-root perf profile artifacts

* test: inject thread-safe base seams

* fix: document Telegram asDocument alias (openclaw#52461) (thanks @bakhtiersizhaev)

* feat(telegram): add asDocument param to message tool

Adds `asDocument` as a user-facing alias for the existing `forceDocument`
parameter in the message tool. When set to `true`, media files (images,
videos, GIFs) are sent via `sendDocument` instead of `sendPhoto`/
`sendVideo`/`sendAnimation`, preserving the original file quality
without Telegram compression.

This is useful when agents need to deliver high-resolution images or
uncompressed files to users via Telegram.

`asDocument` is intentionally an alias rather than a replacement — the
existing `forceDocument` continues to work unchanged.

Changes:
- src/agents/tools/message-tool.ts: add asDocument to send schema
- src/agents/tools/telegram-actions.ts: OR asDocument into forceDocument
- src/infra/outbound/message-action-runner.ts: same OR logic for outbound path
- extensions/telegram/src/channel-actions.ts: read and forward asDocument
- src/channels/plugins/actions/actions.test.ts: add test case

* fix: restore channel-actions.ts to main version (rebase conflict fix)

* fix(test): match asDocument test payload to actual params structure

* fix(telegram): preserve forceDocument alias semantics

* fix: document Telegram asDocument alias (openclaw#52461) (thanks @bakhtiersizhaev)

---------

Co-authored-by: Бахтиер Сижаев <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* fix: refactor deepseek bundled plugin (openclaw#48762) (thanks @07akioni)

* fix: declare typebox runtime dep for mattermost plugin

* test: reset line webhook mocks between cases

* test: split attempt spawn-workspace thread fixtures

* test: remove replaced spawn-workspace monolith

* refactor: isolate attempt context engine thread helpers

* CI: remove npm release preview workflow (openclaw#52825)

* CI: remove npm release preview workflow

* Docs: align release maintainer skill with manual publish

* Docs: expand release maintainer skill flow

* test: stabilize gateway thread harness

* test: fix status plugin pagination expectation

* test: harden channel suite isolation

* build: sync lockfile for mattermost plugin

* fix: ensure env proxy dispatcher before MiniMax and OpenAI Codex OAuth flows (openclaw#52228)

Verified:
- pnpm install --frozen-lockfile
- NPM_CONFIG_CACHE=/tmp/openclaw-npm-cache-52228 pnpm build
- pnpm check
- pnpm test:macmini (failed on inherited pre-existing plugin contract test: src/plugins/contracts/registry.contract.test.ts missing deepseek in bundled provider contract registry outside this PR surface)

Co-authored-by: openperf <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* fix: restore ci gates

* test: stabilize channel ci gate

* docs: refresh generated config baseline

* release: verify control-ui assets are included in npm tarball

* release-check: include stderr/stdout when npm pack fails

* release: add changelog for control UI tarball check

* fix: keep session transcript pointers fresh after compaction (openclaw#50688)

Co-authored-by: Frank Yang <[email protected]>

* fix(msteams): isolate probe test env credentials

* release: automate macOS publishing (openclaw#52853)

* release: automate macOS publishing

* release: keep mac appcast in openclaw repo

* release: add preflight-only release workflow runs

* release: keep appcast updates manual

* release: generate signed appcast as workflow artifact

* release: require preflight before publish

* release: require mac app for every release

* docs: clarify every release ships mac app

* release: document Sparkle feed and SHA rules

* release: keep publish flow tag-based

* release: stabilize mac appcast flow

* release: document local mac fallback

* Update CHANGELOG.md

* Improve PR template regression prompts

* fix(agents): preserve anthropic thinking block order (openclaw#52961)

* fix(release): ship bundled plugins in pack artifacts

* fix(config): keep built-in channels out of plugin allowlists (openclaw#52964)

* fix(config): keep built-in channels out of plugin allowlists

* docs(changelog): note doctor whatsapp allowlist fix

* docs(changelog): move doctor whatsapp fix to top

* Update CHANGELOG.md

* fix(config): keep built-in auto-enable idempotent

* fix(release): preserve shipped channel surfaces in npm tar (openclaw#52913)

* fix(channels): ship official channel catalog (openclaw#52838)

* fix(release): keep shipped bundles in npm tar (openclaw#52838)

* build(release): fix rebased release-check helpers (openclaw#52838)

* fix(gateway): harden supervised lock and browser attach readiness

* fix(matrix): avoid duplicate runtime api exports

* fix(gateway): avoid probe false negatives after connect

* docs(changelog): note release and matrix fixes

* fix(plugins): unblock Discord/Slack message tool sends and Feishu media (openclaw#52991)

* fix(plugins): unblock Discord and Slack message tool payloads

* docs(changelog): note Discord Slack and Feishu message fixes

* fix(channels): preserve external catalog overrides (openclaw#52988)

* fix(channels): preserve external catalog overrides

* fix(channels): clarify catalog precedence

* fix(channels): respect overridden install specs

* fix(gateway): require admin for agent session reset

* fix(voice-call): stabilize plivo v2 replay keys

* fix(gateway): require auth for canvas routes

* feat(context-pruning): cache media to disk during pruning instead of stripping

When cacheMedia is enabled, pruned image blocks are saved to
~/.openclaw/media/cache/ and replaced with a file path reference
so the agent can re-read them on demand. Includes TTL cleanup
and session-scoped cache management.

* feat(media): add media cache module for context pruning

* feat(whatsapp): make block streaming configurable instead of hardcoded off

---------

Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: CodeForgeNet <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Martin Garramon <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: François Martin <[email protected]>
Co-authored-by: Josh Lehman <[email protected]>
Co-authored-by: Charles Dusek <[email protected]>
Co-authored-by: Kevin ONeill <[email protected]>
Co-authored-by: Rick_Xu <[email protected]>
Co-authored-by: rcrick <[email protected]>
Co-authored-by: frankekn <[email protected]>
Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: Frank <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: ruochen <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: artwalker <[email protected]>
Co-authored-by: RichardCao <[email protected]>
Co-authored-by: create <[email protected]>
Co-authored-by: RichardCao <[email protected]>
Co-authored-by: liuyang <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Penchan <[email protected]>
Co-authored-by: Penchan <[email protected]>
Co-authored-by: Julia Bush <[email protected]>
Co-authored-by: Bakhtier Sizhaev <[email protected]>
Co-authored-by: Бахтиер Сижаев <[email protected]>
Co-authored-by: wangchunyue <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Co-authored-by: evann <[email protected]>
Co-authored-by: Robin Waslander <[email protected]>
Co-authored-by: Sathvik Veerapaneni <[email protected]>
Co-authored-by: Nimrod Gutman <[email protected]>
0x666c6f added a commit to 0x666c6f/openclaw that referenced this pull request Mar 26, 2026
…claw#105)

* fix(web-search): mark DuckDuckGo experimental

* docs(tools): update DuckDuckGo Search for landed plugin code

- Mark as experimental (not just unofficial)
- Add region and safeSearch tool parameters (from DDG schema)
- Add plugin config example for region/safeSearch defaults
- Document auto-detection order (100 = last)
- Note SafeSearch defaults to moderate
- Verified against extensions/duckduckgo/src/

* fix(agents): deny local MEDIA paths for MCP results

* Usage: include reset and deleted session archives (openclaw#43215)

Merged via squash.

Prepared head SHA: 49ed6c2
Co-authored-by: rcrick <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* docs(tools): soften DDG wording (scrapes -> pulls/gathers)

* fix(build): add stable memory-cli dist entry (openclaw#51759)

Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: Frank <[email protected]>

* refactor!: drop legacy CLAWDBOT env compatibility

* refactor!: remove moltbot state-dir migration fallback

* fix(gateway): preserve async hook ingress provenance

* fix(ci): write dist build stamp after builds

* perf: trim vitest hot imports and refresh manifests

* fix(security): unwrap time dispatch wrappers

* fix(plugin-sdk): fall back to src root alias files

* fix(ci): skip docs-only preflight pnpm audit

* docs(changelog): note time exec approval fix

* docs: refresh plugin-sdk api baseline

* fix(runtime): make dist-runtime staging idempotent

* fix(media): bound remote error-body snippet reads

* fix(gateway): gate internal command persistence mutations

* fix: restrict remote marketplace plugin sources

* fix(runtime): skip peer resolution for bundled plugin deps

* docs(agents): prefer current test model examples

* fix(exec): escape invisible approval filler chars

* test(models): refresh example model fixtures

* fix(security): unify dispatch wrapper approval hardening

* fix(security): harden explicit-proxy SSRF pinning

* fix: gate synology chat reply name matching

* docs: clarify sessions_spawn ACP vs subagent policies

* refactor(exec): split wrapper resolution modules

* refactor(exec): make dispatch wrapper semantics spec-driven

* refactor(exec): share wrapper trust planning

* refactor(exec): rename wrapper plans for trust semantics

* fix: include .npmrc in onboard docker build

* test: trim docker live auth mounts

* Docs: refresh config baseline for Synology Chat

* refactor: clarify synology delivery identity names

* refactor: centralize synology dangerous name matching

* refactor: narrow synology legacy name lookup

* refactor: audit synology dangerous name matching

* refactor: dedupe synology config schema

* fix: normalize scoped vitest filter paths

* fix(voice-call): harden webhook pre-auth guards

* fix(synology-chat): fail closed shared webhook paths

* docs: credit nexrin in synology changelog

* test: fix base vitest thread regressions

* test: finish base vitest thread fixture fixes

* test(voice-call): accept oversize webhook socket resets

* test: honor env auth in gateway live probes

* fix: harden plugin docker e2e

* Docs: align MiniMax examples with M2.7

* fix(ci): restore stale guardrails and baselines

* Test: isolate qr dashboard integration suite

* Gateway: resolve fallback plugin context lazily

* fix: bind bootstrap setup codes to node profile

* fix(tlon): unify settings reconciliation semantics

* refactor(synology-chat): type startup webhook path policy

* docs(synology-chat): clarify multi-account webhook paths

* refactor: unify minimax model and failover live policies

* docs: sync minimax m2.7 references

* fix: harden Windows Parallels smoke installs

* docs: reorder unreleased changelog by user impact

* refactor: remove embedded runner cwd mutation

* Infra: support shell carrier allow-always approvals

* refactor: centralize bootstrap profile handling

* refactor: reuse canonical setup bootstrap profile

* fix(plugin-sdk): resolve hashed diagnostic events chunks

* fix(plugin-sdk): normalize hashed diagnostic event exports

* test: fix ci env-sensitive assertions

* fix(gateway): fail closed on unresolved discovery endpoints

* feat: add slash plugin installs

* fix(media): block remote-host file URLs in loaders

* fix(media): harden secondary local path seams

* test: harden no-isolate reply teardown

* docs(changelog): add Windows media security fix

* refactor(gateway): centralize discovery target handling

* test: narrow live transcript scaffolding strip

* test: fix ci docs drift and bun qr exit handling

* fix(browser): enforce node browser proxy allowProfiles

* refactor(media): share local file access guards

* test: stabilize ci test harnesses

* test: harden no-isolate test module resets

* fix(plugins): preserve live hook registry during gateway runs

* test: fix channel summary registry setup

* test: harden isolated test mocks

* chore(plugins): remove opik investigation checkpoints

* ACPX: align pinned runtime version (openclaw#52730)

* ACPX: align pinned runtime version

* ACPX: drop version example from help text

* test: stop leaking image workspace temp dirs

* fix(android): gate canvas bridge to trusted pages (openclaw#52722)

* fix(android): gate canvas bridge to trusted pages

* fix(changelog): note android canvas bridge gating

* Update apps/android/app/src/main/java/ai/openclaw/app/node/CanvasActionTrust.kt

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(android): snapshot canvas URL on UI thread

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* test: isolate base vitest thread blockers

* fix: sync agent and autoreply e2e updates

* test: harden no-isolate mocked module resets

* docs: reorder unreleased changelog

* fix(changelog): note windows media path guardrails (openclaw#52738)

* fix: alphabetize web search provider listings

* docs: clarify unreleased breaking changes

* test: harden ci isolated mocks

* fix: align websocket stream fallback types

* test: finish no-isolate suite hardening

* style: format image-generation runtime tests

* fix(memory-core): register memory tools independently to prevent coupled failure (openclaw#52668)

Merged via admin squash because current required CI failures are inherited from base and match latest `main` failures outside this PR's `memory-core` surface.

Prepared head SHA: df7f968
Co-authored-by: artwalker <[email protected]>
Reviewed-by: @frankekn

* fix(status): recompute fallback context window (openclaw#51795)

* fix(status): recompute fallback context window

* fix(status): keep live context token caps on fallback

* fix(status): preserve fallback runtime context windows

* fix(status): preserve configured fallback context caps

* fix(status): keep provider-aware transcript context lookups

* fix(status): preserve explicit fallback context caps

* fix(status): clamp fallback configured context caps

* fix(status): keep raw runtime slash ids

* fix(status): refresh plugin-sdk api baseline

* fix(status): preserve fallback context lookup

* test(status): refresh plugin-sdk api baseline

* fix(status): keep runtime slash-id context lookup

---------

Co-authored-by: create <[email protected]>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: RichardCao <[email protected]>

* fix(telegram): make buttons schema optional in message tool

The Telegram plugin injects a `buttons` property into the message tool
schema via `createMessageToolButtonsSchema()`, but without wrapping it
in `Type.Optional()`. This causes TypeBox to include `buttons` in the
JSON Schema `required` array.

In isolated sessions (e.g. cron jobs) where no `currentChannel` is set,
all plugin schemas are merged into the message tool. When the LLM calls
the message tool without a `buttons` parameter, AJV validation fails
with: `buttons: must have required property 'buttons'`.

Wrap the buttons schema in `Type.Optional()` so it is not required.

* fix: keep message-tool buttons optional for Telegram and Mattermost (openclaw#52589) (thanks @tylerliu612)

* test: update codex test fixtures to gpt-5.4

* fix: repair runtime seams after rebase

* fix: restore Telegram topic announce delivery (openclaw#51688) (thanks @mvanhorn)

When `replyLike.text` or `replyLike.caption` is an unexpected
non-string value (edge case from some Telegram API responses),
the reply body was coerced to "[object Object]" via string
concatenation. Add a `typeof === "string"` guard to gracefully
fall back to empty string, matching the existing pattern used
for `quoteText` in the same function.

Co-authored-by: Penchan <[email protected]>

* docs: sync generated release baselines

* test: isolate pi embedded model thread fixtures

* fix: restore provider runtime lazy boundary

* fix: preserve Telegram reply context text (openclaw#50500) (thanks @p3nchan)

* fix: guard Telegram reply context text (openclaw#50500) (thanks @p3nchan)

* fix: preserve Telegram reply caption fallback (openclaw#50500) (thanks @p3nchan)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* build: prepare 2026.3.22-beta.1

* fix: restore provider runtime lazy boundary

* test: add parallels npm update smoke

* test: split pi embedded model thread fixtures

* fix: stop browser server tests from launching real chrome

* test: stabilize live provider docker probes

* fix: restart windows gateway after npm update

* test: isolate server-context browser harness imports

* test: inject model runtime hooks for thread-safe tests

* test: snapshot ci timeout investigation

* test: target gemini 3.1 flash alias

* test: stabilize trigger handling and hook e2e tests

* build: prepare 2026.3.22

* test: harden channel suite isolation

* test: inject thread-safe deps for agent tools

* test: raise timeout for slow provider auth normalization

* ci: stabilize windows and bun unit lanes

* test: inject thread-safe gateway and ACP seams

* test: isolate pi model and reset-model thread fixtures

* build: prepare 2026.3.23

* test: inject image-tool provider deps for raw threads

* test: stabilize e2e module isolation

* test: decouple vitest config checks from ambient env

* fix: harden parallels smoke agent invocation

* test: avoid repo-root perf profile artifacts

* test: inject thread-safe base seams

* fix: document Telegram asDocument alias (openclaw#52461) (thanks @bakhtiersizhaev)

* feat(telegram): add asDocument param to message tool

Adds `asDocument` as a user-facing alias for the existing `forceDocument`
parameter in the message tool. When set to `true`, media files (images,
videos, GIFs) are sent via `sendDocument` instead of `sendPhoto`/
`sendVideo`/`sendAnimation`, preserving the original file quality
without Telegram compression.

This is useful when agents need to deliver high-resolution images or
uncompressed files to users via Telegram.

`asDocument` is intentionally an alias rather than a replacement — the
existing `forceDocument` continues to work unchanged.

Changes:
- src/agents/tools/message-tool.ts: add asDocument to send schema
- src/agents/tools/telegram-actions.ts: OR asDocument into forceDocument
- src/infra/outbound/message-action-runner.ts: same OR logic for outbound path
- extensions/telegram/src/channel-actions.ts: read and forward asDocument
- src/channels/plugins/actions/actions.test.ts: add test case

* fix: restore channel-actions.ts to main version (rebase conflict fix)

* fix(test): match asDocument test payload to actual params structure

* fix(telegram): preserve forceDocument alias semantics

* fix: document Telegram asDocument alias (openclaw#52461) (thanks @bakhtiersizhaev)

---------

Co-authored-by: Бахтиер Сижаев <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* fix: refactor deepseek bundled plugin (openclaw#48762) (thanks @07akioni)

* fix: declare typebox runtime dep for mattermost plugin

* test: reset line webhook mocks between cases

* test: split attempt spawn-workspace thread fixtures

* test: remove replaced spawn-workspace monolith

* refactor: isolate attempt context engine thread helpers

* CI: remove npm release preview workflow (openclaw#52825)

* CI: remove npm release preview workflow

* Docs: align release maintainer skill with manual publish

* Docs: expand release maintainer skill flow

* test: stabilize gateway thread harness

* test: fix status plugin pagination expectation

* test: harden channel suite isolation

* build: sync lockfile for mattermost plugin

* fix: ensure env proxy dispatcher before MiniMax and OpenAI Codex OAuth flows (openclaw#52228)

Verified:
- pnpm install --frozen-lockfile
- NPM_CONFIG_CACHE=/tmp/openclaw-npm-cache-52228 pnpm build
- pnpm check
- pnpm test:macmini (failed on inherited pre-existing plugin contract test: src/plugins/contracts/registry.contract.test.ts missing deepseek in bundled provider contract registry outside this PR surface)

Co-authored-by: openperf <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* fix: restore ci gates

* test: stabilize channel ci gate

* docs: refresh generated config baseline

* release: verify control-ui assets are included in npm tarball

* release-check: include stderr/stdout when npm pack fails

* release: add changelog for control UI tarball check

* fix: keep session transcript pointers fresh after compaction (openclaw#50688)

Co-authored-by: Frank Yang <[email protected]>

* fix(msteams): isolate probe test env credentials

* release: automate macOS publishing (openclaw#52853)

* release: automate macOS publishing

* release: keep mac appcast in openclaw repo

* release: add preflight-only release workflow runs

* release: keep appcast updates manual

* release: generate signed appcast as workflow artifact

* release: require preflight before publish

* release: require mac app for every release

* docs: clarify every release ships mac app

* release: document Sparkle feed and SHA rules

* release: keep publish flow tag-based

* release: stabilize mac appcast flow

* release: document local mac fallback

* Update CHANGELOG.md

* Improve PR template regression prompts

* fix(agents): preserve anthropic thinking block order (openclaw#52961)

* fix(release): ship bundled plugins in pack artifacts

* fix(config): keep built-in channels out of plugin allowlists (openclaw#52964)

* fix(config): keep built-in channels out of plugin allowlists

* docs(changelog): note doctor whatsapp allowlist fix

* docs(changelog): move doctor whatsapp fix to top

* Update CHANGELOG.md

* fix(config): keep built-in auto-enable idempotent

* fix(release): preserve shipped channel surfaces in npm tar (openclaw#52913)

* fix(channels): ship official channel catalog (openclaw#52838)

* fix(release): keep shipped bundles in npm tar (openclaw#52838)

* build(release): fix rebased release-check helpers (openclaw#52838)

* fix(gateway): harden supervised lock and browser attach readiness

* fix(matrix): avoid duplicate runtime api exports

* fix(gateway): avoid probe false negatives after connect

* docs(changelog): note release and matrix fixes

* fix(plugins): unblock Discord/Slack message tool sends and Feishu media (openclaw#52991)

* fix(plugins): unblock Discord and Slack message tool payloads

* docs(changelog): note Discord Slack and Feishu message fixes

* fix(channels): preserve external catalog overrides (openclaw#52988)

* fix(channels): preserve external catalog overrides

* fix(channels): clarify catalog precedence

* fix(channels): respect overridden install specs

* fix(gateway): require admin for agent session reset

* fix(voice-call): stabilize plivo v2 replay keys

* fix(gateway): require auth for canvas routes

* fix(clawhub): resolve auth token for skill browsing (openclaw#53017)

* fix(clawhub): resolve auth token for skill browsing

* docs(changelog): note clawhub skill auth fix

* fix(release): raise npm pack size budget

* Tests: fix fresh-main regressions (openclaw#53011)

* Tests: fix fresh-main regressions

* Tests: avoid chat notice cache priming

---------

Co-authored-by: Vincent Koc <[email protected]>

* fix(config): ignore stale plugin allow entries

* fix(browser): reuse running loopback browser after probe miss

* fix(clawhub): honor macOS auth config path (openclaw#53034)

* docs: fix nav ordering, missing pages, and stale model references

- Sort providers alphabetically in docs.json nav
- Sort channels alphabetically in docs.json nav (slack before synology-chat)
- Add install/migrating-matrix to Maintenance nav section (was orphaned)
- Remove zh-CN/plugins/architecture from nav (file does not exist)
- Add Voice Call to channels index page
- Add missing providers to providers index (DeepSeek, GitHub Copilot, OpenCode Go, Synthetic)
- Sort providers index alphabetically
- Update stale claude-3-5-sonnet model reference to claude-sonnet-4-6 in webhook docs

* fix(clawhub): preserve XDG auth path on macOS

* Agents: fix runtime web_search provider selection (openclaw#53020)

Co-authored-by: Vincent Koc <[email protected]>

* docs: fix CLI command tree, SDK import path, and tool group listing

- Remove non-existent 'secrets migrate' from CLI command tree
- Add actual secrets subcommands: audit, configure, apply
- Add missing plugin subcommands: inspect, uninstall, update, marketplace list
- Fix plugins info -> inspect (actual command name)
- Add message send and broadcast subcommands to command tree
- Remove misleading deprecated import from sdk-overview
- Add sessions_yield and subagents to group:sessions tool group docs
- Fix formatting

* fix(gateway): guard openrouter auto pricing recursion (openclaw#53055)

* test: refresh thread-safe agent fixtures

* Release: fix npm release preflight under pnpm (openclaw#52985)

Co-authored-by: Vincent Koc <[email protected]>

* docs(changelog): add channel catalog override note (openclaw#52988) (openclaw#53059)

* fix: harden update dev switch and refresh changelog

* fix(mistral): repair max-token defaults and doctor migration (openclaw#53054)

* fix(mistral): repair max-token defaults and doctor migration

* fix(mistral): add missing small-model repair cap

* fix(plugins): enable bundled Brave web search plugin by default (openclaw#52072)

Brave is a bundled web search plugin but was missing from
BUNDLED_ENABLED_BY_DEFAULT, causing it to be filtered out during
provider resolution. This made web_search unavailable even when
plugins.entries.brave.enabled was configured.

Fixes openclaw#51937

Co-authored-by: Ubuntu <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>

* fix(release): fail empty control ui tarballs

* Revert "fix(plugins): enable bundled Brave web search plugin by default (openclaw#52072)"

This reverts commit 0ea3c4d.

* Telegram: preserve inbound debounce order

* Telegram: fix fire-and-forget debounce order

* fix(reply): refresh followup drain callbacks

* Update CHANGELOG.md

* fix(reply): preserve no-debounce inbound concurrency

* fix(reply): clear idle followup callbacks

* fix(inbound): bound tracked debounce keys

* fix: preserve debounce and followup ordering (openclaw#52998) (thanks @osolmaz)

* fix(discord): reply on native command auth failures (openclaw#53072)

* docs(changelog): add missing recent fixes

* fix: bound tracked debounce key accounting

* fix packaged control ui asset lookup (openclaw#53081)

* fix(cli): preserve posix default git dir

* build: prepare 2026.3.23-beta.1

* test: harden canvas host undici isolation

* docs(changelog): credit web search runtime fix

* fix(openai-codex): bootstrap proxy on oauth refresh (openclaw#53078)

Verified:
- pnpm install --frozen-lockfile
- pnpm exec vitest run extensions/openai/openai-codex-provider.runtime.test.ts extensions/openai/openai-provider.test.ts

* release: harden preflight workflows (openclaw#53087)

* release: harden preflight-only workflows

* release: require main for publish runs

* release: select xcode for macos workflow

* release: retry flaky macos preflight steps

* ci: shard bun test lane

* Fix Control UI operator.read scope handling (openclaw#53110)

Preserve Control UI scopes through the device-auth bypass path, normalize implied operator device-auth scopes, ignore cached under-scoped operator tokens, and degrade read-backed main pages gracefully when a connection truly lacks operator.read.

Co-authored-by: Val Alexander <[email protected]>

* build: prepare 2026.3.23

* fix(agents): prefer runtime snapshot for skill secrets

* docs(changelog): note skill secretref runtime fix

* fix(memory): bootstrap lancedb runtime on demand (openclaw#53111)

Bootstrap LanceDB into plugin runtime state on first use for packaged/global installs, keep @lancedb/lancedb plugin-local, and add regression coverage for bundled, cached, retry, and Nix fail-fast runtime paths.

Co-authored-by: Val Alexander <[email protected]>

* build: finalize 2026.3.23 release

* release: upload macos preflight artifacts (openclaw#53105)

* release: upload macos preflight artifacts

* release: speed up macos preflight

* release: use xlarge macos runner

* release: skip dmg path in macos preflight

* fix(subagents): recheck timed-out announce waits (openclaw#53127)

Recheck timed-out subagent announce waits against the latest runtime snapshot before announcing timeout, and keep that recheck best-effort so transient gateway failures do not suppress the announcement.

Co-authored-by: Val Alexander <[email protected]>

* docs(feishu): replace botName with name in config examples (openclaw#52753)

Merged via squash.

Prepared head SHA: 5237726
Co-authored-by: haroldfabla2-hue <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Reviewed-by: @altaywtf

* fix(plugins): accept clawhub uninstall specs

* test(auth): align device scope expectations (openclaw#53151)

* fix: prevent delivery-mirror re-delivery and raise Slack chunk limit (openclaw#45489)

Merged via squash.

Prepared head SHA: c7664c7
Co-authored-by: theo674 <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Reviewed-by: @altaywtf

* Infra: tighten shell-wrapper positional-argv allowlist matching (openclaw#53133)

* Infra: tighten shell carrier allowlist matching

* fix(security): tighten shell carrier allowlist matcher

* fix: generalize api_error detection for fallback model triggering (openclaw#49611)

Co-authored-by: Ayush Ojha <[email protected]>
Co-authored-by: altaywtf <[email protected]>

* feat(modelstudio): add standard (pay-as-you-go) DashScope endpoints for Qwen (openclaw#43878)

Add Standard API Key auth methods for China (dashscope.aliyuncs.com)
and Global/Intl (dashscope-intl.aliyuncs.com) pay-as-you-go endpoints
alongside the existing Coding Plan (subscription) endpoints.

Also updates group label to 'Qwen (Alibaba Cloud Model Studio)' and
fixes glm-4.7 -> glm-5 in Coding Plan note messages.

Co-authored-by: wenmeng zhou <[email protected]>

* Release: privatize macOS publish flow (openclaw#53166)

* fix(diagnostics): redact credentials from cache-trace diagnostic output

Refs openclaw#53103

* Release: document manual macOS asset upload (openclaw#53178)

* Release: document manual macOS asset upload

* Release: document macOS smoke-test mode

* docs(changelog): reorder release highlights

* test(whatsapp): stabilize login coverage in shared workers

* test(whatsapp): preserve session exports in login coverage

* test(whatsapp): preserve media test module exports

* test(whatsapp): preserve harness session exports

* fix(ci): stabilize whatsapp extension checks

* test: make update-cli checkout path assertion platform-safe

* fix(auth): prevent stale auth store reverts (openclaw#53211)

* Doctor: prune stale plugin allowlist and entry refs (openclaw#53187)

Signed-off-by: sallyom <[email protected]>

* test: stabilize test isolation

* test: update command coverage

* test: expand gemini live transcript stripping

* test: fix update-cli default path assertion

* chore(sre:PLA-920): adopt upstream sync changes

* fix(sre:PLA-920): align branch with adopted upstream tree

* build(sre:PLA-920): refresh dist artifacts

* test(sre:PLA-920): align incident-format expectations

---------

Signed-off-by: sallyom <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Rick_Xu <[email protected]>
Co-authored-by: rcrick <[email protected]>
Co-authored-by: frankekn <[email protected]>
Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: oliviareid-svg <[email protected]>
Co-authored-by: Frank <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: ruochen <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: artwalker <[email protected]>
Co-authored-by: RichardCao <[email protected]>
Co-authored-by: create <[email protected]>
Co-authored-by: RichardCao <[email protected]>
Co-authored-by: liuyang <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Penchan <[email protected]>
Co-authored-by: Penchan <[email protected]>
Co-authored-by: Julia Bush <[email protected]>
Co-authored-by: Bakhtier Sizhaev <[email protected]>
Co-authored-by: Бахтиер Сижаев <[email protected]>
Co-authored-by: wangchunyue <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Co-authored-by: evann <[email protected]>
Co-authored-by: Robin Waslander <[email protected]>
Co-authored-by: Sathvik Veerapaneni <[email protected]>
Co-authored-by: Nimrod Gutman <[email protected]>
Co-authored-by: Luke <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: Jamil Zakirov <[email protected]>
Co-authored-by: TheRipper <[email protected]>
Co-authored-by: Quinn H. <[email protected]>
Co-authored-by: Ubuntu <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
Co-authored-by: betoblair <[email protected]>
Co-authored-by: haroldfabla2-hue <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Co-authored-by: Altay <[email protected]>
Co-authored-by: theo674 <[email protected]>
Co-authored-by: theo674 <[email protected]>
Co-authored-by: Ayush Ojha <[email protected]>
Co-authored-by: Ayush Ojha <[email protected]>
Co-authored-by: George Zhang <[email protected]>
Co-authored-by: wenmeng zhou <[email protected]>
Co-authored-by: Onur <[email protected]>
Co-authored-by: Sally O'Malley <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram cli CLI command changes size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Gateway process ignores SIGTERM during Telegram polling recovery — requires SIGKILL

2 participants