Skip to content

release: automate macOS publishing#52853

Merged
osolmaz merged 12 commits intomainfrom
codex/macos-release-automation
Mar 23, 2026
Merged

release: automate macOS publishing#52853
osolmaz merged 12 commits intomainfrom
codex/macos-release-automation

Conversation

@osolmaz
Copy link
Copy Markdown
Contributor

@osolmaz osolmaz commented Mar 23, 2026

Summary

  • add a manual macOS release workflow with an ungated preflight job and a gated publish job
  • add preflight_only to both core release workflows so Actions can validate the npm and macOS build paths before any real publish run
  • require npm preflight and macOS preflight to succeed before publishing any OpenClaw release
  • require failed preflight runs to be fixed on a new commit, then recreate the tag and matching GitHub release from the passing commit before publish continues
  • state clearly that every OpenClaw release ships the npm package and macOS app together
  • keep the production Sparkle feed on https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml
  • keep the shared production appcast stable-only: beta mac releases upload assets but do not generate a production appcast.xml artifact
  • generate the stable signed appcast.xml artifact before uploading public macOS assets and serialize stable appcast-producing runs across tags
  • keep the final appcast.xml commit as an agent-owned step on main
  • extend the release maintainer skill and public release policy to describe the full preflight-first release sequence

Validation

  • bash -n scripts/package-mac-app.sh scripts/make_appcast.sh
  • parsed .github/workflows/openclaw-npm-release.yml and .github/workflows/macos-release.yml with Ruby YAML.load_file
  • git diff --check
  • scripts/committer "release: automate macOS publishing" .github/workflows/macos-release.yml .agents/skills/openclaw-release-maintainer/SKILL.md scripts/package-mac-app.sh scripts/make_appcast.sh
  • scripts/committer "release: keep mac appcast in openclaw repo" .github/workflows/macos-release.yml .agents/skills/openclaw-release-maintainer/SKILL.md scripts/package-mac-app.sh scripts/make_appcast.sh
  • scripts/committer "release: add preflight-only release workflow runs" .github/workflows/openclaw-npm-release.yml .github/workflows/macos-release.yml .agents/skills/openclaw-release-maintainer/SKILL.md
  • scripts/committer "release: keep appcast updates manual" .github/workflows/macos-release.yml .agents/skills/openclaw-release-maintainer/SKILL.md
  • scripts/committer "release: generate signed appcast as workflow artifact" .github/workflows/macos-release.yml .agents/skills/openclaw-release-maintainer/SKILL.md
  • scripts/committer "release: require preflight before publish" .agents/skills/openclaw-release-maintainer/SKILL.md
  • scripts/committer "release: require mac app for every release" docs/reference/RELEASING.md .agents/skills/openclaw-release-maintainer/SKILL.md
  • scripts/committer "docs: clarify every release ships mac app" docs/reference/RELEASING.md .agents/skills/openclaw-release-maintainer/SKILL.md
  • scripts/committer "release: document Sparkle feed and SHA rules" .agents/skills/openclaw-release-maintainer/SKILL.md
  • scripts/committer "release: keep publish flow tag-based" .agents/skills/openclaw-release-maintainer/SKILL.md
  • scripts/committer "release: stabilize mac appcast flow" .github/workflows/macos-release.yml .agents/skills/openclaw-release-maintainer/SKILL.md

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 23, 2026

🔒 Aisle Security Analysis

We found 5 potential security issue(s) in this PR:

# Severity Title
1 🟠 High GitHub Actions supply-chain risk: third-party actions referenced by mutable tags (not pinned to commit SHA)
2 🟠 High Local composite action uses third-party actions by mutable tag (not pinned)
3 🟠 High Privileged GitHub Actions workflow executes code from user-supplied tag with write token and signing/notary secrets
4 🟡 Medium Signed Sparkle appcast generated from untrusted seed appcast.xml (can bless malicious entries)
5 🟡 Medium Release workflow writes signing/notary private keys to disk without restrictive permissions or cleanup

1. 🟠 GitHub Actions supply-chain risk: third-party actions referenced by mutable tags (not pinned to commit SHA)

Property Value
Severity High
CWE CWE-1104
Location .github/workflows/macos-release.yml:42-47

Description

Several workflows invoke third-party GitHub Actions using mutable tags (e.g., @​v6, @​v7) instead of pinning to an immutable commit SHA.

  • If an upstream action tag is moved or the action repository is compromised, a future workflow run could execute attacker-controlled code.
  • This is especially impactful for release workflows that handle signing materials and publish artifacts.

Vulnerable examples:

- name: Checkout selected tag
  uses: actions/checkout@​v6

- name: Upload stable appcast artifact
  uses: actions/upload-artifact@​v7

Recommendation

Pin all third-party actions to a full-length commit SHA (optionally with a comment indicating the intended version tag).

Example:

- uses: actions/checkout@<FULL_COMMIT_SHA> # v6.x.x

- uses: actions/upload-artifact@<FULL_COMMIT_SHA> # v7.x.x

Also consider enabling GitHub’s dependency review / action pinning policies (e.g., organization/repository policy) to prevent unpinned actions from being introduced.


2. 🟠 Local composite action uses third-party actions by mutable tag (not pinned)

Property Value
Severity High
CWE CWE-1104
Location .github/actions/setup-node-env/action.yml:51-68

Description

The local composite action .github/actions/setup-node-env calls third-party actions using mutable tags instead of pinned commit SHAs.

  • actions/setup-node@​v6 and oven-sh/setup-bun@​v2.2.0 are referenced by tags.
  • As with workflows, if these upstream action tags are retargeted or the action repo is compromised, a future run could execute attacker-controlled code.
  • This composite action is used by both release workflows, so it expands the attack surface of privileged pipelines.

Vulnerable code:

- name: Setup Node.js
  uses: actions/setup-node@​v6

- name: Setup Bun
  uses: oven-sh/setup-bun@​v2.2.0

Recommendation

Pin referenced actions to immutable commit SHAs.

Example:

- uses: actions/setup-node@<FULL_COMMIT_SHA> # v6.x.x

- uses: oven-sh/setup-bun@<FULL_COMMIT_SHA> # v2.2.0

If your org mandates action pinning, add a repository/organization policy (or a CI check) that rejects uses: owner/repo@​v* references.


3. 🟠 Privileged GitHub Actions workflow executes code from user-supplied tag with write token and signing/notary secrets

Property Value
Severity High
CWE CWE-250
Location .github/workflows/macos-release.yml:119-136

Description

The publish_macos_release job checks out and executes scripts from a workflow_dispatch input tag (i.e., ref: refs/tags/${{ inputs.tag }}) before and while having elevated privileges and highly sensitive secrets available.

Impact if a malicious/unreviewed tag is selected (or if tag creation is compromised/mistaken):

  • The workflow runs repository scripts from that tag (scripts/package-mac-dist.sh, scripts/make_appcast.sh). Those scripts can be modified in the tagged commit to execute arbitrary commands.
  • The job grants permissions: contents: write, so arbitrary code can use ${{ github.token }} to mutate repository contents, create/replace releases, upload assets, etc.
  • The job exposes signing/notarization and Sparkle private-key secrets (Developer ID P12 + password, App Store Connect API key, Sparkle ed25519 private key) which could be exfiltrated by malicious code in the tagged commit.

Vulnerable pattern:

permissions:
  contents: write
...
- uses: actions/checkout@​v6
  with:
    ref: refs/tags/${{ inputs.tag }}
...
- name: Build, sign, notarize, and package macOS release
  run: scripts/package-mac-dist.sh
...
- name: Generate signed appcast artifact
  run: scripts/make_appcast.sh ...

While environment: mac-release can gate access to secrets (depending on repo settings), the workflow still inherently trusts and executes the tag’s contents in a privileged context, which is a common release-pipeline supply-chain risk.

Recommendation

Reduce trust in the checked-out tag and apply least privilege:

  1. Pin build scripts to a trusted ref (e.g., main or a protected release-tools branch) and only use the tag for source/artifacts.

Example approach:

- name: Checkout release source (tag)
  uses: actions/checkout@​v6
  with:
    ref: refs/tags/${{ inputs.tag }}
    path: src

- name: Checkout trusted release tooling
  uses: actions/checkout@​v6
  with:
    ref: main
    path: tools

- name: Build using trusted scripts
  run: tools/scripts/package-mac-dist.sh
  working-directory: src
  1. Lower token permissions: set job-level permissions: contents: read and only elevate for the single step that needs uploads/releases.

  2. Isolate secrets: only provide notary/signing/Sparkle secrets to the exact steps that require them, and ensure environment protection rules require manual approval by trusted maintainers.

  3. Add provenance checks: verify the tag commit is reachable from a protected branch (e.g., main) and/or verify a signed tag/commit before proceeding in the publish job.

These defenses limit repo mutation and secret-exfiltration blast radius if an untrusted tag is built.


4. 🟡 Signed Sparkle appcast generated from untrusted seed appcast.xml (can bless malicious entries)

Property Value
Severity Medium
CWE CWE-345
Location scripts/make_appcast.sh:40-67

Description

The macOS release workflow seeds appcast.xml from the current main branch and then runs scripts/make_appcast.sh to produce a signed Sparkle appcast.

In scripts/make_appcast.sh, an existing appcast.xml in the repo workspace is copied into the temporary directory passed to Sparkle’s generate_appcast:

  • If appcast.xml on main is compromised/incorrect (or contains unexpected items), it becomes an input to the feed generation process.
  • The script does not validate or constrain what items are preserved from the seed before signing.
  • If Sparkle’s generate_appcast merges/preserves existing items (common behavior), malicious <item> entries could remain in the output and then be signed with the maintainer’s private key, making clients trust them.

This is a chain-of-trust flaw: signing output derived from untrusted state can “bless” attacker-controlled update metadata.

Recommendation

Do not sign an appcast that can carry forward untrusted entries.

Options (pick one that matches your desired release model):

  1. Generate appcast from a trusted, deterministic source only (recommended):

    • Do not copy an existing appcast.xml into the generate_appcast input directory.
    • Generate a new feed from release artifacts only, or from a pinned/verified history file.
  2. Strictly validate/normalize the seeded appcast before signing:

    • Parse XML and drop any <item> not matching an allowlist (expected domains, expected bundle IDs, expected DSA/EdDSA signature formats, expected enclosure types, etc.).
    • Ensure only the newly-built version (and optionally a small, validated set of previous versions) remain.

Example hardening (simplest: refuse to sign if a seed exists unless explicitly allowed):

# Before copying appcast.xml
if [[ -f "$ROOT/appcast.xml" && "${ALLOW_APPCAST_SEED:-0}" != "1" ]]; then
  echo "Refusing to seed from existing appcast.xml; set ALLOW_APPCAST_SEED=1 to override." >&2
  exit 1
fi

Additionally, in the GitHub workflow, avoid using main as an input for a signed artifact unless it is pinned to a reviewed commit SHA or fetched from a protected, verified source.


5. 🟡 Release workflow writes signing/notary private keys to disk without restrictive permissions or cleanup

Property Value
Severity Medium
CWE CWE-922
Location .github/workflows/macos-release.yml:174-237

Description

In .github/workflows/macos-release.yml, secrets are decoded/written into files under $RUNNER_TEMP (P12 certificate, App Store Connect API key .p8, Sparkle Ed25519 private key .pem) but the workflow does not:

  • set restrictive permissions (e.g., chmod 600) after writing the files
  • delete these sensitive files in an if: always() cleanup step

On many systems the default umask can result in files being readable by other users/processes on the runner, and leaving private key material on disk longer than necessary increases exposure risk (especially if subsequent steps upload/collect diagnostics, or if another process on the runner can access the filesystem).

Vulnerable code (examples):

CERT_PATH="$RUNNER_TEMP/openclaw-macos-release.p12"
...
Path(os.environ["CERT_PATH"]).write_bytes(
    base64.b64decode(os.environ["MACOS_DEVELOPER_ID_P12_BASE64"])
)

NOTARYTOOL_KEY_PATH="$RUNNER_TEMP/openclaw-notary.p8"
SPARKLE_PRIVATE_KEY_PATH="$RUNNER_TEMP/openclaw-sparkle-ed25519.pem"
...
Path(os.environ[path_env]).write_text(value, encoding="utf-8")

Only the temporary keychain is deleted; the on-disk key files are not removed.

Recommendation

  1. Create secret files with restrictive permissions and ensure they are deleted even on failure.

Example hardened approach:

umask 077
CERT_PATH="$RUNNER_TEMP/openclaw-macos-release.p12"
NOTARYTOOL_KEY_PATH="$RUNNER_TEMP/openclaw-notary.p8"
SPARKLE_PRIVATE_KEY_PATH="$RUNNER_TEMP/openclaw-sparkle-ed25519.pem"

# write files ...
chmod 600 "$CERT_PATH" "$NOTARYTOOL_KEY_PATH" "$SPARKLE_PRIVATE_KEY_PATH"

cleanup() {
  rm -f "$CERT_PATH" "$NOTARYTOOL_KEY_PATH" "$SPARKLE_PRIVATE_KEY_PATH" || true
  if [[ -n "${KEYCHAIN_PATH:-}" ]]; then
    security delete-keychain "$KEYCHAIN_PATH" >/dev/null 2>&1 || true
  fi
}
trap cleanup EXIT
  1. Alternatively, avoid writing long-lived key material to disk when possible (e.g., use keychain profiles for notarytool; load Sparkle key from a protected location) and keep file lifetime as short as possible.

Analyzed PR: #52853 at commit cc57168

Last updated on: 2026-03-23T15:22:15Z

@openclaw-barnacle openclaw-barnacle bot added scripts Repository scripts size: M maintainer Maintainer-authored PR labels Mar 23, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 23, 2026

Greptile Summary

This PR automates the macOS release pipeline by introducing a new macos-release.yml workflow, updating the canonical Sparkle feed URL to https://openclaw.ai/appcast.xml across the codebase, and extending the release maintainer skill to document the new process.

Key changes:

  • macos-release.yml: Two-job workflow — an ungated preflight_macos_release job (checkout, full build, Swift tests, ad-hoc packaging to prove correctness) followed by a gated publish_macos_release job (real Developer ID signing, notarization, GitHub asset upload, and appcast publication to the openclaw.ai repo). The asset-upload → appcast-publish ordering is correct, ensuring download URLs are live before Sparkle clients can discover the new release. Keychain setup and teardown follows best practices (masking, temporary keychain, if: always() cleanup). The OPENCLAW_AI_REPO_TOKEN secret is used for cross-repo appcast publishing, and the mac-release environment gate provides the required human approval before publishing.
  • scripts/make_appcast.sh and scripts/package-mac-app.sh: Default Sparkle feed URL updated to match the new production URL.
  • scripts/package-mac-app.sh: pnpm install now skippable via SKIP_PNPM_INSTALL=1, letting CI workflows that already ran installation via setup-node-env avoid a redundant re-install.
  • SKILL.md: Documents that the openclaw.ai appcast update is a required gate (not an optional follow-up) and adds the new workflow to the publish checklist.

Confidence Score: 5/5

  • This PR is safe to merge; changes are well-structured with correct step ordering and solid secret handling.
  • The workflow logic is correct — preflight gates publish, asset upload precedes appcast publication, keychain is always cleaned up, and the mac-release environment provides the required human gate. The SKIP_PNPM_INSTALL guard is additive and backward-compatible. The only observation is a minor P2: KEYCHAIN_PASSWORD is stored in $GITHUB_ENV beyond its point of use, which is a low-risk style issue with no blocking impact.
  • No files require special attention; the single P2 in macos-release.yml (unnecessary KEYCHAIN_PASSWORD in env) is non-blocking.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: .github/workflows/macos-release.yml
Line: 179-180

Comment:
**Unnecessary keychain password in `$GITHUB_ENV`**

`KEYCHAIN_PASSWORD` is written to `$GITHUB_ENV` (line 180) but no subsequent step reads it — the cleanup step only uses `KEYCHAIN_PATH`. Persisting the keychain password beyond the step where it is used widens the surface area for any action added later in the job to access it, even though it is already masked.

Consider only exporting `KEYCHAIN_PATH` to the env file, and keeping `KEYCHAIN_PASSWORD` as a local shell variable scoped to this step.

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

Reviews (1): Last reviewed commit: "release: automate macOS publishing" | Re-trigger Greptile

Comment on lines +179 to +180
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> "$GITHUB_ENV"
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 Unnecessary keychain password in $GITHUB_ENV

KEYCHAIN_PASSWORD is written to $GITHUB_ENV (line 180) but no subsequent step reads it — the cleanup step only uses KEYCHAIN_PATH. Persisting the keychain password beyond the step where it is used widens the surface area for any action added later in the job to access it, even though it is already masked.

Consider only exporting KEYCHAIN_PATH to the env file, and keeping KEYCHAIN_PASSWORD as a local shell variable scoped to this step.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/macos-release.yml
Line: 179-180

Comment:
**Unnecessary keychain password in `$GITHUB_ENV`**

`KEYCHAIN_PASSWORD` is written to `$GITHUB_ENV` (line 180) but no subsequent step reads it — the cleanup step only uses `KEYCHAIN_PATH`. Persisting the keychain password beyond the step where it is used widens the surface area for any action added later in the job to access it, even though it is already masked.

Consider only exporting `KEYCHAIN_PATH` to the env file, and keeping `KEYCHAIN_PASSWORD` as a local shell variable scoped to this step.

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: 2a3d0a0637

ℹ️ 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 +278 to +280
if git diff --quiet -- "$OPENCLAW_AI_APPCAST_PATH"; then
echo "Appcast already up to date."
exit 0
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 Stage the bootstrap appcast before checking for changes

When openclaw.ai/public/appcast.xml does not exist yet—which the previous step explicitly allows—git diff --quiet -- "$OPENCLAW_AI_APPCAST_PATH" returns success because Git ignores untracked files. In that first-release scenario this step prints “Appcast already up to date.” and exits without ever committing the generated feed, so the workflow cannot bootstrap https://openclaw.ai/appcast.xml at all.

Useful? React with 👍 / 👎.

fi
git add "$OPENCLAW_AI_APPCAST_PATH"
git commit -m "macos: update appcast for ${RELEASE_TAG}"
git push origin HEAD:main
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 Rebase or serialize the openclaw.ai appcast push

If any commit lands on openclaw.ai/main after checkout—another macOS release run or an unrelated website change—git push origin HEAD:main will be rejected as a non-fast-forward update. Because that happens after the GitHub release assets are already uploaded in the previous step, the workflow can fail with a stale public appcast even though the binaries were published successfully.

Useful? React with 👍 / 👎.

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: 5712c26833

ℹ️ 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".


- name: Build, sign, notarize, and package macOS release
env:
APP_VERSION: ${{ steps.package_version.outputs.value }}
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 Derive a unique Sparkle build for fallback correction tags

This workflow explicitly accepts fallback tags like vYYYY.M.D-N, but the packaged app always gets APP_VERSION from package.json here. scripts/package-mac-app.sh derives APP_BUILD from that short version, so a correction release such as v2026.3.22-1 is emitted with the same CFBundleVersion/Sparkle sparkle:version as the original v2026.3.22 build. In that case the new appcast entry does not compare newer than the already-installed release, so Sparkle users never receive the correction update.

Useful? React with 👍 / 👎.

Comment on lines +122 to +126
- name: Checkout selected tag
uses: actions/checkout@v6
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
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 Pin the publish job to the preflight-tested release SHA

preflight_macos_release validates the current tag commit, but the gated publish job checks out refs/tags/${{ inputs.tag }} again on a fresh runner. If the tag is force-moved after preflight passes but before mac-release approval, this job will build and upload binaries from a different commit than the one that was actually validated, and it will attach them to the existing GitHub release. Reusing the preflight SHA or rerunning the same tag/ancestry validation here would avoid shipping unvetted bits.

Useful? React with 👍 / 👎.

@osolmaz osolmaz requested a review from a team as a code owner March 23, 2026 13:33
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: 5664bd63f2

ℹ️ 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 +271 to +275
- name: Publish appcast to main
env:
RELEASE_TAG: ${{ inputs.tag }}
GH_TOKEN: ${{ github.token }}
run: |
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 beta tags out of the shared Sparkle feed

This workflow accepts vYYYY.M.D-beta.N inputs, but this step always copies the generated feed into main/appcast.xml. Every signed mac build embeds that single feed URL (scripts/package-mac-app.sh:33-37, scripts/package-mac-app.sh:184-185) and the updater has no beta/stable channel split (apps/macos/Sources/OpenClaw/MenuBar.swift:448-457), so a beta mac release dated after the latest stable will be offered as the next Sparkle update to existing stable installs.

Useful? React with 👍 / 👎.

Comment on lines +109 to +113
- name: Checkout
uses: actions/checkout@v6
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
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 Pin npm publish to the preflight-validated SHA

Splitting this workflow into an ungated preflight_openclaw_npm job and a later gated publish job introduces a TOCTOU window: after preflight validates one tag commit, this job checks out refs/tags/${{ inputs.tag }} again on a fresh runner. If the tag is force-moved before npm-release approval, the workflow will publish a different commit than the one that was actually validated.

Useful? React with 👍 / 👎.

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: d7a47cc006

ℹ️ 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 +233 to +237
- name: Upload macOS assets to GitHub release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
VERSION: ${{ steps.package_version.outputs.value }}
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 Generate the signed appcast before publishing release assets

In the new publish job, .github/workflows/macos-release.yml uploads the ZIP/DMG/dSYM to the GitHub release before it seeds appcast.xml and runs scripts/make_appcast.sh later in the job. If appcast generation fails because of a Sparkle tool issue, changelog rendering regression, or secret/config problem, the workflow leaves a public macOS release with no matching updater feed artifact, so existing Sparkle installs never see the update even though the binaries are already published.

Useful? React with 👍 / 👎.

Comment on lines +16 to +18
concurrency:
group: macos-release-${{ inputs.tag }}
cancel-in-progress: false
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 Serialize appcast artifact generation across tags

Fresh evidence in this revision is that the race moved from the old push step to artifact generation: the workflow now snapshots main/appcast.xml and uploads a full replacement artifact, but concurrency is still scoped only to ${{ inputs.tag }}. If two macOS releases for different tags run at the same time, each can generate an artifact from the same stale feed, and committing the second artifact to main later will silently drop the first release entry.

Useful? React with 👍 / 👎.

@openclaw-barnacle openclaw-barnacle bot added the docs Improvements or additions to documentation label Mar 23, 2026
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: 9a46760412

ℹ️ 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 +91 to +92
needs: [preflight_openclaw_npm]
if: ${{ !inputs.preflight_only }}
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 Gate npm publish on a successful macOS preflight

Checked .github/workflows/openclaw-npm-release.yml and .github/workflows/macos-release.yml: this publish job only depends on preflight_openclaw_npm, so maintainers can still approve and publish openclaw@... even if the new macOS preflight was never run or is red. That breaks the new release invariant in docs/reference/RELEASING.md (“Every OpenClaw release ships the npm package and macOS app together”) in the exact scenario where npm is healthy but signing/notarization/appcast generation is broken, leaving an irreversible npm release with no matching macOS release.

Useful? React with 👍 / 👎.

Comment on lines +100 to +104
CODESIGN_TIMESTAMP: "off"
SIGN_IDENTITY: "-"
SKIP_NOTARIZE: "1"
SKIP_PNPM_INSTALL: "1"
SKIP_TSC: "1"
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 Make macOS preflight exercise the real publish path

This “preflight” only proves that an ad-hoc local package build works: it hard-codes SIGN_IDENTITY: "-" and SKIP_NOTARIZE: "1", and the job never runs the later appcast-generation step from publish_macos_release. In practice, expired Developer ID certs, broken notary credentials, missing Sparkle keys, or scripts/make_appcast.sh regressions will first surface after mac-release approval, which defeats the preflight-first release flow introduced in this commit.

Useful? React with 👍 / 👎.

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: d2a94a6ebe

ℹ️ 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 +133 to +136
uses: actions/checkout@v6
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
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 Disable persisted checkout credentials in mac publish job

publish_macos_release grants contents: write and then checks out the tagged repository with the default actions/checkout credential persistence. The next steps execute repository-controlled scripts (scripts/package-mac-dist.sh and later scripts/make_appcast.sh), so a compromised or force-moved release tag can use that persisted auth after mac-release approval to run authenticated git operations against this repo or its release objects. Set persist-credentials: false on the checkout(s) here and pass GH_TOKEN only to the explicit gh command step.

Useful? React with 👍 / 👎.

Comment on lines +242 to +246
BUNDLE_ID: ai.openclaw.mac
BUILD_CONFIG: release
SIGN_IDENTITY: ${{ env.SIGN_IDENTITY }}
SKIP_PNPM_INSTALL: "1"
SPARKLE_FEED_URL: ${{ env.SPARKLE_FEED_URL }}
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 Turn off Sparkle updates for beta mac builds

This packaging step always injects the production SPARKLE_FEED_URL, even when inputs.tag is a beta. Because the updater is enabled for any signed non-debug app (scripts/package-mac-app.sh:33-38,184-191; apps/macos/Sources/OpenClaw/MenuBar.swift:448-457) but later workflow steps intentionally skip publishing any beta appcast entry, users who install a beta build cannot discover later betas and will be moved back onto the stable channel as soon as the next stable feed update appears.

Useful? React with 👍 / 👎.

@osolmaz osolmaz force-pushed the codex/macos-release-automation branch from d2a94a6 to cc57168 Compare March 23, 2026 15:04
@osolmaz osolmaz merged commit 8ed33c2 into main Mar 23, 2026
8 checks passed
@osolmaz osolmaz deleted the codex/macos-release-automation branch March 23, 2026 15:04
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: cc571684e9

ℹ️ 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 +257 to +261
- name: Seed appcast from main
if: ${{ steps.release_channel.outputs.is_beta != 'true' }}
run: |
set -euo pipefail
APPCAST_SOURCE="openclaw-main/appcast.xml"
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 Wait for the previous stable appcast commit before reseeding

Checked .github/workflows/macos-release.yml together with .agents/skills/openclaw-release-maintainer/SKILL.md:117-118,167-170: the documented flow updates main/appcast.xml only after this workflow succeeds. Because this step always reseeds from the current main, a second stable release started before the prior run's artifact has been committed will regenerate a full replacement feed from stale contents, and the later artifact will drop the earlier release entry even though the publish jobs themselves were serialized.

Useful? React with 👍 / 👎.

Comment on lines +261 to +263
APPCAST_SOURCE="openclaw-main/appcast.xml"
if [[ -f "$APPCAST_SOURCE" ]]; then
cp "$APPCAST_SOURCE" appcast.xml
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 Filter beta items out of the stable appcast seed

This copies main/appcast.xml verbatim, but that file already contains beta entries (appcast.xml:245). scripts/make_appcast.sh:40-42 preserves existing items when generating the next feed, so every future “stable-only” artifact produced from this seed will continue advertising those old betas to stable installs unless the seed is filtered or rebuilt from stable releases only.

Useful? React with 👍 / 👎.

iclem pushed a commit to iclem/openclaw that referenced this pull request Mar 23, 2026
* 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
hzq001 pushed a commit to hzq001/openclaw that referenced this pull request Mar 24, 2026
* 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
furaul pushed a commit to furaul/openclaw that referenced this pull request Mar 24, 2026
* 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
Arry8 pushed a commit to Arry8/openclaw that referenced this pull request Mar 25, 2026
* 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
npmisantosh pushed a commit to npmisantosh/openclaw that referenced this pull request Mar 25, 2026
* 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
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

docs Improvements or additions to documentation maintainer Maintainer-authored PR scripts Repository scripts size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant