mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 14:01:24 +08:00
Compare commits
88 Commits
josh/gmail
...
v2026.5.27
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27ae826f65 | ||
|
|
6cdc963096 | ||
|
|
a03cd48b22 | ||
|
|
5ba6a590d9 | ||
|
|
9de88d4986 | ||
|
|
b317664781 | ||
|
|
66a5748352 | ||
|
|
6aea967924 | ||
|
|
0b92a8e6e3 | ||
|
|
1e5ff65770 | ||
|
|
10f1026588 | ||
|
|
b135369099 | ||
|
|
a9aaeeafd9 | ||
|
|
1955319f87 | ||
|
|
15ff646f89 | ||
|
|
e34d4a5924 | ||
|
|
7fdf0060d7 | ||
|
|
91da19643b | ||
|
|
6d97108b6c | ||
|
|
c185c44754 | ||
|
|
ff613c5ccc | ||
|
|
8d552cef45 | ||
|
|
6ebed52d6a | ||
|
|
986baf9c9f | ||
|
|
62ed7bcaf2 | ||
|
|
c3154c7b14 | ||
|
|
c81a1f94b6 | ||
|
|
3ff769fe91 | ||
|
|
791988fafc | ||
|
|
89e0c80a98 | ||
|
|
a33bcd7e79 | ||
|
|
0e84e53c30 | ||
|
|
40bf4475f7 | ||
|
|
0bce1bcc1c | ||
|
|
edb630623b | ||
|
|
66aff8611e | ||
|
|
17deed0a63 | ||
|
|
46c5d749ab | ||
|
|
397a9347f9 | ||
|
|
9769949449 | ||
|
|
bf14fce7c0 | ||
|
|
3bc5377717 | ||
|
|
4ad2090388 | ||
|
|
665c2f2860 | ||
|
|
4295f82411 | ||
|
|
d77f5fe128 | ||
|
|
88b41f3571 | ||
|
|
02f29c12cf | ||
|
|
c155648052 | ||
|
|
350e497c56 | ||
|
|
4248c4c618 | ||
|
|
8abacab27b | ||
|
|
7ce0109232 | ||
|
|
9e338d1eff | ||
|
|
d5a1f2e7ff | ||
|
|
da6368a150 | ||
|
|
ca43325632 | ||
|
|
19514f3bf2 | ||
|
|
5d22828b40 | ||
|
|
87d410dddb | ||
|
|
229e500af7 | ||
|
|
7e95088326 | ||
|
|
34e6037753 | ||
|
|
e72213365a | ||
|
|
c3d3d683e3 | ||
|
|
099f1ec627 | ||
|
|
089795ca25 | ||
|
|
999ad1067a | ||
|
|
2285b00649 | ||
|
|
6c175ac1a9 | ||
|
|
bd826ff036 | ||
|
|
29e23ba19f | ||
|
|
e889eb732b | ||
|
|
a0439d5a44 | ||
|
|
6da4ff2b2b | ||
|
|
00dabd6e40 | ||
|
|
ac825a47c6 | ||
|
|
9cc1a83bcc | ||
|
|
e35638ea7b | ||
|
|
271baa1b47 | ||
|
|
7e3adcc2eb | ||
|
|
be40367a77 | ||
|
|
c13f91a887 | ||
|
|
82dc834da1 | ||
|
|
b9dfef712d | ||
|
|
a2feccfd7c | ||
|
|
46c711a45d | ||
|
|
ee882362cb |
94
.agents/skills/discord-mood/SKILL.md
Normal file
94
.agents/skills/discord-mood/SKILL.md
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: discord-mood
|
||||
description: "Discord release/beta mood summaries from Discrawl with quotes, no URLs by default, and issue/PR correlation."
|
||||
---
|
||||
|
||||
# Discord Mood
|
||||
|
||||
Use this with `$discrawl` when asked what people say, what the vibe/mood is,
|
||||
or how a beta/release is landing in Discord.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Sync when currentness matters or the user asks:
|
||||
|
||||
```bash
|
||||
discrawl sync --update=auto --source discord
|
||||
discrawl status --json
|
||||
```
|
||||
|
||||
2. Query the relevant window with read-only Discrawl SQL. Start from the release
|
||||
time, last answer freshness, or user-provided date. Include release/version,
|
||||
update/install, fast/slow, crash/broken/regression, plugin/LCM, Codex,
|
||||
WebChat/session, and specific feature terms.
|
||||
|
||||
3. Pull nearby channel slices around high-signal hits so quoted messages are not
|
||||
detached from context.
|
||||
|
||||
4. When Discord mentions GitHub issues/PRs, verify live state with `gh api` or
|
||||
`$openclaw-pr-maintainer` before saying open/closed/merged.
|
||||
|
||||
## Output Rules
|
||||
|
||||
- Do not show Discord URLs by default. Add links only when the user explicitly
|
||||
asks for links, wants to act on a specific message, or needs exact audit
|
||||
evidence.
|
||||
- Prefer short direct quotes over paraphrase-only summaries. Quote enough to
|
||||
show tone, not whole messages.
|
||||
- Attribute quotes compactly: `author, #channel, HH:MM UTC: "quote"`.
|
||||
- Keep quotes representative and balanced: positive, negative, uncertainty,
|
||||
and maintainer/triage if present.
|
||||
- Distill first, details second. Use:
|
||||
- `freshness`
|
||||
- `net mood`
|
||||
- `good`
|
||||
- `worry`
|
||||
- `quotes`
|
||||
- `open items` when issues/PRs are involved
|
||||
- Use absolute timestamps and counts. Mention known archive gaps.
|
||||
- Separate runtime sentiment from update/install confidence; these often diverge.
|
||||
- Avoid hype terms unless quoting users. Keep the agent's synthesis sober.
|
||||
|
||||
## Quote Selection
|
||||
|
||||
Good quote candidates:
|
||||
|
||||
- clear sentiment: "fast", "broken", "smooth", "unresponsive"
|
||||
- multiple independent users saying the same thing
|
||||
- specific repro/update/version details
|
||||
- maintainer comments that frame severity or release risk
|
||||
|
||||
Skip:
|
||||
|
||||
- jokes unless they capture a broader mood
|
||||
- long rants without a concrete signal
|
||||
- bot/status spam unless asked for operational stats
|
||||
|
||||
## Common SQL Shape
|
||||
|
||||
```sql
|
||||
select
|
||||
m.created_at,
|
||||
coalesce(nullif(mm.display_name,''), nullif(mm.global_name,''), nullif(mm.username,''), m.author_id) as author,
|
||||
coalesce(nullif(c.name,''), m.channel_id) as channel,
|
||||
m.id,
|
||||
replace(replace(substr(m.content,1,1200), char(10), ' '), char(13), ' ') as content
|
||||
from messages m
|
||||
left join channels c on c.id=m.channel_id and c.guild_id=m.guild_id
|
||||
left join members mm on mm.guild_id=m.guild_id and mm.user_id=m.author_id
|
||||
where m.guild_id='1456350064065904867'
|
||||
and m.created_at >= '<ISO start>'
|
||||
and (
|
||||
lower(m.content) like '%<version>%'
|
||||
or lower(m.content) like '%release%'
|
||||
or lower(m.content) like '%update%'
|
||||
or lower(m.content) like '%install%'
|
||||
or lower(m.content) like '%fast%'
|
||||
or lower(m.content) like '%slow%'
|
||||
or lower(m.content) like '%crash%'
|
||||
or lower(m.content) like '%broken%'
|
||||
or lower(m.content) like '%regression%'
|
||||
)
|
||||
order by m.created_at asc
|
||||
limit 200;
|
||||
```
|
||||
4
.agents/skills/discord-mood/agents/openai.yaml
Normal file
4
.agents/skills/discord-mood/agents/openai.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Discord Mood"
|
||||
short_description: "Summarize Discord release mood with quotes"
|
||||
default_prompt: "Use $discord-mood to sync Discrawl, quote representative Discord feedback without URLs by default, summarize release or beta mood, and verify referenced GitHub issue/PR state."
|
||||
85
.agents/skills/release-openclaw-announcement/SKILL.md
Normal file
85
.agents/skills/release-openclaw-announcement/SKILL.md
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
name: release-openclaw-announcement
|
||||
description: "Draft or post OpenClaw beta/stable Discord release announcements from changelog, GitHub release, registry, and validation evidence. Use when announcing a beta, stable release, release candidate, or asking what users should test after an OpenClaw release."
|
||||
---
|
||||
|
||||
# OpenClaw Release Announcement
|
||||
|
||||
Use with `release-openclaw-maintainer` after a beta or stable release is live.
|
||||
Use with `openclaw-discord` when actually posting to Discord.
|
||||
|
||||
## Evidence First
|
||||
|
||||
Before drafting focus areas, read real release evidence:
|
||||
|
||||
1. Current GitHub release body for the tag.
|
||||
2. `CHANGELOG.md` section for the released base version.
|
||||
3. Commits since the previous shipped version or the operator-specified base.
|
||||
4. Registry/package metadata for the exact version and current dist-tag.
|
||||
5. Validation status that is relevant to user confidence.
|
||||
|
||||
Do not claim a full changelog audit unless you did it. If you only read the
|
||||
generated release notes or top changelog section, say that and either audit
|
||||
properly or draft with that limitation.
|
||||
|
||||
For beta focus areas, prioritize user-observable changes over internal test or
|
||||
CI mechanics:
|
||||
|
||||
- install/update paths
|
||||
- OS/platform-specific behavior
|
||||
- Gateway startup/restart, config, and runtime behavior
|
||||
- provider/model/runtime routing
|
||||
- plugin loading and local plugin development
|
||||
- channels and media paths
|
||||
- security/data-loss/user-impact fixes
|
||||
|
||||
Do not let late release-branch fixes automatically dominate the announcement.
|
||||
If the version includes a large delta from the previous shipped version, rank
|
||||
focus areas by the whole release delta and expected user impact; mention late
|
||||
fixes in their natural category.
|
||||
|
||||
## Required Copy
|
||||
|
||||
Every beta announcement must make beta status explicit and include:
|
||||
|
||||
- exact version, e.g. `OpenClaw 2026.5.25-beta.1`
|
||||
- one-sentence risk framing: beta, useful for testing, not stable promotion
|
||||
- focused test areas derived from evidence, not guesswork
|
||||
- update command promoted near the top:
|
||||
```sh
|
||||
openclaw update --channel beta --yes
|
||||
openclaw --version
|
||||
```
|
||||
- fresh install path:
|
||||
`Install from https://openclaw.ai`
|
||||
- GitHub release link
|
||||
- concise validation note, without making CI the headline
|
||||
|
||||
Do not suggest npm install commands in beta announcements unless the operator
|
||||
explicitly asks for npm-specific copy or troubleshooting text. It is fine to use
|
||||
registry metadata as evidence; do not turn that into public install guidance.
|
||||
|
||||
For stable announcements, use the stable channel wording:
|
||||
|
||||
```sh
|
||||
openclaw update --channel stable --yes
|
||||
openclaw --version
|
||||
```
|
||||
|
||||
Fresh installs still point to `https://openclaw.ai`.
|
||||
|
||||
## Style
|
||||
|
||||
- Discord Markdown, no tables.
|
||||
- Keep it skimmable: short intro, bullets, commands, links.
|
||||
- Lead with what users can feel or test, not proof plumbing.
|
||||
- Mention validation only after install/update instructions.
|
||||
- Be specific about where feedback is useful.
|
||||
- Do not mention private local proof paths in public announcements.
|
||||
- Do not overstate unverified platforms, channels, or provider behavior.
|
||||
|
||||
## Posting
|
||||
|
||||
When asked to post, use the configured Discord workflow from
|
||||
`openclaw-discord` or the approved OpenClaw relay. Never print tokens.
|
||||
For public channels, inspect the final body before sending.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "OpenClaw Release Announcement"
|
||||
short_description: "Draft Discord beta/stable release announcements from evidence."
|
||||
default_prompt: "Use this skill to draft an OpenClaw beta or stable Discord announcement from changelog, release notes, npm/GitHub release proof, and validation evidence."
|
||||
88
.github/workflows/docker-release.yml
vendored
88
.github/workflows/docker-release.yml
vendored
@@ -162,6 +162,50 @@ jobs:
|
||||
provenance: mode=max
|
||||
push: true
|
||||
|
||||
- name: Smoke test amd64 runtime workspace templates
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE_REFS: ${{ steps.tags.outputs.value }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mapfile -t image_refs <<< "${IMAGE_REFS}"
|
||||
image_ref="${image_refs[0]}"
|
||||
if [[ -z "${image_ref}" ]]; then
|
||||
echo "::error::No amd64 image ref resolved for runtime template smoke"
|
||||
exit 1
|
||||
fi
|
||||
docker run --rm --entrypoint /bin/sh "${image_ref}" -lc '
|
||||
set -eu
|
||||
test -f /app/src/agents/templates/HEARTBEAT.md
|
||||
temp_root="$(mktemp -d)"
|
||||
trap "rm -rf \"${temp_root}\"" EXIT
|
||||
mkdir -p "${temp_root}/home" "${temp_root}/cwd"
|
||||
cd "${temp_root}/cwd"
|
||||
set +e
|
||||
HOME="${temp_root}/home" \
|
||||
USERPROFILE="${temp_root}/home" \
|
||||
OPENCLAW_HOME="${temp_root}/home" \
|
||||
OPENCLAW_NO_ONBOARD=1 \
|
||||
OPENCLAW_SUPPRESS_NOTES=1 \
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 \
|
||||
OPENCLAW_DISABLE_BUNDLED_ENTRY_SOURCE_FALLBACK=1 \
|
||||
AWS_EC2_METADATA_DISABLED=true \
|
||||
AWS_SHARED_CREDENTIALS_FILE="${temp_root}/home/.aws/credentials" \
|
||||
AWS_CONFIG_FILE="${temp_root}/home/.aws/config" \
|
||||
node /app/openclaw.mjs agent --message "workspace bootstrap smoke" --session-id "workspace-bootstrap-smoke" --local --timeout 1 --json \
|
||||
>"${temp_root}/out.log" 2>&1
|
||||
status="$?"
|
||||
set -e
|
||||
if grep -F "Missing workspace template:" "${temp_root}/out.log"; then
|
||||
cat "${temp_root}/out.log"
|
||||
exit 1
|
||||
fi
|
||||
test -f "${temp_root}/home/.openclaw/workspace/HEARTBEAT.md"
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
cat "${temp_root}/out.log"
|
||||
fi
|
||||
'
|
||||
|
||||
# Build arm64 image. Default and slim tags point to the same slim runtime.
|
||||
build-arm64:
|
||||
needs: [approve_manual_backfill]
|
||||
@@ -260,6 +304,50 @@ jobs:
|
||||
provenance: mode=max
|
||||
push: true
|
||||
|
||||
- name: Smoke test arm64 runtime workspace templates
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE_REFS: ${{ steps.tags.outputs.value }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mapfile -t image_refs <<< "${IMAGE_REFS}"
|
||||
image_ref="${image_refs[0]}"
|
||||
if [[ -z "${image_ref}" ]]; then
|
||||
echo "::error::No arm64 image ref resolved for runtime template smoke"
|
||||
exit 1
|
||||
fi
|
||||
docker run --rm --entrypoint /bin/sh "${image_ref}" -lc '
|
||||
set -eu
|
||||
test -f /app/src/agents/templates/HEARTBEAT.md
|
||||
temp_root="$(mktemp -d)"
|
||||
trap "rm -rf \"${temp_root}\"" EXIT
|
||||
mkdir -p "${temp_root}/home" "${temp_root}/cwd"
|
||||
cd "${temp_root}/cwd"
|
||||
set +e
|
||||
HOME="${temp_root}/home" \
|
||||
USERPROFILE="${temp_root}/home" \
|
||||
OPENCLAW_HOME="${temp_root}/home" \
|
||||
OPENCLAW_NO_ONBOARD=1 \
|
||||
OPENCLAW_SUPPRESS_NOTES=1 \
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 \
|
||||
OPENCLAW_DISABLE_BUNDLED_ENTRY_SOURCE_FALLBACK=1 \
|
||||
AWS_EC2_METADATA_DISABLED=true \
|
||||
AWS_SHARED_CREDENTIALS_FILE="${temp_root}/home/.aws/credentials" \
|
||||
AWS_CONFIG_FILE="${temp_root}/home/.aws/config" \
|
||||
node /app/openclaw.mjs agent --message "workspace bootstrap smoke" --session-id "workspace-bootstrap-smoke" --local --timeout 1 --json \
|
||||
>"${temp_root}/out.log" 2>&1
|
||||
status="$?"
|
||||
set -e
|
||||
if grep -F "Missing workspace template:" "${temp_root}/out.log"; then
|
||||
cat "${temp_root}/out.log"
|
||||
exit 1
|
||||
fi
|
||||
test -f "${temp_root}/home/.openclaw/workspace/HEARTBEAT.md"
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
cat "${temp_root}/out.log"
|
||||
fi
|
||||
'
|
||||
|
||||
# Create multi-platform manifests
|
||||
create-manifest:
|
||||
needs: [approve_manual_backfill, build-amd64, build-arm64]
|
||||
|
||||
45
.github/workflows/full-release-validation.yml
vendored
45
.github/workflows/full-release-validation.yml
vendored
@@ -225,7 +225,7 @@ jobs:
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
docker_runtime_assets_preflight:
|
||||
name: Verify Docker runtime-assets prune path
|
||||
name: Verify Docker runtime image assets
|
||||
needs: [resolve_target]
|
||||
if: inputs.rerun_group == 'all'
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -250,6 +250,49 @@ jobs:
|
||||
--build-arg OPENCLAW_EXTENSIONS="diagnostics-otel,codex" \
|
||||
.
|
||||
|
||||
- name: Build and smoke test final Docker runtime image
|
||||
env:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
image_ref="openclaw-release-runtime-smoke:${TARGET_SHA}"
|
||||
timeout --kill-after=30s 35m docker build \
|
||||
--build-arg OPENCLAW_EXTENSIONS="diagnostics-otel,codex" \
|
||||
-t "${image_ref}" \
|
||||
.
|
||||
docker run --rm --entrypoint /bin/sh "${image_ref}" -lc '
|
||||
set -eu
|
||||
test -f /app/src/agents/templates/HEARTBEAT.md
|
||||
temp_root="$(mktemp -d)"
|
||||
trap "rm -rf \"${temp_root}\"" EXIT
|
||||
mkdir -p "${temp_root}/home" "${temp_root}/cwd"
|
||||
cd "${temp_root}/cwd"
|
||||
set +e
|
||||
HOME="${temp_root}/home" \
|
||||
USERPROFILE="${temp_root}/home" \
|
||||
OPENCLAW_HOME="${temp_root}/home" \
|
||||
OPENCLAW_NO_ONBOARD=1 \
|
||||
OPENCLAW_SUPPRESS_NOTES=1 \
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 \
|
||||
OPENCLAW_DISABLE_BUNDLED_ENTRY_SOURCE_FALLBACK=1 \
|
||||
AWS_EC2_METADATA_DISABLED=true \
|
||||
AWS_SHARED_CREDENTIALS_FILE="${temp_root}/home/.aws/credentials" \
|
||||
AWS_CONFIG_FILE="${temp_root}/home/.aws/config" \
|
||||
node /app/openclaw.mjs agent --message "workspace bootstrap smoke" --session-id "workspace-bootstrap-smoke" --local --timeout 1 --json \
|
||||
>"${temp_root}/out.log" 2>&1
|
||||
status="$?"
|
||||
set -e
|
||||
if grep -F "Missing workspace template:" "${temp_root}/out.log"; then
|
||||
cat "${temp_root}/out.log"
|
||||
exit 1
|
||||
fi
|
||||
test -f "${temp_root}/home/.openclaw/workspace/HEARTBEAT.md"
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
cat "${temp_root}/out.log"
|
||||
fi
|
||||
'
|
||||
|
||||
normal_ci:
|
||||
name: Run normal full CI
|
||||
needs: [resolve_target, docker_runtime_assets_preflight]
|
||||
|
||||
17
.github/workflows/openclaw-performance.yml
vendored
17
.github/workflows/openclaw-performance.yml
vendored
@@ -551,25 +551,31 @@ jobs:
|
||||
retention-days: ${{ matrix.deep_profile == 'true' && 14 || 30 }}
|
||||
|
||||
- name: Prepare clawgrit reports checkout
|
||||
id: clawgrit_reports
|
||||
if: ${{ steps.kova.outputs.report_json != '' && steps.clawgrit.outputs.present == 'true' }}
|
||||
env:
|
||||
CLAWGRIT_REPORTS_TOKEN: ${{ secrets.CLAWGRIT_REPORTS_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "ready=false" >> "$GITHUB_OUTPUT"
|
||||
reports_root=".artifacts/clawgrit-reports"
|
||||
mkdir -p "$reports_root"
|
||||
git -C "$reports_root" init -b main
|
||||
git -C "$reports_root" remote add origin "https://x-access-token:${CLAWGRIT_REPORTS_TOKEN}@github.com/openclaw/clawgrit-reports.git"
|
||||
if git -C "$reports_root" ls-remote --exit-code --heads origin main >/dev/null 2>&1; then
|
||||
git -C "$reports_root" fetch --depth=1 origin main
|
||||
if timeout 60s git -C "$reports_root" ls-remote --exit-code --heads origin main >/dev/null 2>&1; then
|
||||
if ! timeout 120s git -C "$reports_root" fetch --depth=1 origin main; then
|
||||
echo "::warning::Skipping optional clawgrit report publish because the reports checkout fetch timed out or failed."
|
||||
exit 0
|
||||
fi
|
||||
git -C "$reports_root" checkout -B main FETCH_HEAD
|
||||
else
|
||||
git -C "$reports_root" checkout -B main
|
||||
fi
|
||||
echo "ready=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Publish to clawgrit reports
|
||||
if: ${{ steps.kova.outputs.report_json != '' && steps.clawgrit.outputs.present == 'true' }}
|
||||
if: ${{ steps.kova.outputs.report_json != '' && steps.clawgrit.outputs.present == 'true' && steps.clawgrit_reports.outputs.ready == 'true' }}
|
||||
env:
|
||||
CLAWGRIT_REPORTS_TOKEN: ${{ secrets.CLAWGRIT_REPORTS_TOKEN }}
|
||||
shell: bash
|
||||
@@ -642,6 +648,9 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
sleep $((attempt * 2))
|
||||
git -C "$reports_root" fetch --depth=1 origin main
|
||||
timeout 120s git -C "$reports_root" fetch --depth=1 origin main || {
|
||||
echo "::warning::Skipping optional clawgrit report rebase because the reports fetch timed out or failed."
|
||||
exit 0
|
||||
}
|
||||
git -C "$reports_root" rebase FETCH_HEAD
|
||||
done
|
||||
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -2,20 +2,42 @@
|
||||
|
||||
Docs: https://docs.openclaw.ai
|
||||
|
||||
## Unreleased
|
||||
## 2026.5.27
|
||||
|
||||
### Highlights
|
||||
|
||||
- Stronger security and content boundaries: group prompt text is kept out of the system prompt, repeated-dot hostnames are normalized, side-effecting command wrappers and unsafe Node runtime env overrides are blocked, no-auth Tailscale exposure is rejected, and node/device-role approvals now require admin authority. (#87144, #87305, #87292, #87308, #87146) Thanks @eleqtrizit and @pgondhi987.
|
||||
- More reliable Codex app-server runs: Codex runtime models resolve first, workspace memory is routed through tools, shared app-server clients survive startup and spawned-helper failures, native hook relay generations survive restarts and rotate on fresh fallbacks, and false runtime live switches are avoided. (#87383, #87403, #87375, #72574, #87428) Thanks @yetval.
|
||||
- Faster Gateway and reply paths: session reads, plugin metadata fingerprints, auth env snapshots, auto-enabled plugin config, tool-search catalogs, and stable metadata caches do less hot-path rediscovery while visible replies no longer inherit hidden cleanup timeouts. (#86439, #87044) Thanks @keshavbotagent.
|
||||
- Better provider and model coverage: OpenAI-compatible embedding providers are core, DeepInfra catalog browsing loads the full credential-aware model set, Pixverse adds video generation and API region selection, VLLM thinking params are wired, Claude CLI OAuth overlays load for PI auth profiles, and bare direct Anthropic model ids work. (#85269, #84549, #87167) Thanks @dutifulbob, @ats3v, and @joshavant.
|
||||
- Channel delivery is steadier: Telegram `sendMessage` actions use durable outbound delivery, iMessage suppresses duplicate native exec approval prompts and sends, Slack keeps delivered final replies during late cleanup, Matrix mention previews/finals are stricter, QQBot fallback approval buttons honor slash-command auth, Discord guild requester checks are tighter, recovered Discord tool-warning artifacts stay out of successful replies, and Google Chat stops thread sends in DMs. (#87261, #87154) Thanks @mbelinky and @eleqtrizit.
|
||||
- Release, package, and CI proof paths are harder to wedge: npm/package inventory honors dist exclusions, shrinkwrap override pins merge correctly, Docker runtime workspace templates are packaged and smoked, release postpublish checks are stricter, beta smoke rejects empty runs, and E2E log/probe waits are bounded.
|
||||
|
||||
### Changes
|
||||
|
||||
- Memory: add a core OpenAI-compatible embedding provider for local and hosted OpenAI-style endpoints, with config, doctor, and docs support. (#85269) Thanks @dutifulbob.
|
||||
- Plugin SDK: mark memory-specific embedding provider registration as deprecated compatibility and surface non-bundled usage in plugin compatibility diagnostics. (#85072) Thanks @mbelinky.
|
||||
- Providers: add the Pixverse video generation provider, API region selection, docs, and external plugin packaging support.
|
||||
- DeepInfra: load the full model catalog when users browse models during onboarding, preserve configured API-key catalogs, refresh media/video defaults, and keep pricing/default model metadata aligned. (#84549) Thanks @ats3v.
|
||||
- Plugin SDK: expose plugin approval action metadata and stop exporting Vitest test helpers from the public SDK surface. (#87120) Thanks @RomneyDa.
|
||||
- Channel SDK: move channel message compatibility into core, remove old channel turn runtime aliases, and preserve runtime catalog markdown metadata for plugins.
|
||||
- ClawHub: add plugin display metadata so catalog/package listings use cleaner names. (#87354) Thanks @thewilloftheshadow.
|
||||
- Agents: split the heartbeat runtime template out of docs assets and add compatibility repair for legacy heartbeat template content. (#85416) Thanks @hxy91819.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Harden hostname normalization for repeated trailing dots [AI]. (#87305) Thanks @pgondhi987.
|
||||
- fix: block side-effecting command wrappers [AI]. (#87292) Thanks @pgondhi987.
|
||||
- Block unsafe Node runtime env overrides [AI]. (#87308) Thanks @pgondhi987.
|
||||
- Telegram: route `sendMessage` action replies through durable outbound delivery so completed agent responses remain retryable when the gateway send path times out. (#87261) Thanks @mbelinky.
|
||||
- Gateway/security: require `operator.admin` for node and other non-operator device-role pairing approvals, including trusted-proxy sessions, while keeping pairing-only approvals available for operator-role requests. (#87146)
|
||||
- Security/content boundaries: route untrusted group prompt metadata outside system prompts, normalize repeated trailing hostname dots, block side-effecting command wrappers, reject unsafe Node runtime env overrides, reject no-auth Tailscale exposure, block untrusted Microsoft Teams service URLs, enforce `/allowlist configWrites` origin policy, gate QQBot fallback approval buttons, and require admin for node/device-role approvals. (#87144, #87305, #87292, #87308, #87146, #87154, #87334) Thanks @eleqtrizit and @pgondhi987.
|
||||
- Codex: resolve Codex runtime models before generic routing, route workspace memory through tools, preserve shared app-server clients after startup and spawned-helper failures, preserve native hook relay generations across restarts and fresh fallbacks, keep raw reasoning/source-reply guards intact, report quarantined dynamic tools, keep the attempt watchdog armed for queued terminal turns, and route Codex OAuth compaction through OpenAI-Codex. (#87383, #87403, #87375, #72574, #87428) Thanks @yetval.
|
||||
- Agents/runtime: avoid session event queue self-waits, bound compaction wake and steering retries, preserve grace for pending error diagnostics, avoid false Codex runtime live switches, avoid stale restart continuation reuse, preserve session fallback errors, suppress duplicate Claude CLI skill prompts, keep runtime context before active user turns, strip stale Anthropic thinking, quarantine unsupported tool schemas, recover completed write timeouts safely, release retained session write locks on timeout abort, and validate forced plugin harness support before pinning. (#86123, #55424, #86855, #74341, #87278) Thanks @luoyanglang, @cathrynlavery, and @openperf.
|
||||
- Reply/session delivery: keep visible turn admission unbounded, keep visible fallback delivery on latest targets, preserve bridge hook context, classify direct fallback targets by channel grammar, report approval resolutions in bridge mode, and avoid stale source-reply artifacts. (#87044) Thanks @keshavbotagent.
|
||||
- Channels: make Telegram `sendMessage` action replies durable and preserve SecretRef prompt config, suppress duplicate iMessage native exec approval prompts and sends, keep iMessage approval polling alive after denied reactions, keep Slack delivered final replies during late cleanup, keep Matrix mention previews/finals mention-inert and normally delivered, ignore filename-embedded Matrix IDs, suppress recovered Discord tool-warning artifacts from successful replies, suppress Google Chat thread sends in DMs, and harden Discord guild requester checks. (#87261, #87452) Thanks @mbelinky.
|
||||
- Memory: salvage QMD search JSON after nonzero exits and keep workspace memory routing through the Codex tool path where possible. (#87225, #87383, #87403) Thanks @osolmaz.
|
||||
- Providers/models: forward cached token usage in OpenAI-compatible chat completions, load Claude CLI OAuth overlays for PI auth profiles, send bare direct Anthropic model ids, wire configured VLLM thinking params, honor OpenAI-compatible cache retention, normalize OpenAI Responses replay tool ids, resolve OpenAI `gpt-5.5` without a cached catalog, preserve `retry-after` fallback handling, bound GitHub Copilot auth requests, and load DeepInfra custom/live catalogs consistently. (#82062, #87167, #84549) Thanks @caz0075, @joshavant, and @ats3v.
|
||||
- Gateway/performance: borrow read-only session metadata and active session working stores, cache current/stable plugin metadata fingerprints, cache auto-enabled plugin config, slim metadata identity caches, trust current metadata lifecycle caches, stabilize isolated cron prompt-cache affinity, persist model auth profile suffixes, drain probe client closes, expire browser tokens after auth rotation, and keep default status fast paths bounded. Thanks @ferminquant.
|
||||
- CLI/help/config: reject loose or malformed numeric options for gateway timeouts, model limits, directory limits, message options, webhooks, and partial values; respect subcommand version options; route generated/root/plugin help targets correctly; keep skills JSON output flushing naturally; and keep plugin descriptor loading quiet in root help. (#87398) Thanks @Patrick-Erichsen.
|
||||
- Plugin state/tool search: evict the current namespace when plugin rows hit caps, reuse unchanged tool-search catalogs, align the release catalog reuse wrapper, and keep fallback tool warnings mention-inert.
|
||||
- Install/package/release: match npm globstar exclusions, honor dist package exclusions in inventory, omit unpacked test helpers, skip Homebrew until macOS packages need it, package Docker runtime workspace templates, smoke Docker runtime templates during full validation, merge nested shrinkwrap override pins, preserve forked shrinkwrap pins, pin aged `lru-cache`, harden postpublish verification, accept main full-validation proof, and reject empty beta smoke runs.
|
||||
- E2E/QA/Crabbox: bound Telegram, Open WebUI, ClawHub, Matrix, Tool Search, MCP, gateway network, bundled runtime, kitchen-sink, codex media, config reload, and agent-turn assertion waits; prefer Azure for Windows targets; reinitialize invalid changed-gate git dirs; full-sync sparse container runs; and fail empty explicit test requests. (#87186)
|
||||
|
||||
## 2026.5.26
|
||||
|
||||
@@ -73,7 +95,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/sessions: handle active-fallback failures in `sessions_send` so fallback routing reports the real failure and does not leave callers with an ambiguous dropped send. (#86638)
|
||||
- Agents/hooks/subagents: enforce default hook agent allowlists, recover failed subagent lifecycle completions, and keep node task lifecycle cleanup from closing the Gateway listener. (#86101)
|
||||
- Codex: project newer OpenClaw chat history into resumed app-server threads and keep Codex turn timeouts inside the Codex runtime boundary so timeouts do not poison shared app-server clients or fall through to unrelated provider fallback. (#86677, #86476) Thanks @TurboTheTurtle and @pashpashpash.
|
||||
- Config/doctor/update: narrow profiled tool-section doctor repair, keep runtime-injected legacy web-search provider config out of user-authored config validation, and keep prerelease tags excluded from stable updater resolution. (#87030, #86818, #86559) Thanks @joshavant, @luoyanglang, and @stevenepalmer.
|
||||
- Config/doctor/update: narrow profiled tool-section doctor repair, migrate legacy `memorySearch.provider: "auto"` to `openai`, make restart follow-up guidance actionable, keep runtime-injected legacy web-search provider config out of user-authored config validation, and keep prerelease tags excluded from stable updater resolution. (#87030, #86818, #86559, #87361) Thanks @joshavant, @luoyanglang, @stevenepalmer, and @giodl73-repo.
|
||||
- Doctor/runtime: validate active bundled MCP tool schemas through the same runtime projection path so unsupported MCP input schemas are reported and quarantined instead of poisoning assistant startup.
|
||||
- CLI/Windows: add a Windows-only stack-size respawn for stack-heavy startup paths, default CLI logs to local timestamps, and validate timeout/banner TTY state more strictly. (#87031, #85387) Thanks @giodl73-repo and @vincentkoc.
|
||||
- Locking/security: require owner identity proof before stale plugin lock removal, memoize session lock owner arguments, and avoid writing default exec approval stores unless policy state actually changed. (#86814, #86964) Thanks @Alix-007 and @vincentkoc.
|
||||
|
||||
@@ -178,6 +178,7 @@ COPY --from=runtime-assets --chown=node:node /app/package.json .
|
||||
COPY --from=runtime-assets --chown=node:node /app/pnpm-workspace.yaml .
|
||||
COPY --from=runtime-assets --chown=node:node /app/patches ./patches
|
||||
COPY --from=runtime-assets --chown=node:node /app/openclaw.mjs .
|
||||
COPY --from=runtime-assets --chown=node:node /app/src/agents/templates ./src/agents/templates
|
||||
COPY --from=runtime-assets --chown=node:node /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} ./${OPENCLAW_BUNDLED_PLUGIN_DIR}
|
||||
COPY --from=runtime-assets --chown=node:node /app/skills ./skills
|
||||
COPY --from=runtime-assets --chown=node:node /app/docs ./docs
|
||||
|
||||
@@ -65,8 +65,8 @@ android {
|
||||
applicationId = "ai.openclaw.app"
|
||||
minSdk = 31
|
||||
targetSdk = 36
|
||||
versionCode = 2026052601
|
||||
versionName = "2026.5.26"
|
||||
versionCode = 2026052701
|
||||
versionName = "2026.5.27"
|
||||
ndk {
|
||||
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# OpenClaw iOS Changelog
|
||||
|
||||
## 2026.5.27 - 2026-05-27
|
||||
|
||||
Maintenance update for the current OpenClaw release.
|
||||
|
||||
## 2026.5.26 - 2026-05-26
|
||||
|
||||
Maintenance update for the current OpenClaw release.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Source of truth: apps/ios/version.json
|
||||
// Generated by scripts/ios-sync-versioning.ts.
|
||||
|
||||
OPENCLAW_IOS_VERSION = 2026.5.26
|
||||
OPENCLAW_MARKETING_VERSION = 2026.5.26
|
||||
OPENCLAW_IOS_VERSION = 2026.5.27
|
||||
OPENCLAW_MARKETING_VERSION = 2026.5.27
|
||||
OPENCLAW_BUILD_VERSION = 1
|
||||
|
||||
#include? "../build/Version.xcconfig"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "2026.5.26"
|
||||
"version": "2026.5.27"
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.5.26</string>
|
||||
<string>2026.5.27</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2026052600</string>
|
||||
<string>2026052700</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>OpenClaw</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
53b7621e99d75b98ecc8f4389d38900f84cf213f95dbcc877f36125d763c660d config-baseline.json
|
||||
e92bbf45714e418383118098d4ff15d347fa8ffc7e7837b437b522d2b59ce9fe config-baseline.core.json
|
||||
e058e30260eab3a65ff03e5f4596e3042ca1e8ffc6fa66eb46dbce78b49d410a config-baseline.json
|
||||
13fb390fd71a8d456cdfd42e6d9e577eba286e4509cc4e1a11c42f2e19255514 config-baseline.core.json
|
||||
b901fb766edfd9df630690281476fc4032c64772f69d1d8f7b2e0e913a90f229 config-baseline.channel.json
|
||||
5c214ab364011fd95735755f9fa4298aa4de8ad81144ae8dd08d969bb7ba318b config-baseline.plugin.json
|
||||
99e3acc957594dba162be46f41e70a77f22adc594a079ad95a16518c1e285bbb config-baseline.plugin.json
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
ce09dfd1c6f67d49916da2557fb208744b7d8a4912bde944004f44c0998c8e9d plugin-sdk-api-baseline.json
|
||||
371bdfb13fda61dda885827ffeb922bd46e97ca30e09fa0d09baab80c58a7d1e plugin-sdk-api-baseline.jsonl
|
||||
401b98b29b5057b1ef3f598e2efab5672e15b147724ac964a8da508689408282 plugin-sdk-api-baseline.json
|
||||
b017671bbe8a0e50625b79079aa470383ef66c4d7d060c2e208c36e573b81ea4 plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -183,18 +183,23 @@ in every user turn. Codex loads `AGENTS.md` through its own project-doc
|
||||
discovery. `SOUL.md`, `IDENTITY.md`, `TOOLS.md`, and `USER.md` are forwarded as
|
||||
Codex developer instructions. `HEARTBEAT.md` content is not injected; heartbeat
|
||||
turns get a collaboration-mode note pointing to the file when it exists and is
|
||||
non-empty. `MEMORY.md` and active `BOOTSTRAP.md` content keep the normal
|
||||
turn-context role for now.
|
||||
non-empty. `MEMORY.md` content from the configured agent workspace is not pasted
|
||||
into every native Codex turn; when memory tools are available for that workspace,
|
||||
Codex turns get a small workspace-memory note and should use `memory_search` or
|
||||
`memory_get` when durable memory is relevant. If tools are disabled, memory
|
||||
search is unavailable, or the active workspace differs from the agent memory
|
||||
workspace, `MEMORY.md` falls back to the normal bounded turn-context path. Active
|
||||
`BOOTSTRAP.md` content keeps the normal turn-context role for now.
|
||||
|
||||
On non-Codex harnesses, bootstrap files continue to be composed into the
|
||||
OpenClaw prompt according to their existing gates. `HEARTBEAT.md` is omitted on
|
||||
normal runs when heartbeats are disabled for the default agent or
|
||||
`agents.defaults.heartbeat.includeSystemPromptSection` is false. Keep injected
|
||||
files concise, especially `MEMORY.md`. `MEMORY.md` is intended to stay a curated
|
||||
long-term summary; detailed daily notes belong in `memory/*.md` where
|
||||
files concise, especially non-Codex `MEMORY.md`. `MEMORY.md` is intended to stay
|
||||
a curated long-term summary; detailed daily notes belong in `memory/*.md` where
|
||||
`memory_search` and `memory_get` can retrieve them on demand. Oversized
|
||||
`MEMORY.md` files increase prompt usage and can be partially injected because of
|
||||
the bootstrap file limits below.
|
||||
non-Codex `MEMORY.md` files increase prompt usage and can be partially injected
|
||||
because of the bootstrap file limits below.
|
||||
|
||||
<Note>
|
||||
`memory/*.md` daily files are **not** part of the normal bootstrap Project Context. On ordinary turns they are accessed on demand via the `memory_search` and `memory_get` tools, so they do not count against the context window unless the model explicitly reads them. Bare `/new` and `/reset` turns are the exception: the runtime can prepend recent daily memory as a one-shot startup-context block for that first turn.
|
||||
@@ -209,11 +214,13 @@ occurs, OpenClaw can inject a concise system-prompt warning notice; control this
|
||||
default: `always`). Detailed raw/injected counts stay in diagnostics such as
|
||||
`/context`, `/status`, doctor, and logs.
|
||||
|
||||
For memory files, truncation is not data loss: the file remains intact on disk,
|
||||
but the model only sees the shortened injected copy until it reads or searches
|
||||
memory directly. If `MEMORY.md` is repeatedly truncated, distill it into a
|
||||
shorter durable summary and move detailed history into `memory/*.md`, or
|
||||
intentionally raise the bootstrap limits.
|
||||
For memory files, truncation is not data loss: the file remains intact on disk.
|
||||
On native Codex, `MEMORY.md` is read on demand through memory tools when
|
||||
available, with bounded prompt fallback when tools cannot run. On other
|
||||
harnesses, the model only sees the shortened injected copy until it reads or
|
||||
searches memory directly. If `MEMORY.md` is repeatedly truncated there, distill
|
||||
it into a shorter durable summary and move detailed history into `memory/*.md`,
|
||||
or intentionally raise the bootstrap limits.
|
||||
|
||||
Sub-agent sessions only inject `AGENTS.md` and `TOOLS.md` (other bootstrap files
|
||||
are filtered out to keep the sub-agent context small).
|
||||
|
||||
@@ -517,7 +517,7 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
- **QMD backend**: probes whether the `qmd` binary is available and startable. If not, prints fix guidance including the npm package and a manual binary path option.
|
||||
- **Explicit local provider**: checks for a local model file or a recognized remote/downloadable model URL. If missing, suggests switching to a remote provider.
|
||||
- **Explicit remote provider** (`openai`, `voyage`, etc.): verifies an API key is present in the environment or auth store. Prints actionable fix hints if missing.
|
||||
- **Legacy auto provider**: treats `memorySearch.provider: "auto"` as OpenAI and checks OpenAI readiness.
|
||||
- **Legacy auto provider**: treats `memorySearch.provider: "auto"` as OpenAI, checks OpenAI readiness, and `doctor --fix` rewrites it to `provider: "openai"`.
|
||||
|
||||
When a cached gateway probe result is available (gateway was healthy at the time of the check), doctor cross-references its result with the CLI-visible config and notes any discrepancy. Doctor does not start a fresh embedding ping on the default path; use the deep memory status command when you want a live provider check.
|
||||
|
||||
|
||||
@@ -420,8 +420,15 @@ files. `SOUL.md`, `IDENTITY.md`, `TOOLS.md`, and `USER.md` are forwarded as
|
||||
OpenClaw Codex developer instructions because they define the active agent,
|
||||
available workspace guidance, and user profile. `HEARTBEAT.md` content is not
|
||||
injected; heartbeat turns get a collaboration-mode pointer to read the file when
|
||||
it exists and is non-empty. `BOOTSTRAP.md` and `MEMORY.md` when present are
|
||||
forwarded as OpenClaw turn input reference context.
|
||||
it exists and is non-empty. `MEMORY.md` content from the configured agent
|
||||
workspace is not pasted into native Codex turn input when memory tools are
|
||||
available for that workspace; when it exists, the harness adds a small
|
||||
workspace-memory pointer and Codex should use `memory_search` or `memory_get`
|
||||
when durable memory is relevant. If tools are disabled, memory search is
|
||||
unavailable, or the active workspace differs from the agent memory workspace,
|
||||
`MEMORY.md` uses the normal bounded turn-context path.
|
||||
`BOOTSTRAP.md` when present is forwarded as OpenClaw turn input reference
|
||||
context.
|
||||
|
||||
## Environment overrides
|
||||
|
||||
|
||||
@@ -140,39 +140,39 @@ commands.
|
||||
|
||||
## Official external packages
|
||||
|
||||
| Plugin | Description | Distribution | Surface |
|
||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
|
||||
| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
|
||||
| [amazon-bedrock](/plugins/reference/amazon-bedrock) | Adds Amazon Bedrock model provider support to OpenClaw. | `@openclaw/amazon-bedrock-provider`<br />npm; ClawHub | providers: amazon-bedrock; contracts: memoryEmbeddingProviders |
|
||||
| [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | Adds Amazon Bedrock Mantle model provider support to OpenClaw. | `@openclaw/amazon-bedrock-mantle-provider`<br />npm; ClawHub | providers: amazon-bedrock-mantle |
|
||||
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | Adds Anthropic Vertex model provider support to OpenClaw. | `@openclaw/anthropic-vertex-provider`<br />npm; ClawHub | providers: anthropic-vertex |
|
||||
| [brave](/plugins/reference/brave) | Adds web search provider support. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
|
||||
| [codex](/plugins/reference/codex) | Codex app-server harness and Codex-managed GPT model catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
|
||||
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |
|
||||
| [diagnostics-prometheus](/plugins/reference/diagnostics-prometheus) | OpenClaw diagnostics Prometheus exporter. | `@openclaw/diagnostics-prometheus`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-prometheus` | plugin |
|
||||
| [diffs](/plugins/reference/diffs) | Read-only diff viewer and file renderer for agents. | `@openclaw/diffs`<br />npm; ClawHub | contracts: tools; skills |
|
||||
| [discord](/plugins/reference/discord) | Adds the Discord channel surface for sending and receiving OpenClaw messages. | `@openclaw/discord`<br />npm; ClawHub | channels: discord; contracts: transcriptSourceProviders |
|
||||
| [feishu](/plugins/reference/feishu) | Adds the Feishu channel surface for sending and receiving OpenClaw messages. | `@openclaw/feishu`<br />npm; ClawHub | channels: feishu; contracts: tools; skills |
|
||||
| [google-meet](/plugins/reference/google-meet) | Join Google Meet calls through Chrome or Twilio transports. | `@openclaw/google-meet`<br />npm; ClawHub | contracts: tools |
|
||||
| [googlechat](/plugins/reference/googlechat) | Adds the Google Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/googlechat`<br />npm; ClawHub | channels: googlechat |
|
||||
| [line](/plugins/reference/line) | Adds the LINE channel surface for sending and receiving OpenClaw messages. | `@openclaw/line`<br />npm; ClawHub | channels: line |
|
||||
| [lobster](/plugins/reference/lobster) | Typed workflow tool with resumable approvals. | `@openclaw/lobster`<br />npm; ClawHub | contracts: tools |
|
||||
| [matrix](/plugins/reference/matrix) | Adds the Matrix channel surface for sending and receiving OpenClaw messages. | `@openclaw/matrix`<br />ClawHub: `clawhub:@openclaw/matrix`; npm | channels: matrix |
|
||||
| [memory-lancedb](/plugins/reference/memory-lancedb) | Adds agent-callable tools. | `@openclaw/memory-lancedb`<br />npm; ClawHub | contracts: tools |
|
||||
| [msteams](/plugins/reference/msteams) | Adds the Microsoft Teams channel surface for sending and receiving OpenClaw messages. | `@openclaw/msteams`<br />npm; ClawHub | channels: msteams |
|
||||
| [nextcloud-talk](/plugins/reference/nextcloud-talk) | Adds the Nextcloud Talk channel surface for sending and receiving OpenClaw messages. | `@openclaw/nextcloud-talk`<br />npm; ClawHub | channels: nextcloud-talk |
|
||||
| [nostr](/plugins/reference/nostr) | Adds the Nostr channel surface for sending and receiving OpenClaw messages. | `@openclaw/nostr`<br />npm; ClawHub | channels: nostr |
|
||||
| [openshell](/plugins/reference/openshell) | Sandbox backend powered by the NVIDIA OpenShell CLI with mirrored local workspaces and SSH-based command execution. | `@openclaw/openshell-sandbox`<br />npm; ClawHub | plugin |
|
||||
| [pixverse](/plugins/reference/pixverse) | Adds PixVerse video generation provider support to OpenClaw. | `@openclaw/pixverse-provider`<br />npm; ClawHub | contracts: videoGenerationProviders |
|
||||
| [qqbot](/plugins/reference/qqbot) | Adds the QQ Bot channel surface for sending and receiving OpenClaw messages. | `@openclaw/qqbot`<br />npm; ClawHub | channels: qqbot; contracts: tools; skills |
|
||||
| [slack](/plugins/reference/slack) | Adds the Slack channel surface for sending and receiving OpenClaw messages. | `@openclaw/slack`<br />npm; ClawHub | channels: slack |
|
||||
| [synology-chat](/plugins/reference/synology-chat) | Adds the Synology Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/synology-chat`<br />npm; ClawHub | channels: synology-chat |
|
||||
| [tlon](/plugins/reference/tlon) | Adds the Tlon channel surface for sending and receiving OpenClaw messages. | `@openclaw/tlon`<br />npm; ClawHub | channels: tlon; skills |
|
||||
| [twitch](/plugins/reference/twitch) | Adds the Twitch channel surface for sending and receiving OpenClaw messages. | `@openclaw/twitch`<br />npm; ClawHub | channels: twitch |
|
||||
| [voice-call](/plugins/reference/voice-call) | Adds agent-callable tools. | `@openclaw/voice-call`<br />npm; ClawHub | contracts: tools |
|
||||
| [whatsapp](/plugins/reference/whatsapp) | Adds the WhatsApp channel surface for sending and receiving OpenClaw messages. | `@openclaw/whatsapp`<br />ClawHub: `clawhub:@openclaw/whatsapp`; npm | channels: whatsapp |
|
||||
| [zalo](/plugins/reference/zalo) | Adds the Zalo channel surface for sending and receiving OpenClaw messages. | `@openclaw/zalo`<br />npm; ClawHub | channels: zalo |
|
||||
| [zalouser](/plugins/reference/zalouser) | Adds the Zalo Personal channel surface for sending and receiving OpenClaw messages. | `@openclaw/zalouser`<br />npm; ClawHub | channels: zalouser; contracts: tools |
|
||||
| Plugin | Description | Distribution | Surface |
|
||||
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
|
||||
| [acpx](/plugins/reference/acpx) | OpenClaw ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
|
||||
| [amazon-bedrock](/plugins/reference/amazon-bedrock) | OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support. | `@openclaw/amazon-bedrock-provider`<br />npm; ClawHub | providers: amazon-bedrock; contracts: memoryEmbeddingProviders |
|
||||
| [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing. | `@openclaw/amazon-bedrock-mantle-provider`<br />npm; ClawHub | providers: amazon-bedrock-mantle |
|
||||
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI. | `@openclaw/anthropic-vertex-provider`<br />npm; ClawHub | providers: anthropic-vertex |
|
||||
| [brave](/plugins/reference/brave) | OpenClaw Brave Search provider plugin for web search. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
|
||||
| [codex](/plugins/reference/codex) | OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
|
||||
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter for metrics and traces. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |
|
||||
| [diagnostics-prometheus](/plugins/reference/diagnostics-prometheus) | OpenClaw diagnostics Prometheus exporter for runtime metrics. | `@openclaw/diagnostics-prometheus`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-prometheus` | plugin |
|
||||
| [diffs](/plugins/reference/diffs) | OpenClaw read-only diff viewer plugin and file renderer for agents. | `@openclaw/diffs`<br />npm; ClawHub | contracts: tools; skills |
|
||||
| [discord](/plugins/reference/discord) | OpenClaw Discord channel plugin for channels, DMs, commands, and app events. | `@openclaw/discord`<br />npm; ClawHub | channels: discord; contracts: transcriptSourceProviders |
|
||||
| [feishu](/plugins/reference/feishu) | OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng). | `@openclaw/feishu`<br />npm; ClawHub | channels: feishu; contracts: tools; skills |
|
||||
| [google-meet](/plugins/reference/google-meet) | OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports. | `@openclaw/google-meet`<br />npm; ClawHub | contracts: tools |
|
||||
| [googlechat](/plugins/reference/googlechat) | OpenClaw Google Chat channel plugin for spaces and direct messages. | `@openclaw/googlechat`<br />npm; ClawHub | channels: googlechat |
|
||||
| [line](/plugins/reference/line) | OpenClaw LINE channel plugin for LINE Bot API chats. | `@openclaw/line`<br />npm; ClawHub | channels: line |
|
||||
| [lobster](/plugins/reference/lobster) | Lobster workflow tool plugin for typed pipelines and resumable approvals. | `@openclaw/lobster`<br />npm; ClawHub | contracts: tools |
|
||||
| [matrix](/plugins/reference/matrix) | OpenClaw Matrix channel plugin for rooms and direct messages. | `@openclaw/matrix`<br />ClawHub: `clawhub:@openclaw/matrix`; npm | channels: matrix |
|
||||
| [memory-lancedb](/plugins/reference/memory-lancedb) | OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search. | `@openclaw/memory-lancedb`<br />npm; ClawHub | contracts: tools |
|
||||
| [msteams](/plugins/reference/msteams) | OpenClaw Microsoft Teams channel plugin for bot conversations. | `@openclaw/msteams`<br />npm; ClawHub | channels: msteams |
|
||||
| [nextcloud-talk](/plugins/reference/nextcloud-talk) | OpenClaw Nextcloud Talk channel plugin for conversations. | `@openclaw/nextcloud-talk`<br />npm; ClawHub | channels: nextcloud-talk |
|
||||
| [nostr](/plugins/reference/nostr) | OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages. | `@openclaw/nostr`<br />npm; ClawHub | channels: nostr |
|
||||
| [openshell](/plugins/reference/openshell) | OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution. | `@openclaw/openshell-sandbox`<br />npm; ClawHub | plugin |
|
||||
| [pixverse](/plugins/reference/pixverse) | OpenClaw PixVerse video generation provider plugin. | `@openclaw/pixverse-provider`<br />npm; ClawHub | contracts: videoGenerationProviders |
|
||||
| [qqbot](/plugins/reference/qqbot) | OpenClaw QQ Bot channel plugin for group and direct-message workflows. | `@openclaw/qqbot`<br />npm; ClawHub | channels: qqbot; contracts: tools; skills |
|
||||
| [slack](/plugins/reference/slack) | OpenClaw Slack channel plugin for channels, DMs, commands, and app events. | `@openclaw/slack`<br />npm; ClawHub | channels: slack |
|
||||
| [synology-chat](/plugins/reference/synology-chat) | Synology Chat channel plugin for OpenClaw channels and direct messages. | `@openclaw/synology-chat`<br />npm; ClawHub | channels: synology-chat |
|
||||
| [tlon](/plugins/reference/tlon) | OpenClaw Tlon/Urbit channel plugin for chat workflows. | `@openclaw/tlon`<br />npm; ClawHub | channels: tlon; skills |
|
||||
| [twitch](/plugins/reference/twitch) | OpenClaw Twitch channel plugin for chat and moderation workflows. | `@openclaw/twitch`<br />npm; ClawHub | channels: twitch |
|
||||
| [voice-call](/plugins/reference/voice-call) | OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls. | `@openclaw/voice-call`<br />npm; ClawHub | contracts: tools |
|
||||
| [whatsapp](/plugins/reference/whatsapp) | OpenClaw WhatsApp channel plugin for WhatsApp Web chats. | `@openclaw/whatsapp`<br />ClawHub: `clawhub:@openclaw/whatsapp`; npm | channels: whatsapp |
|
||||
| [zalo](/plugins/reference/zalo) | OpenClaw Zalo channel plugin for bot and webhook chats. | `@openclaw/zalo`<br />npm; ClawHub | channels: zalo |
|
||||
| [zalouser](/plugins/reference/zalouser) | OpenClaw Zalo Personal Account plugin via native zca-js integration. | `@openclaw/zalouser`<br />npm; ClawHub | channels: zalouser; contracts: tools |
|
||||
|
||||
## Source checkout only
|
||||
|
||||
|
||||
@@ -17,17 +17,17 @@ pnpm plugins:inventory:gen
|
||||
|
||||
| Plugin | Description | Distribution | Surface |
|
||||
| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
|
||||
| [acpx](/plugins/reference/acpx) | OpenClaw ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
|
||||
| [admin-http-rpc](/plugins/reference/admin-http-rpc) | OpenClaw admin HTTP RPC endpoint. | `@openclaw/admin-http-rpc`<br />included in OpenClaw | contracts: gatewayMethodDispatch |
|
||||
| [alibaba](/plugins/reference/alibaba) | Adds video generation provider support. | `@openclaw/alibaba-provider`<br />included in OpenClaw | contracts: videoGenerationProviders |
|
||||
| [amazon-bedrock](/plugins/reference/amazon-bedrock) | Adds Amazon Bedrock model provider support to OpenClaw. | `@openclaw/amazon-bedrock-provider`<br />npm; ClawHub | providers: amazon-bedrock; contracts: memoryEmbeddingProviders |
|
||||
| [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | Adds Amazon Bedrock Mantle model provider support to OpenClaw. | `@openclaw/amazon-bedrock-mantle-provider`<br />npm; ClawHub | providers: amazon-bedrock-mantle |
|
||||
| [amazon-bedrock](/plugins/reference/amazon-bedrock) | OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support. | `@openclaw/amazon-bedrock-provider`<br />npm; ClawHub | providers: amazon-bedrock; contracts: memoryEmbeddingProviders |
|
||||
| [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing. | `@openclaw/amazon-bedrock-mantle-provider`<br />npm; ClawHub | providers: amazon-bedrock-mantle |
|
||||
| [anthropic](/plugins/reference/anthropic) | Adds Anthropic model provider support to OpenClaw. | `@openclaw/anthropic-provider`<br />included in OpenClaw | providers: anthropic; contracts: mediaUnderstandingProviders |
|
||||
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | Adds Anthropic Vertex model provider support to OpenClaw. | `@openclaw/anthropic-vertex-provider`<br />npm; ClawHub | providers: anthropic-vertex |
|
||||
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI. | `@openclaw/anthropic-vertex-provider`<br />npm; ClawHub | providers: anthropic-vertex |
|
||||
| [arcee](/plugins/reference/arcee) | Adds Arcee model provider support to OpenClaw. | `@openclaw/arcee-provider`<br />included in OpenClaw | providers: arcee |
|
||||
| [azure-speech](/plugins/reference/azure-speech) | Azure AI Speech text-to-speech (MP3, native Ogg/Opus voice notes, PCM telephony). | `@openclaw/azure-speech`<br />included in OpenClaw | contracts: speechProviders |
|
||||
| [bonjour](/plugins/reference/bonjour) | Advertise the local OpenClaw gateway over Bonjour/mDNS. | `@openclaw/bonjour`<br />included in OpenClaw | plugin |
|
||||
| [brave](/plugins/reference/brave) | Adds web search provider support. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
|
||||
| [brave](/plugins/reference/brave) | OpenClaw Brave Search provider plugin for web search. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
|
||||
| [browser](/plugins/reference/browser) | Adds agent-callable tools. | `@openclaw/browser-plugin`<br />included in OpenClaw | contracts: tools; skills |
|
||||
| [byteplus](/plugins/reference/byteplus) | Adds BytePlus, BytePlus Plan model provider support to OpenClaw. | `@openclaw/byteplus-provider`<br />included in OpenClaw | providers: byteplus, byteplus-plan; contracts: videoGenerationProviders |
|
||||
| [canvas](/plugins/reference/canvas) | Experimental Canvas control and A2UI rendering surfaces for paired nodes. | `@openclaw/canvas-plugin`<br />included in OpenClaw | contracts: tools |
|
||||
@@ -35,29 +35,29 @@ pnpm plugins:inventory:gen
|
||||
| [chutes](/plugins/reference/chutes) | Adds Chutes model provider support to OpenClaw. | `@openclaw/chutes-provider`<br />included in OpenClaw | providers: chutes |
|
||||
| [clickclack](/plugins/reference/clickclack) | Adds the Clickclack channel surface for sending and receiving OpenClaw messages. | `@openclaw/clickclack`<br />included in OpenClaw | channels: clickclack |
|
||||
| [cloudflare-ai-gateway](/plugins/reference/cloudflare-ai-gateway) | Adds Cloudflare AI Gateway model provider support to OpenClaw. | `@openclaw/cloudflare-ai-gateway-provider`<br />included in OpenClaw | providers: cloudflare-ai-gateway |
|
||||
| [codex](/plugins/reference/codex) | Codex app-server harness and Codex-managed GPT model catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
|
||||
| [codex](/plugins/reference/codex) | OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
|
||||
| [comfy](/plugins/reference/comfy) | Adds ComfyUI model provider support to OpenClaw. | `@openclaw/comfy-provider`<br />included in OpenClaw | providers: comfy; contracts: imageGenerationProviders, musicGenerationProviders, videoGenerationProviders |
|
||||
| [copilot-proxy](/plugins/reference/copilot-proxy) | Adds Copilot Proxy model provider support to OpenClaw. | `@openclaw/copilot-proxy`<br />included in OpenClaw | providers: copilot-proxy |
|
||||
| [deepgram](/plugins/reference/deepgram) | Adds media understanding provider support. Adds realtime transcription provider support. | `@openclaw/deepgram-provider`<br />included in OpenClaw | contracts: mediaUnderstandingProviders, realtimeTranscriptionProviders |
|
||||
| [deepinfra](/plugins/reference/deepinfra) | Adds DeepInfra model provider support to OpenClaw. | `@openclaw/deepinfra-provider`<br />included in OpenClaw | providers: deepinfra; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, speechProviders, videoGenerationProviders |
|
||||
| [deepseek](/plugins/reference/deepseek) | Adds DeepSeek model provider support to OpenClaw. | `@openclaw/deepseek-provider`<br />included in OpenClaw | providers: deepseek |
|
||||
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |
|
||||
| [diagnostics-prometheus](/plugins/reference/diagnostics-prometheus) | OpenClaw diagnostics Prometheus exporter. | `@openclaw/diagnostics-prometheus`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-prometheus` | plugin |
|
||||
| [diffs](/plugins/reference/diffs) | Read-only diff viewer and file renderer for agents. | `@openclaw/diffs`<br />npm; ClawHub | contracts: tools; skills |
|
||||
| [discord](/plugins/reference/discord) | Adds the Discord channel surface for sending and receiving OpenClaw messages. | `@openclaw/discord`<br />npm; ClawHub | channels: discord; contracts: transcriptSourceProviders |
|
||||
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter for metrics and traces. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |
|
||||
| [diagnostics-prometheus](/plugins/reference/diagnostics-prometheus) | OpenClaw diagnostics Prometheus exporter for runtime metrics. | `@openclaw/diagnostics-prometheus`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-prometheus` | plugin |
|
||||
| [diffs](/plugins/reference/diffs) | OpenClaw read-only diff viewer plugin and file renderer for agents. | `@openclaw/diffs`<br />npm; ClawHub | contracts: tools; skills |
|
||||
| [discord](/plugins/reference/discord) | OpenClaw Discord channel plugin for channels, DMs, commands, and app events. | `@openclaw/discord`<br />npm; ClawHub | channels: discord; contracts: transcriptSourceProviders |
|
||||
| [document-extract](/plugins/reference/document-extract) | Extract text and fallback page images from local document attachments. | `@openclaw/document-extract-plugin`<br />included in OpenClaw | contracts: documentExtractors |
|
||||
| [duckduckgo](/plugins/reference/duckduckgo) | Adds web search provider support. | `@openclaw/duckduckgo-plugin`<br />included in OpenClaw | contracts: webSearchProviders |
|
||||
| [elevenlabs](/plugins/reference/elevenlabs) | Adds media understanding provider support. Adds realtime transcription provider support. Adds text-to-speech provider support. | `@openclaw/elevenlabs-speech`<br />included in OpenClaw | contracts: mediaUnderstandingProviders, realtimeTranscriptionProviders, speechProviders |
|
||||
| [exa](/plugins/reference/exa) | Adds web search provider support. | `@openclaw/exa-plugin`<br />included in OpenClaw | contracts: webSearchProviders |
|
||||
| [fal](/plugins/reference/fal) | Adds fal model provider support to OpenClaw. | `@openclaw/fal-provider`<br />included in OpenClaw | providers: fal; contracts: imageGenerationProviders, musicGenerationProviders, videoGenerationProviders |
|
||||
| [feishu](/plugins/reference/feishu) | Adds the Feishu channel surface for sending and receiving OpenClaw messages. | `@openclaw/feishu`<br />npm; ClawHub | channels: feishu; contracts: tools; skills |
|
||||
| [feishu](/plugins/reference/feishu) | OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng). | `@openclaw/feishu`<br />npm; ClawHub | channels: feishu; contracts: tools; skills |
|
||||
| [file-transfer](/plugins/reference/file-transfer) | Fetch, list, and write files on paired nodes via dedicated node commands. Bypasses bash stdout truncation by using base64 over node.invoke for binaries up to 16 MB. | `@openclaw/file-transfer`<br />included in OpenClaw | contracts: tools |
|
||||
| [firecrawl](/plugins/reference/firecrawl) | Adds agent-callable tools. Adds web fetch provider support. Adds web search provider support. | `@openclaw/firecrawl-plugin`<br />included in OpenClaw | contracts: tools, webFetchProviders, webSearchProviders |
|
||||
| [fireworks](/plugins/reference/fireworks) | Adds Fireworks model provider support to OpenClaw. | `@openclaw/fireworks-provider`<br />included in OpenClaw | providers: fireworks |
|
||||
| [github-copilot](/plugins/reference/github-copilot) | Adds GitHub Copilot model provider support to OpenClaw. | `@openclaw/github-copilot-provider`<br />included in OpenClaw | providers: github-copilot; contracts: memoryEmbeddingProviders |
|
||||
| [google](/plugins/reference/google) | Adds Google, Google Gemini CLI, Google Vertex model provider support to OpenClaw. | `@openclaw/google-plugin`<br />included in OpenClaw | providers: google, google-gemini-cli, google-vertex; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, musicGenerationProviders, realtimeVoiceProviders, speechProviders, videoGenerationProviders, webSearchProviders |
|
||||
| [google-meet](/plugins/reference/google-meet) | Join Google Meet calls through Chrome or Twilio transports. | `@openclaw/google-meet`<br />npm; ClawHub | contracts: tools |
|
||||
| [googlechat](/plugins/reference/googlechat) | Adds the Google Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/googlechat`<br />npm; ClawHub | channels: googlechat |
|
||||
| [google-meet](/plugins/reference/google-meet) | OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports. | `@openclaw/google-meet`<br />npm; ClawHub | contracts: tools |
|
||||
| [googlechat](/plugins/reference/googlechat) | OpenClaw Google Chat channel plugin for spaces and direct messages. | `@openclaw/googlechat`<br />npm; ClawHub | channels: googlechat |
|
||||
| [gradium](/plugins/reference/gradium) | Adds text-to-speech provider support. | `@openclaw/gradium-speech`<br />included in OpenClaw | contracts: speechProviders |
|
||||
| [groq](/plugins/reference/groq) | Adds Groq model provider support to OpenClaw. | `@openclaw/groq-provider`<br />included in OpenClaw | providers: groq; contracts: mediaUnderstandingProviders |
|
||||
| [huggingface](/plugins/reference/huggingface) | Adds Hugging Face model provider support to OpenClaw. | `@openclaw/huggingface-provider`<br />included in OpenClaw | providers: huggingface |
|
||||
@@ -66,15 +66,15 @@ pnpm plugins:inventory:gen
|
||||
| [irc](/plugins/reference/irc) | Adds the IRC channel surface for sending and receiving OpenClaw messages. | `@openclaw/irc`<br />included in OpenClaw | channels: irc |
|
||||
| [kilocode](/plugins/reference/kilocode) | Adds Kilocode model provider support to OpenClaw. | `@openclaw/kilocode-provider`<br />included in OpenClaw | providers: kilocode |
|
||||
| [kimi](/plugins/reference/kimi) | Adds Kimi, Kimi Coding model provider support to OpenClaw. | `@openclaw/kimi-provider`<br />included in OpenClaw | providers: kimi, kimi-coding |
|
||||
| [line](/plugins/reference/line) | Adds the LINE channel surface for sending and receiving OpenClaw messages. | `@openclaw/line`<br />npm; ClawHub | channels: line |
|
||||
| [line](/plugins/reference/line) | OpenClaw LINE channel plugin for LINE Bot API chats. | `@openclaw/line`<br />npm; ClawHub | channels: line |
|
||||
| [litellm](/plugins/reference/litellm) | Adds LiteLLM model provider support to OpenClaw. | `@openclaw/litellm-provider`<br />included in OpenClaw | providers: litellm; contracts: imageGenerationProviders |
|
||||
| [llm-task](/plugins/reference/llm-task) | Generic JSON-only LLM tool for structured tasks callable from workflows. | `@openclaw/llm-task`<br />included in OpenClaw | contracts: tools |
|
||||
| [lmstudio](/plugins/reference/lmstudio) | Adds LM Studio model provider support to OpenClaw. | `@openclaw/lmstudio-provider`<br />included in OpenClaw | providers: lmstudio; contracts: memoryEmbeddingProviders |
|
||||
| [lobster](/plugins/reference/lobster) | Typed workflow tool with resumable approvals. | `@openclaw/lobster`<br />npm; ClawHub | contracts: tools |
|
||||
| [matrix](/plugins/reference/matrix) | Adds the Matrix channel surface for sending and receiving OpenClaw messages. | `@openclaw/matrix`<br />ClawHub: `clawhub:@openclaw/matrix`; npm | channels: matrix |
|
||||
| [lobster](/plugins/reference/lobster) | Lobster workflow tool plugin for typed pipelines and resumable approvals. | `@openclaw/lobster`<br />npm; ClawHub | contracts: tools |
|
||||
| [matrix](/plugins/reference/matrix) | OpenClaw Matrix channel plugin for rooms and direct messages. | `@openclaw/matrix`<br />ClawHub: `clawhub:@openclaw/matrix`; npm | channels: matrix |
|
||||
| [mattermost](/plugins/reference/mattermost) | Adds the Mattermost channel surface for sending and receiving OpenClaw messages. | `@openclaw/mattermost`<br />included in OpenClaw | channels: mattermost |
|
||||
| [memory-core](/plugins/reference/memory-core) | Adds memory embedding provider support. Adds agent-callable tools. | `@openclaw/memory-core`<br />included in OpenClaw | contracts: memoryEmbeddingProviders, tools |
|
||||
| [memory-lancedb](/plugins/reference/memory-lancedb) | Adds agent-callable tools. | `@openclaw/memory-lancedb`<br />npm; ClawHub | contracts: tools |
|
||||
| [memory-lancedb](/plugins/reference/memory-lancedb) | OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search. | `@openclaw/memory-lancedb`<br />npm; ClawHub | contracts: tools |
|
||||
| [memory-wiki](/plugins/reference/memory-wiki) | Persistent wiki compiler and Obsidian-friendly knowledge vault for OpenClaw. | `@openclaw/memory-wiki`<br />included in OpenClaw | contracts: tools; skills |
|
||||
| [microsoft](/plugins/reference/microsoft) | Adds text-to-speech provider support. | `@openclaw/microsoft-speech`<br />included in OpenClaw | contracts: speechProviders |
|
||||
| [microsoft-foundry](/plugins/reference/microsoft-foundry) | Adds Microsoft Foundry model provider support to OpenClaw. | `@openclaw/microsoft-foundry`<br />included in OpenClaw | providers: microsoft-foundry |
|
||||
@@ -83,9 +83,9 @@ pnpm plugins:inventory:gen
|
||||
| [minimax](/plugins/reference/minimax) | Adds MiniMax, MiniMax Portal model provider support to OpenClaw. | `@openclaw/minimax-provider`<br />included in OpenClaw | providers: minimax, minimax-portal; contracts: imageGenerationProviders, mediaUnderstandingProviders, musicGenerationProviders, speechProviders, videoGenerationProviders, webSearchProviders |
|
||||
| [mistral](/plugins/reference/mistral) | Adds Mistral model provider support to OpenClaw. | `@openclaw/mistral-provider`<br />included in OpenClaw | providers: mistral; contracts: mediaUnderstandingProviders, memoryEmbeddingProviders, realtimeTranscriptionProviders |
|
||||
| [moonshot](/plugins/reference/moonshot) | Adds Moonshot model provider support to OpenClaw. | `@openclaw/moonshot-provider`<br />included in OpenClaw | providers: moonshot; contracts: mediaUnderstandingProviders, webSearchProviders |
|
||||
| [msteams](/plugins/reference/msteams) | Adds the Microsoft Teams channel surface for sending and receiving OpenClaw messages. | `@openclaw/msteams`<br />npm; ClawHub | channels: msteams |
|
||||
| [nextcloud-talk](/plugins/reference/nextcloud-talk) | Adds the Nextcloud Talk channel surface for sending and receiving OpenClaw messages. | `@openclaw/nextcloud-talk`<br />npm; ClawHub | channels: nextcloud-talk |
|
||||
| [nostr](/plugins/reference/nostr) | Adds the Nostr channel surface for sending and receiving OpenClaw messages. | `@openclaw/nostr`<br />npm; ClawHub | channels: nostr |
|
||||
| [msteams](/plugins/reference/msteams) | OpenClaw Microsoft Teams channel plugin for bot conversations. | `@openclaw/msteams`<br />npm; ClawHub | channels: msteams |
|
||||
| [nextcloud-talk](/plugins/reference/nextcloud-talk) | OpenClaw Nextcloud Talk channel plugin for conversations. | `@openclaw/nextcloud-talk`<br />npm; ClawHub | channels: nextcloud-talk |
|
||||
| [nostr](/plugins/reference/nostr) | OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages. | `@openclaw/nostr`<br />npm; ClawHub | channels: nostr |
|
||||
| [nvidia](/plugins/reference/nvidia) | Adds NVIDIA model provider support to OpenClaw. | `@openclaw/nvidia-provider`<br />included in OpenClaw | providers: nvidia |
|
||||
| [oc-path](/plugins/reference/oc-path) | Adds the openclaw path CLI for oc:// workspace file addressing. | `@openclaw/oc-path`<br />included in OpenClaw | plugin |
|
||||
| [ollama](/plugins/reference/ollama) | Adds Ollama model provider support to OpenClaw. | `@openclaw/ollama-provider`<br />included in OpenClaw | providers: ollama; contracts: memoryEmbeddingProviders, webSearchProviders |
|
||||
@@ -94,15 +94,15 @@ pnpm plugins:inventory:gen
|
||||
| [opencode](/plugins/reference/opencode) | Adds OpenCode model provider support to OpenClaw. | `@openclaw/opencode-provider`<br />included in OpenClaw | providers: opencode; contracts: mediaUnderstandingProviders |
|
||||
| [opencode-go](/plugins/reference/opencode-go) | Adds OpenCode Go model provider support to OpenClaw. | `@openclaw/opencode-go-provider`<br />included in OpenClaw | providers: opencode-go; contracts: mediaUnderstandingProviders |
|
||||
| [openrouter](/plugins/reference/openrouter) | Adds OpenRouter model provider support to OpenClaw. | `@openclaw/openrouter-provider`<br />included in OpenClaw | providers: openrouter; contracts: imageGenerationProviders, mediaUnderstandingProviders, musicGenerationProviders, speechProviders, videoGenerationProviders |
|
||||
| [openshell](/plugins/reference/openshell) | Sandbox backend powered by the NVIDIA OpenShell CLI with mirrored local workspaces and SSH-based command execution. | `@openclaw/openshell-sandbox`<br />npm; ClawHub | plugin |
|
||||
| [openshell](/plugins/reference/openshell) | OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution. | `@openclaw/openshell-sandbox`<br />npm; ClawHub | plugin |
|
||||
| [perplexity](/plugins/reference/perplexity) | Adds web search provider support. | `@openclaw/perplexity-plugin`<br />included in OpenClaw | contracts: webSearchProviders |
|
||||
| [pixverse](/plugins/reference/pixverse) | Adds PixVerse video generation provider support to OpenClaw. | `@openclaw/pixverse-provider`<br />npm; ClawHub | contracts: videoGenerationProviders |
|
||||
| [pixverse](/plugins/reference/pixverse) | OpenClaw PixVerse video generation provider plugin. | `@openclaw/pixverse-provider`<br />npm; ClawHub | contracts: videoGenerationProviders |
|
||||
| [policy](/plugins/reference/policy) | Adds policy-backed doctor checks for workspace conformance. | `@openclaw/policy`<br />included in OpenClaw | plugin |
|
||||
| [qa-channel](/plugins/reference/qa-channel) | Adds the QA Channel surface for sending and receiving OpenClaw messages. | `@openclaw/qa-channel`<br />source checkout only | channels: qa-channel |
|
||||
| [qa-lab](/plugins/reference/qa-lab) | OpenClaw QA lab plugin with private debugger UI and scenario runner. | `@openclaw/qa-lab`<br />source checkout only | plugin |
|
||||
| [qa-matrix](/plugins/reference/qa-matrix) | Matrix QA transport runner and substrate. | `@openclaw/qa-matrix`<br />source checkout only | plugin |
|
||||
| [qianfan](/plugins/reference/qianfan) | Adds Qianfan model provider support to OpenClaw. | `@openclaw/qianfan-provider`<br />included in OpenClaw | providers: qianfan |
|
||||
| [qqbot](/plugins/reference/qqbot) | Adds the QQ Bot channel surface for sending and receiving OpenClaw messages. | `@openclaw/qqbot`<br />npm; ClawHub | channels: qqbot; contracts: tools; skills |
|
||||
| [qqbot](/plugins/reference/qqbot) | OpenClaw QQ Bot channel plugin for group and direct-message workflows. | `@openclaw/qqbot`<br />npm; ClawHub | channels: qqbot; contracts: tools; skills |
|
||||
| [qwen](/plugins/reference/qwen) | Adds Qwen, Qwen Cloud, Model Studio, DashScope model provider support to OpenClaw. | `@openclaw/qwen-provider`<br />included in OpenClaw | providers: qwen, qwencloud, modelstudio, dashscope; contracts: mediaUnderstandingProviders, videoGenerationProviders |
|
||||
| [runway](/plugins/reference/runway) | Adds video generation provider support. | `@openclaw/runway-provider`<br />included in OpenClaw | contracts: videoGenerationProviders |
|
||||
| [searxng](/plugins/reference/searxng) | Adds web search provider support. | `@openclaw/searxng-plugin`<br />included in OpenClaw | contracts: webSearchProviders |
|
||||
@@ -110,30 +110,30 @@ pnpm plugins:inventory:gen
|
||||
| [sglang](/plugins/reference/sglang) | Adds SGLang model provider support to OpenClaw. | `@openclaw/sglang-provider`<br />included in OpenClaw | providers: sglang |
|
||||
| [signal](/plugins/reference/signal) | Adds the Signal channel surface for sending and receiving OpenClaw messages. | `@openclaw/signal`<br />included in OpenClaw | channels: signal |
|
||||
| [skill-workshop](/plugins/reference/skill-workshop) | Captures repeatable workflows as workspace skills, with pending review, safe writes, and skill prompt refresh. | `@openclaw/skill-workshop`<br />included in OpenClaw | contracts: tools |
|
||||
| [slack](/plugins/reference/slack) | Adds the Slack channel surface for sending and receiving OpenClaw messages. | `@openclaw/slack`<br />npm; ClawHub | channels: slack |
|
||||
| [slack](/plugins/reference/slack) | OpenClaw Slack channel plugin for channels, DMs, commands, and app events. | `@openclaw/slack`<br />npm; ClawHub | channels: slack |
|
||||
| [stepfun](/plugins/reference/stepfun) | Adds StepFun, StepFun Plan model provider support to OpenClaw. | `@openclaw/stepfun-provider`<br />included in OpenClaw | providers: stepfun, stepfun-plan |
|
||||
| [synology-chat](/plugins/reference/synology-chat) | Adds the Synology Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/synology-chat`<br />npm; ClawHub | channels: synology-chat |
|
||||
| [synology-chat](/plugins/reference/synology-chat) | Synology Chat channel plugin for OpenClaw channels and direct messages. | `@openclaw/synology-chat`<br />npm; ClawHub | channels: synology-chat |
|
||||
| [synthetic](/plugins/reference/synthetic) | Adds Synthetic model provider support to OpenClaw. | `@openclaw/synthetic-provider`<br />included in OpenClaw | providers: synthetic |
|
||||
| [tavily](/plugins/reference/tavily) | Adds agent-callable tools. Adds web search provider support. | `@openclaw/tavily-plugin`<br />included in OpenClaw | contracts: tools, webSearchProviders; skills |
|
||||
| [telegram](/plugins/reference/telegram) | Adds the Telegram channel surface for sending and receiving OpenClaw messages. | `@openclaw/telegram`<br />included in OpenClaw | channels: telegram |
|
||||
| [tencent](/plugins/reference/tencent) | Adds Tencent TokenHub model provider support to OpenClaw. | `@openclaw/tencent-provider`<br />included in OpenClaw | providers: tencent-tokenhub |
|
||||
| [tlon](/plugins/reference/tlon) | Adds the Tlon channel surface for sending and receiving OpenClaw messages. | `@openclaw/tlon`<br />npm; ClawHub | channels: tlon; skills |
|
||||
| [tlon](/plugins/reference/tlon) | OpenClaw Tlon/Urbit channel plugin for chat workflows. | `@openclaw/tlon`<br />npm; ClawHub | channels: tlon; skills |
|
||||
| [together](/plugins/reference/together) | Adds Together model provider support to OpenClaw. | `@openclaw/together-provider`<br />included in OpenClaw | providers: together; contracts: videoGenerationProviders |
|
||||
| [tokenjuice](/plugins/reference/tokenjuice) | Compacts exec and bash tool results with tokenjuice reducers. | `@openclaw/tokenjuice`<br />included in OpenClaw | contracts: agentToolResultMiddleware |
|
||||
| [tts-local-cli](/plugins/reference/tts-local-cli) | Adds text-to-speech provider support. | `@openclaw/tts-local-cli`<br />included in OpenClaw | contracts: speechProviders |
|
||||
| [twitch](/plugins/reference/twitch) | Adds the Twitch channel surface for sending and receiving OpenClaw messages. | `@openclaw/twitch`<br />npm; ClawHub | channels: twitch |
|
||||
| [twitch](/plugins/reference/twitch) | OpenClaw Twitch channel plugin for chat and moderation workflows. | `@openclaw/twitch`<br />npm; ClawHub | channels: twitch |
|
||||
| [venice](/plugins/reference/venice) | Adds Venice model provider support to OpenClaw. | `@openclaw/venice-provider`<br />included in OpenClaw | providers: venice |
|
||||
| [vercel-ai-gateway](/plugins/reference/vercel-ai-gateway) | Adds Vercel AI Gateway model provider support to OpenClaw. | `@openclaw/vercel-ai-gateway-provider`<br />included in OpenClaw | providers: vercel-ai-gateway |
|
||||
| [vllm](/plugins/reference/vllm) | Adds vLLM model provider support to OpenClaw. | `@openclaw/vllm-provider`<br />included in OpenClaw | providers: vllm |
|
||||
| [voice-call](/plugins/reference/voice-call) | Adds agent-callable tools. | `@openclaw/voice-call`<br />npm; ClawHub | contracts: tools |
|
||||
| [voice-call](/plugins/reference/voice-call) | OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls. | `@openclaw/voice-call`<br />npm; ClawHub | contracts: tools |
|
||||
| [volcengine](/plugins/reference/volcengine) | Adds Volcengine, Volcengine Plan model provider support to OpenClaw. | `@openclaw/volcengine-provider`<br />included in OpenClaw | providers: volcengine, volcengine-plan; contracts: speechProviders |
|
||||
| [voyage](/plugins/reference/voyage) | Adds memory embedding provider support. | `@openclaw/voyage-provider`<br />included in OpenClaw | contracts: memoryEmbeddingProviders |
|
||||
| [vydra](/plugins/reference/vydra) | Adds Vydra model provider support to OpenClaw. | `@openclaw/vydra-provider`<br />included in OpenClaw | providers: vydra; contracts: imageGenerationProviders, speechProviders, videoGenerationProviders |
|
||||
| [web-readability](/plugins/reference/web-readability) | Extract readable article content from local HTML web fetch responses. | `@openclaw/web-readability-plugin`<br />included in OpenClaw | contracts: webContentExtractors |
|
||||
| [webhooks](/plugins/reference/webhooks) | Authenticated inbound webhooks that bind external automation to OpenClaw TaskFlows. | `@openclaw/webhooks`<br />included in OpenClaw | plugin |
|
||||
| [whatsapp](/plugins/reference/whatsapp) | Adds the WhatsApp channel surface for sending and receiving OpenClaw messages. | `@openclaw/whatsapp`<br />ClawHub: `clawhub:@openclaw/whatsapp`; npm | channels: whatsapp |
|
||||
| [whatsapp](/plugins/reference/whatsapp) | OpenClaw WhatsApp channel plugin for WhatsApp Web chats. | `@openclaw/whatsapp`<br />ClawHub: `clawhub:@openclaw/whatsapp`; npm | channels: whatsapp |
|
||||
| [xai](/plugins/reference/xai) | Adds xAI model provider support to OpenClaw. | `@openclaw/xai-plugin`<br />included in OpenClaw | providers: xai; contracts: imageGenerationProviders, mediaUnderstandingProviders, realtimeTranscriptionProviders, speechProviders, tools, videoGenerationProviders, webSearchProviders |
|
||||
| [xiaomi](/plugins/reference/xiaomi) | Adds Xiaomi model provider support to OpenClaw. | `@openclaw/xiaomi-provider`<br />included in OpenClaw | providers: xiaomi; contracts: speechProviders |
|
||||
| [zai](/plugins/reference/zai) | Adds Z.AI model provider support to OpenClaw. | `@openclaw/zai-provider`<br />included in OpenClaw | providers: zai; contracts: mediaUnderstandingProviders |
|
||||
| [zalo](/plugins/reference/zalo) | Adds the Zalo channel surface for sending and receiving OpenClaw messages. | `@openclaw/zalo`<br />npm; ClawHub | channels: zalo |
|
||||
| [zalouser](/plugins/reference/zalouser) | Adds the Zalo Personal channel surface for sending and receiving OpenClaw messages. | `@openclaw/zalouser`<br />npm; ClawHub | channels: zalouser; contracts: tools |
|
||||
| [zalo](/plugins/reference/zalo) | OpenClaw Zalo channel plugin for bot and webhook chats. | `@openclaw/zalo`<br />npm; ClawHub | channels: zalo |
|
||||
| [zalouser](/plugins/reference/zalouser) | OpenClaw Zalo Personal Account plugin via native zca-js integration. | `@openclaw/zalouser`<br />npm; ClawHub | channels: zalouser; contracts: tools |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Embedded ACP runtime backend with plugin-owned session and transport management."
|
||||
summary: "OpenClaw ACP runtime backend with plugin-owned session and transport management."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the acpx plugin
|
||||
title: "ACPx plugin"
|
||||
@@ -7,7 +7,7 @@ title: "ACPx plugin"
|
||||
|
||||
# ACPx plugin
|
||||
|
||||
Embedded ACP runtime backend with plugin-owned session and transport management.
|
||||
OpenClaw ACP runtime backend with plugin-owned session and transport management.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds Amazon Bedrock Mantle model provider support to OpenClaw."
|
||||
summary: "OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the amazon-bedrock-mantle plugin
|
||||
title: "Amazon Bedrock Mantle plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Amazon Bedrock Mantle plugin"
|
||||
|
||||
# Amazon Bedrock Mantle plugin
|
||||
|
||||
Adds Amazon Bedrock Mantle model provider support to OpenClaw.
|
||||
OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds Amazon Bedrock model provider support to OpenClaw."
|
||||
summary: "OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the amazon-bedrock plugin
|
||||
title: "Amazon Bedrock plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Amazon Bedrock plugin"
|
||||
|
||||
# Amazon Bedrock plugin
|
||||
|
||||
Adds Amazon Bedrock model provider support to OpenClaw.
|
||||
OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds Anthropic Vertex model provider support to OpenClaw."
|
||||
summary: "OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the anthropic-vertex plugin
|
||||
title: "Anthropic Vertex plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Anthropic Vertex plugin"
|
||||
|
||||
# Anthropic Vertex plugin
|
||||
|
||||
Adds Anthropic Vertex model provider support to OpenClaw.
|
||||
OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds web search provider support."
|
||||
summary: "OpenClaw Brave Search provider plugin for web search."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the brave plugin
|
||||
title: "Brave plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Brave plugin"
|
||||
|
||||
# Brave plugin
|
||||
|
||||
Adds web search provider support.
|
||||
OpenClaw Brave Search provider plugin for web search.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Codex app-server harness and Codex-managed GPT model catalog."
|
||||
summary: "OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the codex plugin
|
||||
title: "Codex plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Codex plugin"
|
||||
|
||||
# Codex plugin
|
||||
|
||||
Codex app-server harness and Codex-managed GPT model catalog.
|
||||
OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "OpenClaw diagnostics OpenTelemetry exporter."
|
||||
summary: "OpenClaw diagnostics OpenTelemetry exporter for metrics and traces."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the diagnostics-otel plugin
|
||||
title: "Diagnostics OpenTelemetry plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Diagnostics OpenTelemetry plugin"
|
||||
|
||||
# Diagnostics OpenTelemetry plugin
|
||||
|
||||
OpenClaw diagnostics OpenTelemetry exporter.
|
||||
OpenClaw diagnostics OpenTelemetry exporter for metrics and traces.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "OpenClaw diagnostics Prometheus exporter."
|
||||
summary: "OpenClaw diagnostics Prometheus exporter for runtime metrics."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the diagnostics-prometheus plugin
|
||||
title: "Diagnostics Prometheus plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Diagnostics Prometheus plugin"
|
||||
|
||||
# Diagnostics Prometheus plugin
|
||||
|
||||
OpenClaw diagnostics Prometheus exporter.
|
||||
OpenClaw diagnostics Prometheus exporter for runtime metrics.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Read-only diff viewer and file renderer for agents."
|
||||
summary: "OpenClaw read-only diff viewer plugin and file renderer for agents."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the diffs plugin
|
||||
title: "Diffs plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Diffs plugin"
|
||||
|
||||
# Diffs plugin
|
||||
|
||||
Read-only diff viewer and file renderer for agents.
|
||||
OpenClaw read-only diff viewer plugin and file renderer for agents.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Discord channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Discord channel plugin for channels, DMs, commands, and app events."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the discord plugin
|
||||
title: "Discord plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Discord plugin"
|
||||
|
||||
# Discord plugin
|
||||
|
||||
Adds the Discord channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Discord channel plugin for channels, DMs, commands, and app events.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Feishu channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng)."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the feishu plugin
|
||||
title: "Feishu plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Feishu plugin"
|
||||
|
||||
# Feishu plugin
|
||||
|
||||
Adds the Feishu channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng).
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Join Google Meet calls through Chrome or Twilio transports."
|
||||
summary: "OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the google-meet plugin
|
||||
title: "Google Meet plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Google Meet plugin"
|
||||
|
||||
# Google Meet plugin
|
||||
|
||||
Join Google Meet calls through Chrome or Twilio transports.
|
||||
OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Google Chat channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Google Chat channel plugin for spaces and direct messages."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the googlechat plugin
|
||||
title: "Google Chat plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Google Chat plugin"
|
||||
|
||||
# Google Chat plugin
|
||||
|
||||
Adds the Google Chat channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Google Chat channel plugin for spaces and direct messages.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the LINE channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw LINE channel plugin for LINE Bot API chats."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the line plugin
|
||||
title: "LINE plugin"
|
||||
@@ -7,7 +7,7 @@ title: "LINE plugin"
|
||||
|
||||
# LINE plugin
|
||||
|
||||
Adds the LINE channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw LINE channel plugin for LINE Bot API chats.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Typed workflow tool with resumable approvals."
|
||||
summary: "Lobster workflow tool plugin for typed pipelines and resumable approvals."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the lobster plugin
|
||||
title: "Lobster plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Lobster plugin"
|
||||
|
||||
# Lobster plugin
|
||||
|
||||
Typed workflow tool with resumable approvals.
|
||||
Lobster workflow tool plugin for typed pipelines and resumable approvals.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Matrix channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Matrix channel plugin for rooms and direct messages."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the matrix plugin
|
||||
title: "Matrix plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Matrix plugin"
|
||||
|
||||
# Matrix plugin
|
||||
|
||||
Adds the Matrix channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Matrix channel plugin for rooms and direct messages.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds agent-callable tools."
|
||||
summary: "OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the memory-lancedb plugin
|
||||
title: "Memory Lancedb plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Memory Lancedb plugin"
|
||||
|
||||
# Memory Lancedb plugin
|
||||
|
||||
Adds agent-callable tools.
|
||||
OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Microsoft Teams channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Microsoft Teams channel plugin for bot conversations."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the msteams plugin
|
||||
title: "Microsoft Teams plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Microsoft Teams plugin"
|
||||
|
||||
# Microsoft Teams plugin
|
||||
|
||||
Adds the Microsoft Teams channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Microsoft Teams channel plugin for bot conversations.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Nextcloud Talk channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Nextcloud Talk channel plugin for conversations."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the nextcloud-talk plugin
|
||||
title: "Nextcloud Talk plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Nextcloud Talk plugin"
|
||||
|
||||
# Nextcloud Talk plugin
|
||||
|
||||
Adds the Nextcloud Talk channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Nextcloud Talk channel plugin for conversations.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Nostr channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the nostr plugin
|
||||
title: "Nostr plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Nostr plugin"
|
||||
|
||||
# Nostr plugin
|
||||
|
||||
Adds the Nostr channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Sandbox backend powered by the NVIDIA OpenShell CLI with mirrored local workspaces and SSH-based command execution."
|
||||
summary: "OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the openshell plugin
|
||||
title: "Openshell plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Openshell plugin"
|
||||
|
||||
# Openshell plugin
|
||||
|
||||
Sandbox backend powered by the NVIDIA OpenShell CLI with mirrored local workspaces and SSH-based command execution.
|
||||
OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds PixVerse video generation provider support to OpenClaw."
|
||||
summary: "OpenClaw PixVerse video generation provider plugin."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the pixverse plugin
|
||||
title: "PixVerse plugin"
|
||||
@@ -7,7 +7,7 @@ title: "PixVerse plugin"
|
||||
|
||||
# PixVerse plugin
|
||||
|
||||
Adds PixVerse video generation provider support to OpenClaw.
|
||||
OpenClaw PixVerse video generation provider plugin.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the QQ Bot channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw QQ Bot channel plugin for group and direct-message workflows."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the qqbot plugin
|
||||
title: "QQ Bot plugin"
|
||||
@@ -7,7 +7,7 @@ title: "QQ Bot plugin"
|
||||
|
||||
# QQ Bot plugin
|
||||
|
||||
Adds the QQ Bot channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw QQ Bot channel plugin for group and direct-message workflows.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Slack channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Slack channel plugin for channels, DMs, commands, and app events."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the slack plugin
|
||||
title: "Slack plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Slack plugin"
|
||||
|
||||
# Slack plugin
|
||||
|
||||
Adds the Slack channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Slack channel plugin for channels, DMs, commands, and app events.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Synology Chat channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "Synology Chat channel plugin for OpenClaw channels and direct messages."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the synology-chat plugin
|
||||
title: "Synology Chat plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Synology Chat plugin"
|
||||
|
||||
# Synology Chat plugin
|
||||
|
||||
Adds the Synology Chat channel surface for sending and receiving OpenClaw messages.
|
||||
Synology Chat channel plugin for OpenClaw channels and direct messages.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Tlon channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Tlon/Urbit channel plugin for chat workflows."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the tlon plugin
|
||||
title: "Tlon plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Tlon plugin"
|
||||
|
||||
# Tlon plugin
|
||||
|
||||
Adds the Tlon channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Tlon/Urbit channel plugin for chat workflows.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Twitch channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Twitch channel plugin for chat and moderation workflows."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the twitch plugin
|
||||
title: "Twitch plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Twitch plugin"
|
||||
|
||||
# Twitch plugin
|
||||
|
||||
Adds the Twitch channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Twitch channel plugin for chat and moderation workflows.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds agent-callable tools."
|
||||
summary: "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the voice-call plugin
|
||||
title: "Voice Call plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Voice Call plugin"
|
||||
|
||||
# Voice Call plugin
|
||||
|
||||
Adds agent-callable tools.
|
||||
OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the WhatsApp channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw WhatsApp channel plugin for WhatsApp Web chats."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the whatsapp plugin
|
||||
title: "WhatsApp plugin"
|
||||
@@ -7,7 +7,7 @@ title: "WhatsApp plugin"
|
||||
|
||||
# WhatsApp plugin
|
||||
|
||||
Adds the WhatsApp channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw WhatsApp channel plugin for WhatsApp Web chats.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Zalo channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Zalo channel plugin for bot and webhook chats."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the zalo plugin
|
||||
title: "Zalo plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Zalo plugin"
|
||||
|
||||
# Zalo plugin
|
||||
|
||||
Adds the Zalo channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Zalo channel plugin for bot and webhook chats.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Adds the Zalo Personal channel surface for sending and receiving OpenClaw messages."
|
||||
summary: "OpenClaw Zalo Personal Account plugin via native zca-js integration."
|
||||
read_when:
|
||||
- You are installing, configuring, or auditing the zalouser plugin
|
||||
title: "Zalo Personal plugin"
|
||||
@@ -7,7 +7,7 @@ title: "Zalo Personal plugin"
|
||||
|
||||
# Zalo Personal plugin
|
||||
|
||||
Adds the Zalo Personal channel surface for sending and receiving OpenClaw messages.
|
||||
OpenClaw Zalo Personal Account plugin via native zca-js integration.
|
||||
|
||||
## Distribution
|
||||
|
||||
|
||||
@@ -524,7 +524,7 @@ two-party event loops that do not go through the shared inbound reply runner.
|
||||
await store.clear();
|
||||
```
|
||||
|
||||
Keyed stores survive restarts and are isolated by the runtime-bound plugin id. Use `registerIfAbsent(...)` for atomic dedupe claims: it returns `true` when the key was missing or expired and registered, or `false` when a live value already exists without overwriting its value, creation time, or TTL. Limits: `maxEntries` per namespace, 1,000 live rows per plugin, JSON values under 64KB, and optional TTL expiry.
|
||||
Keyed stores survive restarts and are isolated by the runtime-bound plugin id. Use `registerIfAbsent(...)` for atomic dedupe claims: it returns `true` when the key was missing or expired and registered, or `false` when a live value already exists without overwriting its value, creation time, or TTL. Limits: `maxEntries` per namespace, 6,000 live rows per plugin, JSON values under 64KB, and optional TTL expiry. When a write would exceed the plugin row cap, the runtime may evict the oldest live rows from the namespace being written; sibling namespaces are not evicted for that write, and the write still fails if the namespace cannot free enough rows.
|
||||
|
||||
<Warning>
|
||||
Bundled plugins only in this release.
|
||||
|
||||
@@ -19,7 +19,7 @@ OpenClaw assembles its own system prompt on every run. It includes:
|
||||
with optional per-agent override at
|
||||
`agents.list[].skillsLimits.maxSkillsPromptChars`.
|
||||
- Self-update instructions
|
||||
- Workspace + bootstrap files (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md` when new, plus `MEMORY.md` when present). Lowercase root `memory.md` is not injected; it is legacy repair input for `openclaw doctor --fix` when paired with `MEMORY.md`. Large files are truncated by `agents.defaults.bootstrapMaxChars` (default: 12000), and total bootstrap injection is capped by `agents.defaults.bootstrapTotalMaxChars` (default: 60000). `memory/*.md` daily files are not part of the normal bootstrap prompt; they remain on-demand via memory tools on ordinary turns, but reset/startup model runs can prepend a one-shot startup-context block with recent daily memory for that first turn. Bare chat `/new` and `/reset` commands are acknowledged without invoking the model. The startup prelude is controlled by `agents.defaults.startupContext`. Post-compaction AGENTS.md excerpts are separate and require explicit `agents.defaults.compaction.postCompactionSections` opt-in.
|
||||
- Workspace + bootstrap files (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md` when new, plus `MEMORY.md` when present). Native Codex turns do not paste raw `MEMORY.md` from the configured agent workspace when memory tools are available for that workspace; they include a small memory pointer and use memory tools on demand. If tools are disabled, memory search is unavailable, or the active workspace differs from the agent memory workspace, `MEMORY.md` uses the normal bounded turn-context path. Lowercase root `memory.md` is not injected; it is legacy repair input for `openclaw doctor --fix` when paired with `MEMORY.md`. Large injected files are truncated by `agents.defaults.bootstrapMaxChars` (default: 12000), and total bootstrap injection is capped by `agents.defaults.bootstrapTotalMaxChars` (default: 60000). `memory/*.md` daily files are not part of the normal bootstrap prompt; they remain on-demand via memory tools on ordinary turns, but reset/startup model runs can prepend a one-shot startup-context block with recent daily memory for that first turn. Bare chat `/new` and `/reset` commands are acknowledged without invoking the model. The startup prelude is controlled by `agents.defaults.startupContext`. Post-compaction AGENTS.md excerpts are separate and require explicit `agents.defaults.compaction.postCompactionSections` opt-in.
|
||||
- Time (UTC + user timezone)
|
||||
- Reply tags + heartbeat behavior
|
||||
- Runtime metadata (host/OS/model/thinking)
|
||||
|
||||
4
extensions/acpx/npm-shrinkwrap.json
generated
4
extensions/acpx/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/claude-agent-acp": "0.37.0",
|
||||
"@zed-industries/codex-acp": "0.15.0",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"enabledByDefault": true,
|
||||
"name": "ACPX Runtime",
|
||||
"description": "Embedded ACP runtime backend with plugin-owned session and transport management.",
|
||||
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
|
||||
"skills": ["./skills"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.5.26",
|
||||
"description": "OpenClaw ACP runtime backend",
|
||||
"version": "2026.5.27",
|
||||
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/openclaw"
|
||||
@@ -26,10 +26,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.26"
|
||||
"pluginApi": ">=2026.5.27"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.26",
|
||||
"openclawVersion": "2026.5.27",
|
||||
"staticAssets": [
|
||||
{
|
||||
"source": "./src/runtime-internals/mcp-proxy.mjs",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/admin-http-rpc",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw admin HTTP RPC endpoint",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/alibaba-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Alibaba Model Studio video provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "0.98.0",
|
||||
"@aws/bedrock-token-generator": "1.1.0",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"id": "amazon-bedrock-mantle",
|
||||
"name": "Amazon Bedrock Mantle",
|
||||
"description": "OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing.",
|
||||
"activation": {
|
||||
"onStartup": false
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"version": "2026.5.26",
|
||||
"description": "OpenClaw Amazon Bedrock Mantle (OpenAI-compatible) provider plugin",
|
||||
"version": "2026.5.27",
|
||||
"description": "OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/openclaw"
|
||||
@@ -25,10 +25,10 @@
|
||||
"minHostVersion": ">=2026.5.12-beta.1"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.26"
|
||||
"pluginApi": ">=2026.5.27"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.26",
|
||||
"openclawVersion": "2026.5.27",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
4
extensions/amazon-bedrock/npm-shrinkwrap.json
generated
4
extensions/amazon-bedrock/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-bedrock": "3.1053.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.1053.0",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"id": "amazon-bedrock",
|
||||
"name": "Amazon Bedrock",
|
||||
"description": "OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support.",
|
||||
"activation": {
|
||||
"onStartup": false
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.5.26",
|
||||
"description": "OpenClaw Amazon Bedrock provider plugin",
|
||||
"version": "2026.5.27",
|
||||
"description": "OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/openclaw"
|
||||
@@ -27,10 +27,10 @@
|
||||
"minHostVersion": ">=2026.5.12-beta.1"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.26"
|
||||
"pluginApi": ">=2026.5.27"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.26",
|
||||
"openclawVersion": "2026.5.27",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
4
extensions/anthropic-vertex/npm-shrinkwrap.json
generated
4
extensions/anthropic-vertex/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/vertex-sdk": "0.16.1",
|
||||
"@earendil-works/pi-agent-core": "0.75.5",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"id": "anthropic-vertex",
|
||||
"name": "Anthropic Vertex",
|
||||
"description": "OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI.",
|
||||
"activation": {
|
||||
"onStartup": false
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.5.26",
|
||||
"description": "OpenClaw Anthropic Vertex provider plugin",
|
||||
"version": "2026.5.27",
|
||||
"description": "OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/openclaw"
|
||||
@@ -25,10 +25,10 @@
|
||||
"minHostVersion": ">=2026.5.12-beta.1"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.26"
|
||||
"pluginApi": ">=2026.5.27"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.26",
|
||||
"openclawVersion": "2026.5.27",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/arcee-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Arcee provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/azure-speech",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Azure Speech plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/bonjour",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"description": "OpenClaw Bonjour/mDNS gateway discovery",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
4
extensions/brave/npm-shrinkwrap.json
generated
4
extensions/brave/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/brave-plugin",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/brave-plugin",
|
||||
"version": "2026.5.26"
|
||||
"version": "2026.5.27"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"id": "brave",
|
||||
"name": "Brave",
|
||||
"description": "OpenClaw Brave Search provider plugin for web search.",
|
||||
"activation": {
|
||||
"onStartup": false
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openclaw/brave-plugin",
|
||||
"version": "2026.5.26",
|
||||
"description": "OpenClaw Brave plugin",
|
||||
"version": "2026.5.27",
|
||||
"description": "OpenClaw Brave Search provider plugin for web search.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/openclaw"
|
||||
@@ -21,10 +21,10 @@
|
||||
"allowInvalidConfigRecovery": true
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.26"
|
||||
"pluginApi": ">=2026.5.27"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.26"
|
||||
"openclawVersion": "2026.5.27"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
51
extensions/brave/src/brave-web-search-provider.merge.test.ts
Normal file
51
extensions/brave/src/brave-web-search-provider.merge.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createBraveWebSearchProvider } from "./brave-web-search-provider.js";
|
||||
|
||||
const runtimeMock = vi.hoisted(() => {
|
||||
const searchConfigs: Array<Record<string, unknown> | undefined> = [];
|
||||
return {
|
||||
searchConfigs,
|
||||
executeBraveSearch: vi.fn(async (_args: unknown, searchConfig?: Record<string, unknown>) => {
|
||||
searchConfigs.push(searchConfig);
|
||||
return { results: [] };
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./brave-web-search-provider.runtime.js", () => ({
|
||||
executeBraveSearch: runtimeMock.executeBraveSearch,
|
||||
}));
|
||||
|
||||
describe("brave web search config merge", () => {
|
||||
it("keeps plugin webSearch runtime-only after merging it for the tool", async () => {
|
||||
const provider = createBraveWebSearchProvider();
|
||||
const tool = provider.createTool({
|
||||
config: {
|
||||
plugins: {
|
||||
entries: {
|
||||
brave: {
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: "brave-test-key",
|
||||
mode: "llm-context",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
searchConfig: { provider: "brave" },
|
||||
});
|
||||
|
||||
await tool?.execute({ query: "OpenClaw docs" });
|
||||
|
||||
const [searchConfig] = runtimeMock.searchConfigs;
|
||||
expect(searchConfig?.brave).toEqual({
|
||||
apiKey: "brave-test-key",
|
||||
mode: "llm-context",
|
||||
});
|
||||
expect(searchConfig?.apiKey).toBe("brave-test-key");
|
||||
expect(Object.keys(searchConfig ?? {})).toEqual(["provider", "apiKey"]);
|
||||
expect(Object.getOwnPropertyDescriptor(searchConfig ?? {}, "brave")?.enumerable).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,15 @@
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
|
||||
import type {
|
||||
SearchConfigRecord,
|
||||
WebSearchProviderPlugin,
|
||||
WebSearchProviderToolDefinition,
|
||||
} from "openclaw/plugin-sdk/provider-web-search";
|
||||
import { createWebSearchProviderContractFields } from "openclaw/plugin-sdk/provider-web-search-config-contract";
|
||||
import {
|
||||
createWebSearchProviderContractFields,
|
||||
mergeScopedSearchConfig,
|
||||
resolveProviderWebSearchPluginConfig,
|
||||
} from "openclaw/plugin-sdk/provider-web-search-config-contract";
|
||||
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
|
||||
const BRAVE_CREDENTIAL_PATH = "plugins.entries.brave.config.webSearch.apiKey";
|
||||
@@ -62,22 +67,8 @@ const BraveSearchSchema = {
|
||||
},
|
||||
} satisfies Record<string, unknown>;
|
||||
|
||||
function resolveProviderWebSearchPluginConfig(
|
||||
config: unknown,
|
||||
pluginId: string,
|
||||
): Record<string, unknown> | undefined {
|
||||
if (!isRecord(config)) {
|
||||
return undefined;
|
||||
}
|
||||
const plugins = isRecord(config.plugins) ? config.plugins : undefined;
|
||||
const entries = isRecord(plugins?.entries) ? plugins.entries : undefined;
|
||||
const entry = isRecord(entries?.[pluginId]) ? entries[pluginId] : undefined;
|
||||
const pluginConfig = isRecord(entry?.config) ? entry.config : undefined;
|
||||
return isRecord(pluginConfig?.webSearch) ? pluginConfig.webSearch : undefined;
|
||||
}
|
||||
|
||||
function resolveLegacyTopLevelBraveCredential(
|
||||
config: unknown,
|
||||
config: OpenClawConfig | undefined,
|
||||
): { path: string; value: unknown } | undefined {
|
||||
if (!isRecord(config)) {
|
||||
return undefined;
|
||||
@@ -91,39 +82,13 @@ function resolveLegacyTopLevelBraveCredential(
|
||||
return { path: "tools.web.search.apiKey", value: search.apiKey };
|
||||
}
|
||||
|
||||
function resolveConfiguredBraveCredential(config: unknown): unknown {
|
||||
function resolveConfiguredBraveCredential(config: OpenClawConfig | undefined): unknown {
|
||||
return (
|
||||
resolveProviderWebSearchPluginConfig(config, "brave")?.apiKey ??
|
||||
resolveLegacyTopLevelBraveCredential(config)?.value
|
||||
);
|
||||
}
|
||||
|
||||
function mergeScopedSearchConfig(
|
||||
searchConfig: Record<string, unknown> | undefined,
|
||||
key: string,
|
||||
pluginConfig: Record<string, unknown> | undefined,
|
||||
options?: { mirrorApiKeyToTopLevel?: boolean },
|
||||
): Record<string, unknown> | undefined {
|
||||
if (!pluginConfig) {
|
||||
return searchConfig;
|
||||
}
|
||||
|
||||
const currentScoped = isRecord(searchConfig?.[key]) ? searchConfig?.[key] : {};
|
||||
const next: Record<string, unknown> = {
|
||||
...searchConfig,
|
||||
[key]: {
|
||||
...currentScoped,
|
||||
...pluginConfig,
|
||||
},
|
||||
};
|
||||
|
||||
if (options?.mirrorApiKeyToTopLevel && pluginConfig.apiKey !== undefined) {
|
||||
next.apiKey = pluginConfig.apiKey;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
function resolveBraveMode(searchConfig?: Record<string, unknown>): "web" | "llm-context" {
|
||||
const brave = isRecord(searchConfig?.brave) ? searchConfig.brave : undefined;
|
||||
return brave?.mode === "llm-context" ? "llm-context" : "web";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/browser-plugin",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw browser tool plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/byteplus-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw BytePlus provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/canvas-plugin",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Canvas plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/cerebras-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Cerebras provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/chutes-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Chutes.ai provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/clickclack",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw ClickClack channel plugin",
|
||||
"type": "module",
|
||||
@@ -18,7 +18,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.26"
|
||||
"openclaw": ">=2026.5.27"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Cloudflare AI Gateway provider plugin",
|
||||
"type": "module",
|
||||
|
||||
4
extensions/codex/npm-shrinkwrap.json
generated
4
extensions/codex/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/codex",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/codex",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"dependencies": {
|
||||
"@earendil-works/pi-coding-agent": "0.75.5",
|
||||
"@openai/codex": "0.134.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "codex",
|
||||
"name": "Codex",
|
||||
"description": "Codex app-server harness and Codex-managed GPT model catalog.",
|
||||
"description": "OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog.",
|
||||
"providers": ["codex"],
|
||||
"contracts": {
|
||||
"mediaUnderstandingProviders": ["codex"],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@openclaw/codex",
|
||||
"version": "2026.5.26",
|
||||
"description": "OpenClaw Codex harness and model provider plugin",
|
||||
"version": "2026.5.27",
|
||||
"description": "OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/openclaw"
|
||||
@@ -27,10 +27,10 @@
|
||||
"minHostVersion": ">=2026.5.1-beta.1"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.26"
|
||||
"pluginApi": ">=2026.5.27"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.26"
|
||||
"openclawVersion": "2026.5.27"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -5,6 +5,11 @@ import {
|
||||
embeddedAgentLog,
|
||||
wrapToolWithBeforeToolCallHook,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import {
|
||||
onInternalDiagnosticEvent,
|
||||
waitForDiagnosticEventsDrained,
|
||||
type DiagnosticEventPayload,
|
||||
} from "openclaw/plugin-sdk/diagnostic-runtime";
|
||||
import {
|
||||
initializeGlobalHookRunner,
|
||||
resetGlobalHookRunner,
|
||||
@@ -293,18 +298,33 @@ describe("createCodexDynamicToolBridge", () => {
|
||||
|
||||
it("quarantines dynamic tools with unsupported input schemas", async () => {
|
||||
const warn = vi.spyOn(embeddedAgentLog, "warn").mockImplementation(() => undefined);
|
||||
const diagnosticEvents: DiagnosticEventPayload[] = [];
|
||||
const unsubscribeDiagnostics = onInternalDiagnosticEvent((event) =>
|
||||
diagnosticEvents.push(event),
|
||||
);
|
||||
const badExecute = vi.fn();
|
||||
const bridge = createCodexDynamicToolBridge({
|
||||
tools: [
|
||||
createTool({ name: "message" }),
|
||||
createTool({
|
||||
name: "dofbot_move_angles",
|
||||
parameters: { type: "array", items: { type: "number" } },
|
||||
execute: badExecute,
|
||||
}),
|
||||
],
|
||||
signal: new AbortController().signal,
|
||||
});
|
||||
let bridge!: ReturnType<typeof createCodexDynamicToolBridge>;
|
||||
try {
|
||||
bridge = createCodexDynamicToolBridge({
|
||||
tools: [
|
||||
createTool({ name: "message" }),
|
||||
createTool({
|
||||
name: "dofbot_move_angles",
|
||||
parameters: { type: "array", items: { type: "number" } },
|
||||
execute: badExecute,
|
||||
}),
|
||||
],
|
||||
signal: new AbortController().signal,
|
||||
hookContext: {
|
||||
runId: "run-1",
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:session-1",
|
||||
},
|
||||
});
|
||||
await waitForDiagnosticEventsDrained();
|
||||
} finally {
|
||||
unsubscribeDiagnostics();
|
||||
}
|
||||
|
||||
expect(bridge.availableSpecs.map((tool) => tool.name)).toEqual(["message"]);
|
||||
expect(bridge.specs.map((tool) => tool.name)).toEqual(["message"]);
|
||||
@@ -325,6 +345,21 @@ describe("createCodexDynamicToolBridge", () => {
|
||||
],
|
||||
}),
|
||||
);
|
||||
const blockedEvents = diagnosticEvents.filter(
|
||||
(event): event is Extract<DiagnosticEventPayload, { type: "tool.execution.blocked" }> =>
|
||||
event.type === "tool.execution.blocked",
|
||||
);
|
||||
expect(blockedEvents).toContainEqual(
|
||||
expect.objectContaining({
|
||||
type: "tool.execution.blocked",
|
||||
runId: "run-1",
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:session-1",
|
||||
toolName: "dofbot_move_angles",
|
||||
deniedReason: "unsupported_tool_schema",
|
||||
reason: 'dofbot_move_angles.inputSchema.type must be "object"',
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await bridge.handleToolCall({
|
||||
threadId: "thread-1",
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
type MessagingToolSourceReplyPayload,
|
||||
wrapToolWithBeforeToolCallHook,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
|
||||
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
||||
import {
|
||||
asOptionalRecord as readRecord,
|
||||
@@ -118,6 +119,7 @@ export function createCodexDynamicToolBridge(params: {
|
||||
...registeredProjection.quarantinedTools,
|
||||
]);
|
||||
warnQuarantinedDynamicTools(quarantinedTools);
|
||||
emitQuarantinedDynamicToolDiagnostics(quarantinedTools, params.hookContext);
|
||||
const telemetry: CodexDynamicToolBridge["telemetry"] = {
|
||||
didSendViaMessagingTool: false,
|
||||
messagingToolSentTexts: [],
|
||||
@@ -337,6 +339,23 @@ function warnQuarantinedDynamicTools(tools: readonly CodexDynamicToolSchemaQuara
|
||||
);
|
||||
}
|
||||
|
||||
function emitQuarantinedDynamicToolDiagnostics(
|
||||
tools: readonly CodexDynamicToolSchemaQuarantine[],
|
||||
ctx: CodexDynamicToolHookContext | undefined,
|
||||
): void {
|
||||
for (const tool of tools) {
|
||||
emitTrustedDiagnosticEvent({
|
||||
type: "tool.execution.blocked",
|
||||
runId: ctx?.runId,
|
||||
sessionId: ctx?.sessionId,
|
||||
sessionKey: ctx?.sessionKey,
|
||||
toolName: tool.tool,
|
||||
deniedReason: "unsupported_tool_schema",
|
||||
reason: tool.violations.join(", "),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function dedupeQuarantinedDynamicTools(
|
||||
tools: readonly CodexDynamicToolSchemaQuarantine[],
|
||||
): CodexDynamicToolSchemaQuarantine[] {
|
||||
|
||||
@@ -47,6 +47,7 @@ import * as approvalBridge from "./approval-bridge.js";
|
||||
import * as authBridge from "./auth-bridge.js";
|
||||
import { resolveCodexAppServerEnvApiKeyCacheKey } from "./auth-bridge.js";
|
||||
import type { CodexAppServerClientFactory } from "./client-factory.js";
|
||||
import { CodexAppServerRpcError } from "./client.js";
|
||||
import {
|
||||
readCodexPluginConfig,
|
||||
resolveCodexAppServerRuntimeOptions,
|
||||
@@ -91,6 +92,7 @@ import {
|
||||
} from "./sandbox-exec-server.js";
|
||||
import { createSandboxContext } from "./sandbox-exec-server.test-helpers.js";
|
||||
import { readCodexAppServerBinding, writeCodexAppServerBinding } from "./session-binding.js";
|
||||
import * as sharedClientModule from "./shared-client.js";
|
||||
import { createCodexTestModel } from "./test-support.js";
|
||||
import {
|
||||
buildContextEngineBinding,
|
||||
@@ -581,6 +583,19 @@ function createNamedDynamicTool(
|
||||
};
|
||||
}
|
||||
|
||||
function setAgentWorkspaceForTest(params: EmbeddedRunAttemptParams, workspaceDir: string): void {
|
||||
params.config = {
|
||||
...params.config,
|
||||
agents: {
|
||||
...params.config?.agents,
|
||||
defaults: {
|
||||
...params.config?.agents?.defaults,
|
||||
workspace: workspaceDir,
|
||||
},
|
||||
},
|
||||
} as EmbeddedRunAttemptParams["config"];
|
||||
}
|
||||
|
||||
async function buildDynamicToolsForTest(
|
||||
params: EmbeddedRunAttemptParams,
|
||||
workspaceDir: string,
|
||||
@@ -842,6 +857,24 @@ type AppServerRequestHandler = (request: {
|
||||
}) => Promise<unknown>;
|
||||
|
||||
function extractRelayIdFromThreadRequest(params: unknown): string {
|
||||
const command = extractNativeHookRelayCommandFromThreadRequest(params);
|
||||
const match = command.match(/--relay-id ([^ ]+)/);
|
||||
if (!match?.[1]) {
|
||||
throw new Error(`relay id missing from command: ${command}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function extractGenerationFromThreadRequest(params: unknown): string {
|
||||
const command = extractNativeHookRelayCommandFromThreadRequest(params);
|
||||
const match = command.match(/--generation ([^ ]+)/);
|
||||
if (!match?.[1]) {
|
||||
throw new Error(`relay generation missing from command: ${command}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function extractNativeHookRelayCommandFromThreadRequest(params: unknown): string {
|
||||
const config = (params as { config?: Record<string, unknown> }).config;
|
||||
let command: string | undefined;
|
||||
for (const key of [
|
||||
@@ -864,11 +897,10 @@ function extractRelayIdFromThreadRequest(params: unknown): string {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const match = command?.match(/--relay-id ([^ ]+)/);
|
||||
if (!match?.[1]) {
|
||||
throw new Error(`relay id missing from command: ${command}`);
|
||||
if (!command) {
|
||||
throw new Error("native hook relay command missing from thread request");
|
||||
}
|
||||
return match[1];
|
||||
return command;
|
||||
}
|
||||
|
||||
describe("runCodexAppServerAttempt", () => {
|
||||
@@ -6571,7 +6603,11 @@ describe("runCodexAppServerAttempt", () => {
|
||||
expect(hookContext.sessionId).toBe("session-1");
|
||||
const threadStart = harness.requests.find((request) => request.method === "thread/start");
|
||||
const threadStartParams = threadStart?.params as { developerInstructions?: string } | undefined;
|
||||
expect(threadStartParams?.developerInstructions).toContain("pre system\n\ncustom codex system");
|
||||
const wrappedPluginSystemContext = (text: string) =>
|
||||
`---\n\nOpenClaw plugin-injected system context. This block is not workspace file content.\n\n${text}\n\n---`;
|
||||
expect(threadStartParams?.developerInstructions).toContain(
|
||||
`${wrappedPluginSystemContext("pre system")}\n\ncustom codex system\n\n${wrappedPluginSystemContext("post system")}`,
|
||||
);
|
||||
const turnStart = harness.requests.find((request) => request.method === "turn/start");
|
||||
const turnStartParams = turnStart?.params as
|
||||
| { input?: Array<{ text?: string; text_elements?: unknown[]; type?: string }> }
|
||||
@@ -6707,7 +6743,7 @@ describe("runCodexAppServerAttempt", () => {
|
||||
expect(secondInputText).toContain("continue from there");
|
||||
});
|
||||
|
||||
it("passes stable workspace files as Codex developer instructions and keeps MEMORY.md as turn context", async () => {
|
||||
it("passes stable workspace files as Codex developer instructions and routes MEMORY.md through tools", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const agentsGuidance = "Follow AGENTS guidance.";
|
||||
@@ -6725,9 +6761,16 @@ describe("runCodexAppServerAttempt", () => {
|
||||
await fs.writeFile(path.join(workspaceDir, "USER.md"), userProfile);
|
||||
await fs.writeFile(path.join(workspaceDir, "HEARTBEAT.md"), heartbeatChecklist);
|
||||
await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), memorySummary);
|
||||
testing.setOpenClawCodingToolsFactoryForTests(() => [
|
||||
createRuntimeDynamicTool("memory_search"),
|
||||
createRuntimeDynamicTool("memory_get"),
|
||||
]);
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.disableTools = false;
|
||||
setAgentWorkspaceForTest(params, workspaceDir);
|
||||
|
||||
const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir));
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
@@ -6780,8 +6823,12 @@ describe("runCodexAppServerAttempt", () => {
|
||||
expect(inputText).not.toContain(toolGuidance);
|
||||
expect(inputText).not.toContain(userProfile);
|
||||
expect(inputText).not.toContain(heartbeatChecklist);
|
||||
expect(inputText).toContain(memorySummary);
|
||||
expect(inputText).toContain("Codex loads AGENTS.md natively");
|
||||
expect(inputText).not.toContain(memorySummary);
|
||||
expect(inputText).toContain("OpenClaw Workspace Memory");
|
||||
expect(inputText).toContain("MEMORY.md exists in the active agent workspace");
|
||||
expect(inputText).toContain("memory_search");
|
||||
expect(inputText).toContain("memory_get");
|
||||
expect(inputText).not.toContain("Codex loads AGENTS.md natively");
|
||||
expect(inputText).not.toContain(agentsGuidance);
|
||||
expect(inputText).toContain("Current user request:\nhello");
|
||||
expect(result.systemPromptReport?.systemPrompt.chars).toBe(
|
||||
@@ -6814,7 +6861,7 @@ describe("runCodexAppServerAttempt", () => {
|
||||
});
|
||||
expect(fileStats.get("MEMORY.md")).toMatchObject({
|
||||
rawChars: memorySummary.length,
|
||||
injectedChars: memorySummary.length,
|
||||
injectedChars: 0,
|
||||
truncated: false,
|
||||
});
|
||||
expect(fileStats.get("HEARTBEAT.md")).toMatchObject({
|
||||
@@ -6829,6 +6876,284 @@ describe("runCodexAppServerAttempt", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("injects bounded MEMORY.md when memory tools are unavailable", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const memorySummary = "Memory summary goes here.";
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), memorySummary);
|
||||
const harness = createStartedThreadHarness();
|
||||
|
||||
const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir));
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
const result = await run;
|
||||
|
||||
const turnStart = harness.requests.find((request) => request.method === "turn/start");
|
||||
const turnStartParams = turnStart?.params as {
|
||||
input?: Array<{ text?: string }>;
|
||||
};
|
||||
const inputText = turnStartParams.input?.[0]?.text ?? "";
|
||||
expect(inputText).not.toContain("OpenClaw Workspace Memory");
|
||||
expect(inputText).not.toContain("memory_search");
|
||||
expect(inputText).toContain(memorySummary);
|
||||
|
||||
const fileStats = new Map(
|
||||
result.systemPromptReport?.injectedWorkspaceFiles.map((file) => [file.name, file]) ?? [],
|
||||
);
|
||||
expect(fileStats.get("MEMORY.md")).toMatchObject({
|
||||
rawChars: memorySummary.length,
|
||||
injectedChars: memorySummary.length,
|
||||
truncated: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("routes MEMORY.md through memory_get when search is unavailable", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const memorySummary = "Memory summary goes here.";
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), memorySummary);
|
||||
testing.setOpenClawCodingToolsFactoryForTests(() => [createRuntimeDynamicTool("memory_get")]);
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.disableTools = false;
|
||||
setAgentWorkspaceForTest(params, workspaceDir);
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
const result = await run;
|
||||
|
||||
const turnStart = harness.requests.find((request) => request.method === "turn/start");
|
||||
const turnStartParams = turnStart?.params as {
|
||||
input?: Array<{ text?: string }>;
|
||||
};
|
||||
const inputText = turnStartParams.input?.[0]?.text ?? "";
|
||||
expect(inputText).toContain("OpenClaw Workspace Memory");
|
||||
expect(inputText).toContain("memory_get");
|
||||
expect(inputText).not.toContain("memory_search");
|
||||
expect(inputText).not.toContain(memorySummary);
|
||||
|
||||
const fileStats = new Map(
|
||||
result.systemPromptReport?.injectedWorkspaceFiles.map((file) => [file.name, file]) ?? [],
|
||||
);
|
||||
expect(fileStats.get("MEMORY.md")).toMatchObject({
|
||||
rawChars: memorySummary.length,
|
||||
injectedChars: 0,
|
||||
truncated: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("reports MEMORY.md as truncated when no-tool fallback exceeds the bootstrap budget", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const soulGuidance = "Soul guidance ".repeat(80);
|
||||
const memorySummary = "Memory summary goes here.";
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.writeFile(path.join(workspaceDir, "SOUL.md"), soulGuidance);
|
||||
await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), memorySummary);
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.config = {
|
||||
agents: {
|
||||
defaults: {
|
||||
bootstrapMaxChars: 1000,
|
||||
bootstrapTotalMaxChars: 1000,
|
||||
},
|
||||
},
|
||||
} as EmbeddedRunAttemptParams["config"];
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
const result = await run;
|
||||
|
||||
const fileStats = new Map(
|
||||
result.systemPromptReport?.injectedWorkspaceFiles.map((file) => [file.name, file]) ?? [],
|
||||
);
|
||||
expect(fileStats.get("MEMORY.md")).toMatchObject({
|
||||
rawChars: memorySummary.length,
|
||||
injectedChars: 0,
|
||||
truncated: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps MEMORY.md out of the Codex workspace context budget", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const memorySummary = "Memory summary ".repeat(300);
|
||||
const hookContext = "Hook context survives the memory budget.";
|
||||
const hookPath = path.join(workspaceDir, "ZZZ.md");
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), memorySummary);
|
||||
registerInternalHook("agent:bootstrap", (event) => {
|
||||
const context = event.context as {
|
||||
bootstrapFiles: Array<{ content: string; missing: boolean; name?: string; path: string }>;
|
||||
};
|
||||
context.bootstrapFiles = [
|
||||
...context.bootstrapFiles,
|
||||
{
|
||||
name: "ZZZ.md",
|
||||
path: hookPath,
|
||||
content: hookContext,
|
||||
missing: false,
|
||||
},
|
||||
];
|
||||
});
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.disableTools = false;
|
||||
params.config = {
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: workspaceDir,
|
||||
bootstrapMaxChars: 1000,
|
||||
bootstrapTotalMaxChars: 2000,
|
||||
},
|
||||
},
|
||||
} as EmbeddedRunAttemptParams["config"];
|
||||
testing.setOpenClawCodingToolsFactoryForTests(() => [
|
||||
createRuntimeDynamicTool("memory_search"),
|
||||
createRuntimeDynamicTool("memory_get"),
|
||||
]);
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
const result = await run;
|
||||
|
||||
const turnStart = harness.requests.find((request) => request.method === "turn/start");
|
||||
const turnStartParams = turnStart?.params as {
|
||||
input?: Array<{ text?: string }>;
|
||||
};
|
||||
const inputText = turnStartParams.input?.[0]?.text ?? "";
|
||||
expect(inputText).toContain("OpenClaw Workspace Memory");
|
||||
expect(inputText).not.toContain(memorySummary);
|
||||
expect(inputText).toContain(hookContext);
|
||||
|
||||
const fileStats = new Map(
|
||||
result.systemPromptReport?.injectedWorkspaceFiles.map((file) => [file.name, file]) ?? [],
|
||||
);
|
||||
expect(fileStats.get("MEMORY.md")).toMatchObject({
|
||||
rawChars: memorySummary.trimEnd().length,
|
||||
injectedChars: 0,
|
||||
truncated: false,
|
||||
});
|
||||
expect(fileStats.get("ZZZ.md")).toMatchObject({
|
||||
rawChars: hookContext.length,
|
||||
injectedChars: hookContext.length,
|
||||
truncated: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps extra MEMORY.md bootstrap files in Codex workspace context", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const rootMemory = "Root memory should stay tool-routed.";
|
||||
const nestedMemory = "Nested package memory remains prompt context.";
|
||||
const nestedMemoryPath = path.join(workspaceDir, "packages/pkg/MEMORY.md");
|
||||
await fs.mkdir(path.dirname(nestedMemoryPath), { recursive: true });
|
||||
await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), rootMemory);
|
||||
await fs.writeFile(nestedMemoryPath, nestedMemory);
|
||||
registerInternalHook("agent:bootstrap", (event) => {
|
||||
const context = event.context as {
|
||||
bootstrapFiles: Array<{ content: string; missing: boolean; name?: string; path: string }>;
|
||||
};
|
||||
context.bootstrapFiles = [
|
||||
...context.bootstrapFiles,
|
||||
{
|
||||
name: "MEMORY.md",
|
||||
path: nestedMemoryPath,
|
||||
content: nestedMemory,
|
||||
missing: false,
|
||||
},
|
||||
];
|
||||
});
|
||||
testing.setOpenClawCodingToolsFactoryForTests(() => [
|
||||
createRuntimeDynamicTool("memory_search"),
|
||||
createRuntimeDynamicTool("memory_get"),
|
||||
]);
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.disableTools = false;
|
||||
setAgentWorkspaceForTest(params, workspaceDir);
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
const result = await run;
|
||||
|
||||
const turnStart = harness.requests.find((request) => request.method === "turn/start");
|
||||
const turnStartParams = turnStart?.params as {
|
||||
input?: Array<{ text?: string }>;
|
||||
};
|
||||
const inputText = turnStartParams.input?.[0]?.text ?? "";
|
||||
expect(inputText).toContain("OpenClaw Workspace Memory");
|
||||
expect(inputText).not.toContain(rootMemory);
|
||||
expect(inputText).toContain(nestedMemory);
|
||||
|
||||
const files = result.systemPromptReport?.injectedWorkspaceFiles ?? [];
|
||||
const rootMemoryStats = files.find(
|
||||
(file) => file.path === path.join(workspaceDir, "MEMORY.md"),
|
||||
);
|
||||
const nestedMemoryStats = files.find((file) => file.path === nestedMemoryPath);
|
||||
expect(rootMemoryStats).toMatchObject({
|
||||
rawChars: rootMemory.length,
|
||||
injectedChars: 0,
|
||||
truncated: false,
|
||||
});
|
||||
expect(nestedMemoryStats).toMatchObject({
|
||||
rawChars: nestedMemory.length,
|
||||
injectedChars: nestedMemory.length,
|
||||
truncated: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("injects MEMORY.md when active workspace is not the memory tool workspace", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const memorySummary = "Memory summary goes here.";
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), memorySummary);
|
||||
testing.setOpenClawCodingToolsFactoryForTests(() => [
|
||||
createRuntimeDynamicTool("memory_search"),
|
||||
createRuntimeDynamicTool("memory_get"),
|
||||
]);
|
||||
const harness = createStartedThreadHarness();
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
params.disableTools = false;
|
||||
setAgentWorkspaceForTest(params, path.join(tempDir, "memory-workspace"));
|
||||
|
||||
const run = runCodexAppServerAttempt(params);
|
||||
await harness.waitForMethod("turn/start");
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
const result = await run;
|
||||
|
||||
const turnStart = harness.requests.find((request) => request.method === "turn/start");
|
||||
const turnStartParams = turnStart?.params as {
|
||||
input?: Array<{ text?: string }>;
|
||||
};
|
||||
const inputText = turnStartParams.input?.[0]?.text ?? "";
|
||||
expect(inputText).not.toContain("OpenClaw Workspace Memory");
|
||||
expect(inputText).toContain(memorySummary);
|
||||
|
||||
const fileStats = new Map(
|
||||
result.systemPromptReport?.injectedWorkspaceFiles.map((file) => [file.name, file]) ?? [],
|
||||
);
|
||||
expect(fileStats.get("MEMORY.md")).toMatchObject({
|
||||
rawChars: memorySummary.length,
|
||||
injectedChars: memorySummary.length,
|
||||
truncated: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("reports hook-supplied bootstrap files that only expose path and content", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
@@ -7953,6 +8278,202 @@ describe("runCodexAppServerAttempt", () => {
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("persists and reuses Codex native hook relay generations for resumed threads", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const firstHarness = createStartedThreadHarness();
|
||||
|
||||
const firstRun = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
|
||||
nativeHookRelay: {
|
||||
enabled: true,
|
||||
events: ["pre_tool_use"],
|
||||
},
|
||||
});
|
||||
await firstHarness.waitForMethod("turn/start");
|
||||
const firstStartRequest = firstHarness.requests.find(
|
||||
(request) => request.method === "thread/start",
|
||||
);
|
||||
const firstRelayId = extractRelayIdFromThreadRequest(firstStartRequest?.params);
|
||||
const firstGeneration = extractGenerationFromThreadRequest(firstStartRequest?.params);
|
||||
|
||||
await firstHarness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await firstRun;
|
||||
expect((await readCodexAppServerBinding(sessionFile))?.nativeHookRelayGeneration).toBe(
|
||||
firstGeneration,
|
||||
);
|
||||
|
||||
const secondHarness = createResumeHarness();
|
||||
const secondParams = createParams(sessionFile, workspaceDir);
|
||||
secondParams.runId = "run-2";
|
||||
const secondRun = runCodexAppServerAttempt(secondParams, {
|
||||
nativeHookRelay: {
|
||||
enabled: true,
|
||||
events: ["pre_tool_use"],
|
||||
},
|
||||
});
|
||||
await secondHarness.waitForMethod("turn/start");
|
||||
|
||||
const resumeRequest = secondHarness.requests.find(
|
||||
(request) => request.method === "thread/resume",
|
||||
);
|
||||
expect(extractRelayIdFromThreadRequest(resumeRequest?.params)).toBe(firstRelayId);
|
||||
expect(extractGenerationFromThreadRequest(resumeRequest?.params)).toBe(firstGeneration);
|
||||
|
||||
await secondHarness.completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
|
||||
await secondRun;
|
||||
testing.flushPendingCodexNativeHookRelayUnregistersForTests();
|
||||
});
|
||||
|
||||
it("accepts a stale first hook generation when resuming a pre-generation binding", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
await writeCodexAppServerBinding(sessionFile, {
|
||||
threadId: "thread-existing",
|
||||
cwd: workspaceDir,
|
||||
model: "gpt-5.4-codex",
|
||||
modelProvider: "openai",
|
||||
dynamicToolsFingerprint: "[]",
|
||||
});
|
||||
const harness = createResumeHarness();
|
||||
|
||||
const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
|
||||
nativeHookRelay: {
|
||||
enabled: true,
|
||||
events: ["pre_tool_use"],
|
||||
},
|
||||
});
|
||||
await harness.waitForMethod("turn/start");
|
||||
|
||||
const resumeRequest = harness.requests.find((request) => request.method === "thread/resume");
|
||||
const relayId = extractRelayIdFromThreadRequest(resumeRequest?.params);
|
||||
const currentGeneration = extractGenerationFromThreadRequest(resumeRequest?.params);
|
||||
expect(currentGeneration).not.toBe("legacy-generation-from-running-thread");
|
||||
await expect(
|
||||
invokeNativeHookRelay({
|
||||
provider: "codex",
|
||||
relayId,
|
||||
generation: "legacy-generation-from-running-thread",
|
||||
event: "pre_tool_use",
|
||||
requireGeneration: true,
|
||||
rawPayload: {
|
||||
hook_event_name: "PreToolUse",
|
||||
tool_name: "Bash",
|
||||
tool_use_id: "first-tool-after-restart",
|
||||
tool_input: { command: "pwd" },
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({ exitCode: 0 });
|
||||
|
||||
await harness.completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
|
||||
await run;
|
||||
expect((await readCodexAppServerBinding(sessionFile))?.nativeHookRelayGeneration).toBe(
|
||||
currentGeneration,
|
||||
);
|
||||
testing.flushPendingCodexNativeHookRelayUnregistersForTests();
|
||||
});
|
||||
|
||||
it("rotates native hook relay generations when an existing binding starts a fresh thread", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
await writeCodexAppServerBinding(sessionFile, {
|
||||
threadId: "thread-existing",
|
||||
cwd: workspaceDir,
|
||||
model: "gpt-5.4-codex",
|
||||
modelProvider: "openai",
|
||||
userMcpServersFingerprint: "stale-user-mcp-fingerprint",
|
||||
nativeHookRelayGeneration: "generation-from-stale-thread",
|
||||
});
|
||||
const harness = createStartedThreadHarness();
|
||||
|
||||
const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
|
||||
nativeHookRelay: {
|
||||
enabled: true,
|
||||
events: ["pre_tool_use"],
|
||||
},
|
||||
});
|
||||
await harness.waitForMethod("turn/start");
|
||||
|
||||
const startRequest = harness.requests.find((request) => request.method === "thread/start");
|
||||
const relayId = extractRelayIdFromThreadRequest(startRequest?.params);
|
||||
const currentGeneration = extractGenerationFromThreadRequest(startRequest?.params);
|
||||
expect(currentGeneration).not.toBe("generation-from-stale-thread");
|
||||
await expect(
|
||||
invokeNativeHookRelay({
|
||||
provider: "codex",
|
||||
relayId,
|
||||
generation: "generation-from-stale-thread",
|
||||
event: "pre_tool_use",
|
||||
requireGeneration: true,
|
||||
rawPayload: {
|
||||
hook_event_name: "PreToolUse",
|
||||
tool_name: "Bash",
|
||||
tool_use_id: "stale-thread-tool",
|
||||
tool_input: { command: "pwd" },
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow("native hook relay bridge stale registration");
|
||||
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
expect((await readCodexAppServerBinding(sessionFile))?.nativeHookRelayGeneration).toBe(
|
||||
currentGeneration,
|
||||
);
|
||||
testing.flushPendingCodexNativeHookRelayUnregistersForTests();
|
||||
});
|
||||
|
||||
it("rotates native hook relay generations when resume fails over to a fresh thread", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
await writeCodexAppServerBinding(sessionFile, {
|
||||
threadId: "thread-existing",
|
||||
cwd: workspaceDir,
|
||||
model: "gpt-5.4-codex",
|
||||
modelProvider: "openai",
|
||||
nativeHookRelayGeneration: "generation-from-failed-resume",
|
||||
});
|
||||
const harness = createStartedThreadHarness(async (method) => {
|
||||
if (method === "thread/resume") {
|
||||
throw new Error("resume failed");
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
|
||||
nativeHookRelay: {
|
||||
enabled: true,
|
||||
events: ["pre_tool_use"],
|
||||
},
|
||||
});
|
||||
await harness.waitForMethod("turn/start");
|
||||
|
||||
const startRequest = harness.requests.find((request) => request.method === "thread/start");
|
||||
const relayId = extractRelayIdFromThreadRequest(startRequest?.params);
|
||||
const currentGeneration = extractGenerationFromThreadRequest(startRequest?.params);
|
||||
expect(currentGeneration).not.toBe("generation-from-failed-resume");
|
||||
await expect(
|
||||
invokeNativeHookRelay({
|
||||
provider: "codex",
|
||||
relayId,
|
||||
generation: "generation-from-failed-resume",
|
||||
event: "pre_tool_use",
|
||||
requireGeneration: true,
|
||||
rawPayload: {
|
||||
hook_event_name: "PreToolUse",
|
||||
tool_name: "Bash",
|
||||
tool_use_id: "failed-resume-stale-tool",
|
||||
tool_input: { command: "pwd" },
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow("native hook relay bridge stale registration");
|
||||
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
expect((await readCodexAppServerBinding(sessionFile))?.nativeHookRelayGeneration).toBe(
|
||||
currentGeneration,
|
||||
);
|
||||
testing.flushPendingCodexNativeHookRelayUnregistersForTests();
|
||||
});
|
||||
|
||||
it("builds deterministic opaque Codex native hook relay ids", () => {
|
||||
const relayId = testing.buildCodexNativeHookRelayId({
|
||||
agentId: "dev-codex",
|
||||
@@ -11959,6 +12480,136 @@ describe("runCodexAppServerAttempt", () => {
|
||||
expect(request.mock.calls.map(([method]) => method)).toEqual(["thread/start", "thread/start"]);
|
||||
});
|
||||
|
||||
it("does not retire the shared Codex client when a spawned helper run fails with a logical thread/start error", async () => {
|
||||
const clearSpy = vi.spyOn(sharedClientModule, "clearSharedCodexAppServerClientIfCurrent");
|
||||
clearSpy.mockClear();
|
||||
let failedClient: unknown;
|
||||
setCodexAppServerClientFactoryForTest(async () => {
|
||||
const c = {
|
||||
request: vi.fn(async (method: string) => {
|
||||
if (method === "thread/start") {
|
||||
throw new CodexAppServerRpcError(
|
||||
{ message: "401 authentication_error: Invalid bearer token" },
|
||||
"thread/start",
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
addNotificationHandler: vi.fn(() => () => undefined),
|
||||
addRequestHandler: vi.fn(() => () => undefined),
|
||||
};
|
||||
failedClient = c;
|
||||
return c as never;
|
||||
});
|
||||
const params = createParams(
|
||||
path.join(tempDir, "session.jsonl"),
|
||||
path.join(tempDir, "workspace"),
|
||||
);
|
||||
params.spawnedBy = "agent:main:session-parent";
|
||||
|
||||
await expect(runCodexAppServerAttempt(params)).rejects.toThrow("Invalid bearer token");
|
||||
const calledWithFailedClient = clearSpy.mock.calls.some(([arg]) => arg === failedClient);
|
||||
expect(calledWithFailedClient).toBe(false);
|
||||
clearSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("retires the shared Codex client when a spawned helper run times out during thread/start", async () => {
|
||||
const clearSpy = vi.spyOn(sharedClientModule, "clearSharedCodexAppServerClientIfCurrent");
|
||||
clearSpy.mockClear();
|
||||
let failedClient: unknown;
|
||||
setCodexAppServerClientFactoryForTest(async () => {
|
||||
const c = {
|
||||
request: vi.fn(async (method: string) => {
|
||||
if (method === "thread/start") {
|
||||
return await new Promise<never>(() => undefined);
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
addNotificationHandler: vi.fn(() => () => undefined),
|
||||
addRequestHandler: vi.fn(() => () => undefined),
|
||||
};
|
||||
failedClient = c;
|
||||
return c as never;
|
||||
});
|
||||
const params = createParams(
|
||||
path.join(tempDir, "session.jsonl"),
|
||||
path.join(tempDir, "workspace"),
|
||||
);
|
||||
params.spawnedBy = "agent:main:session-parent";
|
||||
params.timeoutMs = 1;
|
||||
|
||||
await expect(runCodexAppServerAttempt(params, { startupTimeoutFloorMs: 1 })).rejects.toThrow(
|
||||
"codex app-server startup timed out",
|
||||
);
|
||||
const calledWithFailedClient = clearSpy.mock.calls.some(([arg]) => arg === failedClient);
|
||||
expect(calledWithFailedClient).toBe(true);
|
||||
clearSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("retires the shared Codex client when a spawned helper hits a thread/start connection close", async () => {
|
||||
const clearSpy = vi.spyOn(sharedClientModule, "clearSharedCodexAppServerClientIfCurrent");
|
||||
clearSpy.mockClear();
|
||||
let failedClient: unknown;
|
||||
setCodexAppServerClientFactoryForTest(async () => {
|
||||
const c = {
|
||||
request: vi.fn(async (method: string) => {
|
||||
if (method === "thread/start") {
|
||||
throw new Error("codex app-server client is closed");
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
addNotificationHandler: vi.fn(() => () => undefined),
|
||||
addRequestHandler: vi.fn(() => () => undefined),
|
||||
};
|
||||
failedClient = c;
|
||||
return c as never;
|
||||
});
|
||||
const params = createParams(
|
||||
path.join(tempDir, "session.jsonl"),
|
||||
path.join(tempDir, "workspace"),
|
||||
);
|
||||
params.spawnedBy = "agent:main:session-parent";
|
||||
|
||||
await expect(runCodexAppServerAttempt(params)).rejects.toThrow(
|
||||
"codex app-server client is closed",
|
||||
);
|
||||
const calledWithFailedClient = clearSpy.mock.calls.some(([arg]) => arg === failedClient);
|
||||
expect(calledWithFailedClient).toBe(true);
|
||||
clearSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("does not retire the shared Codex client when a top-level run fails with a logical thread/start error", async () => {
|
||||
const clearSpy = vi.spyOn(sharedClientModule, "clearSharedCodexAppServerClientIfCurrent");
|
||||
clearSpy.mockClear();
|
||||
let failedClient: unknown;
|
||||
setCodexAppServerClientFactoryForTest(async () => {
|
||||
const c = {
|
||||
request: vi.fn(async (method: string) => {
|
||||
if (method === "thread/start") {
|
||||
throw new CodexAppServerRpcError(
|
||||
{ message: "401 authentication_error: Invalid bearer token" },
|
||||
"thread/start",
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
addNotificationHandler: vi.fn(() => () => undefined),
|
||||
addRequestHandler: vi.fn(() => () => undefined),
|
||||
};
|
||||
failedClient = c;
|
||||
return c as never;
|
||||
});
|
||||
const params = createParams(
|
||||
path.join(tempDir, "session.jsonl"),
|
||||
path.join(tempDir, "workspace"),
|
||||
);
|
||||
|
||||
await expect(runCodexAppServerAttempt(params)).rejects.toThrow("Invalid bearer token");
|
||||
const calledWithFailedClient = clearSpy.mock.calls.some(([arg]) => arg === failedClient);
|
||||
expect(calledWithFailedClient).toBe(false);
|
||||
clearSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("passes configured app-server policy, sandbox, service tier, and model on resume", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
|
||||
@@ -34,7 +34,9 @@ import {
|
||||
runAgentHarnessLlmOutputHook,
|
||||
runHarnessContextEngineMaintenance,
|
||||
registerNativeHookRelay,
|
||||
buildBootstrapContextForFiles,
|
||||
resolveBootstrapContextForRun,
|
||||
resolveBootstrapFilesForRun,
|
||||
setActiveEmbeddedRun,
|
||||
supportsModelTools,
|
||||
runAgentCleanupStep,
|
||||
@@ -46,7 +48,11 @@ import {
|
||||
type NativeHookRelayEvent,
|
||||
type NativeHookRelayRegistrationHandle,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import { markAuthProfileBlockedUntil, resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import {
|
||||
markAuthProfileBlockedUntil,
|
||||
resolveAgentDir,
|
||||
resolveAgentWorkspaceDir,
|
||||
} from "openclaw/plugin-sdk/agent-runtime";
|
||||
import {
|
||||
createDiagnosticTraceContextFromActiveScope,
|
||||
emitTrustedDiagnosticEvent,
|
||||
@@ -250,6 +256,7 @@ const CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = new Set([
|
||||
...CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES,
|
||||
]);
|
||||
const CODEX_HEARTBEAT_CONTEXT_BASENAME = "heartbeat.md";
|
||||
const CODEX_MEMORY_TOOL_NAMES = new Set(["memory_search", "memory_get"]);
|
||||
const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS =
|
||||
CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
|
||||
const CODEX_BOOTSTRAP_CONTEXT_ORDER = new Map<string, number>([
|
||||
@@ -277,6 +284,10 @@ type CodexWorkspaceBootstrapContext = CodexBootstrapContext & {
|
||||
developerInstructionFiles?: EmbeddedContextFile[];
|
||||
turnScopedDeveloperInstructionFiles?: EmbeddedContextFile[];
|
||||
heartbeatReferenceFiles?: EmbeddedContextFile[];
|
||||
memoryReferenceFiles?: EmbeddedContextFile[];
|
||||
memoryToolRoutedBootstrapFiles?: CodexBootstrapFile[];
|
||||
memoryToolNames?: string[];
|
||||
memoryToolRouted?: boolean;
|
||||
promptContext?: string;
|
||||
developerInstructions?: string;
|
||||
turnScopedDeveloperInstructions?: string;
|
||||
@@ -1334,12 +1345,14 @@ export async function runCodexAppServerAttempt(
|
||||
historyMessages =
|
||||
(await readMirroredSessionHistoryMessages(activeSessionFile)) ?? historyMessages;
|
||||
}
|
||||
const memoryToolNames = getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs);
|
||||
const workspaceBootstrapContext = await buildCodexWorkspaceBootstrapContext({
|
||||
params,
|
||||
resolvedWorkspace,
|
||||
effectiveWorkspace,
|
||||
sessionKey: contextSessionKey,
|
||||
sessionAgentId,
|
||||
memoryToolNames,
|
||||
});
|
||||
const baseDeveloperInstructions = joinPresentSections(
|
||||
buildDeveloperInstructions(params, {
|
||||
@@ -1351,6 +1364,10 @@ export async function runCodexAppServerAttempt(
|
||||
params,
|
||||
skillsPrompt: params.skillsSnapshot?.prompt,
|
||||
workspacePromptContext: workspaceBootstrapContext.promptContext,
|
||||
workspaceMemoryReference: renderCodexWorkspaceMemoryReference({
|
||||
files: workspaceBootstrapContext.memoryReferenceFiles ?? [],
|
||||
toolNames: workspaceBootstrapContext.memoryToolNames,
|
||||
}),
|
||||
});
|
||||
let promptText = params.prompt;
|
||||
let developerInstructions = baseDeveloperInstructions;
|
||||
@@ -1516,13 +1533,18 @@ export async function runCodexAppServerAttempt(
|
||||
timeoutMs: params.timeoutMs,
|
||||
timeoutFloorMs: options.startupTimeoutFloorMs,
|
||||
});
|
||||
try {
|
||||
emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "startup" },
|
||||
});
|
||||
const buildNativeHookRelayFinalConfigPatch = (
|
||||
decision: { action: "resume"; binding: CodexAppServerThreadBinding } | { action: "start" },
|
||||
) => {
|
||||
nativeHookRelay?.unregister();
|
||||
nativeHookRelay = createCodexNativeHookRelay({
|
||||
options: options.nativeHookRelay,
|
||||
generation:
|
||||
decision.action === "resume" ? decision.binding.nativeHookRelayGeneration : undefined,
|
||||
generationMismatchGraceMs:
|
||||
decision.action === "resume" && !decision.binding.nativeHookRelayGeneration
|
||||
? CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS
|
||||
: undefined,
|
||||
events: nativeHookRelayEvents,
|
||||
agentId: sessionAgentId,
|
||||
sessionId: params.sessionId,
|
||||
@@ -1535,15 +1557,24 @@ export async function runCodexAppServerAttempt(
|
||||
turnStartTimeoutMs: params.timeoutMs,
|
||||
signal: runAbortController.signal,
|
||||
});
|
||||
const nativeHookRelayConfig = nativeHookRelay
|
||||
? buildCodexNativeHookRelayConfig({
|
||||
relay: nativeHookRelay,
|
||||
events: nativeHookRelayEvents,
|
||||
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec,
|
||||
})
|
||||
: options.nativeHookRelay?.enabled === false
|
||||
? buildCodexNativeHookRelayDisabledConfig()
|
||||
: undefined;
|
||||
return {
|
||||
configPatch: nativeHookRelay
|
||||
? buildCodexNativeHookRelayConfig({
|
||||
relay: nativeHookRelay,
|
||||
events: nativeHookRelayEvents,
|
||||
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec,
|
||||
})
|
||||
: options.nativeHookRelay?.enabled === false
|
||||
? buildCodexNativeHookRelayDisabledConfig()
|
||||
: undefined,
|
||||
nativeHookRelayGeneration: nativeHookRelay?.generation,
|
||||
};
|
||||
};
|
||||
try {
|
||||
emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "startup" },
|
||||
});
|
||||
const threadConfig = mergeCodexThreadConfigs(
|
||||
bundleMcpThreadConfig?.configPatch as JsonObject | undefined,
|
||||
);
|
||||
@@ -1696,7 +1727,7 @@ export async function runCodexAppServerAttempt(
|
||||
appServer: pluginAppServer,
|
||||
developerInstructions: promptBuild.developerInstructions,
|
||||
config: threadConfig,
|
||||
finalConfigPatch: nativeHookRelayConfig,
|
||||
buildFinalConfigPatch: buildNativeHookRelayFinalConfigPatch,
|
||||
nativeCodeModeEnabled: nativeToolSurfaceEnabled,
|
||||
nativeCodeModeOnlyEnabled: appServer.codeModeOnly,
|
||||
userMcpServersEnabled: nativeToolSurfaceEnabled,
|
||||
@@ -1819,7 +1850,9 @@ export async function runCodexAppServerAttempt(
|
||||
} catch (error) {
|
||||
nativeHookRelay?.unregister();
|
||||
await releaseSandboxExecEnvironment();
|
||||
clearSharedCodexAppServerClientIfCurrent(startupClientForCleanup);
|
||||
if (runAbortController.signal.aborted || shouldClearSharedClientAfterStartupRace(error)) {
|
||||
clearSharedCodexAppServerClientIfCurrent(startupClientForCleanup);
|
||||
}
|
||||
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
||||
throw error;
|
||||
}
|
||||
@@ -3945,6 +3978,8 @@ function createCodexNativeHookRelay(params: {
|
||||
gatewayTimeoutMs?: number;
|
||||
}
|
||||
| undefined;
|
||||
generation?: string;
|
||||
generationMismatchGraceMs?: number;
|
||||
events: readonly NativeHookRelayEvent[];
|
||||
agentId: string | undefined;
|
||||
sessionId: string;
|
||||
@@ -3967,6 +4002,10 @@ function createCodexNativeHookRelay(params: {
|
||||
sessionId: params.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
}),
|
||||
...(params.generation ? { generation: params.generation } : {}),
|
||||
...(params.generationMismatchGraceMs
|
||||
? { generationMismatchGraceMs: params.generationMismatchGraceMs }
|
||||
: {}),
|
||||
...(params.agentId ? { agentId: params.agentId } : {}),
|
||||
sessionId: params.sessionId,
|
||||
...(params.sessionKey ? { sessionKey: params.sessionKey } : {}),
|
||||
@@ -5573,9 +5612,17 @@ async function buildCodexWorkspaceBootstrapContext(params: {
|
||||
effectiveWorkspace: string;
|
||||
sessionKey: string;
|
||||
sessionAgentId: string;
|
||||
memoryToolNames: readonly string[];
|
||||
}): Promise<CodexWorkspaceBootstrapContext> {
|
||||
try {
|
||||
const bootstrapContext = await resolveBootstrapContextForRun({
|
||||
const memoryToolsAvailable =
|
||||
params.memoryToolNames.length > 0 &&
|
||||
canRouteCodexWorkspaceMemoryThroughTools({
|
||||
config: params.params.config,
|
||||
agentId: params.params.agentId ?? params.sessionAgentId,
|
||||
workspaceDir: params.effectiveWorkspace,
|
||||
});
|
||||
const bootstrapFiles = await resolveBootstrapFilesForRun({
|
||||
workspaceDir: params.resolvedWorkspace,
|
||||
config: params.params.config,
|
||||
sessionKey: params.sessionKey,
|
||||
@@ -5585,14 +5632,45 @@ async function buildCodexWorkspaceBootstrapContext(params: {
|
||||
contextMode: params.params.bootstrapContextMode,
|
||||
runKind: params.params.bootstrapContextRunKind,
|
||||
});
|
||||
const contextFiles = bootstrapContext.contextFiles.map((file) =>
|
||||
const memoryToolRoutedBootstrapFiles = memoryToolsAvailable
|
||||
? selectCodexWorkspaceMemoryReferenceFiles({
|
||||
bootstrapFiles,
|
||||
workspaceDir: params.resolvedWorkspace,
|
||||
})
|
||||
: [];
|
||||
const memoryReferenceFiles = memoryToolRoutedBootstrapFiles.map((file) =>
|
||||
remapCodexContextFilePath({
|
||||
file: toCodexEmbeddedContextFile(file),
|
||||
sourceWorkspaceDir: params.resolvedWorkspace,
|
||||
targetWorkspaceDir: params.effectiveWorkspace,
|
||||
}),
|
||||
);
|
||||
const contextFiles = buildBootstrapContextForFiles(
|
||||
memoryToolsAvailable
|
||||
? bootstrapFiles.filter(
|
||||
(file) =>
|
||||
!isCodexWorkspaceRootMemoryBootstrapFile({
|
||||
file,
|
||||
workspaceDir: params.resolvedWorkspace,
|
||||
}),
|
||||
)
|
||||
: bootstrapFiles,
|
||||
{
|
||||
config: params.params.config,
|
||||
agentId: params.params.agentId ?? params.sessionAgentId,
|
||||
warn: (message) => embeddedAgentLog.warn(message),
|
||||
},
|
||||
).map((file) =>
|
||||
remapCodexContextFilePath({
|
||||
file,
|
||||
sourceWorkspaceDir: params.resolvedWorkspace,
|
||||
targetWorkspaceDir: params.effectiveWorkspace,
|
||||
}),
|
||||
);
|
||||
const promptContextFiles = selectCodexWorkspacePromptContextFiles(contextFiles);
|
||||
const promptContextFiles = selectCodexWorkspacePromptContextFiles(contextFiles, {
|
||||
excludeMemory: memoryToolsAvailable,
|
||||
memoryWorkspaceDir: params.effectiveWorkspace,
|
||||
});
|
||||
const developerInstructionFiles = shouldInjectCodexOpenClawPromptContext(params.params)
|
||||
? selectCodexWorkspaceInheritedDeveloperInstructionFiles(contextFiles)
|
||||
: [];
|
||||
@@ -5603,12 +5681,16 @@ async function buildCodexWorkspaceBootstrapContext(params: {
|
||||
: [];
|
||||
const heartbeatReferenceFiles = selectCodexWorkspaceHeartbeatReferenceFiles(contextFiles);
|
||||
return {
|
||||
...bootstrapContext,
|
||||
bootstrapFiles,
|
||||
contextFiles,
|
||||
promptContextFiles,
|
||||
developerInstructionFiles,
|
||||
turnScopedDeveloperInstructionFiles,
|
||||
heartbeatReferenceFiles,
|
||||
memoryReferenceFiles,
|
||||
memoryToolRoutedBootstrapFiles,
|
||||
memoryToolNames: [...params.memoryToolNames],
|
||||
memoryToolRouted: memoryToolsAvailable,
|
||||
promptContext: renderCodexWorkspaceBootstrapPromptContext(promptContextFiles),
|
||||
developerInstructions:
|
||||
renderCodexWorkspaceThreadDeveloperInstructions(developerInstructionFiles),
|
||||
@@ -5665,6 +5747,9 @@ function buildCodexSystemPromptReport(params: {
|
||||
...(params.workspaceBootstrapContext.developerInstructionFiles ?? []),
|
||||
...(params.workspaceBootstrapContext.turnScopedDeveloperInstructionFiles ?? []),
|
||||
],
|
||||
memoryToolRoutedBootstrapFiles:
|
||||
params.workspaceBootstrapContext.memoryToolRoutedBootstrapFiles ?? [],
|
||||
memoryToolRouted: params.workspaceBootstrapContext.memoryToolRouted === true,
|
||||
}),
|
||||
skills: {
|
||||
promptChars: skillsPrompt.length,
|
||||
@@ -5782,11 +5867,19 @@ function buildCodexBootstrapInjectionStats(params: {
|
||||
bootstrapFiles: CodexBootstrapFile[];
|
||||
injectedFiles: EmbeddedContextFile[];
|
||||
developerInstructionFiles?: EmbeddedContextFile[];
|
||||
memoryToolRoutedBootstrapFiles?: CodexBootstrapFile[];
|
||||
memoryToolRouted?: boolean;
|
||||
}): CodexSystemPromptReport["injectedWorkspaceFiles"] {
|
||||
const injectedIndex = indexCodexContextFileContent(params.injectedFiles);
|
||||
const developerInstructionIndex = indexCodexContextFileContent(
|
||||
params.developerInstructionFiles ?? [],
|
||||
);
|
||||
const memoryToolRoutedIndex = new Set(
|
||||
(params.memoryToolRoutedBootstrapFiles ?? []).map((file) => {
|
||||
const fileName = readNonEmptyString(file.name);
|
||||
return readNonEmptyString(file.path) ?? fileName ?? "";
|
||||
}),
|
||||
);
|
||||
return params.bootstrapFiles.map((file) => {
|
||||
const fileName = readNonEmptyString(file.name);
|
||||
const pathValue = readNonEmptyString(file.path) ?? fileName ?? "";
|
||||
@@ -5798,6 +5891,10 @@ function buildCodexBootstrapInjectionStats(params: {
|
||||
readCodexIndexedContextFileContent(developerInstructionIndex, pathValue, fileName);
|
||||
let injectedChars = injected?.length ?? 0;
|
||||
let truncated = !file.missing && injectedChars < rawChars;
|
||||
if (params.memoryToolRouted === true && memoryToolRoutedIndex.has(pathValue)) {
|
||||
injectedChars = 0;
|
||||
truncated = false;
|
||||
}
|
||||
if (injected === undefined) {
|
||||
if (CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) {
|
||||
injectedChars = rawChars;
|
||||
@@ -5894,6 +5991,7 @@ function buildCodexOpenClawPromptContext(params: {
|
||||
params: EmbeddedRunAttemptParams;
|
||||
skillsPrompt?: string;
|
||||
workspacePromptContext?: string;
|
||||
workspaceMemoryReference?: string;
|
||||
}): string | undefined {
|
||||
if (!shouldInjectCodexOpenClawPromptContext(params.params)) {
|
||||
return undefined;
|
||||
@@ -5905,6 +6003,7 @@ function buildCodexOpenClawPromptContext(params: {
|
||||
params.workspacePromptContext?.trim()
|
||||
? ["## OpenClaw Workspace Context", "", params.workspacePromptContext.trim()].join("\n")
|
||||
: undefined,
|
||||
params.workspaceMemoryReference?.trim(),
|
||||
].filter(isNonEmptyString);
|
||||
if (sections.length === 0) {
|
||||
return undefined;
|
||||
@@ -5968,6 +6067,7 @@ function renderCodexWorkspaceBootstrapPromptContext(
|
||||
|
||||
function selectCodexWorkspacePromptContextFiles(
|
||||
contextFiles: EmbeddedContextFile[],
|
||||
options: { excludeMemory?: boolean; memoryWorkspaceDir?: string } = {},
|
||||
): EmbeddedContextFile[] {
|
||||
return contextFiles
|
||||
.filter((file) => {
|
||||
@@ -5977,6 +6077,13 @@ function selectCodexWorkspacePromptContextFiles(
|
||||
!CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName) &&
|
||||
!CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES.has(baseName) &&
|
||||
baseName !== CODEX_HEARTBEAT_CONTEXT_BASENAME &&
|
||||
!(
|
||||
options.excludeMemory === true &&
|
||||
isCodexWorkspaceRootMemoryContextFile({
|
||||
file,
|
||||
workspaceDir: options.memoryWorkspaceDir,
|
||||
})
|
||||
) &&
|
||||
!isMissingCodexBootstrapContextFile(file)
|
||||
);
|
||||
})
|
||||
@@ -6087,10 +6194,117 @@ function renderCodexWorkspaceHeartbeatReference(files: EmbeddedContextFile[]): s
|
||||
return lines.join("\n").trim();
|
||||
}
|
||||
|
||||
function selectCodexWorkspaceMemoryReferenceFiles(params: {
|
||||
bootstrapFiles: CodexBootstrapFile[];
|
||||
workspaceDir: string;
|
||||
}): CodexBootstrapFile[] {
|
||||
return params.bootstrapFiles
|
||||
.filter((file) => {
|
||||
return (
|
||||
isCodexWorkspaceRootMemoryBootstrapFile({
|
||||
file,
|
||||
workspaceDir: params.workspaceDir,
|
||||
}) &&
|
||||
!file.missing &&
|
||||
(file.content ?? "").trim().length > 0
|
||||
);
|
||||
})
|
||||
.toSorted(compareCodexBootstrapFiles);
|
||||
}
|
||||
|
||||
function renderCodexWorkspaceMemoryReference(params: {
|
||||
files: EmbeddedContextFile[];
|
||||
toolNames?: readonly string[];
|
||||
}): string | undefined {
|
||||
if (params.files.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const toolNames = params.toolNames?.length
|
||||
? params.toolNames
|
||||
: Array.from(CODEX_MEMORY_TOOL_NAMES);
|
||||
const lines = [
|
||||
"## OpenClaw Workspace Memory",
|
||||
"",
|
||||
`MEMORY.md exists in the active agent workspace. OpenClaw does not paste its contents into native Codex turns; use ${toolNames.join(" or ")} when durable memory is relevant and the tools are available.`,
|
||||
"",
|
||||
];
|
||||
for (const file of params.files) {
|
||||
lines.push(`- ${file.path}`);
|
||||
}
|
||||
return lines.join("\n").trim();
|
||||
}
|
||||
|
||||
function getCodexWorkspaceMemoryToolNames(tools: readonly { name: string }[]): string[] {
|
||||
const availableToolNames = new Set(tools.map((tool) => normalizeCodexDynamicToolName(tool.name)));
|
||||
return Array.from(CODEX_MEMORY_TOOL_NAMES).filter((name) => availableToolNames.has(name));
|
||||
}
|
||||
|
||||
function canRouteCodexWorkspaceMemoryThroughTools(params: {
|
||||
config: EmbeddedRunAttemptParams["config"] | undefined;
|
||||
agentId: string;
|
||||
workspaceDir: string;
|
||||
}): boolean {
|
||||
if (!params.config) {
|
||||
return false;
|
||||
}
|
||||
return isSameCodexWorkspacePath(
|
||||
resolveAgentWorkspaceDir(params.config, params.agentId),
|
||||
params.workspaceDir,
|
||||
);
|
||||
}
|
||||
|
||||
function isMissingCodexBootstrapContextFile(file: EmbeddedContextFile): boolean {
|
||||
return file.content.trimStart().startsWith("[MISSING] Expected at:");
|
||||
}
|
||||
|
||||
function toCodexEmbeddedContextFile(file: CodexBootstrapFile): EmbeddedContextFile {
|
||||
return {
|
||||
path: readNonEmptyString(file.path) ?? readNonEmptyString(file.name) ?? "",
|
||||
content: file.content ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
function isCodexWorkspaceRootMemoryBootstrapFile(params: {
|
||||
file: CodexBootstrapFile;
|
||||
workspaceDir: string;
|
||||
}): boolean {
|
||||
return isCodexWorkspaceRootMemoryPath({
|
||||
filePath: readNonEmptyString(params.file.path) ?? readNonEmptyString(params.file.name) ?? "",
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
}
|
||||
|
||||
function isCodexWorkspaceRootMemoryContextFile(params: {
|
||||
file: EmbeddedContextFile;
|
||||
workspaceDir?: string;
|
||||
}): boolean {
|
||||
if (!params.workspaceDir) {
|
||||
return false;
|
||||
}
|
||||
return isCodexWorkspaceRootMemoryPath({
|
||||
filePath: params.file.path,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
}
|
||||
|
||||
function isCodexWorkspaceRootMemoryPath(params: {
|
||||
filePath: string;
|
||||
workspaceDir: string;
|
||||
}): boolean {
|
||||
const filePath = params.filePath.trim();
|
||||
if (!filePath) {
|
||||
return false;
|
||||
}
|
||||
const absolutePath = path.isAbsolute(filePath)
|
||||
? path.resolve(filePath)
|
||||
: path.resolve(params.workspaceDir, filePath);
|
||||
return absolutePath === path.join(path.resolve(params.workspaceDir), "MEMORY.md");
|
||||
}
|
||||
|
||||
function isSameCodexWorkspacePath(left: string, right: string): boolean {
|
||||
return path.resolve(left) === path.resolve(right);
|
||||
}
|
||||
|
||||
function remapCodexContextFilePath(params: {
|
||||
file: EmbeddedContextFile;
|
||||
sourceWorkspaceDir: string;
|
||||
@@ -6135,6 +6349,13 @@ function compareCodexContextFiles(left: EmbeddedContextFile, right: EmbeddedCont
|
||||
return leftPath.localeCompare(rightPath);
|
||||
}
|
||||
|
||||
function compareCodexBootstrapFiles(left: CodexBootstrapFile, right: CodexBootstrapFile): number {
|
||||
return compareCodexContextFiles(
|
||||
toCodexEmbeddedContextFile(left),
|
||||
toCodexEmbeddedContextFile(right),
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeCodexContextFilePath(filePath: string): string {
|
||||
return filePath.trim().replaceAll("\\", "/").toLowerCase();
|
||||
}
|
||||
@@ -6339,6 +6560,15 @@ function waitForCodexNotificationDispatchTurn(): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function shouldClearSharedClientAfterStartupRace(error: unknown): boolean {
|
||||
return (
|
||||
error instanceof Error &&
|
||||
(error.message === "codex app-server startup timed out" ||
|
||||
error.message === "codex app-server startup aborted" ||
|
||||
error.message.endsWith(" timed out"))
|
||||
);
|
||||
}
|
||||
|
||||
function handleApprovalRequest(params: {
|
||||
method: string;
|
||||
params: JsonValue | undefined;
|
||||
|
||||
@@ -60,6 +60,7 @@ describe("codex app-server session binding", () => {
|
||||
modelProvider: "openai",
|
||||
dynamicToolsFingerprint: "tools-v1",
|
||||
userMcpServersFingerprint: "user-mcp-v1",
|
||||
nativeHookRelayGeneration: "generation-v1",
|
||||
});
|
||||
|
||||
const binding = await readCodexAppServerBinding(sessionFile);
|
||||
@@ -72,6 +73,7 @@ describe("codex app-server session binding", () => {
|
||||
expect(binding?.modelProvider).toBe("openai");
|
||||
expect(binding?.dynamicToolsFingerprint).toBe("tools-v1");
|
||||
expect(binding?.userMcpServersFingerprint).toBe("user-mcp-v1");
|
||||
expect(binding?.nativeHookRelayGeneration).toBe("generation-v1");
|
||||
const bindingStat = await fs.stat(resolveCodexAppServerBindingPath(sessionFile));
|
||||
expect(bindingStat.isFile()).toBe(true);
|
||||
});
|
||||
|
||||
@@ -42,6 +42,7 @@ export type CodexAppServerThreadBinding = {
|
||||
dynamicToolsFingerprint?: string;
|
||||
userMcpServersFingerprint?: string;
|
||||
mcpServersFingerprint?: string;
|
||||
nativeHookRelayGeneration?: string;
|
||||
pluginAppsFingerprint?: string;
|
||||
pluginAppsInputFingerprint?: string;
|
||||
pluginAppPolicyContext?: PluginAppPolicyContext;
|
||||
@@ -116,6 +117,11 @@ export async function readCodexAppServerBinding(
|
||||
: undefined,
|
||||
mcpServersFingerprint:
|
||||
typeof parsed.mcpServersFingerprint === "string" ? parsed.mcpServersFingerprint : undefined,
|
||||
nativeHookRelayGeneration:
|
||||
typeof parsed.nativeHookRelayGeneration === "string" &&
|
||||
parsed.nativeHookRelayGeneration.trim()
|
||||
? parsed.nativeHookRelayGeneration
|
||||
: undefined,
|
||||
pluginAppsFingerprint:
|
||||
typeof parsed.pluginAppsFingerprint === "string" ? parsed.pluginAppsFingerprint : undefined,
|
||||
pluginAppsInputFingerprint:
|
||||
@@ -166,6 +172,7 @@ export async function writeCodexAppServerBinding(
|
||||
dynamicToolsFingerprint: binding.dynamicToolsFingerprint,
|
||||
userMcpServersFingerprint: binding.userMcpServersFingerprint,
|
||||
mcpServersFingerprint: binding.mcpServersFingerprint,
|
||||
nativeHookRelayGeneration: binding.nativeHookRelayGeneration,
|
||||
pluginAppsFingerprint: binding.pluginAppsFingerprint,
|
||||
pluginAppsInputFingerprint: binding.pluginAppsInputFingerprint,
|
||||
pluginAppPolicyContext: binding.pluginAppPolicyContext,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
embeddedAgentLog,
|
||||
formatErrorMessage,
|
||||
isActiveHarnessContextEngine,
|
||||
type EmbeddedRunAttemptParams,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
@@ -7,7 +8,11 @@ import { buildCodexUserMcpServersThreadConfigPatch } from "openclaw/plugin-sdk/c
|
||||
import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
|
||||
import { CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY } from "../../prompt-overlay.js";
|
||||
import { isModernCodexModel } from "../../provider.js";
|
||||
import { isCodexAppServerConnectionClosedError, type CodexAppServerClient } from "./client.js";
|
||||
import {
|
||||
CodexAppServerRpcError,
|
||||
isCodexAppServerConnectionClosedError,
|
||||
type CodexAppServerClient,
|
||||
} from "./client.js";
|
||||
import { codexSandboxPolicyForTurn, type CodexAppServerRuntimeOptions } from "./config.js";
|
||||
import {
|
||||
resolveCodexContextEngineProjectionMaxChars,
|
||||
@@ -56,6 +61,26 @@ export type CodexAppServerThreadLifecycleBinding = CodexAppServerThreadBinding &
|
||||
lifecycle: CodexAppServerThreadLifecycle;
|
||||
};
|
||||
|
||||
class CodexThreadStartRequestError extends Error {
|
||||
constructor(cause: unknown) {
|
||||
super(formatErrorMessage(cause), { cause });
|
||||
this.name = "CodexThreadStartRequestError";
|
||||
}
|
||||
}
|
||||
|
||||
export function isCodexThreadStartRequestError(error: unknown): boolean {
|
||||
return error instanceof CodexThreadStartRequestError;
|
||||
}
|
||||
|
||||
export type CodexThreadFinalConfigPatchDecision =
|
||||
| { action: "resume"; binding: CodexAppServerThreadBinding }
|
||||
| { action: "start" };
|
||||
|
||||
export type CodexThreadFinalConfigPatchResult = {
|
||||
configPatch?: JsonObject;
|
||||
nativeHookRelayGeneration?: string;
|
||||
};
|
||||
|
||||
export type CodexContextEngineThreadBootstrapProjection = {
|
||||
mode: "thread_bootstrap";
|
||||
epoch: string;
|
||||
@@ -202,6 +227,10 @@ export async function startOrResumeThread(params: {
|
||||
developerInstructions?: string;
|
||||
config?: JsonObject;
|
||||
finalConfigPatch?: JsonObject;
|
||||
buildFinalConfigPatch?: (
|
||||
decision: CodexThreadFinalConfigPatchDecision,
|
||||
) => CodexThreadFinalConfigPatchResult;
|
||||
nativeHookRelayGeneration?: string;
|
||||
nativeCodeModeEnabled?: boolean;
|
||||
nativeCodeModeOnlyEnabled?: boolean;
|
||||
userMcpServersEnabled?: boolean;
|
||||
@@ -387,10 +416,17 @@ export async function startOrResumeThread(params: {
|
||||
} else {
|
||||
try {
|
||||
const authProfileId = params.params.authProfileId ?? binding.authProfileId;
|
||||
const finalConfigPatch = params.buildFinalConfigPatch?.({
|
||||
action: "resume",
|
||||
binding,
|
||||
}) ?? {
|
||||
configPatch: params.finalConfigPatch,
|
||||
nativeHookRelayGeneration: params.nativeHookRelayGeneration,
|
||||
};
|
||||
const resumeConfig = mergeCodexThreadConfigs(
|
||||
params.config,
|
||||
userMcpServersConfigPatch,
|
||||
params.finalConfigPatch,
|
||||
finalConfigPatch.configPatch,
|
||||
);
|
||||
const resumeParams = lifecycleTiming.measureSync("thread_resume_params", () =>
|
||||
buildThreadResumeParams(params.params, {
|
||||
@@ -433,6 +469,8 @@ export async function startOrResumeThread(params: {
|
||||
dynamicToolsFingerprint,
|
||||
userMcpServersFingerprint,
|
||||
mcpServersFingerprint: nextMcpServersFingerprint,
|
||||
nativeHookRelayGeneration:
|
||||
finalConfigPatch.nativeHookRelayGeneration ?? binding.nativeHookRelayGeneration,
|
||||
pluginAppsFingerprint: binding.pluginAppsFingerprint,
|
||||
pluginAppsInputFingerprint: binding.pluginAppsInputFingerprint,
|
||||
pluginAppPolicyContext: binding.pluginAppPolicyContext,
|
||||
@@ -475,6 +513,8 @@ export async function startOrResumeThread(params: {
|
||||
dynamicToolsFingerprint,
|
||||
userMcpServersFingerprint,
|
||||
mcpServersFingerprint: nextMcpServersFingerprint,
|
||||
nativeHookRelayGeneration:
|
||||
finalConfigPatch.nativeHookRelayGeneration ?? binding.nativeHookRelayGeneration,
|
||||
pluginAppsFingerprint: binding.pluginAppsFingerprint,
|
||||
pluginAppsInputFingerprint: binding.pluginAppsInputFingerprint,
|
||||
pluginAppPolicyContext: binding.pluginAppPolicyContext,
|
||||
@@ -500,12 +540,16 @@ export async function startOrResumeThread(params: {
|
||||
params.pluginThreadConfig?.build(),
|
||||
)))
|
||||
: undefined;
|
||||
const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
||||
configPatch: params.finalConfigPatch,
|
||||
nativeHookRelayGeneration: params.nativeHookRelayGeneration,
|
||||
};
|
||||
const config = lifecycleTiming.measureSync("merge_thread_config", () =>
|
||||
mergeCodexThreadConfigs(
|
||||
params.config,
|
||||
userMcpServersConfigPatch,
|
||||
pluginThreadConfig?.configPatch,
|
||||
params.finalConfigPatch,
|
||||
finalConfigPatch.configPatch,
|
||||
),
|
||||
);
|
||||
const startParams = lifecycleTiming.measureSync("thread_start_params", () =>
|
||||
@@ -520,11 +564,17 @@ export async function startOrResumeThread(params: {
|
||||
environmentSelection: params.environmentSelection,
|
||||
}),
|
||||
);
|
||||
const response = assertCodexThreadStartResponse(
|
||||
await lifecycleTiming.measure("thread_start_request", () =>
|
||||
params.client.request("thread/start", startParams),
|
||||
),
|
||||
);
|
||||
const threadStartResponse = await lifecycleTiming.measure("thread_start_request", async () => {
|
||||
try {
|
||||
return await params.client.request("thread/start", startParams);
|
||||
} catch (error) {
|
||||
if (error instanceof CodexAppServerRpcError) {
|
||||
throw new CodexThreadStartRequestError(error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
const response = assertCodexThreadStartResponse(threadStartResponse);
|
||||
const modelProvider = resolveCodexAppServerModelProvider({
|
||||
provider: params.params.provider,
|
||||
authProfileId: params.params.authProfileId,
|
||||
@@ -548,6 +598,7 @@ export async function startOrResumeThread(params: {
|
||||
dynamicToolsFingerprint,
|
||||
userMcpServersFingerprint,
|
||||
mcpServersFingerprint: nextMcpServersFingerprint,
|
||||
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
||||
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
||||
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
||||
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
||||
@@ -592,6 +643,7 @@ export async function startOrResumeThread(params: {
|
||||
dynamicToolsFingerprint,
|
||||
userMcpServersFingerprint,
|
||||
mcpServersFingerprint: nextMcpServersFingerprint,
|
||||
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
||||
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
||||
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
||||
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/comfy-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw ComfyUI provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/copilot-proxy",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Copilot Proxy provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepgram-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw Deepgram media-understanding provider",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepinfra-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw DeepInfra provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepseek-provider",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"private": true,
|
||||
"description": "OpenClaw DeepSeek provider plugin",
|
||||
"type": "module",
|
||||
|
||||
4
extensions/diagnostics-otel/npm-shrinkwrap.json
generated
4
extensions/diagnostics-otel/npm-shrinkwrap.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/diagnostics-otel",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/diagnostics-otel",
|
||||
"version": "2026.5.26",
|
||||
"version": "2026.5.27",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "1.9.1",
|
||||
"@opentelemetry/api-logs": "0.218.0",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user