mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 14:01:24 +08:00
Compare commits
21 Commits
codex/node
...
v2026.5.4-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41f028e2ea | ||
|
|
303ff716d4 | ||
|
|
5fcdeae80c | ||
|
|
b73317c217 | ||
|
|
8f6bf65162 | ||
|
|
8017dc4c3b | ||
|
|
578d9072cf | ||
|
|
30b73bbf41 | ||
|
|
ade922ba98 | ||
|
|
997f8af734 | ||
|
|
6204a6fecc | ||
|
|
9f15c29397 | ||
|
|
cac973972c | ||
|
|
f8f18d53fc | ||
|
|
696f639cf6 | ||
|
|
079b937b46 | ||
|
|
32e36d355d | ||
|
|
12e1c67f22 | ||
|
|
766d02ff3b | ||
|
|
e9ebb6ce6c | ||
|
|
e0002c4b5b |
15
CHANGELOG.md
15
CHANGELOG.md
@@ -2,7 +2,7 @@
|
||||
|
||||
Docs: https://docs.openclaw.ai
|
||||
|
||||
## Unreleased
|
||||
## 2026.5.4
|
||||
|
||||
### Highlights
|
||||
|
||||
@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Changes
|
||||
|
||||
- Gateway/Windows: bind the default loopback gateway listener only to `127.0.0.1` on Windows so libuv's dual-stack `::1` behavior cannot wedge localhost HTTP requests. (#69701, fixes #69674) Thanks @SARAMALI15792.
|
||||
- Plugins/migration: emit catalog-backed install hints when `plugins.entries` or `plugins.allow` references an official external plugin that is not installed, so upgraded configs point operators to `openclaw plugins install <spec>` instead of telling them to remove valid plugin config. (#77483) Thanks @hclsys.
|
||||
- OpenAI/Codex media: advertise Codex audio transcription in runtime and manifest metadata and route active Codex chat models to the OpenAI transcription default instead of sending chat model ids to audio transcription. Thanks @vincentkoc.
|
||||
- Dependencies: refresh runtime and provider packages including Pi 0.73.0, ACPX adapters, OpenAI, Anthropic, Slack, and TypeScript native preview, while keeping the Bedrock runtime installer override pinned below the Windows ARM Node 24 npm resolver failure.
|
||||
@@ -59,22 +60,32 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/ClawHub: annotate 429 errors from ClawHub with the reset window from `RateLimit-Reset`/`Retry-After` and append a `Sign in for higher rate limits.` hint when the request was unauthenticated, so users can see when downloads will recover and how to lift the cap. Thanks @romneyda.
|
||||
- Plugins/runtime state: add `registerIfAbsent` for atomic keyed-store dedupe claims that return whether a plugin successfully claimed a key without overwriting an existing live value. Thanks @amknight.
|
||||
- Plugin SDK: add plugin-owned `SessionEntry` slot projection and scoped trusted-policy session extension reads. (#75609; replaces part of #73384/#74483) Thanks @100yenadmin.
|
||||
- Sandbox/Windows: accept drive-absolute Docker bind sources while keeping sandbox blocked-path and allowed-root policy comparisons Windows-case-insensitive. (#42174) Thanks @6607changchun.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Plugins/install: honor the beta update channel for onboarding and doctor-managed plugin installs by requesting floating npm and ClawHub specs with `@beta` while keeping persistent install records on the catalog default. Thanks @vincentkoc.
|
||||
- WhatsApp/onboarding: canonicalize setup and pairing allowlist entries to WhatsApp's digit-only phone ids while still accepting E.164, JID, and `whatsapp:` inputs, so personal-phone allowlists match WhatsApp Web sender ids after setup. Thanks @vincentkoc.
|
||||
- Gateway/startup: load provider plugins that own explicitly configured image, video, or music generation defaults so generation tools become live after gateway restart instead of remaining catalog-only. Fixes #77244. Thanks @buyuangtampan, @Nikoxx99, and @vincentkoc.
|
||||
- Slack/subagents: keep resumed parent `message.send` calls in the originating Slack thread when ambient session thread context is present, and suppress successful silent child completion rows from follow-up findings. Thanks @bek91.
|
||||
- Slack/mentions: record thread participation for successful visible threaded Slack sends, including message-tool and media delivery paths, so unmentioned replies in bot-participated threads can bypass mention gating as documented. Fixes #77648. Thanks @bek91.
|
||||
- Infra/Windows: skip the POSIX `/tmp/openclaw` preferred path on Windows in `resolvePreferredOpenClawTmpDir` so log files, TTS temp files, and other writes land in `%TEMP%\openclaw-<uid>` instead of `C:\tmp\openclaw`. Fixes #60713. Thanks @juan-flores077.
|
||||
- Media/Windows: open saved attachment temp files read/write before fsync so Windows WebChat and `chat.send` media offloads no longer fail with EPERM during durability flush. (#76593) Thanks @qq230849622-a11y.
|
||||
- Agents/tools: honor narrow runtime tool allowlists when constructing embedded-runner tool families and bundled MCP/LSP runtimes, so cron/subagent runs that request tools such as `update_plan`, `browser`, `x_search`, channel login tools, or `group:plugins` no longer start with missing tools or unrelated bootstrap work. (#77519, #77532)
|
||||
- Codex plugin: mirror the experimental upstream app-server protocol and format generated TypeScript before drift checks, keeping OpenClaw's `experimentalApi` bridge compatible with latest Codex while preserving formatter gates.
|
||||
- Telegram/media: derive no-caption inbound media placeholders from saved MIME metadata instead of the Telegram `photo` shape, so non-image and mixed attachments no longer reach the model as `<media:image>`. Fixes #69793. Thanks @aspalagin.
|
||||
- Telegram/streaming: reuse the active preview as the first chunk for long text finals, so multi-chunk replies no longer create a transient extra bubble that appears and then disappears. Thanks @vincentkoc.
|
||||
- Agents/cache: keep per-turn runtime context out of ordinary chat system prompts while still delivering hidden current-turn context, restoring prompt-cache reuse on chat continuations. Fixes #77431. Thanks @Udjin79.
|
||||
- Gateway/startup: include resolved thinking and fast-mode defaults in the `agent model` startup log line, defaulting unset startup thinking to `medium` without mixing in reasoning visibility.
|
||||
- Gateway/update: resolve local gateway probe auth from the installed config during post-update restart verification, so token/device-authenticated VPS gateways are not misreported as unhealthy port conflicts after a package swap. Thanks @vincentkoc.
|
||||
- Agents/Tools: add post-compaction loop guard in `pi-embedded-runner` that arms after auto-compaction-retry and aborts the run with `compaction_loop_persisted` when the agent emits the same `(tool, args, result)` triple `windowSize` times (default 3) within that window. Disable via existing `tools.loopDetection.enabled`; tune via `tools.loopDetection.postCompactionGuard.windowSize`. Targets the failure mode where context-overflow + compaction does not break a tool-call loop. Refs #77474; carries forward #21597. Thanks @efpiva.
|
||||
- Gateway/watch: suppress sync-I/O trace output during `pnpm gateway:watch --benchmark` unless explicitly requested, so CPU profiling no longer floods the terminal with stack traces.
|
||||
- Gateway/watch: when benchmark sync-I/O tracing is explicitly enabled, tee trace blocks to the benchmark output log and filter them from the terminal pane while keeping normal Gateway logs visible.
|
||||
- Plugins/runtime-deps: include `json5` in the memory-core plugin runtime dependency set so packaged `memory_search` sandboxes can resolve generated OpenClaw runtime chunks that parse JSON5 config. Fixes #77461.
|
||||
- Plugins/Windows: show a Git install hint when npm plugin installation fails with `spawn git ENOENT`, and document the WhatsApp plugin's Git-on-PATH requirement for Baileys/libsignal installs.
|
||||
- Codex harness: preserve app-server usage-limit reset details and deliver OpenClaw-owned runtime failure notices through tool-only source-reply mode, so Telegram and other chat channels tell users when Codex subscription limits or API failures block a turn instead of going silent. (#77557) Thanks @pashpashpash.
|
||||
- Agents/OpenAI: default direct OpenAI Responses models to the SSE transport instead of WebSocket auto-selection, preventing pi runtime chat turns from hanging on servers where the WebSocket path stalls while the OpenAI HTTP stream works. Thanks @vincentkoc.
|
||||
- Plugins/update: repair missing plugin-local `openclaw` peer links before skipping unchanged npm plugin updates, so current external Codex installs can recover `openclaw/plugin-sdk/*` resolution during OTA repair. (#77544) Thanks @ProspectOre.
|
||||
- Discord/replies: treat failed final reply delivery as a failed turn instead of counting it as a delivered automatic visible reply, so guild/channel turns no longer show done when the final message was dropped. Fixes #77520. Thanks @Patrick-Erichsen.
|
||||
- Discord: prefer IPv4 for Discord REST and gateway WebSocket startup paths so IPv4-only networks no longer stall before Gateway READY and inbound message dispatch. Fixes #77398; refs #77526. Thanks @Beandon13.
|
||||
- Channels/plugins: key bundled package-state probes, env/config presence, and read-only command defaults by channel id instead of manifest plugin id, preserving setup and native-command detection for channel plugins whose package id differs from the channel alias. Thanks @vincentkoc.
|
||||
@@ -216,6 +227,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Google Meet: make Twilio setup status require an enabled `voice-call` plugin entry instead of treating a missing entry as ready. Thanks @vincentkoc.
|
||||
- Telegram: render shared interactive reply buttons in reply delivery so plugin approval messages show inline keyboards. (#76238) Thanks @keshavbotagent.
|
||||
- Cron/sessions: keep cron metadata rows without an on-disk transcript non-resumable until a transcript exists, so doctor and `sessions cleanup --fix-missing` no longer report or prune pre-transcript cron rows as broken sessions. Refs #77011.
|
||||
- OpenAI Codex: recreate missing bound app-server threads once when a stale `/codex bind` sidecar survives a restart, preserving the selected auth profile and turn overrides before retrying the inbound turn. (#76936) Thanks @keshavbotagent.
|
||||
- Agents/cli-runner: drop a saved `claude-cli` resume sessionId at preparation time when its on-disk transcript no longer exists in `~/.claude/projects/`, so a stale binding from a half-installed `update.run` cannot trap follow-up runs (auto-reply / Telegram direct) in a `claude --resume` timeout loop; the run starts fresh and the new sessionId is written back through the existing post-run flow. (#77030; refs #77011) Thanks @openperf.
|
||||
- Release validation: install the cross-OS TypeScript harness through Windows-safe Node/npm shims so native Windows package checks reach the OpenClaw smoke suites instead of exiting before artifact capture. Thanks @vincentkoc.
|
||||
- Release validation: let Windows packaged-upgrade checks continue after the shipped 2026.5.2 updater hits its native-module swap cleanup fallback, verifying the fallback-installed candidate through package metadata and downstream smoke instead of crashing on the immediate update-status probe. Thanks @vincentkoc.
|
||||
@@ -1396,6 +1408,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/plugins: enable the native `require()` fast path on Windows for bundled plugin modules so plugin loading uses `require()` instead of Jiti's transform pipeline, reducing startup from ~39s to ~2s on typical 6-plugin setups. Fixes #68656. (#74173) Thanks @galiniliev.
|
||||
- macOS app: detect stale Gateway TLS certificate pins, automatically repair trusted Tailscale Serve rotations, and surface paired-but-disconnected Mac companion nodes so partial Gateway connections no longer look healthy. Thanks @guti.
|
||||
- Feishu: recreate WebSocket clients with monitor-owned backoff only after SDK reconnect exhaustion, preserving heartbeat defaults and shutdown cleanup without treating recoverable SDK callback errors as terminal, so persistent connections recover without manual gateway restart. Fixes #52618; duplicate evidence #59753; related #55532, #68766, #72411, and #73739. Thanks @vincentkoc, @schumilin, @alex-xuweilong, @120106835, @sirfengyu, and @tianhaocui.
|
||||
- Agents/skills: require exact `<location>` skill paths for both single-skill and multi-skill prompt selection, so agents do not guess or hard-code skill file paths. (#74161) Thanks @lanzhi-lee.
|
||||
|
||||
## 2026.4.27
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
43c6f668cd8301f485c64e6a663dc1b19d38c146ce2572943e2dc961973e0c6f plugin-sdk-api-baseline.json
|
||||
1d877d94bebb634d90d929fe0581ba4bccf4d12d8342d179ae9bf1053e68c013 plugin-sdk-api-baseline.jsonl
|
||||
50bd395c818460886af1fd545da4e3ace0fc7f7c36a43abd58f0a8cf76659f09 plugin-sdk-api-baseline.json
|
||||
bc609d44abbd58515f69ec88c947531c679f7f7910208c0761f52fda4481fa6e plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -344,6 +344,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
For text-only replies:
|
||||
|
||||
- short DM/group/topic previews: OpenClaw keeps the same preview message and performs a final edit in place, unless a visible non-preview message was sent after the preview appeared
|
||||
- long text finals that split into multiple Telegram messages reuse the existing preview as the first final chunk when possible, then send only the remaining chunks
|
||||
- previews followed by visible non-preview output: OpenClaw sends the completed reply as a fresh final message and cleans up the older preview, so the final answer appears after intermediate output
|
||||
- previews older than about one minute: OpenClaw sends the completed reply as a fresh final message and then cleans up the preview, so Telegram's visible timestamp reflects completion time instead of the preview creation time
|
||||
|
||||
|
||||
@@ -26,6 +26,16 @@ openclaw plugins install @openclaw/whatsapp
|
||||
Use the bare package to follow the current official release tag. Pin an exact
|
||||
version only when you need a reproducible install.
|
||||
|
||||
On Windows, the WhatsApp plugin needs Git on `PATH` during npm install because
|
||||
one of its Baileys/libsignal dependencies is fetched from a git URL. Install
|
||||
Git for Windows, then restart the shell and rerun the install:
|
||||
|
||||
```powershell
|
||||
winget install --id Git.Git -e
|
||||
```
|
||||
|
||||
Portable Git also works if its `bin` directory is on `PATH`.
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Pairing" icon="link" href="/channels/pairing">
|
||||
Default DM policy is pairing for unknown senders.
|
||||
|
||||
@@ -232,6 +232,8 @@ Scenarios (`extensions/qa-lab/src/live-transports/telegram/telegram-live.runtime
|
||||
- `telegram-tools-compact-command`
|
||||
- `telegram-whoami-command`
|
||||
- `telegram-context-command`
|
||||
- `telegram-long-final-reuses-preview`
|
||||
- `telegram-long-final-three-chunks`
|
||||
|
||||
Output artifacts:
|
||||
|
||||
|
||||
@@ -18,6 +18,16 @@ Adds the WhatsApp channel surface for sending and receiving OpenClaw messages.
|
||||
|
||||
channels: whatsapp
|
||||
|
||||
## Windows install note
|
||||
|
||||
On Windows, the WhatsApp plugin needs Git on `PATH` during npm install because one of its Baileys/libsignal dependencies is fetched from a git URL. Install Git for Windows, then restart the shell and rerun the install:
|
||||
|
||||
```powershell
|
||||
winget install --id Git.Git -e
|
||||
```
|
||||
|
||||
Portable Git also works if its `bin` directory is on `PATH`.
|
||||
|
||||
## Related docs
|
||||
|
||||
- [whatsapp](/channels/whatsapp)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw ACP runtime backend",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -25,10 +25,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4",
|
||||
"openclawVersion": "2026.5.4-beta.3",
|
||||
"staticAssets": [
|
||||
{
|
||||
"source": "./src/runtime-internals/mcp-proxy.mjs",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/alibaba-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Alibaba Model Studio video provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Amazon Bedrock Mantle (OpenAI-compatible) provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Amazon Bedrock provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic Vertex provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/arcee-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Arcee provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/azure-speech",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Azure Speech plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/bluebubbles",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw BlueBubbles channel plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -12,7 +12,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -53,10 +53,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/bonjour",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Bonjour/mDNS gateway discovery",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/brave-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Brave plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -20,10 +20,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/browser-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw browser tool plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/byteplus-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw BytePlus provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/cerebras-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Cerebras provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/chutes-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Chutes.ai provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Cloudflare AI Gateway provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/codex",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Codex harness and model provider plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -27,10 +27,10 @@
|
||||
"minHostVersion": ">=2026.5.1-beta.1"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -48,7 +48,10 @@ describe("codex conversation binding", () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
agentRuntimeMocks.ensureAuthProfileStore.mockReturnValue({ version: 1, profiles: {} });
|
||||
agentRuntimeMocks.ensureAuthProfileStore.mockReturnValue({
|
||||
version: 1,
|
||||
profiles: {},
|
||||
});
|
||||
agentRuntimeMocks.resolveAuthProfileOrder.mockReturnValue([]);
|
||||
agentRuntimeMocks.resolveOpenClawAgentDir.mockReturnValue("/agent");
|
||||
agentRuntimeMocks.resolveProviderIdForAuth.mockImplementation((provider: string) => provider);
|
||||
@@ -56,7 +59,9 @@ describe("codex conversation binding", () => {
|
||||
|
||||
it("uses the default Codex auth profile and omits the public OpenAI provider for new binds", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const config = { auth: { order: { "openai-codex": ["openai-codex:default"] } } };
|
||||
const config = {
|
||||
auth: { order: { "openai-codex": ["openai-codex:default"] } },
|
||||
};
|
||||
const requests: Array<{ method: string; params: Record<string, unknown> }> = [];
|
||||
agentRuntimeMocks.ensureAuthProfileStore.mockReturnValue({
|
||||
version: 1,
|
||||
@@ -220,6 +225,142 @@ describe("codex conversation binding", () => {
|
||||
expect(result).toEqual({ handled: true });
|
||||
});
|
||||
|
||||
it("recreates a missing bound thread and preserves auth plus turn overrides", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
agentRuntimeMocks.ensureAuthProfileStore.mockReturnValue({
|
||||
version: 1,
|
||||
profiles: {
|
||||
work: {
|
||||
type: "oauth",
|
||||
provider: "openai-codex",
|
||||
access: "access-token",
|
||||
},
|
||||
},
|
||||
});
|
||||
await fs.writeFile(
|
||||
`${sessionFile}.codex-app-server.json`,
|
||||
JSON.stringify({
|
||||
schemaVersion: 1,
|
||||
threadId: "thread-old",
|
||||
cwd: tempDir,
|
||||
authProfileId: "work",
|
||||
model: "gpt-5.4-mini",
|
||||
modelProvider: "openai",
|
||||
approvalPolicy: "on-request",
|
||||
sandbox: "workspace-write",
|
||||
serviceTier: "fast",
|
||||
}),
|
||||
);
|
||||
const requests: Array<{ method: string; params: Record<string, unknown> }> = [];
|
||||
const notificationHandlers: Array<(notification: Record<string, unknown>) => void> = [];
|
||||
sharedClientMocks.getSharedCodexAppServerClient.mockResolvedValue({
|
||||
request: vi.fn(async (method: string, requestParams: Record<string, unknown>) => {
|
||||
requests.push({ method, params: requestParams });
|
||||
if (method === "turn/start" && requestParams.threadId === "thread-old") {
|
||||
throw new Error("thread not found: thread-old");
|
||||
}
|
||||
if (method === "thread/start") {
|
||||
return {
|
||||
thread: { id: "thread-new", cwd: tempDir },
|
||||
model: "gpt-5.4-mini",
|
||||
};
|
||||
}
|
||||
if (method === "turn/start" && requestParams.threadId === "thread-new") {
|
||||
setImmediate(() => {
|
||||
for (const handler of notificationHandlers) {
|
||||
handler({
|
||||
method: "turn/completed",
|
||||
params: {
|
||||
threadId: "thread-new",
|
||||
turn: {
|
||||
id: "turn-new",
|
||||
status: "completed",
|
||||
items: [
|
||||
{
|
||||
id: "assistant-1",
|
||||
type: "agentMessage",
|
||||
text: "Recovered",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
return { turn: { id: "turn-new" } };
|
||||
}
|
||||
throw new Error(`unexpected method: ${method}`);
|
||||
}),
|
||||
addNotificationHandler: vi.fn((handler) => {
|
||||
notificationHandlers.push(handler);
|
||||
return () => undefined;
|
||||
}),
|
||||
addRequestHandler: vi.fn(() => () => undefined),
|
||||
});
|
||||
|
||||
const result = await handleCodexConversationInboundClaim(
|
||||
{
|
||||
content: "hi again",
|
||||
bodyForAgent: "hi again",
|
||||
channel: "telegram",
|
||||
isGroup: false,
|
||||
commandAuthorized: true,
|
||||
},
|
||||
{
|
||||
channelId: "telegram",
|
||||
pluginBinding: {
|
||||
bindingId: "binding-1",
|
||||
pluginId: "codex",
|
||||
pluginRoot: tempDir,
|
||||
channel: "telegram",
|
||||
accountId: "default",
|
||||
conversationId: "5185575566",
|
||||
boundAt: Date.now(),
|
||||
data: {
|
||||
kind: "codex-app-server-session",
|
||||
version: 1,
|
||||
sessionFile,
|
||||
workspaceDir: tempDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ timeoutMs: 500 },
|
||||
);
|
||||
|
||||
expect(result).toEqual({ handled: true, reply: { text: "Recovered" } });
|
||||
expect(requests.map((request) => request.method)).toEqual([
|
||||
"turn/start",
|
||||
"thread/start",
|
||||
"turn/start",
|
||||
]);
|
||||
expect(sharedClientMocks.getSharedCodexAppServerClient).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ authProfileId: "work" }),
|
||||
);
|
||||
expect(requests[1]?.params).toMatchObject({
|
||||
model: "gpt-5.4-mini",
|
||||
approvalPolicy: "on-request",
|
||||
sandbox: "workspace-write",
|
||||
serviceTier: "fast",
|
||||
});
|
||||
expect(requests[1]?.params).not.toHaveProperty("modelProvider");
|
||||
expect(requests[2]?.params).toMatchObject({
|
||||
threadId: "thread-new",
|
||||
approvalPolicy: "on-request",
|
||||
serviceTier: "fast",
|
||||
});
|
||||
const savedBinding = JSON.parse(
|
||||
await fs.readFile(`${sessionFile}.codex-app-server.json`, "utf8"),
|
||||
);
|
||||
expect(savedBinding).toMatchObject({
|
||||
threadId: "thread-new",
|
||||
authProfileId: "work",
|
||||
approvalPolicy: "on-request",
|
||||
sandbox: "workspace-write",
|
||||
serviceTier: "fast",
|
||||
});
|
||||
expect(savedBinding).not.toHaveProperty("modelProvider");
|
||||
});
|
||||
|
||||
it("returns a clean failure reply when app-server turn start rejects", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
await fs.writeFile(
|
||||
|
||||
@@ -10,8 +10,11 @@ import { CODEX_CONTROL_METHODS } from "./app-server/capabilities.js";
|
||||
import {
|
||||
codexSandboxPolicyForTurn,
|
||||
resolveCodexAppServerRuntimeOptions,
|
||||
type CodexAppServerApprovalPolicy,
|
||||
type CodexAppServerSandboxMode,
|
||||
} from "./app-server/config.js";
|
||||
import {
|
||||
type CodexServiceTier,
|
||||
type CodexThreadResumeResponse,
|
||||
type CodexThreadStartResponse,
|
||||
type CodexTurnStartResponse,
|
||||
@@ -59,6 +62,9 @@ type CodexConversationStartParams = {
|
||||
model?: string;
|
||||
modelProvider?: string;
|
||||
authProfileId?: string;
|
||||
approvalPolicy?: CodexAppServerApprovalPolicy;
|
||||
sandbox?: CodexAppServerSandboxMode;
|
||||
serviceTier?: CodexServiceTier;
|
||||
};
|
||||
|
||||
type BoundTurnResult = {
|
||||
@@ -100,6 +106,9 @@ export async function startCodexConversationThread(
|
||||
model: params.model,
|
||||
modelProvider: params.modelProvider,
|
||||
authProfileId,
|
||||
approvalPolicy: params.approvalPolicy,
|
||||
sandbox: params.sandbox,
|
||||
serviceTier: params.serviceTier,
|
||||
config: params.config,
|
||||
});
|
||||
} else {
|
||||
@@ -110,6 +119,9 @@ export async function startCodexConversationThread(
|
||||
model: params.model,
|
||||
modelProvider: params.modelProvider,
|
||||
authProfileId,
|
||||
approvalPolicy: params.approvalPolicy,
|
||||
sandbox: params.sandbox,
|
||||
serviceTier: params.serviceTier,
|
||||
config: params.config,
|
||||
});
|
||||
}
|
||||
@@ -137,7 +149,7 @@ export async function handleCodexConversationInboundClaim(
|
||||
}
|
||||
try {
|
||||
const result = await enqueueBoundTurn(data.sessionFile, () =>
|
||||
runBoundTurn({
|
||||
runBoundTurnWithMissingThreadRecovery({
|
||||
data,
|
||||
prompt,
|
||||
event,
|
||||
@@ -177,9 +189,14 @@ async function attachExistingThread(params: {
|
||||
model?: string;
|
||||
modelProvider?: string;
|
||||
authProfileId?: string;
|
||||
approvalPolicy?: CodexAppServerApprovalPolicy;
|
||||
sandbox?: CodexAppServerSandboxMode;
|
||||
serviceTier?: CodexServiceTier;
|
||||
config?: CodexAppServerAuthProfileLookup["config"];
|
||||
}): Promise<void> {
|
||||
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
|
||||
const runtime = resolveCodexAppServerRuntimeOptions({
|
||||
pluginConfig: params.pluginConfig,
|
||||
});
|
||||
const modelProvider = resolveThreadRequestModelProvider({
|
||||
authProfileId: params.authProfileId,
|
||||
modelProvider: params.modelProvider,
|
||||
@@ -196,10 +213,12 @@ async function attachExistingThread(params: {
|
||||
threadId: params.threadId,
|
||||
...(params.model ? { model: params.model } : {}),
|
||||
...(modelProvider ? { modelProvider } : {}),
|
||||
approvalPolicy: runtime.approvalPolicy,
|
||||
approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
|
||||
approvalsReviewer: runtime.approvalsReviewer,
|
||||
sandbox: runtime.sandbox,
|
||||
...(runtime.serviceTier ? { serviceTier: runtime.serviceTier } : {}),
|
||||
sandbox: params.sandbox ?? runtime.sandbox,
|
||||
...((params.serviceTier ?? runtime.serviceTier)
|
||||
? { serviceTier: params.serviceTier ?? runtime.serviceTier }
|
||||
: {}),
|
||||
persistExtendedHistory: true,
|
||||
},
|
||||
{ timeoutMs: runtime.requestTimeoutMs },
|
||||
@@ -217,9 +236,9 @@ async function attachExistingThread(params: {
|
||||
authProfileId: params.authProfileId,
|
||||
modelProvider: response.modelProvider ?? params.modelProvider,
|
||||
}),
|
||||
approvalPolicy: runtime.approvalPolicy,
|
||||
sandbox: runtime.sandbox,
|
||||
serviceTier: runtime.serviceTier,
|
||||
approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
|
||||
sandbox: params.sandbox ?? runtime.sandbox,
|
||||
serviceTier: params.serviceTier ?? runtime.serviceTier,
|
||||
},
|
||||
{
|
||||
config: params.config,
|
||||
@@ -234,9 +253,14 @@ async function createThread(params: {
|
||||
model?: string;
|
||||
modelProvider?: string;
|
||||
authProfileId?: string;
|
||||
approvalPolicy?: CodexAppServerApprovalPolicy;
|
||||
sandbox?: CodexAppServerSandboxMode;
|
||||
serviceTier?: CodexServiceTier;
|
||||
config?: CodexAppServerAuthProfileLookup["config"];
|
||||
}): Promise<void> {
|
||||
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
|
||||
const runtime = resolveCodexAppServerRuntimeOptions({
|
||||
pluginConfig: params.pluginConfig,
|
||||
});
|
||||
const modelProvider = resolveThreadRequestModelProvider({
|
||||
authProfileId: params.authProfileId,
|
||||
modelProvider: params.modelProvider,
|
||||
@@ -253,10 +277,12 @@ async function createThread(params: {
|
||||
cwd: params.workspaceDir,
|
||||
...(params.model ? { model: params.model } : {}),
|
||||
...(modelProvider ? { modelProvider } : {}),
|
||||
approvalPolicy: runtime.approvalPolicy,
|
||||
approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
|
||||
approvalsReviewer: runtime.approvalsReviewer,
|
||||
sandbox: runtime.sandbox,
|
||||
...(runtime.serviceTier ? { serviceTier: runtime.serviceTier } : {}),
|
||||
sandbox: params.sandbox ?? runtime.sandbox,
|
||||
...((params.serviceTier ?? runtime.serviceTier)
|
||||
? { serviceTier: params.serviceTier ?? runtime.serviceTier }
|
||||
: {}),
|
||||
developerInstructions:
|
||||
"This Codex thread is bound to an OpenClaw conversation. Answer normally; OpenClaw will deliver your final response back to the conversation.",
|
||||
experimentalRawEvents: true,
|
||||
@@ -276,9 +302,9 @@ async function createThread(params: {
|
||||
authProfileId: params.authProfileId,
|
||||
modelProvider: response.modelProvider ?? params.modelProvider,
|
||||
}),
|
||||
approvalPolicy: runtime.approvalPolicy,
|
||||
sandbox: runtime.sandbox,
|
||||
serviceTier: runtime.serviceTier,
|
||||
approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
|
||||
sandbox: params.sandbox ?? runtime.sandbox,
|
||||
serviceTier: params.serviceTier ?? runtime.serviceTier,
|
||||
},
|
||||
{
|
||||
config: params.config,
|
||||
@@ -293,7 +319,9 @@ async function runBoundTurn(params: {
|
||||
pluginConfig?: unknown;
|
||||
timeoutMs?: number;
|
||||
}): Promise<BoundTurnResult> {
|
||||
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
|
||||
const runtime = resolveCodexAppServerRuntimeOptions({
|
||||
pluginConfig: params.pluginConfig,
|
||||
});
|
||||
const binding = await readCodexAppServerBinding(params.data.sessionFile);
|
||||
const threadId = binding?.threadId;
|
||||
if (!threadId) {
|
||||
@@ -350,7 +378,10 @@ async function runBoundTurn(params: {
|
||||
"turn/start",
|
||||
{
|
||||
threadId,
|
||||
input: buildCodexConversationTurnInput({ prompt: params.prompt, event: params.event }),
|
||||
input: buildCodexConversationTurnInput({
|
||||
prompt: params.prompt,
|
||||
event: params.event,
|
||||
}),
|
||||
cwd: binding.cwd || params.data.workspaceDir,
|
||||
approvalPolicy: binding.approvalPolicy ?? runtime.approvalPolicy,
|
||||
approvalsReviewer: runtime.approvalsReviewer,
|
||||
@@ -389,6 +420,39 @@ async function runBoundTurn(params: {
|
||||
}
|
||||
}
|
||||
|
||||
async function runBoundTurnWithMissingThreadRecovery(params: {
|
||||
data: CodexConversationBindingData;
|
||||
prompt: string;
|
||||
event: PluginHookInboundClaimEvent;
|
||||
pluginConfig?: unknown;
|
||||
timeoutMs?: number;
|
||||
}): Promise<BoundTurnResult> {
|
||||
try {
|
||||
return await runBoundTurn(params);
|
||||
} catch (error) {
|
||||
if (!isCodexThreadNotFoundError(error)) {
|
||||
throw error;
|
||||
}
|
||||
const binding = await readCodexAppServerBinding(params.data.sessionFile);
|
||||
await startCodexConversationThread({
|
||||
pluginConfig: params.pluginConfig,
|
||||
sessionFile: params.data.sessionFile,
|
||||
workspaceDir: binding?.cwd || params.data.workspaceDir,
|
||||
model: binding?.model,
|
||||
modelProvider: binding?.modelProvider,
|
||||
authProfileId: binding?.authProfileId,
|
||||
approvalPolicy: binding?.approvalPolicy,
|
||||
sandbox: binding?.sandbox,
|
||||
serviceTier: binding?.serviceTier,
|
||||
});
|
||||
return await runBoundTurn(params);
|
||||
}
|
||||
}
|
||||
|
||||
function isCodexThreadNotFoundError(error: unknown): boolean {
|
||||
return /\bthread not found:/iu.test(formatErrorMessage(error));
|
||||
}
|
||||
|
||||
function enqueueBoundTurn<T>(key: string, run: () => Promise<T>): Promise<T> {
|
||||
const state = getGlobalState();
|
||||
const previous = state.queues.get(key) ?? Promise.resolve();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/comfy-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw ComfyUI provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/copilot-proxy",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Copilot Proxy provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepgram-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Deepgram media-understanding provider",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepinfra-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw DeepInfra provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/deepseek-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw DeepSeek provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/diagnostics-otel",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw diagnostics OpenTelemetry exporter",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -34,10 +34,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/diagnostics-prometheus",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw diagnostics Prometheus exporter",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,10 +21,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/diffs",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw diff viewer plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -30,10 +30,10 @@
|
||||
"minHostVersion": ">=2026.4.30"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4",
|
||||
"openclawVersion": "2026.5.4-beta.3",
|
||||
"staticAssets": [
|
||||
{
|
||||
"source": "./assets/viewer-runtime.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/discord",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Discord channel plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,7 +21,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -65,10 +65,10 @@
|
||||
"allowInvalidConfigRecovery": true
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/document-extract-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw local document extraction plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/duckduckgo-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw DuckDuckGo plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/elevenlabs-speech",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw ElevenLabs speech plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/exa-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Exa plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/fal-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw fal provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/feishu",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Feishu/Lark channel plugin (community maintained by @m1heng)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,7 +16,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -47,10 +47,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/file-transfer",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw file transfer plugin (file_fetch, dir_list, dir_fetch, file_write)",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/firecrawl-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Firecrawl plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/fireworks-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Fireworks provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/github-copilot-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw GitHub Copilot provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/google-meet",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Google Meet participant plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,7 +16,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -33,10 +33,10 @@
|
||||
"minHostVersion": ">=2026.4.20"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/google-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Google plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/googlechat",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Google Chat channel plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -17,7 +17,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -75,10 +75,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/gradium-speech",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Gradium speech plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/groq-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Groq media-understanding provider",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/huggingface-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Hugging Face provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/image-generation-core",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw image generation runtime package",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/imessage",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw iMessage channel plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/inworld-speech",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Inworld speech plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/irc",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw IRC channel plugin",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/kilocode-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Kilo Gateway provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/kimi-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Kimi provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/line",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw LINE channel plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -15,7 +15,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -45,10 +45,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/litellm-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw LiteLLM provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/llm-task",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw JSON-only LLM task plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/lmstudio-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw LM Studio provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/lobster",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -25,10 +25,10 @@
|
||||
"minHostVersion": ">=2026.4.25"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/matrix",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Matrix channel plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,7 +21,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/mattermost",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Mattermost channel plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -15,7 +15,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/media-understanding-core",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw media understanding runtime package",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/memory-core",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw core memory search plugin",
|
||||
"type": "module",
|
||||
@@ -14,7 +14,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/memory-lancedb",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw LanceDB-backed long-term memory plugin with auto-recall/capture",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -26,10 +26,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/memory-wiki",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw persistent wiki plugin",
|
||||
"type": "module",
|
||||
@@ -13,7 +13,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/microsoft-foundry",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Microsoft Foundry provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/microsoft-speech",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Microsoft speech plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/migrate-claude",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "Claude to OpenClaw migration provider",
|
||||
"type": "module",
|
||||
@@ -9,7 +9,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/migrate-hermes",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "Hermes to OpenClaw migration provider",
|
||||
"type": "module",
|
||||
@@ -12,7 +12,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/minimax-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw MiniMax provider and OAuth plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/mistral-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Mistral provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/moonshot-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Moonshot provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/msteams",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Microsoft Teams channel plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -22,7 +22,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -58,10 +58,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/nextcloud-talk",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Nextcloud Talk channel plugin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -15,7 +15,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -47,10 +47,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/nostr",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"description": "OpenClaw Nostr channel plugin for NIP-04 encrypted DMs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,7 +16,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -54,10 +54,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/nvidia-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw NVIDIA provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/ollama-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Ollama provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/open-prose",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenProse VM skill pack plugin (slash command + telemetry).",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/openai-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw OpenAI provider plugins",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/opencode-go-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw OpenCode Go provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/opencode-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw OpenCode Zen provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/openrouter-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw OpenRouter provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/openshell-sandbox",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw OpenShell sandbox backend",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/perplexity-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Perplexity plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/qa-channel",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw QA synthetic channel plugin",
|
||||
"type": "module",
|
||||
@@ -18,7 +18,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/qa-lab",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw QA lab plugin with private debugger UI and scenario runner",
|
||||
"type": "module",
|
||||
@@ -18,7 +18,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"./index.ts"
|
||||
],
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,6 +333,8 @@ describe("telegram live qa runtime", () => {
|
||||
"telegram-context-command",
|
||||
"telegram-current-session-status-tool",
|
||||
"telegram-mentioned-message-reply",
|
||||
"telegram-long-final-reuses-preview",
|
||||
"telegram-long-final-three-chunks",
|
||||
"telegram-mention-gating",
|
||||
]);
|
||||
expect(scenarios.map((scenario) => scenario.id)).toEqual([
|
||||
@@ -343,6 +345,8 @@ describe("telegram live qa runtime", () => {
|
||||
"telegram-context-command",
|
||||
"telegram-current-session-status-tool",
|
||||
"telegram-mentioned-message-reply",
|
||||
"telegram-long-final-reuses-preview",
|
||||
"telegram-long-final-three-chunks",
|
||||
"telegram-mention-gating",
|
||||
]);
|
||||
expect(
|
||||
@@ -355,6 +359,25 @@ describe("telegram live qa runtime", () => {
|
||||
.find((scenario) => scenario.id === "telegram-mentioned-message-reply")
|
||||
?.buildRun("sut_bot").replyToLatestSutMessage,
|
||||
).toBe(true);
|
||||
expect(
|
||||
scenarios
|
||||
.find((scenario) => scenario.id === "telegram-long-final-reuses-preview")
|
||||
?.buildRun("sut_bot"),
|
||||
).toMatchObject({
|
||||
expectedJoinedSutTextIncludes: ["TELEGRAM-LONG-FINAL-BEGIN", "TELEGRAM-LONG-FINAL-END"],
|
||||
expectedSutMessageCount: 2,
|
||||
});
|
||||
expect(
|
||||
scenarios
|
||||
.find((scenario) => scenario.id === "telegram-long-final-three-chunks")
|
||||
?.buildRun("sut_bot"),
|
||||
).toMatchObject({
|
||||
expectedJoinedSutTextIncludes: [
|
||||
"TELEGRAM-LONG-FINAL-3CHUNK-BEGIN",
|
||||
"TELEGRAM-LONG-FINAL-3CHUNK-END",
|
||||
],
|
||||
expectedSutMessageCount: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps bot-to-bot plain mentions out of the default Telegram live set", () => {
|
||||
@@ -382,6 +405,160 @@ describe("telegram live qa runtime", () => {
|
||||
).toEqual(["allowlist-block", "top-level-reply-shape", "restart-resume"]);
|
||||
});
|
||||
|
||||
it("asserts long Telegram final replies reuse the streamed preview message", () => {
|
||||
expect(() =>
|
||||
__testing.assertTelegramScenarioMessageSet({
|
||||
expectedJoinedSutTextIncludes: ["TELEGRAM-LONG-FINAL-BEGIN", "TELEGRAM-LONG-FINAL-END"],
|
||||
expectedSutMessageCount: 2,
|
||||
groupId: "-100123",
|
||||
scenarioId: "telegram-long-final-reuses-preview",
|
||||
sutBotId: 99,
|
||||
observedMessages: [
|
||||
{
|
||||
updateId: 1,
|
||||
messageId: 10,
|
||||
chatId: -100123,
|
||||
senderId: 99,
|
||||
senderIsBot: true,
|
||||
scenarioId: "telegram-long-final-reuses-preview",
|
||||
scenarioTitle: "Telegram long final reuses the preview message",
|
||||
matchedScenario: true,
|
||||
text: "TELEGRAM-LONG-FINAL-BEGIN part one ",
|
||||
timestamp: 1_700_000_000_000,
|
||||
inlineButtons: [],
|
||||
mediaKinds: [],
|
||||
},
|
||||
{
|
||||
updateId: 2,
|
||||
messageId: 11,
|
||||
chatId: -100123,
|
||||
senderId: 99,
|
||||
senderIsBot: true,
|
||||
scenarioId: "telegram-long-final-reuses-preview",
|
||||
scenarioTitle: "Telegram long final reuses the preview message",
|
||||
matchedScenario: true,
|
||||
text: "part two TELEGRAM-LONG-FINAL-END",
|
||||
timestamp: 1_700_000_001_000,
|
||||
inlineButtons: [],
|
||||
mediaKinds: [],
|
||||
},
|
||||
],
|
||||
}),
|
||||
).not.toThrow();
|
||||
|
||||
expect(() =>
|
||||
__testing.assertTelegramScenarioMessageSet({
|
||||
expectedSutMessageCount: 2,
|
||||
groupId: "-100123",
|
||||
scenarioId: "telegram-long-final-reuses-preview",
|
||||
sutBotId: 99,
|
||||
observedMessages: [
|
||||
{
|
||||
updateId: 1,
|
||||
messageId: 10,
|
||||
chatId: -100123,
|
||||
senderId: 99,
|
||||
senderIsBot: true,
|
||||
scenarioId: "telegram-long-final-reuses-preview",
|
||||
scenarioTitle: "Telegram long final reuses the preview message",
|
||||
matchedScenario: true,
|
||||
text: "preview",
|
||||
timestamp: 1_700_000_000_000,
|
||||
inlineButtons: [],
|
||||
mediaKinds: [],
|
||||
},
|
||||
{
|
||||
updateId: 2,
|
||||
messageId: 11,
|
||||
chatId: -100123,
|
||||
senderId: 99,
|
||||
senderIsBot: true,
|
||||
scenarioId: "telegram-long-final-reuses-preview",
|
||||
scenarioTitle: "Telegram long final reuses the preview message",
|
||||
matchedScenario: true,
|
||||
text: "final chunk one",
|
||||
timestamp: 1_700_000_001_000,
|
||||
inlineButtons: [],
|
||||
mediaKinds: [],
|
||||
},
|
||||
{
|
||||
updateId: 3,
|
||||
messageId: 12,
|
||||
chatId: -100123,
|
||||
senderId: 99,
|
||||
senderIsBot: true,
|
||||
scenarioId: "telegram-long-final-reuses-preview",
|
||||
scenarioTitle: "Telegram long final reuses the preview message",
|
||||
matchedScenario: true,
|
||||
text: "final chunk two",
|
||||
timestamp: 1_700_000_002_000,
|
||||
inlineButtons: [],
|
||||
mediaKinds: [],
|
||||
},
|
||||
],
|
||||
}),
|
||||
).toThrow("expected 2 SUT message(s), observed 3");
|
||||
});
|
||||
|
||||
it("accepts legitimate three-chunk Telegram final replies", () => {
|
||||
expect(() =>
|
||||
__testing.assertTelegramScenarioMessageSet({
|
||||
expectedJoinedSutTextIncludes: [
|
||||
"TELEGRAM-LONG-FINAL-3CHUNK-BEGIN",
|
||||
"TELEGRAM-LONG-FINAL-3CHUNK-END",
|
||||
],
|
||||
expectedSutMessageCount: 3,
|
||||
groupId: "-100123",
|
||||
scenarioId: "telegram-long-final-three-chunks",
|
||||
sutBotId: 99,
|
||||
observedMessages: [
|
||||
{
|
||||
updateId: 1,
|
||||
messageId: 10,
|
||||
chatId: -100123,
|
||||
senderId: 99,
|
||||
senderIsBot: true,
|
||||
scenarioId: "telegram-long-final-three-chunks",
|
||||
scenarioTitle: "Telegram three-chunk final keeps only final chunks",
|
||||
matchedScenario: true,
|
||||
text: "TELEGRAM-LONG-FINAL-3CHUNK-BEGIN part one ",
|
||||
timestamp: 1_700_000_000_000,
|
||||
inlineButtons: [],
|
||||
mediaKinds: [],
|
||||
},
|
||||
{
|
||||
updateId: 2,
|
||||
messageId: 11,
|
||||
chatId: -100123,
|
||||
senderId: 99,
|
||||
senderIsBot: true,
|
||||
scenarioId: "telegram-long-final-three-chunks",
|
||||
scenarioTitle: "Telegram three-chunk final keeps only final chunks",
|
||||
matchedScenario: true,
|
||||
text: "part two ",
|
||||
timestamp: 1_700_000_001_000,
|
||||
inlineButtons: [],
|
||||
mediaKinds: [],
|
||||
},
|
||||
{
|
||||
updateId: 3,
|
||||
messageId: 12,
|
||||
chatId: -100123,
|
||||
senderId: 99,
|
||||
senderIsBot: true,
|
||||
scenarioId: "telegram-long-final-three-chunks",
|
||||
scenarioTitle: "Telegram three-chunk final keeps only final chunks",
|
||||
matchedScenario: true,
|
||||
text: "part three TELEGRAM-LONG-FINAL-3CHUNK-END",
|
||||
timestamp: 1_700_000_002_000,
|
||||
inlineButtons: [],
|
||||
mediaKinds: [],
|
||||
},
|
||||
],
|
||||
}),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it("matches scenario replies by thread or exact marker", () => {
|
||||
expect(
|
||||
__testing.matchesTelegramScenarioReply({
|
||||
|
||||
@@ -48,6 +48,8 @@ type TelegramQaScenarioId =
|
||||
| "telegram-whoami-command"
|
||||
| "telegram-context-command"
|
||||
| "telegram-current-session-status-tool"
|
||||
| "telegram-long-final-three-chunks"
|
||||
| "telegram-long-final-reuses-preview"
|
||||
| "telegram-mentioned-message-reply"
|
||||
| "telegram-mention-gating";
|
||||
|
||||
@@ -56,8 +58,11 @@ type TelegramQaScenarioRun = {
|
||||
expectReply: boolean;
|
||||
input: string;
|
||||
expectedTextIncludes?: string[];
|
||||
expectedJoinedSutTextIncludes?: string[];
|
||||
expectedSutMessageCount?: number;
|
||||
matchText?: string;
|
||||
replyToLatestSutMessage?: boolean;
|
||||
settleMs?: number;
|
||||
};
|
||||
|
||||
type TelegramQaScenarioDefinition = LiveTransportScenarioDefinition<TelegramQaScenarioId> & {
|
||||
@@ -295,6 +300,39 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
|
||||
replyToLatestSutMessage: true,
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "telegram-long-final-reuses-preview",
|
||||
title: "Telegram long final reuses the preview message",
|
||||
defaultEnabled: false,
|
||||
timeoutMs: 60_000,
|
||||
buildRun: (sutUsername) => ({
|
||||
allowAnySutReply: true,
|
||||
expectReply: true,
|
||||
input: `@${sutUsername} Telegram long final QA check. Use the scripted long final response.`,
|
||||
expectedTextIncludes: ["TELEGRAM-LONG-FINAL-BEGIN"],
|
||||
expectedJoinedSutTextIncludes: ["TELEGRAM-LONG-FINAL-BEGIN", "TELEGRAM-LONG-FINAL-END"],
|
||||
expectedSutMessageCount: 2,
|
||||
settleMs: 4_000,
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "telegram-long-final-three-chunks",
|
||||
title: "Telegram three-chunk final keeps only final chunks",
|
||||
defaultEnabled: false,
|
||||
timeoutMs: 60_000,
|
||||
buildRun: (sutUsername) => ({
|
||||
allowAnySutReply: true,
|
||||
expectReply: true,
|
||||
input: `@${sutUsername} Telegram long final three chunk QA check. Use the scripted three chunk final response.`,
|
||||
expectedTextIncludes: ["TELEGRAM-LONG-FINAL-3CHUNK-BEGIN"],
|
||||
expectedJoinedSutTextIncludes: [
|
||||
"TELEGRAM-LONG-FINAL-3CHUNK-BEGIN",
|
||||
"TELEGRAM-LONG-FINAL-3CHUNK-END",
|
||||
],
|
||||
expectedSutMessageCount: 3,
|
||||
settleMs: 4_000,
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "telegram-mention-gating",
|
||||
standardId: "mention-gating",
|
||||
@@ -744,6 +782,102 @@ async function waitForObservedMessage(params: {
|
||||
throw new Error(timeoutMessage);
|
||||
}
|
||||
|
||||
async function collectObservedMessages(params: {
|
||||
token: string;
|
||||
initialOffset: number;
|
||||
settleMs: number;
|
||||
predicate: (message: TelegramObservedMessage) => boolean;
|
||||
observedMessages: TelegramObservedMessage[];
|
||||
observationScenarioId: string;
|
||||
observationScenarioTitle: string;
|
||||
}) {
|
||||
const startedAt = Date.now();
|
||||
let offset = params.initialOffset;
|
||||
while (Date.now() - startedAt < params.settleMs) {
|
||||
const remainingMs = Math.max(1, params.settleMs - (Date.now() - startedAt));
|
||||
const timeoutSeconds = Math.max(1, Math.min(2, Math.ceil(remainingMs / 1000)));
|
||||
let updates: TelegramUpdate[];
|
||||
try {
|
||||
updates = await callTelegramApi<TelegramUpdate[]>(
|
||||
params.token,
|
||||
"getUpdates",
|
||||
{
|
||||
offset,
|
||||
timeout: timeoutSeconds,
|
||||
allowed_updates: ["message", "edited_message"],
|
||||
},
|
||||
timeoutSeconds * 1000 + 5_000,
|
||||
);
|
||||
} catch (error) {
|
||||
if (!isRecoverableTelegramQaPollError(error)) {
|
||||
throw error;
|
||||
}
|
||||
await waitForTelegramPollRetryDelay(params.settleMs - (Date.now() - startedAt));
|
||||
continue;
|
||||
}
|
||||
if (updates.length === 0) {
|
||||
continue;
|
||||
}
|
||||
offset = (updates.at(-1)?.update_id ?? offset) + 1;
|
||||
for (const update of updates) {
|
||||
const normalized = normalizeTelegramObservedMessage(update);
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
params.observedMessages.push({
|
||||
...normalized,
|
||||
scenarioId: params.observationScenarioId,
|
||||
scenarioTitle: params.observationScenarioTitle,
|
||||
matchedScenario: params.predicate(normalized),
|
||||
});
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
function assertTelegramScenarioMessageSet(params: {
|
||||
expectedJoinedSutTextIncludes?: string[];
|
||||
expectedSutMessageCount?: number;
|
||||
groupId: string;
|
||||
observedMessages: TelegramObservedMessage[];
|
||||
scenarioId: string;
|
||||
sutBotId: number;
|
||||
}) {
|
||||
if (
|
||||
params.expectedSutMessageCount === undefined &&
|
||||
(params.expectedJoinedSutTextIncludes ?? []).length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const byMessageId = new Map<number, TelegramObservedMessage>();
|
||||
for (const message of params.observedMessages) {
|
||||
if (
|
||||
message.scenarioId === params.scenarioId &&
|
||||
message.chatId === Number(params.groupId) &&
|
||||
message.senderId === params.sutBotId
|
||||
) {
|
||||
byMessageId.set(message.messageId, message);
|
||||
}
|
||||
}
|
||||
const messages = [...byMessageId.values()].toSorted((a, b) => a.messageId - b.messageId);
|
||||
if (
|
||||
params.expectedSutMessageCount !== undefined &&
|
||||
messages.length !== params.expectedSutMessageCount
|
||||
) {
|
||||
throw new Error(
|
||||
`expected ${params.expectedSutMessageCount} SUT message(s), observed ${messages.length}: ${messages
|
||||
.map((message) => message.messageId)
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
const joinedText = messages.map((message) => message.text).join("");
|
||||
for (const expected of params.expectedJoinedSutTextIncludes ?? []) {
|
||||
if (!joinedText.includes(expected)) {
|
||||
throw new Error(`joined SUT reply text missing expected text: ${expected}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForTelegramChannelRunning(
|
||||
gateway: Awaited<ReturnType<typeof startQaGatewayChild>>,
|
||||
accountId: string,
|
||||
@@ -1374,6 +1508,25 @@ export async function runTelegramQaLive(params: {
|
||||
}),
|
||||
});
|
||||
driverOffset = matched.nextOffset;
|
||||
if (scenarioRun.settleMs !== undefined) {
|
||||
driverOffset = await collectObservedMessages({
|
||||
token: runtimeEnv.driverToken,
|
||||
initialOffset: driverOffset,
|
||||
settleMs: scenarioRun.settleMs,
|
||||
observedMessages,
|
||||
observationScenarioId: scenario.id,
|
||||
observationScenarioTitle: scenario.title,
|
||||
predicate: (message) =>
|
||||
matchesTelegramScenarioReply({
|
||||
allowAnySutReply: scenarioRun.allowAnySutReply,
|
||||
groupId: runtimeEnv.groupId,
|
||||
matchText: scenarioRun.matchText,
|
||||
message,
|
||||
sentMessageId: sent.message_id,
|
||||
sutBotId: sutIdentity.id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (!scenarioRun.expectReply) {
|
||||
throw new Error(`unexpected reply message ${matched.message.messageId} matched`);
|
||||
}
|
||||
@@ -1381,14 +1534,26 @@ export async function runTelegramQaLive(params: {
|
||||
expectedTextIncludes: scenarioRun.expectedTextIncludes,
|
||||
message: matched.message,
|
||||
});
|
||||
assertTelegramScenarioMessageSet({
|
||||
expectedJoinedSutTextIncludes: scenarioRun.expectedJoinedSutTextIncludes,
|
||||
expectedSutMessageCount: scenarioRun.expectedSutMessageCount,
|
||||
groupId: runtimeEnv.groupId,
|
||||
observedMessages,
|
||||
scenarioId: scenario.id,
|
||||
sutBotId: sutIdentity.id,
|
||||
});
|
||||
const rttMs = matched.observedAtMs - requestStartedAtMs;
|
||||
const suffix =
|
||||
scenarioRun.expectedSutMessageCount === undefined
|
||||
? ""
|
||||
: `; observed ${scenarioRun.expectedSutMessageCount} SUT message(s)`;
|
||||
const result = {
|
||||
id: scenario.id,
|
||||
title: scenario.title,
|
||||
status: "pass",
|
||||
details: redactPublicMetadata
|
||||
? `reply matched in ${rttMs}ms`
|
||||
: `reply message ${matched.message.messageId} matched in ${rttMs}ms`,
|
||||
? `reply matched in ${rttMs}ms${suffix}`
|
||||
: `reply message ${matched.message.messageId} matched in ${rttMs}ms${suffix}`,
|
||||
rttMs,
|
||||
requestStartedAt,
|
||||
responseObservedAt: new Date(matched.observedAtMs).toISOString(),
|
||||
@@ -1565,6 +1730,7 @@ export const __testing = {
|
||||
buildObservedMessagesArtifact,
|
||||
canaryFailureMessage,
|
||||
callTelegramApi,
|
||||
assertTelegramScenarioMessageSet,
|
||||
isRecoverableTelegramQaPollError,
|
||||
assertTelegramScenarioReply,
|
||||
classifyCanaryReply,
|
||||
|
||||
@@ -221,6 +221,48 @@ describe("qa mock openai server", () => {
|
||||
expect(partialBody).toContain('"type":"response.output_text.delta"');
|
||||
expect(partialBody).toContain("QA_PARTIAL_OK");
|
||||
|
||||
const telegramLongResponse = await fetch(`${server.baseUrl}/v1/responses`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
stream: true,
|
||||
input: [
|
||||
makeUserInput("Telegram long final QA check. Use the scripted long final response."),
|
||||
],
|
||||
}),
|
||||
});
|
||||
expect(telegramLongResponse.status).toBe(200);
|
||||
const telegramLongBody = await telegramLongResponse.text();
|
||||
expect(telegramLongBody).toContain('"type":"response.output_text.delta"');
|
||||
expect(telegramLongBody).toContain('"phase":"final_answer"');
|
||||
expect(telegramLongBody).toContain("TELEGRAM-LONG-FINAL-BEGIN");
|
||||
expect(telegramLongBody).toContain("TELEGRAM-LONG-FINAL-END");
|
||||
expect(telegramLongBody.length).toBeGreaterThan(4_500);
|
||||
|
||||
const telegramThreeChunkLongResponse = await fetch(`${server.baseUrl}/v1/responses`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
stream: true,
|
||||
input: [
|
||||
makeUserInput(
|
||||
"Telegram long final three chunk QA check. Use the scripted three chunk final response.",
|
||||
),
|
||||
],
|
||||
}),
|
||||
});
|
||||
expect(telegramThreeChunkLongResponse.status).toBe(200);
|
||||
const telegramThreeChunkLongBody = await telegramThreeChunkLongResponse.text();
|
||||
expect(telegramThreeChunkLongBody).toContain('"type":"response.output_text.delta"');
|
||||
expect(telegramThreeChunkLongBody).toContain('"phase":"final_answer"');
|
||||
expect(telegramThreeChunkLongBody).toContain("TELEGRAM-LONG-FINAL-3CHUNK-BEGIN");
|
||||
expect(telegramThreeChunkLongBody).toContain("TELEGRAM-LONG-FINAL-3CHUNK-END");
|
||||
expect(telegramThreeChunkLongBody.length).toBeGreaterThan(8_000);
|
||||
|
||||
const blockResponse = await fetch(`${server.baseUrl}/v1/responses`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
||||
@@ -153,6 +153,8 @@ const QA_GROUP_VISIBLE_REPLY_TOOL_PROMPT_RE = /qa group visible reply tool check
|
||||
const QA_GROUP_MESSAGE_UNAVAILABLE_FALLBACK_PROMPT_RE =
|
||||
/qa group message unavailable fallback check/i;
|
||||
const QA_TELEGRAM_CURRENT_SESSION_STATUS_PROMPT_RE = /telegram current session_status qa check/i;
|
||||
const QA_TELEGRAM_LONG_FINAL_THREE_CHUNK_PROMPT_RE = /telegram long final three chunk qa check/i;
|
||||
const QA_TELEGRAM_LONG_FINAL_PROMPT_RE = /telegram long final qa check/i;
|
||||
const QA_SUBAGENT_DIRECT_FALLBACK_PROMPT_RE = /subagent direct fallback qa check/i;
|
||||
const QA_SUBAGENT_DIRECT_FALLBACK_WORKER_RE = /subagent direct fallback worker/i;
|
||||
const QA_SUBAGENT_DIRECT_FALLBACK_MARKER = "QA-SUBAGENT-DIRECT-FALLBACK-OK";
|
||||
@@ -1034,6 +1036,23 @@ function splitMockStreamingText(text: string, parts = 3) {
|
||||
return chunks.length > 1 ? chunks : [text.slice(0, 1), text.slice(1)];
|
||||
}
|
||||
|
||||
function buildTelegramLongFinalText({
|
||||
endMarker = "TELEGRAM-LONG-FINAL-END",
|
||||
segmentCount = 54,
|
||||
startMarker = "TELEGRAM-LONG-FINAL-BEGIN",
|
||||
}: {
|
||||
endMarker?: string;
|
||||
segmentCount?: number;
|
||||
startMarker?: string;
|
||||
} = {}) {
|
||||
const body = Array.from(
|
||||
{ length: segmentCount },
|
||||
(_, index) =>
|
||||
`telegram-long-final-segment-${String(index + 1).padStart(3, "0")} ${"x".repeat(54)}`,
|
||||
).join("\n");
|
||||
return `${startMarker}\n${body}\n${endMarker}`;
|
||||
}
|
||||
|
||||
function buildAssistantOutputItem(spec: MockAssistantMessageSpec) {
|
||||
return {
|
||||
type: "message",
|
||||
@@ -1310,6 +1329,32 @@ async function buildResponsesPayload(
|
||||
}
|
||||
return buildAssistantEvents("");
|
||||
}
|
||||
if (QA_TELEGRAM_LONG_FINAL_THREE_CHUNK_PROMPT_RE.test(allInputText)) {
|
||||
const text = buildTelegramLongFinalText({
|
||||
endMarker: "TELEGRAM-LONG-FINAL-3CHUNK-END",
|
||||
segmentCount: 96,
|
||||
startMarker: "TELEGRAM-LONG-FINAL-3CHUNK-BEGIN",
|
||||
});
|
||||
return buildAssistantEvents([
|
||||
{
|
||||
id: "msg_mock_telegram_long_final_three_chunk",
|
||||
phase: "final_answer",
|
||||
streamDeltas: splitMockStreamingText(text),
|
||||
text,
|
||||
},
|
||||
]);
|
||||
}
|
||||
if (QA_TELEGRAM_LONG_FINAL_PROMPT_RE.test(allInputText)) {
|
||||
const text = buildTelegramLongFinalText();
|
||||
return buildAssistantEvents([
|
||||
{
|
||||
id: "msg_mock_telegram_long_final",
|
||||
phase: "final_answer",
|
||||
streamDeltas: splitMockStreamingText(text),
|
||||
text,
|
||||
},
|
||||
]);
|
||||
}
|
||||
if (QA_STREAMING_PROMPT_RE.test(allInputText) && exactReplyDirective) {
|
||||
return buildAssistantEvents([
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/qa-matrix",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Matrix QA runner plugin",
|
||||
"type": "module",
|
||||
@@ -13,7 +13,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -25,7 +25,7 @@
|
||||
"./index.ts"
|
||||
],
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/qianfan-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Qianfan provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/qqbot",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": false,
|
||||
"description": "OpenClaw QQ Bot channel plugin",
|
||||
"repository": {
|
||||
@@ -21,7 +21,7 @@
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.5.4"
|
||||
"openclaw": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -50,10 +50,10 @@
|
||||
"minHostVersion": ">=2026.4.10"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.5.4"
|
||||
"pluginApi": ">=2026.5.4-beta.3"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.5.4"
|
||||
"openclawVersion": "2026.5.4-beta.3"
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/qwen-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Qwen Cloud provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/runway-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw Runway video provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/searxng-plugin",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw SearXNG plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/senseaudio-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw SenseAudio media-understanding provider",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/sglang-provider",
|
||||
"version": "2026.5.4",
|
||||
"version": "2026.5.4-beta.3",
|
||||
"private": true,
|
||||
"description": "OpenClaw SGLang provider plugin",
|
||||
"type": "module",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user