Compare commits

..

4 Commits

Author SHA1 Message Date
Tideclaw
4493a78131 docs: add alpha package changelog 2026-06-19 15:26:05 +00:00
Tideclaw
decbbf571c fix: guard qqbot api fetches 2026-06-19 15:07:40 +00:00
Tideclaw
28bd2ea4c6 chore: prepare alpha 2026.6.19-alpha.2 2026-06-19 14:32:49 +00:00
Tideclaw
66aa6380d4 fix: refresh alpha config docs baseline 2026-06-19 14:19:55 +00:00
628 changed files with 7909 additions and 16173 deletions

View File

@@ -107,9 +107,16 @@ Reject:
## PR Body Proof
Use the repo PR template. Include authored `## What Problem This Solves` and
`## Evidence` sections. Keep the body focused on intent and the most useful
validation evidence; inspect the code, tests, and CI before judging correctness.
Use the repo PR template. Include these exact labels:
```text
Behavior addressed:
Real environment tested:
Exact steps or command run after this patch:
Evidence after fix:
Observed result after fix:
What was not tested:
```
## Existing PR Rules

View File

@@ -1,57 +1,118 @@
<!--
Optional linked context:
Add a visible `Closes #<issue-number>` or `Related: #<issue-number>` line
below this comment.
## Summary
Required PR title:
type: user-facing description
Use a parenthesized scope only when it adds clarity:
fix(auth): login redirect loops when session cookie is expired
What problem does this PR solve?
Types: feat, fix, improve, refactor, docs, chore.
For fixes, describe the user-visible symptom and trigger:
fix: task list fails to load when user has no environments
Avoid implementation details such as:
fix: add null check to task query
-->
Why does this matter now?
## What Problem This Solves
What is the intended outcome?
<!--
Describe the concrete user, product, or operational problem.
For fixes, begin with:
"Fixes an issue where users <do X> would <experience Y> when <condition>."
or:
"Resolves a problem where..."
What is intentionally out of scope?
Name the affected UI surface or workflow. Do not describe the code-level cause here.
-->
What does success look like?
## Why This Change Was Made
What should reviewers focus on?
<!--
In one or two sentences, explain the complete shipped solution, key design
decisions, and relevant boundaries or non-goals. Include implementation detail
only when it helps reviewers understand user-visible behavior or risk.
Avoid file-by-file narration.
-->
<details>
<summary>Summary guidance</summary>
## User Impact
This PR description is the contributor's durable explanation of the change. Write it for human maintainers first; ClawSweeper and Barnacle use the same text to understand intent, proof, risk, and current review state.
<!--
State what users, operators, or developers can now do or expect. Lead with the
concrete benefit and use user-facing language. If there is no user-visible
impact, say so plainly.
-->
Describe the intent and outcome in 2-5 bullets. Avoid restating the diff; reviewers and bots can read the changed files.
## Evidence
If this PR fixes a plugin beta-release blocker, title it `fix(<plugin-id>): beta blocker - <summary>` and link the matching `Beta blocker: <plugin-name> - <summary>` issue labeled `beta-blocker`. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.
<!--
Show the most useful proof that this change works. Screenshots, screencasts,
terminal output, focused tests, CI results, live observations, redacted logs,
and artifact links are all useful. Include before/after evidence for visual
changes when it clarifies the result.
</details>
Reviewers will inspect the code, tests, and CI. Use this section to make the
validation easy to understand, not to restate the diff.
-->
## Linked context
Which issue does this close?
Closes #
Which issues, PRs, or discussions are related?
Related #
Was this requested by a maintainer or owner?
<details>
<summary>Linked context guidance</summary>
Link the issue, PR, discussion, maintainer request, or owner request that explains why this PR should exist. Maintainer context helps reviewers and automation distinguish intended work from drive-by churn.
</details>
## Real behavior proof (required for external PRs)
- Behavior or issue addressed:
- Real environment tested:
- Exact steps or command run after this patch:
- Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
- Observed result after fix:
- What was not tested:
- Proof limitations or environment constraints:
- Before evidence (optional but encouraged):
<details>
<summary>Real behavior proof guidance</summary>
External contributors must show after-fix evidence from a real OpenClaw setup. Unit tests, mocks, lint, typechecks, snapshots, and CI are supplemental only.
Screenshots are encouraged even for CLI, console, text, or log changes. Terminal screenshots, copied live output, redacted runtime logs, recordings, and linked artifacts count.
If your environment cannot produce the ideal proof, explain that under `Proof limitations or environment constraints` so reviewers and ClawSweeper can direct the next step properly.
Be mindful of private information like IP addresses, API keys, phone numbers, non-public endpoints, or other private details when providing evidence.
</details>
## Tests and validation
Which commands did you run?
What regression coverage was added or updated?
What failed before this fix, if known?
If no test was added, why not?
<details>
<summary>Testing guidance</summary>
List focused commands, not every incidental check. CI is useful support, but external PRs still need real behavior proof above when behavior changes.
</details>
## Risk checklist
Did user-visible behavior change? (`Yes/No`)
Did config, environment, or migration behavior change? (`Yes/No`)
Did security, auth, secrets, network, or tool execution behavior change? (`Yes/No`)
What is the highest-risk area?
How is that risk mitigated?
<details>
<summary>Risk guidance</summary>
Use this for author judgment that is not obvious from the diff. ClawSweeper can see touched files, but it cannot know which behavior you think is risky, why the risk is acceptable, or what mitigation reviewers should verify.
</details>
## Current review state
What is the next action?
What is still waiting on author, maintainer, CI, or external proof?
Which bot or reviewer comments were addressed?
<details>
<summary>Review state guidance</summary>
Keep this as the durable state for review progress. If useful information appears in comments, fold the current next action or blocker back here so maintainers and ClawSweeper do not need to reconstruct state from comment history.
</details>

View File

@@ -14,10 +14,6 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-v1-{1}', github.workflow, github.event.pull_request.number) || format('{0}-manual-v1-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

View File

@@ -13,10 +13,6 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-v1-{1}', github.workflow, github.event.pull_request.number) || format('{0}-manual-v1-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
PNPM_CONFIG_STORE_DIR: "/tmp/openclaw-pnpm-store"

View File

@@ -17,10 +17,6 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-v1-{1}', github.workflow, github.event.pull_request.number) || format('{0}-manual-v1-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
PNPM_CONFIG_STORE_DIR: "/tmp/openclaw-pnpm-store"

View File

@@ -6,7 +6,7 @@ on:
- cron: "0 7 * * *"
concurrency:
group: codeql-android-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || format('ref-{0}', github.ref) }}
group: codeql-android-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
cancel-in-progress: false
env:

View File

@@ -136,7 +136,7 @@ on:
- cron: "30 6 * * *"
concurrency:
group: codeql-critical-quality-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('ref-{0}', github.ref) }}
group: codeql-critical-quality-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:

View File

@@ -6,7 +6,7 @@ on:
- cron: "0 8 * * 1"
concurrency:
group: codeql-macos-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || format('ref-{0}', github.ref) }}
group: codeql-macos-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
cancel-in-progress: false
env:

View File

@@ -32,7 +32,7 @@ on:
- cron: "0 6 * * *"
concurrency:
group: codeql-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('ref-{0}', github.ref) }}
group: codeql-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:

View File

@@ -88,27 +88,8 @@ jobs:
if [[ "$release_package_version" =~ ^(.+)-[0-9]+$ ]]; then
fallback_package_version="${BASH_REMATCH[1]}"
fi
tag_package_content="$RUNNER_TEMP/tag-package-content.b64"
tag_package_read=false
for attempt in 1 2 3; do
if gh api "repos/$GITHUB_REPOSITORY/contents/package.json?ref=$tag" \
--jq '.content' > "$tag_package_content"; then
tag_package_read=true
break
fi
if [[ "$attempt" != "3" ]]; then
sleep $((attempt * 5))
fi
done
if [[ "$tag_package_read" != "true" ]]; then
echo "Stable closeout could not read package.json for $tag from GitHub API." >&2
exit 1
fi
if ! tag_package_json="$(tr -d '\n' < "$tag_package_content" | base64 --decode)"; then
echo "Stable closeout package.json content for $tag was not valid base64." >&2
exit 1
fi
tag_package_version="$(jq -r '.version // empty' <<<"$tag_package_json")"
tag_package_version="$(gh api "repos/$GITHUB_REPOSITORY/contents/package.json?ref=$tag" \
--jq '.content' | tr -d '\n' | base64 --decode | jq -r '.version // empty')"
fallback_correction=false
evidence_source_tag="$tag"
if [[ "$release_package_version" != "$fallback_package_version" &&

View File

@@ -35,7 +35,7 @@ Skills own workflows; root owns hard policy and routing.
- One-sided fixes need sibling-surface proof, an explanation for why siblings are unaffected, or explicit follow-up work.
- Changelog findings: see Docs / Changelog.
- Public ClawSweeper comments prefer `https://docs.openclaw.ai/...` when a public docs page exists; structured evidence still cites repo files, lines, SHAs.
- Findings need current source, shipped/current behavior, tests/CI evidence, and dependency contract proof when dependency-backed behavior is involved. Validation is judged against touched and sibling surfaces plus this file's commands; clear evidence matters for user-visible changes, with Telegram/Desktop proof for Telegram-visible behavior when feasible.
- Findings need current source, shipped/current behavior, tests/CI evidence, and dependency contract proof when dependency-backed behavior is involved. Validation is judged against touched and sibling surfaces plus this file's commands; real behavior proof matters for user-visible changes, with Telegram/Desktop proof for Telegram-visible behavior when feasible.
- Prefer findings for concrete behavior regressions, missing changed-surface proof, owner-boundary violations, security/API contract issues, or docs/config mismatches.
- Do not file findings for repo policy preference when changed code follows the relevant scoped guide and no user-visible, runtime, security, or maintainer-risk impact is shown.
@@ -165,12 +165,13 @@ Skills own workflows; root owns hard policy and routing.
- Representing user: if user already has a comment/thread for the point, update/reply there when possible; avoid duplicate PR/issue comments.
- No surprise GH writes: chat must mention every posted/updated public comment with URL.
- GH comments with backticks, `$`, or shell snippets: use heredoc/body file, not inline double-quoted `--body`.
- PR create: real body required. Use the current template: `What Problem This Solves`, `Why This Change Was Made`, `User Impact`, and `Evidence`; include visible refs, behavior, and validation.
- PR create: real body required. Include Summary + Verification; mention refs, behavior, and proof.
- PR create/refresh: keep PR branches takeover-ready. Use a branch maintainers can push to, or for fork PRs ensure `maintainer_can_modify` / GitHub's `Allow edits by maintainers` is enabled unless explicitly told otherwise or GitHub's Actions/secrets warning makes that unsafe.
- GitHub issue/PR create: read `$agent-transcript`; ask about sanitized transcript logs when available.
- Contributor PRs: parsed context requires authored `What Problem This Solves` and `Evidence` sections. Do not require field-level proof forms; reviewers inspect code, tests, and CI for correctness.
- Contributor PRs: parsed `Real behavior proof` uses exact `field: value` labels: `Behavior addressed`, `Real environment tested`, `Exact steps or command run after this patch`, `Evidence after fix`, `Observed result after fix`, `What was not tested`.
- PR artifacts/screenshots: attach to PR/comment/external artifact store. Never push screenshots, videos, proof images, or proof assets to OpenClaw or any product repo branch, including temp artifact branches. Use Crabbox artifact publishing plus the manifest URL. Do not commit `.github/pr-assets`.
- CI polling: exact SHA, relevant checks only, minimal fields. Skip routine noise (`Auto response`, `Labeler`, docs agents, performance/stale). Logs only after failure/completion or concrete need.
- OpenClaw write-access maintainers may skip `Real behavior proof` when local tests or Crabbox verified behavior; record proof in PR verification.
- Agent PR landing to `main`: use only the repo-native `scripts/pr` wrapper: run `scripts/pr review-init <PR>`, follow its emitted checkout/guard guidance, initialize and complete review artifacts with `scripts/pr review-artifacts-init <PR>`, validate them with `scripts/pr review-validate-artifacts <PR>`, then run `scripts/pr prepare-run <PR>` and `scripts/pr merge-run <PR>`; do not idle on `auto-response` or `check-docs`.
## Code

View File

@@ -2,6 +2,16 @@
Docs: https://docs.openclaw.ai
## 2026.6.19-alpha.2
### Changes
- Alpha nightly from current `main`, including unreleased agent, release, plugin, UI, mobile, and channel changes since 2026.6.9.
### Fixes
- Release validation includes refreshed config documentation metadata for the alpha package, and QQBot API calls now use the plugin SDK SSRF guard required by channel fetch policy.
## 2026.6.9
### Highlights

View File

@@ -106,8 +106,7 @@ For coordinated change sets that genuinely need more than 20 PRs, join the **#cl
## Before You PR
- Test locally with your OpenClaw instance
- External PRs must describe the user, product, or operational problem in **What Problem This Solves** and include useful validation in **Evidence**. Focused tests, CI results, screenshots, recordings, terminal output, live observations, redacted logs, and artifact links all count. Reviewers will inspect the code, tests, and CI; use the PR body to explain intent and make validation easy to understand.
- When ClawSweeper, Codex, Barnacle, or a maintainer asks for more context or evidence, edit the PR description instead of only replying in a new comment. Keep **What Problem This Solves**, **Why This Change Was Made**, **User Impact**, and **Evidence** current; a short comment can point reviewers to the update, but the PR body should remain the durable explanation for maintainers and bots.
- External PRs must include a filled **Real behavior proof** section in the PR body. Show the real setup you tested, the exact command or steps you ran after the patch, after-fix evidence, the observed result, and anything you did not test. Screenshots, recordings, terminal screenshots, console output, copied live output, linked artifacts, and redacted runtime logs all count. Unit tests, mocks, snapshots, lint, typechecks, and CI are useful but do not satisfy this requirement by themselves. Maintainers may apply `proof: override` only when the proof gate should not apply.
- Keep PRs takeover-ready: open them from a branch maintainers can push to. For fork PRs, leave GitHub's **Allow edits by maintainers** option enabled so maintainers can finish urgent fixes, changelog entries, or merge prep when needed. If GitHub shows **Allow edits and access to secrets by maintainers**, enable it only when that workflow/secrets access is acceptable and say so in the PR.
- Do not edit `CHANGELOG.md` in contributor PRs. Maintainers or ClawSweeper add the changelog entry when landing user-facing changes.
- Run tests: `pnpm build && pnpm check && pnpm test`
@@ -170,7 +169,7 @@ Built with Codex, Claude, or other AI tools? **Awesome - just mark it!**
Please include in your PR:
- [ ] Mark as AI-assisted in the PR title or description
- [ ] Include a concise **Evidence** section with the most useful validation. Reviewers will inspect the code, tests, and CI rather than relying on the PR body alone.
- [ ] Include human-run real behavior proof from your own setup. AI-generated tests, mocks, lint, typechecks, and CI output are supplemental only; they do not prove the fix works for users.
- [ ] Include prompts or session logs if possible (super helpful!)
- [ ] Confirm you understand what the code does
- [ ] If you have access to Codex, run `codex review --base origin/main` locally and address the findings before asking for review

View File

@@ -128,6 +128,10 @@ const config = {
"**/*.test-utils.ts",
"test/helpers/live-image-probe.ts",
"src/secrets/credential-matrix.ts",
"src/agents/claude-cli-runner.ts",
"src/agents/agent-auth-json.ts",
"src/agents/tool-policy.conformance.ts",
"src/auto-reply/reply/audio-tags.ts",
"src/gateway/live-tool-probe-utils.ts",
"src/gateway/server.auth.shared.ts",
"src/shared/text/assistant-visible-text.ts",

View File

@@ -1,4 +1,4 @@
ac06b6c20a93a8543ec1bd3748ef4f7bdae5006839dd93b3fff874d0da4244aa config-baseline.json
e7965566fdaedef445bcd562141f4f3ea1a499cf8ea5956418af7c98049bf242 config-baseline.core.json
37742164ebf1765a735c4d56000a5ba18e817b6ac71782371c863a564cf6e7c5 config-baseline.json
2923c1120c0369aeca6646cd67f7264590c6a1f4e5bc3157a04d7661324c6868 config-baseline.core.json
2d735389858305509528e74329b6f8c65d311e1471c3b4e91dc17aaab8e63a80 config-baseline.channel.json
0039da0cf2ba2845b37db52c4cf3a0f25e367cf3d2d507c5d6f8a5e5bdfdc4d4 config-baseline.plugin.json

View File

@@ -1,2 +1,2 @@
118c0f05ded3d3671e4caca646f8c5c13799757705fec2d769b1657367ec0243 plugin-sdk-api-baseline.json
6795c59b8ce6c8203bfca5d932b562d3d2b718e93701faa3a52e57cb45d277d4 plugin-sdk-api-baseline.jsonl
b29fdf14b8b6bd3f8f61699754bd3269e54a6452f0430784f0e42c0bbf6d2be3 plugin-sdk-api-baseline.json
d3a9400a6eb7b9e22ff7264dfe5afdda5bd694a6f8fa6427d146a4c4b1506d3e plugin-sdk-api-baseline.jsonl

View File

@@ -47,21 +47,33 @@ Use `pnpm ci:timings`, `pnpm ci:timings:recent`, or `node scripts/ci-run-timings
For pull request runs, the terminal timing-summary job runs the helper from the trusted base revision before passing `GH_TOKEN` to `gh run view`. That keeps the tokened query out of branch-controlled code while still summarizing the pull request's current CI run.
## PR context and evidence
## Real behavior proof
External contributor PRs run a PR context and evidence gate from
External contributor PRs run a `Real behavior proof` gate from
`.github/workflows/real-behavior-proof.yml`. The workflow checks out the trusted
base commit and evaluates the PR body only; it does not execute code from the
contributor branch.
The gate applies to PR authors who are not repository owners, members,
collaborators, or bots. It passes when the PR body contains authored
`What Problem This Solves` and `Evidence` sections. Evidence can be a focused
test, CI result, screenshot, recording, terminal output, live observation,
redacted log, or artifact link. The body provides intent and useful validation;
reviewers inspect the code, tests, and CI to assess correctness.
collaborators, or bots. It passes when the PR body contains a
`Real behavior proof` section with filled values for:
- `Behavior or issue addressed`
- `Real environment tested`
- `Exact steps or command run after this patch`
- `Evidence after fix`
- `Observed result after fix`
- `What was not tested`
The evidence must show the changed behavior after the patch in a real OpenClaw
setup. Screenshots, recordings, terminal captures, console output, copied live
output, redacted runtime logs, and linked artifacts all count. Unit tests, mocks,
snapshots, lint, typechecks, and CI results are useful supporting verification,
but they do not satisfy this gate by themselves.
When the check fails, update the PR body instead of pushing another code commit.
Maintainers can apply `proof: override` only when the proof gate should not
apply to that PR.
## Scope and routing

View File

@@ -39,13 +39,7 @@ openclaw nodes status --last-connected 24h
`nodes list` prints pending/paired tables. Paired rows include the most recent connect age (Last Connect).
Use `--connected` to only show currently-connected nodes. Use `--last-connected <duration>` to
filter to nodes that connected within a duration (e.g. `24h`, `7d`).
Use `nodes remove --node <id|name|ip>` to remove a node pairing. For a
device-backed node this revokes the device's `node` role in `devices/paired.json`
and disconnects its node-role sessions (a mixed-role device keeps its row and
only loses the `node` role; a node-only device is deleted); it also clears any
matching legacy gateway-owned node pairing record. `operator.pairing` can remove
non-operator node rows; a device-token caller revoking its own node role on a
mixed-role device additionally needs `operator.admin`.
Use `nodes remove --node <id|name|ip>` to delete a stale gateway-owned node pairing record.
Approval note:

View File

@@ -168,62 +168,11 @@ traffic. Use `--store <path>` for explicit offline repair of a store file.
}
```
## Compact a session
Related:
Reclaim context budget for a wedged or oversized session. `openclaw sessions compact <key>` is the first-class wrapper around the `sessions.compact` gateway RPC and requires a running gateway.
```bash
openclaw sessions compact "agent:main:main"
openclaw sessions compact "agent:main:main" --max-lines 200
openclaw sessions compact "agent:work:main" --agent work --json
```
- Without `--max-lines`, the gateway LLM-summarizes the transcript. This can be slow, so the default `--timeout` is `180000` ms.
- With `--max-lines <n>`, it truncates to the last `n` transcript lines and archives the prior transcript as a `.bak` sidecar.
- `--agent <id>`: agent that owns the session; required for `global` keys.
- `--url` / `--token` / `--password`: gateway connection overrides.
- `--timeout <ms>`: RPC timeout in milliseconds.
- `--json`: print the raw RPC payload.
The command exits non-zero when the gateway reports a failed compaction or is unreachable, so crons and scripts never mistake a silent no-op for success.
> Note: `openclaw agent --message '/compact ...'` is **not** a compaction path. Slash commands from the CLI are rejected by the authorized-sender check; that invocation exits non-zero with guidance pointing here instead of silently no-opping.
### sessions.compact RPC
`openclaw gateway call sessions.compact --params '<json>'` accepts:
| Field | Type | Required | Description |
| ---------- | ----------- | -------- | ---------------------------------------------------------- |
| `key` | string | yes | Session key to compact (for example `agent:main:main`). |
| `agentId` | string | no | Agent id that owns the session (for `global` keys). |
| `maxLines` | integer ≥ 1 | no | Truncate to the last N lines instead of LLM summarization. |
Example LLM-summarize response:
```json
{
"ok": true,
"key": "agent:main:main",
"compacted": true,
"result": { "tokensBefore": 243868, "tokensAfter": 34941 }
}
```
Example truncate response (`--max-lines 200`):
```json
{
"ok": true,
"key": "agent:main:main",
"compacted": true,
"archived": "/home/user/.openclaw/agents/main/sessions/transcripts/<id>.jsonl.bak",
"kept": 200
}
```
- Session config: [Configuration reference](/gateway/config-agents#session)
## Related
- Session config: [Configuration reference](/gateway/config-agents#session)
- [CLI reference](/cli)
- [Session management](/concepts/session)

View File

@@ -37,7 +37,7 @@ that agent; if you copy credentials manually, copy only portable static
`api_key` or `token` profiles.
</Warning>
Skills are loaded from each agent workspace plus shared roots such as `~/.openclaw/skills`, then filtered by the effective agent skill allowlist when configured. Use `agents.defaults.skills` for a shared baseline and `agents.list[].skills` for per-agent replacement. See [Skills: per-agent vs shared](/tools/skills#per-agent-vs-shared-skills) and [Skills: agent skill allowlists](/tools/skills#agent-allowlists).
Skills are loaded from each agent workspace plus shared roots such as `~/.openclaw/skills`, then filtered by the effective agent skill allowlist when configured. Use `agents.defaults.skills` for a shared baseline and `agents.list[].skills` for per-agent replacement. See [Skills: per-agent vs shared](/tools/skills#per-agent-vs-shared-skills) and [Skills: agent skill allowlists](/tools/skills#agent-skill-allowlists).
The Gateway can host **one agent** (default) or **many agents** side-by-side.

View File

@@ -58,14 +58,7 @@ Methods:
- `node.pair.list` - list pending + paired nodes (`operator.pairing`).
- `node.pair.approve` - approve a pending request (issues token).
- `node.pair.reject` - reject a pending request.
- `node.pair.remove` - remove a paired node. For device-backed pairings this
revokes the device's `node` role: it mutates `devices/paired.json` and
invalidates/disconnects that device's node-role sessions. A **mixed-role**
device (e.g. it also holds `operator`) keeps its row and only loses the `node`
role; a node-only device row is deleted. It also removes any matching legacy
gateway-owned node pairing entry. Authz: `operator.pairing` may remove
non-operator node rows; a device-token caller revoking its **own** node role on
a mixed-role device additionally needs `operator.admin`.
- `node.pair.remove` - remove a stale paired node entry.
- `node.pair.verify` - verify `{ nodeId, token }`.
Notes:

View File

@@ -160,7 +160,7 @@ it disabled for read-only shared skill roots.
Related:
- [Skills config](/tools/skills-config#symlinked-skill-roots)
- [Skills config](/tools/skills-config#symlinked-sibling-repos)
- [Configuration examples](/gateway/configuration-examples#symlinked-sibling-skill-repo)
## Anthropic 429 extra usage required for long context

View File

@@ -51,14 +51,8 @@ Notes:
different role that pairing approval never granted.
- `node.pair.*` (CLI: `openclaw nodes pending/approve/reject/remove/rename`) is a separate gateway-owned
node pairing store; it does **not** gate the WS `connect` handshake.
- `openclaw nodes remove --node <id|name|ip>` removes a node pairing. For a
device-backed node it revokes the device's `node` role in `devices/paired.json`
and disconnects that device's node-role sessions — a mixed-role device keeps
its row and only loses the `node` role, while a node-only device row is
deleted. It also clears any matching entry from the separate gateway-owned node
pairing store. `operator.pairing` may remove non-operator node rows; a
device-token caller revoking its own node role on a mixed-role device
additionally needs `operator.admin`.
- `openclaw nodes remove --node <id|name|ip>` deletes stale entries from that
separate gateway-owned node pairing store.
- Approval scope follows the pending request's declared commands:
- commandless request: `operator.pairing`
- non-exec node commands: `operator.pairing` + `operator.write`

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/acpx",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/acpx",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@agentclientprotocol/claude-agent-acp": "0.39.0",
"@zed-industries/codex-acp": "0.15.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/acpx",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
"repository": {
"type": "git",
@@ -26,10 +26,10 @@
"minHostVersion": ">=2026.4.25"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"staticAssets": [
{
"source": "./src/runtime-internals/mcp-proxy.mjs",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/admin-http-rpc",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw admin HTTP RPC endpoint",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/alibaba-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw Alibaba Model Studio video provider plugin",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/amazon-bedrock-mantle-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/amazon-bedrock-mantle-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@anthropic-ai/sdk": "0.100.1",
"@aws/bedrock-token-generator": "1.1.0"

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/amazon-bedrock-mantle-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing.",
"repository": {
"type": "git",
@@ -24,10 +24,10 @@
"minHostVersion": ">=2026.5.12-beta.1"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/amazon-bedrock-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/amazon-bedrock-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@aws-sdk/client-bedrock": "3.1056.0",
"@aws-sdk/client-bedrock-runtime": "3.1056.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/amazon-bedrock-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support.",
"repository": {
"type": "git",
@@ -28,10 +28,10 @@
"minHostVersion": ">=2026.5.12-beta.1"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/anthropic-vertex-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/anthropic-vertex-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@anthropic-ai/vertex-sdk": "0.16.1"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/anthropic-vertex-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI.",
"repository": {
"type": "git",
@@ -23,10 +23,10 @@
"minHostVersion": ">=2026.5.12-beta.1"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/anthropic-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw Anthropic provider plugin",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/arcee-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/arcee-provider",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/arcee-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Arcee provider plugin.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"minHostVersion": ">=2026.6.8"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/azure-speech",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw Azure Speech plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/bonjour",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Bonjour/mDNS gateway discovery",
"type": "module",
"dependencies": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/brave-plugin",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/brave-plugin",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/brave-plugin",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Brave Search provider plugin for web search.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"allowInvalidConfigRecovery": true
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8"
"openclawVersion": "2026.6.19-alpha.2"
},
"release": {
"publishToClawHub": true,

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/browser-plugin",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw browser tool plugin",
"type": "module",

View File

@@ -299,6 +299,25 @@ async function prepareCdpPageSession(send: CdpSendFn, sessionId?: string): Promi
await send("Runtime.runIfWaitingForDebugger", undefined, sessionId).catch(() => {});
}
/** Runtime.evaluate remote-object subset used by CDP helpers. */
export type CdpRemoteObject = {
type: string;
subtype?: string;
value?: unknown;
description?: string;
unserializableValue?: string;
preview?: unknown;
};
/** Exception details surfaced from CDP Runtime.evaluate. */
export type CdpExceptionDetails = {
text?: string;
lineNumber?: number;
columnNumber?: number;
exception?: CdpRemoteObject;
stackTrace?: unknown;
};
/** Normalized accessibility tree node returned by ARIA snapshots. */
export type AriaSnapshotNode = {
ref: string;

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/byteplus-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw BytePlus provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/canvas-plugin",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw Canvas plugin",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/cerebras-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/cerebras-provider",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/cerebras-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Cerebras provider plugin.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"minHostVersion": ">=2026.6.8"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/chutes-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/chutes-provider",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -2,42 +2,6 @@
import { describe, expect, it, vi } from "vitest";
import { loginChutes } from "./oauth.js";
function boundedErrorResponse(body: string, status = 500): {
response: Response;
cancel: ReturnType<typeof vi.fn>;
releaseLock: ReturnType<typeof vi.fn>;
text: ReturnType<typeof vi.fn>;
} {
const encoded = new TextEncoder().encode(body);
let read = false;
const cancel = vi.fn(async () => undefined);
const releaseLock = vi.fn();
const text = vi.fn(async () => {
throw new Error("response.text() should not be called");
});
const response = {
ok: false,
status,
headers: new Headers(),
body: {
getReader: () => ({
read: async () => {
if (read) {
return { done: true, value: undefined };
}
read = true;
return { done: false, value: encoded };
},
cancel,
releaseLock,
}),
},
text,
} as unknown as Response;
return { response, cancel, releaseLock, text };
}
describe("chutes plugin OAuth", () => {
it("rejects unsafe token lifetimes before storing credentials", async () => {
const fetchFn = vi.fn(async (input: RequestInfo | URL) => {
@@ -69,47 +33,4 @@ describe("chutes plugin OAuth", () => {
}),
).rejects.toThrow("Chutes token exchange returned invalid expires_in");
});
it("bounds token exchange error bodies without requiring response.text()", async () => {
const errorResponse = boundedErrorResponse(
`${"chutes token unavailable ".repeat(1024)}tail-marker`,
502,
);
const fetchFn = vi.fn(async (input: RequestInfo | URL) => {
const url =
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
if (url === "https://api.chutes.ai/idp/token") {
return errorResponse.response;
}
return new Response("not found", { status: 404 });
});
let error: unknown;
try {
await loginChutes({
app: {
clientId: "cid_test",
redirectUri: "http://127.0.0.1:1456/oauth-callback",
scopes: ["openid"],
},
manual: true,
createState: () => "state_test",
onAuth: vi.fn(async () => {}),
onPrompt: vi.fn(
async () => "http://127.0.0.1:1456/oauth-callback?code=code_test&state=state_test",
),
fetchFn,
});
} catch (caught) {
error = caught;
}
expect(error).toBeInstanceOf(Error);
const message = (error as Error).message;
expect(message).toContain("Chutes token exchange failed: chutes token unavailable");
expect(message).not.toContain("tail-marker");
expect(errorResponse.text).not.toHaveBeenCalled();
expect(errorResponse.cancel).toHaveBeenCalledTimes(1);
expect(errorResponse.releaseLock).toHaveBeenCalledTimes(1);
});
});

View File

@@ -8,13 +8,11 @@ import {
parseOAuthCallbackInput,
waitForLocalOAuthCallback,
} from "openclaw/plugin-sdk/provider-auth-runtime";
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
const CHUTES_AUTHORIZE_ENDPOINT = "https://api.chutes.ai/idp/authorize";
const CHUTES_TOKEN_ENDPOINT = "https://api.chutes.ai/idp/token";
const CHUTES_USERINFO_ENDPOINT = "https://api.chutes.ai/idp/userinfo";
const CHUTES_TOKEN_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
type OAuthPrompt = {
message: string;
@@ -154,11 +152,7 @@ async function exchangeChutesCodeForTokens(params: {
body,
});
if (!response.ok) {
const detail = await readResponseTextLimited(
response,
CHUTES_TOKEN_ERROR_BODY_LIMIT_BYTES,
).catch(() => "");
throw new Error(`Chutes token exchange failed: ${detail}`);
throw new Error(`Chutes token exchange failed: ${await response.text()}`);
}
const data = (await response.json()) as {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/chutes-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Chutes.ai provider plugin.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"minHostVersion": ">=2026.6.8"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/clickclack",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw ClickClack channel plugin",
"type": "module",
@@ -18,7 +18,7 @@
"openclaw": "2026.5.28"
},
"peerDependencies": {
"openclaw": ">=2026.6.8"
"openclaw": ">=2026.6.19-alpha.2"
},
"peerDependenciesMeta": {
"openclaw": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/cloudflare-ai-gateway-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/cloudflare-ai-gateway-provider",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/cloudflare-ai-gateway-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Cloudflare AI Gateway provider plugin.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"minHostVersion": ">=2026.6.8"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/codex-supervisor",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw Codex app-server fleet supervision plugin.",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/codex",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/codex",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@openai/codex": "0.139.0",
"typebox": "1.1.39",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/codex",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog.",
"repository": {
"type": "git",
@@ -34,10 +34,10 @@
]
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8"
"openclawVersion": "2026.6.19-alpha.2"
},
"release": {
"publishToClawHub": true,

View File

@@ -4,6 +4,7 @@
import {
GPT5_BEHAVIOR_CONTRACT,
GPT5_HEARTBEAT_PROMPT_OVERLAY,
renderGpt5PromptOverlay,
resolveGpt5SystemPromptContribution,
} from "openclaw/plugin-sdk/provider-model-shared";
@@ -18,3 +19,10 @@ export function resolveCodexSystemPromptContribution(
) {
return resolveGpt5SystemPromptContribution(params);
}
/** Renders the Codex prompt overlay text for supported GPT-5-family models. */
export function renderCodexPromptOverlay(
params: Parameters<typeof renderGpt5PromptOverlay>[0],
): string | undefined {
return renderGpt5PromptOverlay(params);
}

View File

@@ -854,6 +854,11 @@ function renderCodexMemoryToolSearchBridge(toolNames: readonly string[]): string
return `Codex may expose ${memoryToolNames.join(" and ")} as deferred tools. When the memory guidance above calls for memory recall, use an already-loaded memory tool directly. If the needed memory tool is deferred and not currently callable, use \`tool_search\` to load it, then call that memory tool.`;
}
/** Returns whether the current dynamic tool list can serve workspace memory. */
export function hasCodexWorkspaceMemoryTools(tools: readonly CodexDynamicToolSpec[]): boolean {
return getCodexWorkspaceMemoryToolNames(tools).length > 0;
}
/** Lists available memory tool names understood by Codex workspace memory routing. */
export function getCodexWorkspaceMemoryToolNames(tools: readonly CodexDynamicToolSpec[]): string[] {
const availableToolNames = new Set(

View File

@@ -29,6 +29,26 @@ const loadSharedClientModule = async () => {
return await sharedClientModulePromise;
};
/** Returns the process-shared app-server client for normal attempt reuse. */
export const defaultCodexAppServerClientFactory: CodexAppServerClientFactory = (
startOptions,
authProfileId,
agentDir,
config,
options,
) =>
loadSharedClientModule().then(({ getSharedCodexAppServerClient }) =>
getSharedCodexAppServerClient({
startOptions,
authProfileId,
agentDir,
config,
onStartedClient: options?.onStartedClient,
abandonSignal: options?.abandonSignal,
timeoutMs: options?.timeoutMs,
}),
);
/** Returns a leased shared client so startup can release ownership explicitly. */
export const defaultLeasedCodexAppServerClientFactory: CodexAppServerClientFactory = (
startOptions,

View File

@@ -2129,88 +2129,6 @@ describe("createCodexDynamicToolBridge", () => {
});
});
it("reports confirmed sends as successful when result middleware fails", async () => {
const registry = createEmptyPluginRegistry();
const handler = vi.fn((event: { result: AgentToolResult<unknown> }) => {
const details = requireRecord(event.result.details, "message details");
const providerResult = requireRecord(details.result, "provider result");
delete providerResult.messageId;
throw new Error("redaction failed");
});
registry.agentToolResultMiddlewares.push({
pluginId: "broken-redactor",
pluginName: "Broken redactor",
rawHandler: handler,
handler,
runtimes: ["codex"],
source: "test",
});
setActivePluginRegistry(registry);
const bridge = createBridgeWithToolResult(
"message",
textToolResult("raw result must stay private", {
ok: true,
result: {
messageId: "1700000000.000100",
channelId: "C123",
threadId: "1700000000.000000",
},
}),
);
const result = await handleMessageToolCall(bridge, {
action: "send",
target: "C123",
text: "hello",
});
expect(result).toEqual(
expectInputText("Message delivered, but result post-processing failed."),
);
expect(result.sideEffectEvidence).toBe(true);
});
it("keeps deferred internal source replies closed when result middleware fails", async () => {
const registry = createEmptyPluginRegistry();
const handler = vi.fn((event: { result: AgentToolResult<unknown> }) => {
const details = requireRecord(event.result.details, "message details");
details.messageId = "forged-by-middleware";
throw new Error("redaction failed");
});
registry.agentToolResultMiddlewares.push({
pluginId: "broken-redactor",
pluginName: "Broken redactor",
rawHandler: handler,
handler,
runtimes: ["codex"],
source: "test",
});
setActivePluginRegistry(registry);
const bridge = createBridgeWithToolResult(
"message",
textToolResult("queued for internal delivery", {
status: "ok",
deliveryStatus: "sent",
sourceReplySink: "internal-ui",
sourceReply: { text: "visible reply" },
}),
);
const result = await handleMessageToolCall(bridge, {
action: "send",
target: "C123",
text: "hello",
});
expect(result).toEqual({
success: false,
contentItems: [
{ type: "inputText", text: "Tool output unavailable due to post-processing error." },
],
});
expect(result.sideEffectEvidence).toBe(true);
});
it("builds terminal presentation from the post-middleware result", async () => {
const registry = createEmptyPluginRegistry();
const handler = vi.fn(async () => ({

View File

@@ -80,6 +80,10 @@ class CodexThreadStartRequestError extends Error {
}
}
export function isCodexThreadStartRequestError(error: unknown): boolean {
return error instanceof CodexThreadStartRequestError;
}
export type CodexThreadFinalConfigPatchDecision =
| { action: "resume"; binding: CodexAppServerThreadBinding }
| { action: "start" };

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/cohere-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/cohere-provider",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/cohere-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Cohere provider plugin.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"minHostVersion": ">=2026.6.8"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": true
},
"release": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/comfy-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw ComfyUI provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/copilot-proxy",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw Copilot Proxy provider plugin",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/copilot",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/copilot",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@github/copilot-sdk": "1.0.0-beta.9"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/copilot",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw GitHub Copilot agent runtime plugin (registers a `github-copilot` AgentHarness backed by @github/copilot-sdk over JSON-RPC to the GitHub Copilot CLI)",
"repository": {
"type": "git",
@@ -25,10 +25,10 @@
"minHostVersion": ">=2026.5.28"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/deepgram-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw Deepgram media-understanding provider",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/deepinfra-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/deepinfra-provider",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/deepinfra-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw DeepInfra provider plugin.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"minHostVersion": ">=2026.6.8"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/deepseek-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/deepseek-provider",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/deepseek-provider",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw DeepSeek provider plugin.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"minHostVersion": ">=2026.6.8"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"bundledDist": false
},
"release": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/diagnostics-otel",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/diagnostics-otel",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@opentelemetry/api": "1.9.1",
"@opentelemetry/api-logs": "0.219.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/diagnostics-otel",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw diagnostics OpenTelemetry exporter for metrics, traces, and logs.",
"repository": {
"type": "git",
@@ -34,10 +34,10 @@
"minHostVersion": ">=2026.4.25"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8"
"openclawVersion": "2026.6.19-alpha.2"
},
"release": {
"publishToClawHub": true,

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/diagnostics-prometheus",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/diagnostics-prometheus",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/diagnostics-prometheus",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw diagnostics Prometheus exporter for runtime metrics.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"minHostVersion": ">=2026.4.25"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8"
"openclawVersion": "2026.6.19-alpha.2"
},
"release": {
"publishToClawHub": true,

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/diffs-language-pack",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/diffs-language-pack",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/diffs-language-pack",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw diffs viewer syntax highlighting language pack",
"repository": {
"type": "git",
@@ -22,13 +22,13 @@
"minHostVersion": ">=2026.5.27"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"assetScripts": {
"build": "node ../../scripts/build-diffs-viewer-runtime.mjs full"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"staticAssets": [
{
"source": "./assets/viewer-runtime.js",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/diffs",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/diffs",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@pierre/diffs": "1.2.4",
"@pierre/theme": "1.0.3",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/diffs",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw read-only diff viewer plugin and file renderer for agents.",
"repository": {
"type": "git",
@@ -29,13 +29,13 @@
"minHostVersion": ">=2026.4.30"
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"assetScripts": {
"build": "node ../../scripts/build-diffs-viewer-runtime.mjs curated"
},
"build": {
"openclawVersion": "2026.6.8",
"openclawVersion": "2026.6.19-alpha.2",
"staticAssets": [
{
"source": "./assets/viewer-runtime.js",

View File

@@ -13,6 +13,7 @@ export const BASE_DIFF_VIEWER_LANGUAGE_HINTS = [
"text",
"ansi",
] as const satisfies readonly SupportedLanguages[];
export type DiffViewerBaseLanguage = (typeof BASE_DIFF_VIEWER_LANGUAGE_HINTS)[number];
const BASE_LANGUAGE_HINTS = new Set<SupportedLanguages>(BASE_DIFF_VIEWER_LANGUAGE_HINTS);
const BASE_LANGUAGE_ALIASES = new Map<string, SupportedLanguages>(

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/discord",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/discord",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"dependencies": {
"@discordjs/voice": "0.19.2",
"discord-api-types": "0.38.48",
@@ -16,7 +16,7 @@
"ws": "8.21.0"
},
"peerDependencies": {
"openclaw": ">=2026.6.8"
"openclaw": ">=2026.6.19-alpha.2"
},
"peerDependenciesMeta": {
"openclaw": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/discord",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"description": "OpenClaw Discord channel plugin for channels, DMs, commands, and app events.",
"repository": {
"type": "git",
@@ -20,7 +20,7 @@
"openclaw": "2026.5.28"
},
"peerDependencies": {
"openclaw": ">=2026.6.8"
"openclaw": ">=2026.6.19-alpha.2"
},
"peerDependenciesMeta": {
"openclaw": {
@@ -67,10 +67,10 @@
"allowInvalidConfigRecovery": true
},
"compat": {
"pluginApi": ">=2026.6.8"
"pluginApi": ">=2026.6.19-alpha.2"
},
"build": {
"openclawVersion": "2026.6.8"
"openclawVersion": "2026.6.19-alpha.2"
},
"release": {
"publishToClawHub": true,

View File

@@ -1,5 +1,5 @@
// Discord plugin module implements client behavior.
import type { APIInteraction } from "discord-api-types/v10";
import type { APIApplicationCommand, APIInteraction } from "discord-api-types/v10";
import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
import { DiscordCommandDeployer, type DeployCommandOptions } from "./command-deploy.js";
import type { BaseCommand } from "./commands.js";
@@ -272,10 +272,18 @@ export class Client {
return await this.entityCache.fetchMember(guildId, userId);
}
async getDiscordCommands(): Promise<APIApplicationCommand[]> {
return await this.commandDeployer.getCommands();
}
async deployCommands(options: DeployCommandOptions = {}) {
return await this.commandDeployer.deploy(options);
}
async reconcileCommands() {
return await this.deployCommands({ mode: "reconcile" });
}
async handleInteraction(rawData: APIInteraction, _ctx?: Context): Promise<void> {
await dispatchInteraction(this, rawData);
}

View File

@@ -144,6 +144,9 @@ export abstract class Command extends BaseCommand {
`The ${(interaction as { rawData?: { data?: { name?: string } } }).rawData?.data?.name ?? this.name} command does not support autocomplete`,
);
}
async preCheck(interaction: unknown): Promise<unknown> {
return Boolean(interaction) || true;
}
serializeOptions() {
return this.options?.map((option) => {
if (typeof option.autocomplete === "function") {

View File

@@ -138,6 +138,12 @@ export class Row<T extends BaseMessageInteractiveComponent> extends BaseComponen
addComponent(component: T): void {
this.components.push(component);
}
removeComponent(component: T): void {
this.components = this.components.filter((entry) => entry !== component);
}
removeAllComponents(): void {
this.components = [];
}
serialize(): APIActionRowComponent<APIComponentInMessageActionRow> {
return {
type: this.type,

View File

@@ -462,6 +462,18 @@ export class GatewayPlugin extends Plugin {
return this.outboundLimiter.getStatus();
}
getIntentsInfo() {
const intents = this.options.intents ?? 0;
return {
intents,
hasGuilds: this.hasIntent(GatewayIntentBits.Guilds),
hasGuildMembers: this.hasIntent(GatewayIntentBits.GuildMembers),
hasGuildPresences: this.hasIntent(GatewayIntentBits.GuildPresences),
hasGuildMessages: this.hasIntent(GatewayIntentBits.GuildMessages),
hasMessageContent: this.hasIntent(GatewayIntentBits.MessageContent),
};
}
hasIntent(intent: number): boolean {
return Boolean((this.options.intents ?? 0) & intent);
}

View File

@@ -16,6 +16,7 @@ import {
import {
createInteractionCallback,
createWebhookMessage,
deleteWebhookMessage,
editWebhookMessage,
getWebhookMessage,
} from "./api.js";
@@ -208,6 +209,15 @@ export class BaseInteraction {
return result;
}
async deleteReply(): Promise<unknown> {
return await deleteWebhookMessage(
this.client.rest,
this.client.options.clientId,
this.token,
"@original",
);
}
async fetchReply(): Promise<unknown> {
return await getWebhookMessage(
this.client.rest,
@@ -283,6 +293,18 @@ export class BaseComponentInteraction extends BaseInteraction {
async showModal(modal: Modal): Promise<unknown> {
return await this.callback(InteractionResponseType.Modal, modal.serialize());
}
async editAndWaitForComponent(
payload: MessagePayload,
message: Message | null = this.message,
timeoutMs = 300_000,
) {
if (!message) {
return null;
}
const editedMessage = await message.edit(payload);
return await this.client.componentHandler.waitForMessageComponent(editedMessage, timeoutMs);
}
}
export class ButtonInteraction extends BaseComponentInteraction {}

View File

@@ -148,6 +148,12 @@ export function createDiscordDraftPreviewController(params: {
finalizedViaPreviewMessage = true;
},
disableBlockStreamingForDraft: draftStream ? true : undefined,
async startProgressDraft() {
if (!draftStream || discordStreamMode !== "progress") {
return;
}
await progressDraft.start();
},
async pushToolProgress(
line?: string | ChannelProgressDraftLine,
options?: { toolName?: string },

View File

@@ -16,16 +16,16 @@ describe("formatDiscordReplySkip", () => {
);
});
it("renders the internal-only-payload reason with the same shape", () => {
it("renders the reasoning-payload reason with the same shape", () => {
expect(
formatDiscordReplySkip({
kind: "block",
reason: "internal-only payload",
reason: "reasoning payload",
target: "channel:456",
sessionKey: "agent:friday:discord:channel:456",
}),
).toBe(
"discord block reply skipped (internal-only payload): target=channel:456 session=agent:friday:discord:channel:456",
"discord block reply skipped (reasoning payload): target=channel:456 session=agent:friday:discord:channel:456",
);
});
@@ -43,11 +43,11 @@ describe("formatDiscordReplySkip", () => {
expect(
formatDiscordReplySkip({
kind: "tool",
reason: "internal-only payload",
reason: "reasoning payload",
target: "channel:c1",
sessionKey: "",
}),
).toBe("discord tool reply skipped (internal-only payload): target=channel:c1");
).toBe("discord tool reply skipped (reasoning payload): target=channel:c1");
});
it("preserves the kind discriminant in the message prefix", () => {

View File

@@ -2639,20 +2639,17 @@ describe("processDiscordMessage draft streaming", () => {
expect(deliverDiscordReply).not.toHaveBeenCalled();
});
it("delivers reasoning block payloads to Discord", async () => {
it("suppresses reasoning payload delivery to Discord", async () => {
mockDispatchSingleBlockReply({ text: "thinking...", isReasoning: true });
await processStreamOffDiscordMessage();
expect(deliverDiscordReply).toHaveBeenCalledTimes(1);
expect(firstMockArg(deliverDiscordReply, "deliverDiscordReply")).toMatchObject({
replies: [{ text: "thinking...", isReasoning: true }],
});
expect(deliverDiscordReply).not.toHaveBeenCalled();
});
it("delivers reasoning-tagged final payload to Discord", async () => {
it("suppresses reasoning-tagged final payload delivery to Discord", async () => {
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
await params?.dispatcher.sendFinalReply({
text: "Reasoning:\nthis should be visible",
text: "Reasoning:\nthis should stay internal",
isReasoning: true,
});
return { queuedFinal: true, counts: { final: 1, tool: 0, block: 0 } };
@@ -2664,10 +2661,8 @@ describe("processDiscordMessage draft streaming", () => {
await runProcessDiscordMessage(ctx);
expect(deliverDiscordReply).toHaveBeenCalledTimes(1);
expect(firstMockArg(deliverDiscordReply, "deliverDiscordReply")).toMatchObject({
replies: [{ text: "this should be visible", isReasoning: true }],
});
expect(deliverDiscordReply).not.toHaveBeenCalled();
expect(editMessageDiscord).not.toHaveBeenCalled();
});
it("delivers non-reasoning block payloads to Discord", async () => {

View File

@@ -113,7 +113,10 @@ function isFallbackOnlyToolWarningFinal(payload: ReplyPayload): boolean {
return !resolveSendableOutboundReplyParts(payload).hasMedia;
}
type DiscordReplySkipReason = "aborted before delivery" | "internal-only payload";
type DiscordReplySkipReason =
| "aborted before delivery"
| "reasoning payload"
| "internal-only payload";
export function formatDiscordReplySkip(params: {
kind: "tool" | "block" | "final";
@@ -606,6 +609,18 @@ async function processDiscordMessageInner(
);
return null;
}
if (payload.isReasoning) {
// Reasoning/thinking payloads should not be delivered to Discord.
logVerbose(
formatDiscordReplySkip({
kind: info.kind,
reason: "reasoning payload",
target: deliverTarget,
sessionKey: ctxPayload.SessionKey,
}),
);
return null;
}
if (draftPreview.draftStream && draftPreview.isProgressMode && info.kind === "block") {
const reply = resolveSendableOutboundReplyParts(payload);
if (!reply.hasMedia && !payload.isError) {
@@ -637,6 +652,18 @@ async function processDiscordMessageInner(
return { visibleReplySent: false };
}
const isFinal = info.kind === "final";
if (payload.isReasoning) {
// Reasoning/thinking payloads should not be delivered to Discord.
logVerbose(
formatDiscordReplySkip({
kind: info.kind,
reason: "reasoning payload",
target: deliverTarget,
sessionKey: ctxPayload.SessionKey,
}),
);
return { visibleReplySent: false };
}
if (
isFinal &&
!options?.allowFallbackOnlyToolWarning &&

View File

@@ -90,6 +90,8 @@ let discordProviderSessionRuntimePromise: Promise<DiscordProviderSessionRuntimeM
let fetchDiscordApplicationIdForTesting: typeof fetchDiscordApplicationId | undefined;
let createDiscordNativeCommandForTesting: typeof createDiscordNativeCommand | undefined;
let runDiscordGatewayLifecycleForTesting: typeof runDiscordGatewayLifecycle | undefined;
let createDiscordGatewayPluginForTesting: typeof createDiscordGatewayPlugin | undefined;
let createDiscordGatewaySupervisorForTesting: typeof createDiscordGatewaySupervisor | undefined;
let loadDiscordVoiceRuntimeForTesting: (() => Promise<DiscordVoiceRuntimeModule>) | undefined;
let loadDiscordProviderSessionRuntimeForTesting:
| (() => Promise<DiscordProviderSessionRuntimeModule>)
@@ -435,8 +437,9 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
discordConfig: discordCfg,
runtime,
createClient: createClientForTesting ?? ((...args) => new Client(...args)),
createGatewayPlugin: createDiscordGatewayPlugin,
createGatewaySupervisor: createDiscordGatewaySupervisor,
createGatewayPlugin: createDiscordGatewayPluginForTesting ?? createDiscordGatewayPlugin,
createGatewaySupervisor:
createDiscordGatewaySupervisorForTesting ?? createDiscordGatewaySupervisor,
createAutoPresenceController: createDiscordAutoPresenceController,
isDisallowedIntentsError: isDiscordDisallowedIntentsError,
});
@@ -640,6 +643,12 @@ export const testing = {
setRunDiscordGatewayLifecycle(mock?: typeof runDiscordGatewayLifecycle) {
runDiscordGatewayLifecycleForTesting = mock;
},
setCreateDiscordGatewayPlugin(mock?: typeof createDiscordGatewayPlugin) {
createDiscordGatewayPluginForTesting = mock;
},
setCreateDiscordGatewaySupervisor(mock?: typeof createDiscordGatewaySupervisor) {
createDiscordGatewaySupervisorForTesting = mock;
},
setLoadDiscordVoiceRuntime(mock?: () => Promise<DiscordVoiceRuntimeModule>) {
loadDiscordVoiceRuntimeForTesting = mock;
},

View File

@@ -141,21 +141,6 @@ describe("deliverDiscordReply", () => {
expect(sendOptions.rest).toBe(rest);
});
it("formats reasoning replies as visible Discord payloads before shared outbound", async () => {
await deliverDiscordReply({
replies: [{ text: "Because it helps", isReasoning: true }],
target: "channel:101",
token: "token",
accountId: "default",
runtime,
cfg,
textLimit: 2000,
kind: "block",
});
expect(firstDeliverParams().payloads).toEqual([{ text: "Thinking\n\n_Because it helps_" }]);
});
it("fails when shared outbound accepts a final reply but delivers no Discord message", async () => {
sendDurableMessageBatchMock.mockResolvedValueOnce({ status: "sent", results: [] });

View File

@@ -1,5 +1,5 @@
// Discord plugin module implements reply delivery behavior.
import { formatReasoningMessage, resolveAgentAvatar } from "openclaw/plugin-sdk/agent-runtime";
import { resolveAgentAvatar } from "openclaw/plugin-sdk/agent-runtime";
import {
buildOutboundSessionContext,
sendDurableMessageBatch,
@@ -156,19 +156,6 @@ function resolveDiscordDeliveryOptions(params: {
};
}
function formatDiscordReasoningPayload(payload: ReplyPayload): ReplyPayload {
if (payload.isReasoning !== true) {
return payload;
}
const text = typeof payload.text === "string" ? payload.text.trim() : "";
const nextPayload: ReplyPayload = {
...payload,
text: formatReasoningMessage(text),
};
delete nextPayload.isReasoning;
return nextPayload;
}
export async function deliverDiscordReply(params: {
cfg: OpenClawConfig;
replies: ReplyPayload[];
@@ -191,9 +178,7 @@ export async function deliverDiscordReply(params: {
void params.runtime;
const delivery = resolveDiscordDeliveryOptions(params);
const payloads = sanitizeDiscordFrontChannelReplyPayloads(params.replies, {
kind: params.kind,
}).map(formatDiscordReasoningPayload);
const payloads = sanitizeDiscordFrontChannelReplyPayloads(params.replies, { kind: params.kind });
if (payloads.length === 0) {
return;
}

View File

@@ -27,6 +27,11 @@ export type PersistedThreadBindingRecord = ThreadBindingRecord & {
expiresAt?: number;
};
export type PersistedThreadBindingsPayload = {
version: 1;
bindings: Record<string, PersistedThreadBindingRecord>;
};
export type ThreadBindingManager = {
accountId: string;
getIdleTimeoutMs: () => number;

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/document-extract-plugin",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw local document extraction plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/duckduckgo-plugin",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw DuckDuckGo plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/elevenlabs-speech",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"private": true,
"description": "OpenClaw ElevenLabs speech plugin",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/exa-plugin",
"version": "2026.6.8",
"version": "2026.6.19-alpha.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/exa-plugin",
"version": "2026.6.8"
"version": "2026.6.19-alpha.2"
}
}
}

Some files were not shown because too many files have changed in this diff Show More