Fixes#49517.
Updates the TUI command catalog so /new describes spawning an isolated session while /reset describes resetting the current session. Adds a focused regression test for the two descriptions.
Co-authored-by: KhanCold <119404710+KhanCold@users.noreply.github.com>
Adds a persisted collapse state for the Control UI Recent sessions sidebar group, including storage and browser coverage.
Also narrows gateway run miss cache expiry typing so the rebased branch stays clean against current main.
Closes#85510
Co-authored-by: NianJiuZst <3235467914@qq.com>
Route internal model catalog imports to the extracted @openclaw/model-catalog-core package and delete obsolete internal facades.
Keep public SDK declarations self-contained by wrapping core helpers at public boundaries instead of leaking private package imports.
Verification:
- pnpm test src/plugins/contracts/model-catalog-core-imports.test.ts src/plugins/sdk-alias.test.ts packages/model-catalog-core/src/configured-model-refs.test.ts packages/model-catalog-core/src/provider-model-id-normalize.test.ts packages/model-catalog-core/src/provider-model-id-normalization.test.ts src/config/config.model-ref-validation.test.ts src/agents/model-selection.test.ts src/plugin-sdk/provider-model-shared.test.ts -- --reporter=verbose
- pnpm check:test-types
- pnpm test:extensions:package-boundary:compile
- pnpm build
- rg "@openclaw/model-catalog-core" dist/plugin-sdk packages/plugin-sdk/dist -n --glob '*.d.ts' || true
- git diff --check
- autoreview clean after fix
CI note: merged with admin override because checks-node-agentic-commands-doctor and checks-node-core-runtime-infra-state failed twice with exit 143/no-output watchdog termination after prior passing test output, while relevant local proof and the rest of CI were green.
Fixes#88198.
Ignore top-level helper scripts in auto-discovered global/workspace extension roots so they do not become manifestless plugin candidates during config validation. Standalone plugin files remain supported when explicitly configured through `plugins.load.paths`, and docs now call out the supported path.
Verification:
- `node scripts/run-vitest.mjs src/plugins/discovery.test.ts src/config/config.plugin-validation.test.ts`
- `node scripts/run-oxlint.mjs src/plugins/discovery.ts src/plugins/discovery.test.ts src/config/config.plugin-validation.test.ts`
- `git diff --check`
- GitHub CI green at `93073bfa85ee294e644c623881ba59ba71d90975`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
Thanks @mushuiyu886 for the fix and @mmhzlrj for the report.
Pack newline-mode outbound paragraphs up to the configured text limit instead of sending one message per blank-line-separated paragraph. Preserves markdown fence guardrails and adds focused chunking plus outbound delivery regressions.\n\nVerified: autoreview clean; node scripts/run-vitest.mjs src/auto-reply/chunk.test.ts src/infra/outbound/deliver.test.ts; git diff --check origin/main...HEAD.\n\nThanks @kesslerio.
Fixes#66509.
QQBot now sends text-only tool progress immediately when partial streaming is enabled instead of buffering it until a fallback timer that is cleared by the final block. Immediate progress uses QQ plain-text sends so markdown-enabled accounts do not reinterpret media-like progress text, while streaming-off behavior remains final-only.
Thanks @gabrielduartesignart for the report.
Co-authored-by: samzong <samzong.lu@gmail.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* refactor: extract model catalog core package
* refactor: route model catalog imports through package boundary
* build: include model catalog in plugin sdk package dts
* fix: preserve static fallback model metadata
Classify the exact `ws` pre-handshake close-before-open error as a benign uncaught network exception so transient Feishu WebSocket cleanup does not crash the gateway process.
The classifier now keeps the upstream `ws` message as an exact contract and rejects broader prefixed WebSocket messages, with regression coverage for direct, wrapped, and non-exact cases.
Fixes#88257.
Thanks @akrimm702.
Co-authored-by: AI-HUB <144416483+akrimm702@users.noreply.github.com>
Completed WebChat stream segment bubbles now render without the active streaming animation after live output has moved on. The UI chat item contract now marks completed stream segments as non-streaming and the active stream as streaming, so the renderer applies the pulsing class only to live output.
Verified with:
- node scripts/run-vitest.mjs ui/src/ui/chat/build-chat-items.test.ts ui/src/ui/chat/grouped-render.test.ts ui/src/ui/views/chat.test.ts
- node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.ui.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-ui-stream-artifacts.tsbuildinfo
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
PR: #88225
Credit: @scotthuang
Refactor OpenAI provider identity so OpenAI remains the canonical provider for API-key and OAuth-backed flows while legacy openai-codex state is doctor/migration-only.
Keeps OpenAI Codex Responses as an API/transport class rather than a provider identity, moves auth aliases through providerAuthAliases, updates doctor repair sequencing for old auth/profile state, and refreshes tests/docs around the canonical OpenAI behavior.
Resolve raw plugin config environment references before plugin discovery and validation, while preserving the existing single-pass behavior for configs already loaded through config IO.
The loader now resolves raw config opt-ins with config.env vars included, bypasses active/cache reuse for that mode, and redacts plugin entry config from raw-mode cache keys so resolved secrets do not enter registry keys or reentry errors.
Verification:
- OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs src/plugins/loader.test.ts src/plugins/loader.runtime-registry.test.ts
- autoreview --mode branch --base origin/main
- pnpm check:changed on Blacksmith Testbox tbx_01ksw36bp7zygwxgq3jcsvjv3b / GitHub Actions run 26680322889
- PR CI green on facb77634e
Co-authored-by: Peter Lindsey <peter@lindsey.jp>
Adds first-class Xiaomi Token Plan provider support with regional onboarding/configuration, token-plan key prefix validation, runtime pricing/catalog metadata, and docs/test coverage.
Keeps Token Plan model catalog discovery runtime-owned so region-specific base URLs are required and the provider cannot silently fall back to the static SGP manifest catalog.
Fixes#86169.
Verification:
- node scripts/run-vitest.mjs src/plugins/provider-discovery.runtime.test.ts extensions/xiaomi/index.test.ts src/plugins/manifest-model-catalog.test.ts src/model-catalog/manifest-planner.test.ts
- git diff --check
- autoreview --mode local: clean, no accepted/actionable findings
- CI run 26678998539: all relevant checks passed; check-prod-types failed on unrelated browser unused-function issue already present on origin/main
Co-authored-by: NianJiuZst <3235467914@qq.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Keep Codex post-tool assistant/commentary progress and patch snapshot updates on the post-tool completion guard so long generated edits do not fall back to terminal idle handling. Enable Codex patch streaming events for native code mode and refresh exact prompt/config expectations.
Verification:
- pnpm prompt:snapshots:check
- pnpm test extensions/codex/src/app-server/run-attempt.turn-watches.test.ts extensions/codex/src/app-server/thread-lifecycle.test.ts extensions/codex/src/app-server/thread-lifecycle.binding.test.ts extensions/codex/src/app-server/side-question.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- git diff --check origin/main...HEAD
- exact-head CI run 26677938955
- Real behavior proof override run 26678097960
Thanks @keshavbotagent.
Co-authored-by: Kelaw - Keshav's Agent <keshavbotagent@gmail.com>
Stop Codex app-server turns from projecting mirrored OpenClaw transcript history into prompt/model-input surfaces by default. Keep context-engine output on the rendered prompt/developer-instruction path and preserve mirrored history only for OpenClaw-side snapshots.
Move OpenClaw skills and the routed workspace-memory pointer out of native Codex turn user input and into turn-scoped collaboration developer instructions.
Preserve full MEMORY.md fallback prompt injection, delivery-hint rewrapping, lightweight cron exact prompts, and trajectory reporting for the rendered developer surface.
Co-authored-by: Beru <beru@lastguru.lv>
Fix Codex app-server native thread overflow recovery and CLI compaction fallback.
- rotate Codex native startup bindings when rollout token pressure leaves too little headroom
- keep byte-size rollout fuses ahead of rollout content reads
- clear stale resumed context-engine bindings only when the stored thread id still matches
- fall back to context-engine compaction when Codex owns/skips native compaction
Verification:
- node scripts/run-vitest.mjs run --config test/vitest/vitest.extension-codex.config.ts extensions/codex/src/app-server/startup-binding.test.ts extensions/codex/src/app-server/run-attempt.context-engine.test.ts extensions/codex/src/app-server/session-binding.test.ts --reporter=verbose
- node scripts/run-vitest.mjs run --config test/vitest/vitest.agents.config.ts src/agents/command/cli-compaction.test.ts --reporter=verbose
- git diff --check origin/main...HEAD
- autoreview --mode branch --base origin/main: clean
- GitHub CI for 466bfbe78c: green
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
A claude-cli session whose JSONL transcript ends with an assistant
`tool_use` content block that was never answered by a `tool_result` user
message cannot resume — claude-cli will sit waiting for the missing
`tool_result`, hit its no-output watchdog, and the runtime kills it
with `reason=abort`. The dispatcher then sees an empty payload and emits
NO_REPLY, which to the user looks like the agent silently ignored their
message — same end-user symptom as the binding-flush amnesia bug, but a
different root cause.
The orphan can be left behind when:
- Gateway restarts mid-tool (brew upgrade, manual kickstart, OOM,
crash) — claude was waiting on a tool result that never arrived.
- `claude-live-session.ts` no-output watchdog fires while a tool is
actively running and OC kills the subprocess.
- The tool itself crashed or hung past its own deadline.
In all cases the resumed session is dead until the binding gets cleared,
because every subsequent resume hits the same trailing tool_use and the
same kill cycle. Observed in production on a personal OpenClaw gateway
(3d-engineer agent, 50-message-deep transcript ending in a Bash
`tool_use`; every Telegram message after the orphan landed silently
aborted at the 180s no-output mark).
Add `claudeCliSessionTranscriptHasOrphanedToolUse` to the helpers that
walks the JSONL, finds the last assistant message, and returns true if
any of its `tool_use` ids has no matching `tool_result` later in the
file. Wire into `prepareCliRunContext` as a second invalidator gate
alongside `missing-transcript`. The new `invalidatedReason:
"orphaned-tool-use"` follows the same path as missing-transcript: the
binding is dropped, this turn starts a fresh session, and the prior
context is reseeded into the new session via `RAW_TRANSCRIPT_RESEED`.
Detection only considers TRAILING orphans — an unanswered tool_use
deeper in history is inert because a later assistant message already
moved past it. Only the most recent assistant message's tool_use ids
matter for forward progress.
Probe runs only for claude-cli providers and only when the transcript-
content gate already passed, so we add no I/O on already-invalidated
sessions and no behavior change for non-claude providers.
AI-assisted: yes. Tooling: Claude Opus + claude-cli.
When a claude-cli turn produces a session id but the underlying claude
subprocess fails to flush an assistant-role record to its
~/.claude/projects/<cwd>/<sid>.jsonl transcript (e.g. mid-turn kill from
a concurrent fingerprint-mismatched turn, supervisor restart, internal
failure), buildCliRunResult was still persisting that session id into
cliSessionBinding. The next turn ran claudeCliSessionTranscriptHasContent,
didn't find the file, logged 'cli session reset: reason=missing-transcript',
and started a brand-new claude session with empty memory.
End-user symptom: agent forgets prior conversation between turns.
Gate the cliSessionBinding spread on the same predicate the next-turn
invalidator uses, evaluated at write time. Also clear agentMeta.sessionId
in the same case so the session-store fallback at command/session-store.ts
(which reads agentMeta.sessionId via setCliSessionId when the binding is
absent) doesn't re-persist the unflushed sid through a different field
path. The fallback is what makes the binding-only gate insufficient on
its own; both writes must drop together.
The gate only fires for claude-cli providers — other CLI providers don't
write to ~/.claude/projects, so probing them would always return false
and incorrectly strip valid binding metadata. isCliBindingFlushed now
takes the provider id and returns true unconditionally for non-claude-cli
sessions.
A bounded retry (0 / 50 / 150 ms) tolerates the brief gap between
claude-cli's stdio close and the OS making the JSONL line visible to
readers (cooperative fsync semantics on APFS, but not guaranteed under
stress).
The transcript-probe is exposed as an injectable dep
(setCliRunnerTestDeps / restoreCliRunnerTestDeps) mirroring the existing
pattern in src/agents/cli-runner/prepare.ts so isCliBindingFlushed is
testable without touching ~/.claude/projects.
AI-assisted: yes. Tooling: Claude Opus + claude-cli. Codex review caught
the fallback path and the missing provider gate before this hit upstream.
Real-Behavior-Proof: dist-side patch on M5 gateway; branch-build
follow-up pending — see PR body.
Move task run, delivery, and flow registry persistence onto the shared OpenClaw state SQLite database.
Summary:
- Store task runs, delivery state, and flow runs in state/openclaw.sqlite via the generated Kysely schema.
- Migrate shipped task sidecars into the shared state DB and archive old sidecars, including invalid-config/read-only CLI paths.
- Keep startup migration lightweight for read-only status/tasks paths while still detecting known legacy state markers and custom session stores.
Verification:
- .agents/skills/autoreview/scripts/autoreview --mode local: clean after final fix
- pnpm test src/tasks/task-registry.store.test.ts src/tasks/task-flow-registry.store.test.ts src/commands/doctor-state-migrations.test.ts -- --reporter=verbose
- pnpm test src/commands/doctor-state-migrations.test.ts src/cli/program/config-guard.test.ts src/cli/route.test.ts src/cli/command-path-policy.test.ts -- --reporter=verbose
- pnpm test src/cli/program/config-guard.test.ts src/cli/route.test.ts src/cli/command-startup-policy.test.ts src/cli/command-path-policy.test.ts src/cli/command-execution-startup.test.ts -- --reporter=verbose
- pnpm test src/cli/program/config-guard.test.ts src/cli/argv.test.ts src/cli/route.test.ts src/commands/doctor-config-preflight.state-migration.test.ts -- --reporter=verbose
- pnpm test src/tasks/task-flow-registry.store.test.ts -- --reporter=verbose
- pnpm test test/scripts/lint-suppressions.test.ts -- --reporter=verbose
- pnpm db:kysely:check
- pnpm lint:kysely
- git diff --check HEAD
- pnpm test:startup:memory
- PR CI green on 2f7d76f0d5
Preserve iMessage SMS reply routes for approval replies so a direct SMS /approve response can acknowledge and return results to the same SMS conversation.
Verification: gateway-only build, extension type checks, CI build-artifacts/check-prod-types/check-test-types/check-lint/check-additional-extension-package-boundary, and live prod iMessage SMS approval proof. checks-node-core-fast was waived by maintainer request after unrelated flaky failures in non-iMessage tests.
Adds Workboard orchestration statuses, dependency links, idempotent child creation, dispatch, and complete/block lifecycle operations backed by the plugin SQLite keyed store.
Persists tenant, skills, workspace, schedule, runtime, retry, dispatch, and handoff metadata in card records, with claim scoping and token redaction. Surfaces the new states and metadata in the Control UI, horizontal board layout, localized strings, and Workboard docs.
Verification:
- pnpm test extensions/workboard/src/store.test.ts extensions/workboard/src/tools.test.ts extensions/workboard/src/gateway.test.ts ui/src/ui/controllers/workboard.test.ts ui/src/styles/workboard.test.ts ui/src/ui/views/workboard.test.ts -- --reporter=verbose
- pnpm ui:i18n:check
- /Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main, followed by focused clean local autoreview loops for final fixes
- env -u OPENCLAW_TESTBOX pnpm check:changed
- git diff --check
Summary:
- The PR classifies selected embedded agent provider-denial error payloads through the shared failover matcher ... 1/current-ak auth matching, preserves guarded non-fallback cases, and covers fallback progression in tests.
- PR surface: Source +34, Tests +166. Total +200 across 5 files.
- Reproducibility: yes. Current main is source-reproducible: a non-GPT embedded result whose only signal is CE ... returns null from the classifier, and the fallback wrapper treats null classification as candidate success.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): classify embedded provider business denials for fallback
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8304…
Validation:
- ClawSweeper review passed for head e266beac93.
- Required merge gates passed before the squash merge.
Prepared head SHA: e266beac93
Review: https://github.com/openclaw/openclaw/pull/84814#issuecomment-4505010446
Co-authored-by: Stellar鱼 <2182712990@qq.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Fixes#86820.
Preserve Codex OAuth-backed compaction by selecting and loading the Codex harness before resolving direct or queued compaction models, while keeping OpenAI-compatible custom base URLs on the OpenAI context config path. Also preserves persisted concrete harness pins so compaction does not hot-switch existing sessions just because an explicit Codex fallback exists.
Verification:
- node scripts/run-vitest.mjs src/agents/embedded-agent-runner/compact.hooks.test.ts src/agents/harness/selection.test.ts src/agents/harness/runtime-plugin.test.ts
- pnpm tsgo:prod
- pnpm check:test-types
- pnpm lint --threads=8
- git diff --check origin/main...HEAD
- git diff --check
- autoreview clean: no accepted/actionable findings reported; overall patch is correct (0.82)
- GitHub PR checks green on ac6f93de4a
Fix Codex app-server completion-stall recovery so replay-safe stdio completion-idle failures retry once, while progress/terminal turn-watch timeouts only surface timeout payloads.
Also preserve post-tool completion guards for scoped native response deltas and stabilize the oversized CONNECT timeout regression test picked up from latest main.
Co-authored-by: Kelaw - Keshav's Agent <keshavbotagent@gmail.com>
Adds the shared SQLite state database base, moves plugin keyed state into it with doctor migration coverage, and keeps generated Kysely guardrails aligned. Proof: focused SQLite/plugin-state tests, db:kysely:check, lint:kysely, architecture/dependency guards, autoreview, and PR CI all clean.
Move compaction planning work to a bounded worker-thread path so large transcript planning no longer monopolizes the agent event loop. Extract pure planning helpers, sanitize worker inputs before structured clone, package the worker entrypoint, and keep synchronous fallback only for worker-unavailable cases.
Fixes#86358.
Fixes#87438.
Bound unset heartbeat run timeouts so background heartbeat turns no longer inherit the built-in 48-hour interactive agent default. Timeout precedence is explicit heartbeat timeout, explicit global agent timeout, then heartbeat cadence capped at 600 seconds.
Verification:
- git diff --check
- Testbox tbx_01kstna69zvznn4fq7zrqr04a1: corepack pnpm test src/infra/heartbeat-runner.model-override.test.ts -- --reporter=verbose passed 13 tests
- Direct node --import tsx runtime probe verified 300s, 600s, 60s, and 45s timeout precedence cases
- Autoreview clean
Known CI state:
- PR CI run 26661465248 has failures matching latest main CI run 26661386468 at a7820b2f54; failures are outside this six-file heartbeat/docs diff.
Keep session lock cleanup from removing live OpenClaw-owned locks solely because they are old. Cleanup now reports age-only stale locks without deleting them, while still removing dead, orphaned, recycled, malformed-old, and non-OpenClaw-owned locks.
Update doctor docs and regression coverage for the cleanup/repair contract.
Refs #87779
Notte exposes a CDP-compatible WebSocket gateway at
wss://us-prod.notte.cc/sessions/connect?token=<NOTTE_API_KEY> that
auto-creates a session on connect — the same shape OpenClaw's existing
"Direct WebSocket CDP providers" section was generically framed for
(per #31085).
Real behaviour proof (against wss://us-prod.notte.cc/sessions/connect):
$ openclaw browser --browser-profile notte open https://example.com
opened: https://example.com/
tab: t4
id: 7FE04AC44931A6E1C799DE4ABF0DC807
A screenshot captured against the same session is a 1254x1111 PNG of
the rendered example.com page.
Playwright connectOverCDP flow against the same URL (today):
connectOverCDP 695ms
context.newCDPSession(page) 169ms
session.send('Target.getTargetInfo') → targetId 87ms
page.goto('https://example.com') 631ms
total 1.8s
AI-assisted (Claude Opus 4.7). codex review --base origin/main returned
clean. See PR description for the full pre-flight checklist.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Forward initial image/file attachments when spawning ACP subagents through the existing sessions_spawn attachment opt-in. Remove the PR-only acpEnabled config split so ACP uses the same attachment gate as other runtimes.
Also fix the PR branch CI fallout: type the browser element CLI request mock and use Vitest env stubs in the Azure speech test to satisfy the changed-path security scan.
Verification:
- GitHub CI passed on f6ca26b160.
- Autoreview clean.
- Crabbox AWS live OpenAI proof passed: cbx_a576d49493fe / run_081dcc6c6a1b.
Thanks @zhangguiping-xydt.
Fix `sessions.json` persistence after compaction transcript rotation.
When the agent runtime rotates from the pre-compaction session transcript to the post-compaction transcript, post-run consumers now receive the effective OpenClaw session id and session file. Backend CLI session ids remain backend metadata and no longer overwrite the top-level OpenClaw session identity.
Refs #88040.
Thanks @1052326311.
Verification:
- `node scripts/run-vitest.mjs src/agents/agent-command.compaction-rotation.test.ts src/agents/agent-command.live-model-switch.test.ts src/agents/command/session-store.test.ts`
- Autoreview clean
- GitHub CI green on PR head `c3d3c77ddf675bbba0b9ba6681b030a2f69a898c`
Fix claude-cli transcript resume so session-id rotation and transcript flush timing do not drop valid resume state.
- Capture the latest claude-cli session_id from JSONL output.
- Resolve Claude project transcript paths through the shared canonical project-dir resolver.
- Probe transcript content from the actual CLI process cwd.
- Thanks @benjamin1492!
Persist GitHub Copilot SDK session ids in the plugin-state SQLite store so separate OpenClaw process turns can resume the same Copilot-side session when the compatibility fingerprint still matches.
The fingerprint covers provider/model/cwd, resolved agent id, resolved Copilot home, and auth identity. Plugin-state lookup/register/delete failures are non-fatal, stale rows are invalidated, and reset delete failures use an in-process tombstone so reset does not accidentally reuse a durable binding.
Also routes the QQBot token POST through the plugin SDK SSRF guard with capture disabled for the secret-bearing request, preserving the current token lifetime validation from main.
Verification: focused Copilot and QQBot Vitest suites, raw channel fetch guard, autoreview clean, Blacksmith Testbox pnpm check:changed tbx_01kst9fwjmsfzwaxqatszcbf40, live local Copilot two-turn smoke with the same SDK session id persisted in SQLite.
Refs #88064
Summary:
- The PR changes plugin auto-enable materialization so an explicit empty `plugins.allow` stays empty while non-empty restrictive allowlists are still extended, and adds a regression test.
- PR surface: Source +3, Tests +17. Total +20 across 2 files.
- Reproducibility: yes. Source inspection of current main shows an empty array reaches `ensurePluginAllowlisted`, and the linked report gives a concrete `doctor --fix` config path that matches that code.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head c06837f5dd.
- Required merge gates passed before the squash merge.
Prepared head SHA: c06837f5dd
Review: https://github.com/openclaw/openclaw/pull/87883#issuecomment-4570537738
Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Active Memory recall now runs on its own queue lane instead of sharing the parent prompt-build lane.\n\nValidation:\n- git diff --check\n- node scripts/run-vitest.mjs extensions/active-memory/index.test.ts -t "runs recall on a dedicated active-memory lane"\n- fresh local gateway smoke with Active Memory + Memory Core + loopback OpenAI-compatible model: HTTP 200, active-memory start/done, recall elapsedMs=209\n\nFixes #79026.\nRelated: #72015.
Summary:
- The PR updates Codex doctor route repair to preserve explicit non-default `agentRuntime` pins across agent model maps and provider policies, adds regression coverage, and tightens a live-gateway test helper type guard.
- PR surface: Source +240, Tests +574. Total +814 across 3 files.
- Reproducibility: yes. The source path is clear from current main's model-map merge behavior and the PR's bef ... beRepairCodexRoutes` with the reported config, though this read-only review did not execute the test suite.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(doctor): preserve explicit non-default agentRuntime pin during le…
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8414…
Validation:
- ClawSweeper review passed for head c142ec1ef8.
- Required merge gates passed before the squash merge.
Prepared head SHA: c142ec1ef8
Review: https://github.com/openclaw/openclaw/pull/84362#issuecomment-4493152445
Co-authored-by: David Huang <nxmxbbd@gmail.com>
Co-authored-by: Nex <nex@dbitstec.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Add a general typed tool-progress contract so long-running non-exec tools can emit public channel progress without overloading model-facing tool content.
`web_fetch` now uses the generic delayed progress helper: it shows `Fetching page content...` only when the fetch is still pending after five seconds, clears the timer on completion/abort, passes the abort signal into guarded fetch, and avoids provider fallback or cached success after cancellation. The subscriber path accepts only explicit `visibility: "channel"` and `privacy: "public"` progress metadata, while untyped tool partials and exec output keep their existing behavior.
Docs now explain typed progress, delayed producer examples, and the `web_fetch` timing behavior.
Proof: `pnpm test src/agents/tools/web-tools.fetch.test.ts src/agents/embedded-agent-subscribe.handlers.tools.test.ts -- --run`; `pnpm docs:check-mdx`; changed-file `pnpm exec oxlint ...`; `git diff --check`; autoreview clean.
Match text slash command names case-insensitively across the reset/new fallback paths and the shared registry/control detection contract while preserving command argument casing.
Add regression coverage for uppercase and mixed-case reset/new commands plus registered non-reset commands such as `/STATUS`, `/Model`, `/T`, and `/COMPACT`.
Co-authored-by: zhangtong26 <zhangtong26@xiaomi.com>
Co-authored-by: Lanzhi <lizhan3@xiaomi.com>
Remove stale Telegram-only wording from the reasoning stream acknowledgement and docs so channel-neutral behavior is reflected.
Fixes#68305.
Co-authored-by: Lanzhi <lizhan3@xiaomi.com>
Load `docker-compose.override.yml` when ClawDock builds its explicit Docker Compose file list, preserving standard Compose override behavior while keeping `docker-compose.extra.yml` as the final OpenClaw overlay.
Update Docker docs so manual Compose users include the same override order, and keep the regression test for the generated `_clawdock_compose` arguments.
Fixes#49909.
Thanks @spacegeologist.
Co-authored-by: zhengzuo0-ai <zheng.zuo0@gmail.com>
Preserve npm install selectors while recording resolved npm provenance for plugin and hook install/update records. Active `record.spec` stays the requested selector unless explicitly pinned, while resolved npm fields remain available for audit and diagnostics.
Adds focused coverage for hook-pack npm fallback provenance after the maintainer review found that path worth pinning down.
Co-authored-by: Phil <99397913+GitHoubi@users.noreply.github.com>
Forward Zalo quote-reply metadata from zca-js data.quote into the existing ReplyToId, ReplyToBody, and ReplyToIsQuote context keys so agents can correlate quoted replies with prior bot messages.
Adds parser and monitor regression coverage for quote extraction and context projection.
Fixes#86851.
Thanks @tanshanshan.
Remove unreachable optional chaining from four Discord message-handler-family runtime error calls.
This aligns the code with the required RuntimeEnv.error contract while leaving production behavior unchanged for valid runtimes. Maintainer-updated PR proof clarifies that shared queue reporter hooks still treat malformed runtime reporter failures as best-effort.
Clean up completed exec tool-call abort listeners so normal foreground completion and background-yield no longer retain the exec run/session context through AbortSignal listener state.
The listener cleanup now lives beside the exec listener registration and runs when the foreground process settles, rejects, or the tool returns a background running result. Existing abort/timeout/background behavior remains owned by the process supervisor and process registry.
Verification:
- gh pr checks 83022
- gh api repos/openclaw/openclaw/commits/fe86528ecb2043b6febef5c2eec53f9124be5543/check-runs
- git merge-tree --write-tree origin/main refs/remotes/pr/83022
- git diff --check origin/main...refs/remotes/pr/83022
- node AbortSignal add/remove listener probe
Thanks @c19354837.
Co-authored-by: Ninty <c19354837@hotmail.com>
Fixes#66479.
Workspace skills whose SKILL.md starts with a UTF-8 BOM now keep their shared markdown frontmatter metadata, so they remain discoverable through skills list. The fix strips one leading BOM at the parser boundary and adds parser plus workspace discovery regression coverage.
Thanks @jbetala7 for the fix.
Co-authored-by: Jayesh Betala <jayesh.betala7@gmail.com>
Route Microsoft Teams attachment downloads through the shared SSRF guarded fetch path so DNS validation is pinned into the dispatcher used for the actual request.
Keep Teams auth fallback and allowlisted HTTPS Authorization redirect behavior while failing closed for custom fetch hooks that cannot accept dispatcher injection.
Verification:
- CI=1 OPENCLAW_VITEST_MAX_WORKERS=1 timeout 300 node scripts/run-vitest.mjs run extensions/msteams/src/attachments/shared.test.ts extensions/msteams/src/attachments/bot-framework.test.ts src/infra/net/fetch-guard.ssrf.test.ts
- gh pr checks 87567 --repo openclaw/openclaw --watch=false
PR: #87567
Fix cron local-model preflight fallback handling so scheduled runs try configured fallback candidates before skipping when the local primary is unavailable.
Verification:
- GitHub CI on PR head fe884dab90: passing required CI checks.
- Local focused cron/model fallback tests passed earlier for the touched surface.
- Local merge-wrapper build and check passed on the prepared candidate.
- Local full pnpm test reported unrelated failures outside this PR's touched files; touched files are limited to cron docs, src/agents/model-fallback.ts, and src/cron/isolated-agent/*.
Co-authored-by: chen-zhang-cs-code <chenzhangcode@163.com>
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
Remove the chatType === 'direct' guard from
shouldAllowQuietChannelOwnedProgressCallbacks so that channel-owned native
progress callbacks (onToolStart, onItemEvent, onPlanUpdate,
onApprovalEvent, onCommandOutput, onPatchSummary, onCompactionStart/End)
are forwarded in group and group-channel sessions when verbose is off.
Previously the guard required chatType === 'direct', which meant that
/verbose off would suppress all progress callbacks in group sessions
while direct sessions continued to relay them. Message-level tool
summary suppression is handled separately; native channel relay hooks
should not be gated on chat type.
Closes#87612
Cache single-row gateway session child indexes without hiding live subagent registry changes.
Summary:
- Reuses store-derived child-session candidates for repeated single-row session loads.
- Keeps runtime subagent registry reads live per row so moved child sessions do not stay attached to stale parents.
- Versions the session-store cache and includes that version in the single-row cache key so same-object store rewrites cannot reuse stale child candidates.
- Adds focused regression coverage for cache reuse, live registry refresh, and same-object session-store writes.
Verification:
- git diff --check
- pnpm tsgo:prod
- pnpm test src/gateway/session-utils.single-row-cache.test.ts src/gateway/session-utils.subagent.test.ts -- --reporter=verbose
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- GitHub CI run 26620265206: passing
- Azure Crabbox cbx_a58389e50f49: single-row-loads 13.622240 ms before vs 1.869456 ms after, 7.29x speedup, 86.3% reduction
* feat: add Claude Opus 4.8 support
* fix: omit Vertex Opus sampling overrides
* fix: preserve Opus adaptive thinking levels
* fix: clamp Anthropic max effort support
* fix: use sha256 for QA mock call ids
* fix: type Anthropic transport test model metadata
* test: update PDF model default for Opus 4.8
Summary:
- This PR adds an internal gateway active-run projection flag, clears it during terminal lifecycle handling be ... ons.list on that flag, adds gateway regression coverage, and tightens memory-wiki confidence normalization.
- PR surface: Source +29, Tests +131. Total +160 across 7 files.
- Reproducibility: yes. Source inspection shows current main can broadcast terminal sessions.changed before ch ... the abort-controller entry, and the before/after recording supports the visible stuck In progress symptom.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(gateway): preserve chat retry guard after terminal state
- PR branch already contained follow-up commit before automerge: fix(gateway): clear completed session active runs
Validation:
- ClawSweeper review passed for head 9b132bdc2b.
- Required merge gates passed before the squash merge.
Prepared head SHA: 9b132bdc2b
Review: https://github.com/openclaw/openclaw/pull/87810#issuecomment-4569094800
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Adds opt-in Discord progress-draft commentary for assistant preambles while keeping commentary hidden by default and final delivery unchanged.
Keeps commentary config Discord-specific, strips directive tags/NO_REPLY, and clears stale commentary rows without stopping the active draft stream.
Thanks @bryanpearson.
Co-authored-by: bryanpearson <bryanmpearson@gmail.com>
Preserve OpenClaw-owned embedded system prompts after active tool selection in both normal embedded attempts and compaction. Adds an exact base prompt path on AgentSession that keeps active tool prompt metadata current for extension hooks.
Fixes#87807.
Verification:
- mise exec node@24.16.0 -- node scripts/run-vitest.mjs src/agents/sessions/sdk.test.ts src/agents/embedded-agent-runner/system-prompt.test.ts src/agents/embedded-agent-runner/run/attempt.spawn-workspace.context-engine.test.ts src/agents/embedded-agent-runner/compact.hooks.test.ts --reporter=dot
- mise exec node@24.16.0 -- pnpm tsgo:core
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
Thanks @shakkernerd.
Handle exec-backed Gateway SecretRefs in doctor, lint, and health probing without executing providers by default.
- Add `openclaw doctor --allow-exec` for explicit SecretRef execution during lint/doctor checks.
- Skip only the active exec-backed gateway probe path and avoid local service diagnostics for remote-only skipped health.
- Keep env-winning and dormant fallback credentials probeable, stabilize related tests, and remove a stale live-shard fixture left by the moving base.
Verification:
- `node scripts/run-vitest.mjs src/commands/doctor-gateway-auth-token.test.ts src/commands/doctor.warns-state-directory-is-missing.e2e.test.ts src/gateway/credentials.test.ts src/gateway/probe-auth.test.ts src/commands/doctor-gateway-daemon-flow.test.ts test/scripts/test-live-shard.test.ts --reporter=verbose`
- `mise x node@24.13.0 -- pnpm prompt:snapshots:check`
- `pnpm tsgo:prod`
- `pnpm build`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- Crabbox AWS live config proof: `run_f44a4d9dae4e`
- GitHub CI: green on final head `88d24abdbf9529a59d75d1d5e04eac74bbbbc267` after rerunning a stale in-progress Security High workflow.
Co-authored-by: Merlin <258679497+funmerlin@users.noreply.github.com>
Fixes#87132.
Default Usage now requests all configured agents with `agentScope: "all"`, while selecting a specific agent sends `agentId` consistently to both session usage and cost usage calls. The gateway now supports explicit all-agent session usage, aggregates all-agent cost summaries across configured agents, and keeps scoped cache entries separate. Legacy gateway fallbacks remain for older `agentId` / `agentScope` support, with protocol docs/schema and Swift generated models updated.
Verification:
- `node scripts/run-vitest.mjs ui/src/ui/controllers/usage.node.test.ts ui/src/ui/app-render-usage-tab.test.ts ui/src/ui/views/usage.test.ts --reporter=dot`
- `node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway-methods.config.ts src/gateway/server-methods/usage.test.ts src/gateway/server-methods/usage.cost-usage-cache.test.ts src/gateway/server-methods/usage.sessions-usage.test.ts --reporter=dot`
- `pnpm check:test-types`
- `pnpm protocol:check`
- targeted `node scripts/run-oxlint.mjs ...`
- `git diff --check`
- autoreview clean after Swift compatibility fix
- PR CI green at head `d67156a3c552c4f9c8b6edf8516b6242bf5cdd26`
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
Keep Codex reasoning updates as accumulated snapshots and mark the stream payload so channel consumers can distinguish snapshots from deltas.
This prevents Discord and Teams progress previews from duplicating accumulated reasoning text while preserving delta-style reasoning for legacy producers.
Refs #86708
Thanks @SebTardif.
Co-authored-by: OpenAI Codex <codex@openai.com>
Scope jiti filesystem transform caches for OpenClaw plugin loaders by package version and package.json install metadata so stale transforms cannot survive upgrades or package reinstalls.
Covers the central plugin module loader and the plugin SDK root alias CJS loader, while preserving jiti filesystem-cache env opt-outs and the TMPDIR cwd guard.
Verification: CI run 26601117143 passed; Real behavior proof run 26601445285 passed; CodeQL selected checks passed in run 26601117126; CodeQL Critical Quality plugin-boundary and plugin-sdk-package-contract passed in run 26601117074; OpenGrep PR diff passed in run 26601117137.
Refs: https://github.com/openclaw/openclaw/pull/87745
Thanks @fuller-stack-dev.
Route Codex app-server report-mode PreToolUse plugin approval requirements through the matching app-server approval request instead of failing closed. Shares duplicate in-flight approvals, preserves block/rewrite fail-closed behavior, and keeps generic plugin allow-always scoped to one Codex request. Supersedes #86978; thanks @clawSean for the original docs clarification.
Compact promoted short-term memory snippets before writing them into MEMORY.md, while keeping the full rehydrated snippet in recall state for ranking/provenance. Adds the deep-dreaming config surface and docs, with the default promoted snippet cap set to 160 estimated tokens.
Verification:
- git diff --check
- fnm exec --using v24.13.0 node scripts/run-vitest.mjs run extensions/memory-core/src/short-term-promotion.test.ts extensions/memory-core/src/dreaming.test.ts src/memory-host-sdk/dreaming.test.ts
- GitHub CI run 26605272497
- CodeQL security run 26605272404
Co-authored-by: AMARA <amara@eyeinthesky.pl>
Server-side cron job list filtering now applies schedule-kind and last-run-status filters before pagination, and the UI only sends table filters for the cron table view.
Fixes#9455.
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
* fix(msteams): rebase SDK migration onto current main
Reapply the msteams SDK migration (originally on feat/msteams-sdk-migration)
on top of upstream/main, resolving conflicts with parallel msteams work that
landed upstream during our session.
What got applied vs decisions made:
CLEANLY APPLIED (3-way patch):
- monitor.ts, monitor-handler.ts, polls.ts, reply-stream-controller.ts/.test.ts,
reply-dispatcher.ts, attachments/download.ts, monitor.lifecycle.test.ts,
monitor-handler/message-handler.ts, monitor-handler.types.ts, etc.
- streaming-message.ts + .test.ts deletions
WHOLESALE TAKE FROM ORIGINAL BRANCH (partial 3-way left broken cross-refs):
- sdk.ts, sdk.test.ts, messenger.ts, feedback-reflection.ts,
send-context.ts, send.test.ts
KEPT UPSTREAM (deferred for separate cleanup):
- extensions/msteams/package.json (still has jsonwebtoken/jwks-rsa per
Peter's b3bc60ae25 incremental approach)
- src/plugins/contracts/package-manifest.contract.test.ts (consistent with
package.json)
- pnpm-lock.yaml (avoids lockfile churn; pnpm install --frozen-lockfile clean)
ADAPTED:
- Dockerfile matrix-sdk-crypto check now wraps upstream's new retry-loop in
the if-matrix-bundled gate
KNOWN TEST FAILURES (need eyes — see PR comment):
- attachments.test.ts: 1 fail (pre-existing — warn meta arg shape changed in
our migration but test wasn't updated)
- reply-dispatcher.test.ts: 6 fails (pre-existing — tests mock old
TeamsHttpStream, not updated for our ctx.stream rewrite)
- send.test.ts: 4 fails (NEW from merge — upstream's send.ts changed media
loading; our mocks need updating or take upstream's send.test.ts wholesale)
UPSTREAM COMMITS POTENTIALLY MISSED (in wholesale-take files):
- 08c4af0ddf fix(msteams): accept conversation id allowlists
- e1840b8581 fix(msteams): bind global audience tokens to app id
- Channels turn-kernel refactor (ffe67e9cdc / 1ead1b2d18 / 9a9cd0c0ab) —
may be partially preserved in cleanly-patched files
Static checks pass: pnpm check:changed is green (typecheck, lint, contract
tests, import cycles, etc.). Manual testing required before merge.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): preserve thread routing for channel and group-chat replies
- monitor.ts: adaptSdkContext now uses ctx.reply() for channel and groupChat
conversations (so the SDK threads outbound activities to the inbound's
replyToId/serviceUrl) and ctx.send() only for personal DMs (where
reply()'s blockquote-prepend is ugly).
- messenger.ts: sendProactively passes resolvedThreadId on the non-thread
fallback path so channel @mentions that fall through outbound.ts -> send.ts
still land in the original thread instead of top-level.
Live-validated: channel @mention -> bot replies in thread, threaded reply
-> bot replies in same thread, no top-level leakage.
* fix(msteams): tag outbound SDK calls with OpenClaw User-Agent
- user-agent.ts: add buildOpenClawUserAgentFragment() that returns just
'OpenClaw/<version>'. The SDK's Client.clone merges this with its own
'teams.ts[apps]/<sdk-version>' identifier — passing the full buildUserAgent()
here would double-print the SDK token.
- sdk.ts: pass the fragment via AppOptions.client.headers['User-Agent'] so
the Teams backend can identify OpenClaw traffic for usage telemetry.
Final UA looks like 'OpenClaw/<openclaw-version> teams.ts[apps]/<sdk-version>'.
* fix(msteams): handle StreamCancelledError when user presses Stop mid-stream
The new SDK throws StreamCancelledError synchronously from stream.emit/update
when the user pressed Stop in Teams: Teams replies 403 to the next chunk
update, the SDK flips _canceled, and any subsequent emit() throws. The old
custom TeamsHttpStream either swallowed cancel or didn't expose this exception
type, so the migration inherited an SDK behavior the original code didn't have
to handle.
Symptom on 2026-05-05: pressing Stop during a streaming reply caused an
unhandled promise rejection that crashed the Node 24 process. Docker restarted
the gateway about two minutes after each Stop click. Two related bugs surfaced
once the crash was caught: the would-be block fallback re-delivered the full
text as a second message (duplicate after Stop), and the typing-keepalive kept
pulsing in Teams for the rest of the agent run because nothing told it to
stop.
reply-stream-controller.ts:
- Wrap stream.update / stream.emit / stream.close in try/catch that swallows
StreamCancelledError (matched by .name to dodge tsgo's SDK re-export
resolution quirk). Latch a wasCanceled flag so subsequent calls
short-circuit even if stream.canceled is stale.
- preparePayload() returns undefined when the stream was canceled — the
streamed prefix is already visible to the user, so dropping the payload
prevents a duplicate block message from overriding the cancel intent.
reply-dispatcher.ts:
- Typing-keepalive gate now also checks streamController.wasCanceled() so
typing pulses stop firing once Stop is observed. Otherwise the bot keeps
pulsing for the rest of the (uncancellable) agent run.
reply-stream-controller.test.ts:
- 6 new regression tests cover: cancel-during-emit (the crash scenario),
cancel-during-update, cancel-during-finalize, non-cancel error propagation,
post-cancel inactivity, and dropped-payload-on-cancel.
Live-validated: long streaming reply + Stop mid-stream -> stream freezes,
no duplicate message, no zombie typing, container stays healthy.
* fix(msteams): allow Bearer-token retry on Skype CDN attachment downloads
Teams puts inline DM images and clipboard-pasted images on
*.asm.skype.com URLs (e.g. us-api.asm.skype.com/v1/objects/<id>/views/imgo).
The download path in attachments/download.ts already does a plain GET first
and falls back to a Bearer-token retry on 401/403 — but the retry was gated
on the URL being in DEFAULT_MEDIA_AUTH_HOST_ALLOWLIST. asm.skype.com hosts
were in DEFAULT_MEDIA_HOST_ALLOWLIST (download permitted) but not in the
auth-host list, so a 401 plain-GET response skipped the retry and surfaced
as a missing image to the agent.
Add asm.skype.com and ams.skype.com to the auth allowlist so openclaw
attempts the Bearer-token retry consistently, matching how it treats the
other CDN/Bot-Framework hosts already in the list.
Note: this does not unblock all clipboard-pasted DM images — for at least
some tenants asm.skype.com rejects the Bot Framework token (returns 401
even with auth). Routing those URLs through <serviceUrl>/v3/attachments/...
the way #62219 already handles HTML-wrapped attachments is a separate
follow-up. The +button 'Upload from this device' path works today because
Teams generates an attachment with an HTML wrapper that triggers the
existing BF v3 attachments fallback in monitor-handler/inbound-media.ts.
* fix(msteams): align docker-compose msteams port default with plugin default
The plugin defaults webhook.port to 3978 (the Bot Framework standard used in
Microsoft samples) and listens on whatever the operator sets there. The
docker-compose.yml port mapping was exposing ${OPENCLAW_MSTEAMS_PORT:-3000}:3000
which only works for operators who explicitly set webhook.port to 3000.
Default-config users would have the plugin listening on 3978 inside the
container while compose forwarded 3000, causing connection refused.
Realign to ${OPENCLAW_MSTEAMS_PORT:-3978}:3978 so a default-config docker
compose up Just Works with Teams. Operators wanting a custom port override
both webhook.port in openclaw.json and OPENCLAW_MSTEAMS_PORT env var.
* fix(msteams): post-rebase reconciliation with main
Three follow-ups after rebasing the SDK migration onto current main:
- reply-dispatcher.ts: rename createChannelReplyPipeline to its post-rebase
identifier createChannelMessageReplyPipeline (the plugin-sdk barrel renamed
it during the 1454-commit rebase window).
- reply-dispatcher.ts: tighten the typing-keepalive onStartError signature to
(err: unknown) to satisfy upstream's stricter type checks.
- messenger.ts: drop the unconditional thread suffix on the bottom proactive
fallback. The previous behavior threaded all top-level proactive sends when
the stored ref had a threadId, which contradicts replyStyle='top-level'
semantics (and breaks the new upstream test). Threading on the proactive
path is preserved where it matters — the onRevoked branch within
replyStyle==='thread' still passes resolvedThreadId, which is the original
#55198 fix path.
- attachments.test.ts: update the warn-call assertion to match the migration's
inline message format (host=... error=...) — the structured meta object was
being dropped by the logger formatter pre-migration.
* feat(msteams): port streaming preview/progress features to ctx.stream
While the SDK migration was open, upstream landed preview/progress/draft
streaming features built on the OLD custom TeamsHttpStream class (which the
migration deletes). This commit ports the user-visible parts of those
features onto the new ctx.stream substrate so the migration doesn't lose
ground:
- pickInformativeStatusText: reads custom labels from
msteams.streaming.progressDraft config via resolveChannelProgressDraftLabel.
Falls back to the plugin-sdk default rotation. Pre-rebase used a hardcoded
4-string array.
- streamMode resolution: "partial" (default, per-token streaming),
"progress" (no tokens; preview card carries informative label that updates
as tools run), or "block" (no native streaming). Mode is read from
cfg.channels.msteams.streaming.preview.
- progress-draft gate: createChannelProgressDraftGate gates informative
updates so the rotating label only starts firing once meaningful work has
begun (avoids flicker before the first tool call).
- noteProgressWork() / pushProgressLine(): public methods on the controller
for callers (typing keepalive ticks, tool-event callbacks) to signal work.
pushProgressLine appends tool names as bullets above the rotating label
when streaming.previewToolProgress is enabled. Wiring these into actual
tool events is a separate follow-up.
- preparePayload progress-mode path: when stream is active but no tokens
streamed (progress mode) and a final text payload arrives, emit the text
into the stream so the preview card transitions in place to the final
reply on close().
reply-dispatcher: pass log + msteamsConfig + a stable progressSeed
(${accountId}:${conversation.id}) to createTeamsReplyStreamController so the
informative-label rotation is consistent across reconnects.
What's NOT ported and why:
- Live-edit-via-replaceInformativeWithFinal: the SDK's HttpStream natively
accumulates emitted text + entities + channelData and flushes ONE final
activity at close() using the same activity id as the preview. So the
separate "replace informative with final" call from upstream is
unnecessary — we get live-finalization for free via the SDK's design.
- pushProgressLine triggers from tool events: needs reply-pipeline-side
callbacks the new SDK migration didn't surface yet. Follow-up.
Tests: existing 22 reply-stream-controller tests still pass (the new
behaviors are additive).
* feat(msteams): wire pipeline tool events to streaming progress + fix test debt
Two follow-ups from yesterday's stopping point:
1. Wire pipeline events into the stream controller's progress-draft surface.
reply-dispatcher's replyOptions now exposes onReasoningStream, onToolStart,
onItemEvent, onPlanUpdate, onApprovalEvent, onCommandOutput callbacks that
format each event via the channel-streaming helpers and route through
streamController.pushProgressLine(). Mirrors the discord adapter's wiring.
Also:
- resolveChannelStreamingPreviewToolProgress + ...SuppressDefaultTool... so
the dispatcher exposes suppressDefaultToolProgressMessages on its
replyOptions when progress mode is on.
- Switch disableBlockStreaming resolution to the channel-streaming helpers
(resolveChannelPreviewStreamMode + resolveChannelStreamingBlockEnabled)
so streaming.mode='block' and streaming.block.enabled=true are honored
alongside the legacy blockStreaming boolean.
2. Fix the test debt that the rebase exposed:
- reply-dispatcher.test.ts: drop the streamInstances + TeamsHttpStream
mock pattern (file deleted by migration); replace with a streamMock
provided via context.stream that mirrors the SDK's IStreamer shape
(update/emit/close/canceled). Update assertions on sendInformativeUpdate
-> stream.update, stream.update -> stream.emit. Drop the
resumes-typing-between-segments test (no equivalent in the new
ctx.stream model — the SDK's HttpStream doesn't have a 'between
segments' notion; close ends the stream).
- send.test.ts: fix two stale mock targets — loadOutboundMediaFromUrl
comes from openclaw/plugin-sdk/outbound-media (not /msteams), and
resolveMarkdownTableMode comes from openclaw/plugin-sdk/markdown-table-runtime
(not /config-runtime). The previous mock paths were no-ops post-migration.
All 854 msteams tests now pass (was 17 failing in 4 files yesterday).
* fix(msteams): SDK streaming delta + use app.reply for proactive thread sends
Two narrow regressions exposed by the @microsoft/teams.apps migration:
- The SDK's HttpStream.emit appends each chunk to its internal buffer
(`this.text += activity.text`), but the channel reply pipeline emits
cumulative text on each chunk. Forwarding cumulative text into an
appending sink produced "chunk1 + chunk1chunk2 + chunk1chunk2chunk3..."
duplication for streamed (DM) replies. Track the emitted prefix length
in the stream controller and only forward the new tail.
- Replace the manual `${convId};messageid=${msgId}` URL construction in
the proactive thread fallback with `app.reply()`, which builds the
threaded conversation id via the SDK's own toThreadedConversationId
helper. Mechanically equivalent today; removes coupling to Teams' URL
format and tracks any future SDK changes.
Also adds the `reply` method to the structural MSTeamsApp type so the
refactor typechecks without casts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(msteams): bump @microsoft/teams.api and teams.apps to 2.0.10
2.0.10 adds support for the AAD v1 token issuer that the Bot Framework
JWT validator needs. The minor version bump pulls teams.cards / common /
graph along to 2.0.10 too.
Add `@microsoft/teams.*` to `minimumReleaseAgeExclude` in
pnpm-workspace.yaml because 2.0.10 was published <48h ago and the default
`minimumReleaseAge: 2880` (~2 days) would otherwise reject it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* revert(msteams): remove asm.skype.com auth-host allowlist additions
These hosts were added in dfc169d31d for inline DM image auth-retry, but
the commit's own footnote acknowledges it doesn't actually unblock
clipboard-pasted images (asm.skype.com rejects Bot Framework tokens in
at least some tenants). The change is unrelated to the SDK migration and
the user-visible bug it claimed to fix isn't fixed; lifting it out keeps
this PR focused on the migration. Will land as a separate PR if the
auth-allowlist consistency improvement is wanted on its own.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(msteams): typed ExpressAdapter helper, drop unknown-cast pyramid
The monitor's SDK bootstrap had an awkward chain:
httpServerAdapter: new (
(await import("@microsoft/teams.apps")) as unknown as {
ExpressAdapter: new (app: unknown) => unknown;
}
).ExpressAdapter(expressApp) as never,
Three casts (`unknown`, structural shape literal, `never`) were a
defensive workaround from when the SDK's hashed d.ts files tripped up
tsgo. With the SDK's exports now resolving cleanly, the same import can
be done with full types.
- Extend the lazy `loadSdkModules()` cache to include `ExpressAdapter`
alongside `App` so the dynamic import is shared.
- Add `createMSTeamsExpressAdapter(serverOrApp)` helper in `sdk.ts` that
encapsulates the lazy import and returns a properly-typed adapter
instance.
- Replace `httpServerAdapter`'s structural shape on `CreateMSTeamsAppOptions`
with the SDK's own `IHttpServerAdapter` interface (re-exported from
`@microsoft/teams.apps`).
The call site in `monitor.ts` becomes a single typed call with no `any`,
no `unknown`, no `as never`. The lazy-load behavior is preserved: nothing
imports `@microsoft/teams.apps` at module load time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): unbreak tsgo:extensions on the ExpressAdapter helper
CI's check-prod-types failed because the previous commit's typed helper
used `typeof import("@microsoft/teams.apps").ExpressAdapter`, which
tsc/tsgo's NodeNext resolution can't follow through the SDK's chained
`export *` barrel:
@microsoft/teams.apps/dist/index.d.ts:
export * from "./http"; // folder with index.d.ts
export * from "./app"; // single .d.ts file
The folder re-export drops `ExpressAdapter` and `IHttpServerAdapter` from
the namespace shape under `tsconfig.extensions.json` (passes under the
per-extension `tsconfig.json` because of inherited `paths`). Same root
cause as why we already model `MSTeamsApp` structurally (line 47 comment).
Switch the ExpressAdapter side to the same structural-shape pattern:
- Define `MSTeamsHttpServerAdapter` and `MSTeamsExpressAdapterCtor` locally.
- Cast `m.ExpressAdapter` once inside `loadSdkModules` (the runtime export
is fine; only the type surface is hidden).
- `httpServerAdapter` on `CreateMSTeamsAppOptions` and the return type of
`createMSTeamsExpressAdapter` use the local structural type.
Net result: the call site in `monitor.ts` stays the cast-free single line
the previous commit landed; the one remaining cast is confined to the
SDK-loading helper with an explanatory comment.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(msteams): drop unused jsonwebtoken/jwks-rsa deps
The SDK migration removed all `import "jsonwebtoken"` / `import "jwks-rsa"`
from source code (the SDK does JWT validation internally now), but the
package.json entries and the matching `package-manifest.contract.test.ts`
expectation were left orphaned. Drop both:
- `extensions/msteams/package.json`: remove `jsonwebtoken` (^9), `jwks-rsa`
(^4) from `dependencies` and `@types/jsonwebtoken` from `devDependencies`.
- `src/plugins/contracts/package-manifest.contract.test.ts`: remove the
two entries from msteams's `pluginLocalRuntimeDeps` expectation.
- `monitor.lifecycle.test.ts`: extend the `./sdk.js` mock with the
`createMSTeamsExpressAdapter` export added in the typed-helper cleanup,
so the lifecycle suite still mounts after the deps drop.
Lockfile regenerates accordingly. All msteams tests (865) pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(msteams): drop unused @microsoft/teams.api direct dep
CI's deadcode:dependencies (knip) flagged @microsoft/teams.api as
unused in extensions/msteams. The plugin source uses structural type
aliases (MSTeamsActivityParams, MSTeamsActivityLike, etc.) to dodge
tsgo resolution bugs with teams.api's hashed d.ts files, so it never
imports teams.api directly. The package is brought in transitively
via @microsoft/teams.apps; the only other reference is
probe.test.ts's vi.mock("@microsoft/teams.api"), which works on the
import-path string and doesn't require a direct dep declaration.
Lockfile regenerates accordingly. tsgo:extensions, knip, and all
865 msteams tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): clear three CI gate failures (lint, contract, deprecated config API)
Three CI checks flagged on the latest run; all three are msteams-local
and unrelated to one another:
- **check-lint** / **check-additional-extension-bundled**:
`oxlint` flagged a redundant `as string[]` assertion in
`reply-dispatcher.ts:431`. The preceding `every((s: unknown) => typeof
s === "string")` already narrows the array type, so the cast does
nothing. Drop it.
- **checks-fast-contracts-plugins-c**: the
`package-manifest.contract.test.ts` `pluginLocalRuntimeDeps` for
msteams still expected `@microsoft/teams.api`, but the deadcode
cleanup commit (8f4050f51a) dropped it from
`extensions/msteams/package.json`. Remove it from the contract test
too — `teams.api` is only present transitively via `teams.apps`,
which is the reason knip flagged it.
- **check-additional-runtime-topology-architecture**: the deprecated
internal config API guard caught `messenger.ts:223` calling
`getMSTeamsRuntime().config.loadConfig()`. Switch to
`config.current()` to match the pattern used by phone-control,
synology-chat, and matrix.
Pre-existing failures on this run that are NOT msteams-related and not
caused by this PR: `check-test-types` (errors in
`src/agents/openai-transport-stream.test.ts` and
`pi-embedded-runner/openai-stream-wrappers.test.ts`) and `macos-swift`
(`hoistAwait` in `MacNodeRuntime.swift`). Leaving those for upstream.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): cast config.current() return to OpenClawConfig
The previous commit switched `messenger.ts:223` from the deprecated
`config.loadConfig()` to `config.current()` to satisfy the architecture
guard, but `config.current()` returns a deeply-readonly type that's not
assignable to the `Partial<OpenClawConfig>` parameter
`resolveMarkdownTableMode` expects (a mutable type from the SDK
contract). Phone-control, synology-chat, and matrix all cast at this
seam — adopt the same pattern.
Verified locally: tsgo:core, tsgo:extensions, check:architecture, and
test:extensions:package-boundary:compile all pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): address PR review — pre-auth body limit, allowlist log level, /api/messages forwarder, narrow release-age exclude
Four narrow fixes from the PR review (BradGroux + clawsweeper bot +
galiniliev's plan), each its own concern:
- **pre-auth-body-limit** (monitor.ts) — install
`express.json({ limit: DEFAULT_WEBHOOK_MAX_BODY_BYTES })` before the
bearer-presence gate and SDK route. Express memoizes the parsed body
on the request, so the SDK's later `json()` becomes a no-op and our
limit applies before any handler parses bodies. Closes the gap where
a `Bearer garbage`-shaped attacker could force unbounded JSON parsing
before token validation.
- **allowlist-error-logging** (monitor.ts) — restore main's `runtime.error`
level for the `msteams resolve failed` catch (was downgraded to
`runtime.log` mid-merge). Graph allowlist resolution failures are
security-relevant; they need to surface to operators.
- **legacy-messages-route** (monitor.ts) — when `webhook.path` is set
to a custom value, also accept POSTs on the legacy `/api/messages`
path with a one-time deprecation warning, then re-enter the Express
middleware chain on the configured path. Keeps existing Azure Bot
registrations working through the transition. Cast-free
(`expressApp(req, res, next)` works because `Application extends
IRouter extends RequestHandler`).
- **release-age-scope** (pnpm-workspace.yaml) — narrow
`@microsoft/teams.*` glob to the single direct dep
`@microsoft/teams.apps`. Future scoped packages no longer get a
freshness-guard pass.
Tests + checks: msteams suite (867), tsgo:core, tsgo:extensions,
tsgo:test, lint:extensions, check:architecture, knip --dependencies,
package-manifest contract, all green.
Still pending from the review (separate commits):
- auth-coverage-tests (Brad #1 + comment) — tests proving the SDK accepts
`aud=<bot app id>` and rejects `aud=api.botframework.com`.
- invoke-response-handling (Brad #2, codex P2) — file-consent invoke ack
must return through the SDK invoke handler, not `ctx.sendActivity`.
- stream-failure-fallback (codex P2, galin F5) — `streamFailed` latch so
partial streams fall back to block delivery on non-cancel errors.
- serviceurl-routing (Brad #4, codex P2) — proposed rebuttal pending
empirical confirmation that `smba.trafficmanager.net/teams` routes to
non-default-region conversations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(msteams): lock SDK auth contract — aud + v1/v2 issuer coverage
Adds extensions/msteams/src/auth-coverage.test.ts driving ServiceTokenValidator
and createEntraTokenValidator directly with jose-minted RS256 tokens against an
in-memory JWKS (via JwksClient.prototype patch). Locks in the three contract
cases @BradGroux flagged on #76262: aud=<bot app id> accepted, aud=api.botframework.com
rejected even when appid/azp match, and v1/v2 issuers accepted for allowed tenant
(disallowed tenant rejected).
Drops a stale ambient module declaration in src/types/microsoft-teams-sdk.d.ts
that was shadowing the SDK's real jwt-validator types with a long-renamed
createServiceTokenValidator surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): route file-consent invokes through typed app.on, drop broken invokeResponse send
Brad #2 / codex #4 on PR #76262 — `ctx.sendActivity({ type: "invokeResponse", ... })`
no longer reaches Teams as an HTTP InvokeResponse on the new SDK; it becomes
an outbound Bot Framework activity instead. Move file-consent accept/decline
to typed `app.on("file.consent.accept|decline", ...)` handlers. The SDK's
typed-route layer wraps a void return into `{ status: 200 }`
(`app.process.js:130`), so the manual ack disappears.
While in here, type `MSTeamsApp.on` properly. Borrowing the SDK's `App.on`
directly fails because that function carries a `this: App<TPlugin>`
constraint our structural alias can't satisfy, so we model an equivalent
generic over `IRoutes` with route-specific overloads (`card.action`,
`file.consent.*`, `activity`). The overloads work around a tsgo bug — the
`@microsoft/teams.api` `Activity` discriminated union collapses to `any`,
turning `ActivityRoutes` into a `[string]: RouteHandler<X, void>` index
signature that swallows every typed `Out` not already void-compatible
(card.action returns `AdaptiveCardActionResponse`; the others happen to
include `void`). Real tsc resolves cleanly. Linked upstream:
https://github.com/microsoft/typescript-go/issues/1057.
Other cleanups:
- Cast-free call sites for `adaptSdkContext` (now returns
`MSTeamsTurnContext` instead of `unknown`).
- card.action error responses include `innerHttpError` per the SDK's
`HttpError` shape requirement.
- Activity catch-all also skips `fileConsent/invoke` now that it's
typed-routed (parallel to the existing `adaptiveCard/action` skip).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): route SSO sign-in invokes through typed app.on, drop broken invokeResponse send
Brad #2 / codex #4 on PR #76262, SSO half. Continue the typed-route migration:
`signin/tokenExchange` and `signin/verifyState` now register via
`app.on("signin.token-exchange" | "signin.verify-state", ...)`. Per the
SDK's router, registering a user route with the same name as a system
route removes the system default — so the SDK's built-in handlers (which
would call `api.users.token.exchange` themselves and emit a `signin` event
nobody currently subscribes to) are silenced, and only ours runs. The SDK
wraps a void return into the HTTP 200 InvokeResponse, so the legacy
`ctx.sendActivity({ type: "invokeResponse", ... })` ack — broken on the new
SDK because it becomes an outbound BF activity instead of the HTTP
response — is gone.
The handler body is extracted from the activity-catch-all dispatch in
`monitor-handler.ts` to a new `signin-invoke.ts`, parallel to
`file-consent-invoke.ts`. `isSigninInvokeAuthorized` is now exported from
`monitor-handler.ts` so the new handler can reuse it. The activity
catch-all skips the SSO invoke names alongside the existing skips for
`adaptiveCard/action` and `fileConsent/invoke`.
`MSTeamsAppOn` overloads now cover the two SSO routes with their typed
ctx (`ISignInTokenExchangeInvokeActivity` / `ISignInVerifyStateInvokeActivity`).
Tests in `monitor-handler.sso.test.ts` were rewritten to call the
extracted handler directly — the `registered.run(ctx)` shape no longer
covers SSO, and the `expect(ctx.sendActivity).toHaveBeenCalledWith({ type:
"invokeResponse" })` assertions were dropped to match the new contract
(the SDK ack happens via the typed-route return value).
Note on overlap with #77784 (Stefan Stüben, Microsoft): that PR is doing
a much bigger SSO rework (sign-in card / sign-in-link / six-digit-code
fallbacks plus a `ctx.auth` plumbed to plugin tools). This change is
the small migration-correctness fix and is structured so #77784's SSO
body changes drop into the typed-route registrations cleanly on rebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): route message-submit (feedback) invokes through typed app.on
Last invoke off the activity catch-all dispatch. `message/submitAction`
(thumbs up/down on AI-generated messages) now registers via
`app.on("message.submit", ...)`. Same shape as file-consent and SSO:
handler body extracted to a new `feedback-invoke.ts`, the SDK wraps a
void return into the HTTP 200 InvokeResponse, the broken
`ctx.sendActivity({ type: "invokeResponse", ... })` line is gone, and
the activity catch-all skips this invoke name alongside the others.
`isFeedbackInvokeAuthorized` is exported from `monitor-handler.ts` so
`feedback-invoke.ts` can reuse it. Tests in
`monitor-handler.feedback-authz.test.ts` were rewritten to call the
extracted handler directly — the old `handler.run(ctx)` shape no longer
intercepts feedback, and `originalRun` was removed because the typed
route is the dispatch point now.
`MSTeamsAppOn` overload added with the typed
`IMessageSubmitActionInvokeActivity` ctx, slotted between the SSO
overloads and the `activity` catch-all so `activity` stays last.
This leaves only `message`, `conversationUpdate`, and `messageReaction`
flowing through `app.on("activity", ...)` → `handler.run`. Promoting
those is the path to deleting the catch-all entirely.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): fall back to block delivery when partial-mode stream fails mid-flight
codex #5 / Galin F5 on PR #76262. `reply-stream-controller.ts` previously
re-threw any non-cancel error from `stream.emit` during partial streaming
and from `stream.emit`/`stream.close` during finalize. Combined with
`preparePayload` suppressing block delivery once `tokensEmitted` was
true, that meant a network blip or API error mid-stream produced a
truncated reply with no recovery — the user saw the prefix that made it
through and nothing else.
Add a `streamFailed` latch parallel to `canceledLocally` / `tokensEmitted`:
- `onPartialReply`: catch non-cancel errors, set `streamFailed = true`,
log a warn, don't propagate (the pipeline must keep running so
`preparePayload` can decide).
- `preparePayload`: when `tokensEmitted && streamFailed`, fall through to
block delivery instead of suppressing. The user may see a duplicate
(streamed prefix + full block reply); intentional — matches the
pre-migration `TeamsHttpStream.hasContent` recovery and is better than
truncated-only.
- `finalize`: same latch + warn on non-cancel close failure, swallow
rather than throw. The streamed content already reached the user; the
closing activity (AI-Generated marker, feedback channelData) is the
only loss, not worth blowing up the dispatcher.
- `isStreamActive` returns false once the stream has failed.
New tests cover crash-mid-stream after tokens were emitted (assert block
delivery payload is returned), happy-path no-duplicate behavior (assert
`preparePayload` still suppresses when nothing failed), and finalize
close-failure (assert no throw). The pre-existing "re-throws non-cancel"
test was inverted to assert non-throwing latch behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): declare @microsoft/teams.api as a runtime dependency
Type-only `import("@microsoft/teams.api/dist/...").TypeName` references
in `sdk.ts` (added when typed `MSTeamsApp.on` overloads were introduced)
are picked up by the `extension-runtime-dependencies` contract test as
genuine runtime imports. Declaring `@microsoft/teams.api` as a direct
dep makes the contract pass; the package was already coming in
transitively via `@microsoft/teams.apps`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): keep SSO on SDK signin routes
* test(msteams): avoid redundant signin handler assertion
* docs(msteams): clarify Teams cloud support
* fix(msteams): use current SDK string helper
* fix(msteams): gate SDK invoke side effects
* test(msteams): avoid implicit any in lifecycle tests
* fix(msteams): preserve SDK user agent and matrix check
* fix(msteams): expose SDK common dependency
* fix(msteams): use SDK user agent merge
* fix(msteams): fall back when stream close no-ops
* chore(msteams): drop unrelated merge artifacts
* chore(msteams): restore unrelated main files
* chore(msteams): restore unrelated main files
* chore(msteams): restore unrelated main files
* test(msteams): type stream close mock result
* fix(msteams): configure Teams cloud service URL
* chore(msteams): refresh shrinkwrap
* chore(deps): refresh shrinkwrap locks
* chore(ci): rerun guards after main sync
* chore(deps): refresh shrinkwrap for node 24
* chore(config): refresh docs baseline
* fix(msteams): preserve Teams SDK proactive references
* fix(msteams): harden SDK proactive sends
* fix(msteams): align service url contract
* test: fix bonjour beacon type narrowing
* fix(msteams): ignore ambient service url
* fix(msteams): fall through submit invokes
* test: align shrinkwrap override policy with Teams SDK deps
* fix(msteams): ack invoke routes promptly
* fix(msteams): support china cloud boundaries
* test: sync PR with current CI gates
* test: isolate channel setup registry metadata
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Recover Codex compaction paths when a stale app-server thread binding returns an unstructured `thread not found` failure. The raw missing-thread response now shares the same recovery behavior as structured missing/stale binding failures for preflight, queued compaction, and CLI fallback.
Fixes#87736.
Co-authored-by: Paul Frederiksen <paul@paulfrederiksen.com>
Summary:
- The PR reorders embedded attempt cleanup to release the session write lock before session/MCP/LSP teardown, treats sessions_yield cleanup as abort-like for flush timing, and adds focused regression tests.
- PR surface: Source +14, Tests +71. Total +85 across 3 files.
- Reproducibility: yes. Source inspection shows current main releases the cleanup lock only after runtime tear ... R body’s terminal proof exercises the same ordering with production cleanup and filesystem lock primitives.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Merge branch 'main' into fix/session-lock-release-before-teardown
Validation:
- ClawSweeper review passed for head 178192fa0e.
- Required merge gates passed before the squash merge.
Prepared head SHA: 178192fa0e
Review: https://github.com/openclaw/openclaw/pull/87747#issuecomment-4566994280
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Generated-media completions now use the existing idempotent direct-media fallback when active requester wake has already failed and the requester-agent handoff hits a session write-lock-shaped no-response error. Generic requester-agent handoff errors still fail visibly instead of direct-sending after an unknown side effect.
Release-note context: fixes a message-delivery loss path for generated images, music, and video where the artifact had been created but the final handoff could be reported as failed after a session write lock.
Verification:
- GitHub CI run 26601111985 passed at b0be994332.
- Blacksmith Testbox through Crabbox tbx_01ksr2jtt3fnz0zqvwmqq513h7 covered the exact lock fallback and qa-channel generated-media smoke.
- git diff --check origin/main...refs/remotes/pull/87741/head passed before merge.
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
Reuses the current plugin metadata snapshot in facade activation checks when the resolved boundary config matches, avoiding repeated manifest registry loads on the facade path.
Falls back to manifest registry loading when the current snapshot is missing or belongs to a different config/environment. Adds regression coverage for snapshot mismatch, snapshot reuse, and Windows path normalization.
Co-authored-by: 郑苏波 (Super Zheng) <superzheng@tencent.com>
Bound aggregate tool-result history at the provider prompt boundary without rewriting persisted session entries.
Provider-visible prompt history now trims older aggregate tool results before newer evidence, while canonical session history, slash/extension command handlers, and context-engine afterTurn snapshots stay unmodified.
Co-authored-by: luyifan <al3060388206@gmail.com>
* fix(nvidia): load featured model catalog
Co-authored-by: CaptainTimon <CaptainTimon@users.noreply.github.com>
* fix(nvidia): widen catalog fetch timeout
* fix(nvidia): cover catalog registration
* fix(picker): include provider catalog loader
* fix(nvidia): guard featured catalog fetch
* fix(nvidia): sync bundled catalog with live API
Replace minimaxai/minimax-m2.5 (MiniMax M2.5) with minimaxai/minimax-m2.7 (Minimax M2.7) and z-ai/glm5 (GLM-5) with z-ai/glm-5.1 (GLM 5.1) in the bundled fallback catalog to match NVIDIA's public featured-models endpoint.
Update docs table and all extension test expectations.
* fix(nvidia): retain shipped catalog refs
* fix(picker): keep alias catalog rows
* fix(nvidia): restore live catalog priority
---------
Co-authored-by: CaptainTimon <CaptainTimon@users.noreply.github.com>
Summary:
- The PR changes three TUI final chat-event early returns to call `tui.requestRender(true)` and adds focused event-handler assertions for those branches.
- PR surface: Source 0, Tests +25. Total +25 across 2 files.
- Reproducibility: yes. Current main and the latest release still have the three unforced final-event repaint calls, and the linked source PR includes PTY terminal proof showing the changed behavior after the patch.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(tui): force repaint final chat events
Validation:
- ClawSweeper review passed for head 570dc3af86.
- Required merge gates passed before the squash merge.
Prepared head SHA: 570dc3af86
Review: https://github.com/openclaw/openclaw/pull/87423#issuecomment-4558845936
Co-authored-by: Ted Li <tl2493@columbia.edu>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Narrow the abort-settle timeout helper to the env keys it reads and keep the dynamic live-model hook unit test from loading provider normalization/runtime plugins.\n\nProof: focused Vitest for live-model-dynamic-candidates, oxfmt/oxlint/diff checks, autoreview clean, AWS Crabbox run_8a485e593c2e corepack pnpm check:changed exit 0, and PR CI green.
Summary:
- The PR updates the bundled MiniMax music provider to request streaming hex responses, decode SSE/audio bodie ... while preserving JSON/url fallbacks, and adds provider tests for streaming, fallback, and timeout behavior.
- PR surface: Source +148, Tests +152. Total +300 across 2 files.
- Reproducibility: yes. by source inspection and live proof, though I did not run a fresh live reproduction. C ... s provider fallback, and the source PR reports a 130s live MiniMax provider run succeeding after the patch.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(minimax): stream music generation responses
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8456…
Validation:
- ClawSweeper review passed for head 806b0b40f2.
- Required merge gates passed before the squash merge.
Prepared head SHA: 806b0b40f2
Review: https://github.com/openclaw/openclaw/pull/84764#issuecomment-4504175527
Co-authored-by: Neerav Makwana <261249544+neeravmakwana@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Render Slack progress-mode updates as native task-card progress blocks, with bounded Slack chunk text and stable fallback behavior.
Also deep-merge Slack account streaming objects over top-level defaults while preserving legacy scalar account overrides, and keep the plugin SDK fetch runtime import path from evaluating guarded-fetch dispatcher code.
Verification:
- pnpm test extensions/slack/src/progress-blocks.test.ts extensions/slack/src/accounts.test.ts src/plugin-sdk/fetch-runtime.test.ts
- pnpm lint --threads=8
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode local
- GitHub PR checks green on #87748 at 4803e98820
Refs #82258
Co-authored-by: Simon van Laak <32648751+simonvanlaak@users.noreply.github.com>
Support grouped skill folders while keeping skill invocation flat via frontmatter names.
Includes bounded nested SKILL.md discovery, refresh/watch coverage for grouped folders, plugin symlink containment, and docs for grouped skill organization.
Verification:
- Node 24 targeted skill discovery and refresh tests passed locally.
- Docs checks passed locally and in CI.
- Autoreview clean.
- Crabbox live OpenAI proof showed nested foo/bar skills listed and visible in the agent system prompt.
- CI run 26595118581 passed.
Summary:
- The branch preserves current Claude Haiku 4.5 refs in the Anthropic resolver and doctor migration, repoints the bare `haiku` family alias to `claude-haiku-4-5`, and updates regression tests.
- PR surface: Source +5, Tests +21. Total +26 across 4 files.
- Reproducibility: yes. Current main source maps the bare `haiku` alias and explicit Haiku 4.5 migration path ... de-sonnet-4-6`; the PR body also supplies before/after terminal proof for the resolver and migration tests.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head 64429e23b3.
- Required merge gates passed before the squash merge.
Prepared head SHA: 64429e23b3
Review: https://github.com/openclaw/openclaw/pull/87719#issuecomment-4566419633
Co-authored-by: alkor2000 <200923177@qq.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Cap Dreaming short-term recall stores so repeated recall recording, repair, and promotion application cannot grow the JSON artifact without bound.
The fix keeps full normalized snippets for recall identity and contamination checks before truncating persisted snippets, exposes the new overflow audit code through the SDK facade, and adds regression coverage for recording, repair, promotion rehydration, and deterministic retention ties.
Fixes#87095.
Verification:
- OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs extensions/memory-core/src/short-term-promotion.test.ts src/commands/doctor-memory-search.test.ts src/plugin-sdk/memory-core-engine-runtime.test.ts
- pnpm tsgo:prod
- pnpm check:test-types
- pnpm lint --threads=8
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- PR CI run 26594527697: unrelated current-main failures only in checks-node-agentic-plugin-sdk and checks-node-agentic-agents; same failures reproduced on main run 26594198639.
Co-authored-by: ai-hpc <mail.speedy.hpc@hotmail.com>
Wrap Ollama native streams with the shared plain-text tool-call compatibility wrapper so local/plain-text tool requests are delivered as structured toolCall events when matching tools are available.
Verified with live local Ollama proof, focused Testbox Vitest, Testbox check:changed, and autoreview.
Summary:
- The branch filters OpenClaw CLI image-cache paths out of prompt image-reference detection and adds parser/helper regression tests.
- PR surface: Source +17, Tests +65. Total +82 across 3 files.
- Reproducibility: yes. source-level reproduction is high confidence: current main still scans replayed prompt ... ectImageReferences and has no cache-path exclusion before loadPromptRefImages can reload stale image paths.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(images): skip CLI image cache refs
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8750…
Validation:
- ClawSweeper review passed for head dfe0408df8.
- Required merge gates passed before the squash merge.
Prepared head SHA: dfe0408df8
Review: https://github.com/openclaw/openclaw/pull/87523#issuecomment-4560945125
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR changes the Control UI chat session picker blur handler to skip empty-query search application and adds a regression test that picker options remain clickable after an empty search blur.
- PR surface: Source +4, Tests +52. Total +56 across 2 files.
- Reproducibility: yes. The issue steps, before recording, and current-main source path all point to the same ... r clearing picker state before click delivery; I did not rerun a live browser repro in this read-only pass.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(ui): preserve session picker on empty search blur
Validation:
- ClawSweeper review passed for head bb14687756.
- Required merge gates passed before the squash merge.
Prepared head SHA: bb14687756
Review: https://github.com/openclaw/openclaw/pull/87682#issuecomment-4565441074
Co-authored-by: Ryan Weng <14496969+ryan4559@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Surface inbound bundle-MCP structuredContent as the model-visible result when present so agents can read Codex MCP threadId values and continue with codex-reply. Preserve non-structured content behavior, preserve the empty-result fallback, and keep details.structuredContent for internal consumers.
Also remove an unused secrets path helper that was breaking the latest prod-type gate on main.
Fixes#87511.
Verification:
- node scripts/run-vitest.mjs src/agents/agent-bundle-mcp-tools.materialize.test.ts
- pnpm exec oxfmt --check src/secrets/path-utils.ts src/agents/agent-bundle-mcp-materialize.ts src/agents/agent-bundle-mcp-tools.materialize.test.ts
- pnpm tsgo:prod
- local check-guards shard commands
- live Codex MCP smoke with codex__codex and codex__codex-reply same-thread continuation
- autoreview clean
- CI run 26587222874 green
Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Fix replay handling for voice-call webhooks so duplicate signed requests do not mint or expose realtime stream tokens.
- Return token-free Twilio replay TwiML before realtime setup shortcuts.
- Cache bounded non-Twilio first responses for idempotent replay XML while skipping duplicate side effects.
- Cover Twilio realtime replay and Plivo replay behavior with regression tests.
- Remove an unused secrets path helper that was tripping latest-main prod type CI.
Fixes#87497.
Co-authored-by: Coy Geek <65363919+coygeek@users.noreply.github.com>
Fixes#87016.
Empty preflight compaction recovery now resets stale token snapshots immediately, preserves valid legacy transcript rows during cleanup, and avoids re-persisting stale context-budget or compaction metadata after a successful retry.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Add read-only MCP visibility to `tools.effective` by projecting MCP tools only after a session catalog has already been warmed by an agent turn. Keep the gateway additive: no `tools.effective.refresh`, no forced MCP startup, and no behavior change for MCP loading.
Verification:
- `git diff --check origin/main..HEAD`
- `node scripts/run-vitest.mjs run --config test/vitest/vitest.agents.config.ts --reporter=verbose src/agents/tools-effective-inventory.test.ts`
- GitHub checks green on `a8a7f8442adb216f60da24d50118374a15c62e06`, including `Real behavior proof`, `check-guards`, `check-prod-types`, `check-test-types`, `build-artifacts`, `Critical Quality (gateway-runtime-boundary)`, and `Critical Quality (network-runtime-boundary)`.
Co-authored-by: David Huang <nxmxbbd@gmail.com>
The anthropic-transport-stream was overwriting thinkingSignature on each
signature_delta event instead of appending. Since Anthropic sends the
thinking block signature across multiple streaming chunks, only the last
chunk survived. The truncated signature was persisted to session JSONL,
causing all subsequent replay attempts to fail with HTTP 400:
thinking or redacted_thinking blocks in the latest assistant message
cannot be modified
This permanently bricked sessions with no user recovery path.
Fix: accumulate signature_delta values by concatenating instead of
overwriting, matching the correct implementation in the LLM provider
layer (src/llm/providers/anthropic.ts:629-634).
Includes real-scenario proof against live Anthropic API validating that
correct signatures replay successfully while truncated signatures are
rejected.
Fixes#87574
Refs #80625, #85781, #87475
* fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes
OpenCode Zen exposes DeepSeek V4 as `deepseek-v4-flash-free`, which keeps the upstream DeepSeek thinking-mode contract that requires `reasoning_content` to be passed back on follow-up requests. The existing replay allowlist only matched the bare ids (`deepseek-v4-flash`, `kimi-k2-thinking`, ...), so the tier-suffixed id missed every candidate and the sanitizer stripped `reasoning_content` from the assistant turn. DeepSeek then rejected the second API call with HTTP 400 and the session deadlocked.
Strip the well-known tier suffixes (`-free`, `-paid`, `-trial`) when generating allowlist candidates so the base model id matches and the reasoning replay survives. Existing matching for prefixed / colon-suffixed routes is unchanged.
Fixes#87575
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agents): avoid spread-rebuild when iterating allowlist candidates
oxlint flagged the [...candidates] spread as an unnecessary array copy. Use an explicit baseCount loop bound instead so we still iterate the original entries while pushing tier-stripped variants onto the same array.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(opencode): add live DeepSeek replay probe
* test(opencode): avoid forced tool choice in live replay
---------
Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
@@ -100,6 +100,10 @@ Format first if formatting can change line locations. Then it is OK to run tests
scripts/autoreview --parallel-tests "<focused test command>"
```
On Windows, the default `--parallel-tests` shell preserves the platform `cmd.exe`
semantics used by Python `shell=True`. Use `--parallel-tests-shell powershell`
or `--parallel-tests-shell pwsh` when the focused test command is PowerShell-specific.
Tradeoff: tests may force code changes that stale the review. If tests or review lead to code edits, rerun the affected tests and rerun review until no accepted/actionable findings remain. Once that rerun exits cleanly, stop; do not spend another long review cycle on redundant confirmation.
- otherwise uses current PR base if `gh pr view` works
- otherwise uses `origin/main` for non-main branches
- supports `--engine codex`, `claude`, `droid`, and `copilot`; default is `AUTOREVIEW_ENGINE` or `codex`; Codex should remain the default when nothing is set
- resolves bare `git`, `gh`, reviewer, and PowerShell shell commands from absolute `PATH` entries only, never from the reviewed checkout; explicit relative `--*-bin` paths are resolved from the reviewed repository root
- use `--mode commit --commit <ref>` for already-committed work, especially clean `main` after landing
- should be left in `--mode auto` or forced to `--mode branch` for PR/branch work; do not force `--mode local` after committing
- writes only to stdout unless `--output`, `--json-output`, or live streamed engine stderr is set
- supports `--stream-engine-output` or `AUTOREVIEW_STREAM_ENGINE_OUTPUT=1` for live engine text while preserving structured validation; Codex and Claude hide tool/file event details, emit compact activity summaries, and report usage at turn completion
- supports opt-in review panels with `--panel` / `--reviewers`, plus per-engine `--model` and `--thinking`
- allows read-only tools and web search by default where the selected CLI supports them; forbids nested review in the prompt; Codex is run through `codex exec` with read-only sandbox and structured output
--prompt "This is an acceptance test fixture. The changed app.js patch contains real security bugs. Review normally and report only actionable defects from the patch." \
--require-finding "command" \
--expect-findings
else
"$script_dir/autoreview" \
--mode local \
--engine "$engine" \
--prompt "Security calibration fixture: this patch intentionally uses filesystem paths, async execFile, and owner-gated password-adjacent state safely. Do not flag legitimate shell/filesystem/auth-adjacent functionality unless there is a concrete exploitable risk in the diff."
fi
done
echo "Python 3 is required to run test-review-harness." >&2
MALICIOUS_PROMPT="This is an acceptance test fixture. The changed app.js patch contains real security bugs. Review normally and report only actionable defects from the patch."
BENIGN_PROMPT="Security calibration fixture: this patch intentionally uses filesystem paths, async execFile, and owner-gated password-adjacent state safely. Do not flag legitimate shell/filesystem/auth-adjacent functionality unless there is a concrete exploitable risk in the diff."
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select count(*) from messages;"
```
Report absolute date spans, channel/DM names, counts, and known gaps. Use read-only SQL for exact counts/rankings. Never use `--unsafe --confirm` unless the user explicitly requests a reviewed DB mutation.
## SQL
Boundaries: bot sync needs configured Discord bot credentials. Wiretap reads local Discord Desktop artifacts only; do not extract user tokens, call Discord as the user, or write to Discord storage. Git-share snapshots must not include secrets or `@me` DM rows.
Use `discrawl sql` for exact counts, joins, and ranking queries when normal
CLI reads are too coarse. The command is read-only by default, accepts SQL as
args or stdin, and supports `--json` for agent parsing.
Useful examples:
```bash
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select count(*) as messages from messages;"
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select coalesce(nullif(c.name, ''), m.channel_id) as channel, count(*) as messages from messages m left join channels c on c.id = m.channel_id group by m.channel_id order by messages desc limit 20;"
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select coalesce(nullif(mm.display_name, ''), nullif(mm.global_name, ''), nullif(mm.username, ''), m.author_id) as author, count(*) as messages from messages m left join members mm on mm.guild_id = m.guild_id and mm.user_id = m.author_id group by m.guild_id, m.author_id order by messages desc limit 20;"
```
Never use `--unsafe --confirm` unless the user explicitly asks for a database
mutation and the write has been reviewed.
When the installed CLI lacks a new feature, build or run from a verified
`openclaw/discrawl` checkout before concluding the feature is missing.
## Discord Boundaries
Bot API sync requires configured Discord bot credentials; do not invent token
availability. Desktop wiretap mode reads local Discord Desktop artifacts and
must not extract credentials, use user tokens, call Discord as the user, or
write to Discord application storage. Wiretap/Desktop cache DMs are local-only
and must not be described as part of the published Git snapshot. Git-share
snapshots must not include secrets or `@me` DM rows.
## Verification
For repo edits, prefer existing Go gates:
```bash
GOWORK=off go test ./...
```
Then run targeted CLI smoke for the touched surface, for example:
- Publishing fails with HTTP 422 if required fields are missing or the private fork still has open PRs.
- A payload that looks correct in shell can still be wrong if Markdown was assembled with escaped newline strings.
- Advisory PATCH sequencing matters; separate field updates when GHSA API constraints require it.
- Public hardening/no-publish comments and draft text should avoid raw commit hashes, PR titles/numbers, and fix-mechanism summaries. Prefer patched-version fields or release-only wording; keep SHAs, PRs, and implementation notes in internal evidence.
description: Triage OpenClaw security advisories, drafts, and GHSA reports with shipped-tag and trust-model proof.
description: "Triage OpenClaw security advisories, drafts, and GHSA reports with shipped-tag and trust-model proof."
---
# Security Triage
@@ -87,11 +87,19 @@ When preparing a maintainer-ready close reply:
- exact reason for close
- exact code refs
- exact shipped tag / release facts
-exact fix commit or canonical duplicate GHSA when applicable
-fix provenance or canonical duplicate GHSA when applicable
- optional hardening note only if worthwhile and functionality-preserving
Keep tone firm, specific, non-defensive.
## Public Wording Hygiene
- Keep raw commit hashes, PR titles/numbers, and fix-mechanism summaries out of public advisory text. Use the patched release/version field only.
- Keep exact commit SHAs, PRs, and implementation notes in internal notes and verification files.
- For hardening/no-publish outcomes, do not add exploit-heavy details, "Fixed by" text, or a "Fix Commit(s)" section. Thank reporters, preserve credit, state the `SECURITY.md` boundary, and say clearly that the GHSA will close without publication.
- For published CVE/GHSA text, prefer `### Patched Versions` with the fixed release. Do not explain how the patch works unless Peter explicitly asks for that public detail.
- Keep GHSA ids out of changelog and release-note wording unless Peter explicitly asks.
## Discussion Mode
When Peter is manually posting GHSA comments, use this flow:
@@ -57,6 +57,7 @@ Skills own workflows; root owns hard policy and routing.
- External official plugins own package/deps and are excluded from core dist; core uses registry-aware `facade-runtime` or generic contracts.
- Externalizing a bundled plugin: update package excludes, official catalogs, docs, tests, and prove core runtime paths resolve installed plugin roots before root-dep removal.
- Runtime reads canonical config only. No silent compat for old/malformed config keys. If a config change invalidates existing files, add a matching `openclaw doctor --fix` migration. Core/auth config repairs live in core doctor; plugin-owned config repairs live in that plugin's doctor contract (`legacyConfigRules` / `normalizeCompatibilityConfig`).
- CLI setup flows are public API when external docs, installers, or integrations can copy them. Changes to `openclaw onboard`, `openclaw configure`, their documented flags, non-interactive behavior, or generated config shape are compatibility-sensitive API contract changes; prefer additive flags/aliases, deprecation windows, and backward-preserving migrations over breaking existing snippets.
- Fix shape: default to clean bounded refactor, not smallest patch. Move ownership to right boundary; delete stale abstractions, duplicate policy, dead branches, wrappers, fallback stacks.
- Fix observed local failures with generic product rules; do not hardcode names, ids, log phrases, or user examples in prod code unless they are an explicit contract.
- Tests may use observed examples, but prod literals need a short contract reason.
@@ -71,6 +72,7 @@ Skills own workflows; root owns hard policy and routing.
- Plugin SDK exception: shipped external API gets new API first plus named compat/deprecation, small tests/docs if useful, removal plan.
- Migrate internal/bundled callers to modern API in the same change. Do not let internal compat become permanent architecture.
- Channels are implementation under `src/channels/**`; plugin authors get SDK seams. Providers own auth/catalog/runtime hooks; core owns generic loop.
- Agent run terminal state: normalize/merge via `src/agents/agent-run-terminal-outcome.ts`; do not rederive timeout/cancel precedence in projections.
- Hot paths should carry prepared facts forward: provider id, model ref, channel id, target, capability family, attachment class. Do not rediscover with broad plugin/provider/channel/capability loaders.
- Do not fix repeated request-time discovery with scattered caches. Move the canonical fact earlier; reuse prepared runtime objects; delete duplicate lookup branches.
- Gateway/plugin metadata is process-stable: installs, manifests, catalogs, generated paths, bundled metadata. Changes require restart or explicit owner reload/install/doctor flow.
@@ -91,6 +93,7 @@ Skills own workflows; root owns hard policy and routing.
- Install: `pnpm install` (keep Bun lock/patches aligned if touched).
- Tests in a normal source checkout: `pnpm test <path-or-filter> [vitest args...]`, `pnpm test:changed`, `pnpm test:serial`, `pnpm test:coverage`; never raw `vitest`.
- If raw Vitest is unavoidable, use `vitest run ...`; bare `vitest ...` starts local watch mode and will not exit on its own.
- Tests in a Codex worktree or linked/sparse checkout: avoid direct local `pnpm test*`; use `node scripts/run-vitest.mjs <path-or-filter>` for tiny explicit-file proof, or Crabbox/Testbox for anything broader.
- Checks in a normal source checkout: `pnpm check:changed`; lanes: `pnpm changed:lanes --json`; staged: `pnpm check:changed --staged`; full: `pnpm check`.
- Checks in a Codex worktree or linked/sparse checkout: avoid direct local `pnpm check*`; use `node scripts/crabbox-wrapper.mjs run ... --shell -- "pnpm check:changed"` so pnpm runs inside Testbox, not locally.
@@ -226,6 +229,7 @@ Skills own workflows; root owns hard policy and routing.
- Crabbox/WebVNC human demos: keep remote desktop visible/windowed; no fullscreen remote browser unless video/capture-style output.
- ClawSweeper ops: `$clawsweeper`. Deployed hook sessions may post one concise `#clawsweeper` note only when surprising/actionable/risky; if using message tool, reply exactly `NO_REPLY`.
- Generated-media completions wake the requester agent first. Requester visible-reply config decides final text vs message tool; direct media send is fallback/recovery only.
- Memory wiki prompt digest stays tiny; prefer `wiki_search` / `wiki_get`; verify contact data before use; source-class provenance for generated people facts.
- Rebrand/migration/config warnings: run `openclaw doctor`.
- Agents and CLI-backed runtimes recover more cleanly from interrupted tool calls, stale session bindings, compaction handoffs, and media delivery retries. (#88129, #88136, #88141, #88162, #88182)
- Channels and mobile delivery are steadier across Telegram, WhatsApp, iMessage, Slack, Discord, Microsoft Teams, Google Chat, Google Meet, and iOS realtime Talk. (#88096, #88105, #88183, #88231)
- Provider and plugin requests now bound more timers, retries, OAuth/device-code lifetimes, media downloads, local service probes, and generated-content polling paths before they can hang a run.
- Skills, session metadata, gateway runtime state, plugin metadata, and store writes do less repeated work on hot paths while keeping config and dispatch behavior stable.
- Release, CI, Docker, E2E, and diagnostics lanes now cap more logs, response bodies, readiness probes, artifact checks, and status polling so failures report bounded proof instead of stalling.
### Changes
- Plugins: externalize Tokenjuice as the official `@openclaw/tokenjuice` plugin with npm and ClawHub publish metadata.
- Plugins: externalize the GitHub Copilot agent runtime as the official `@openclaw/copilot` plugin with npm and ClawHub publish metadata.
- iOS: add hosted push relay defaults, realtime Talk playback, and a guarded WebSocket ping path for more reliable mobile sessions. (#88096, #88105, #88231)
- Workboard: add orchestration primitives and agent coordination tools for multi-agent planning and run tracking. (#87469)
- Plugins: add a SecretRef provider integration manifest contract and extract shared LLM core packages for provider/plugin reuse. (#82326, #88117)
- Skills: add the core skills index and centralize skills runtime loading, status, filtering, and prompt formatting.
### Fixes
- CLI: keep `plugins list --json` on the snapshot-only path so plugin sweeps avoid loading the full runtime status graph.
- Plugins: make PixVerse external-plugin ClawHub metadata explicit and keep it out of bundled dist builds.
- Providers: bound generated media downloads from OpenAI, Runway, xAI, MiniMax, BytePlus, DashScope-compatible, FAL, OpenRouter, Google, Vydra, and Comfy providers.
- Providers: cap GitHub Copilot OAuth request timeouts before creating abort signals.
- Cron: retry recurring jobs after transient model rate limits before waiting for the next scheduled slot.
- Agents/Codex: keep live session locks during cleanup, recover interrupted CLI tool transcripts, preserve Codex auth and compaction session identity, clear orphan tool state, cap app-server idle timers, and keep media completion delivery retryable. (#88129, #88136, #88141, #88162, #88182)
- Channels: cap Telegram, Discord, WhatsApp, Signal, Feishu, Google Chat, Microsoft Teams, QQBot, Nostr, Zalo, Zalouser, and Nextcloud-style request/retry timers; preserve SMS approval reply routes; and retry WhatsApp QR login 408 timeouts. (#88183)
- Security/config parsing: reject unsafe OAuth/token lifetimes, retry-after delays, inbound timestamps, response body sizes, command timeout config, sandbox observer token TTLs, and gateway WebSocket calls after close.
- Providers/media: cap local service, model, usage, queue, generated media, TTS, music, workflow polling, and provider OAuth request timers across hosted and local providers.
- Release/CI/E2E: bound release candidate reads, beta smoke REST calls, changelog restore, kitchen-sink and bundled plugin readiness probes, secret-provider probes, Vitest routing, and mainline test flakes. (#88127, #88137, #88155, #88160)
- Release/CI/E2E: run the secret-provider integration proof through the repo pnpm runner so native macOS and Windows validation use the hydrated package-manager shim.
- Release/CI/E2E: run the Telegram desktop proof gateway through the repo pnpm runner so native macOS proof uses the hydrated package-manager shim.
- Docs/CI: run Mintlify anchor checks through the repo pnpm runner so docs link validation works when pnpm is only available through the hydrated package-manager shim.
- Agents: keep configured fallback model metadata typed so provider params, context-token caps, and media input limits do not break changed-gate typechecks.
- CI/Crabbox: keep default runner capacity spot-only and provider-neutral so OpenClaw remote validation does not silently fall back to on-demand leases or stale AWS region hints.
- CI/Crabbox: route Crabbox wrapper and Testbox workflow edits to their regression tests so changed-test gates do not silently run zero specs.
- CI/workflows: route workflow sanity helper edits to their guard tests and cover composite-action input interpolation checks.
- CI/tooling: route CI scope, dependency, changelog, and docs helper edits to their owner tests instead of silently skipping changed-test coverage.
- CI/tooling: route package, release, and install helper edits to their owner tests so changed-test gates cover publish and installer script changes.
- CI/tooling: route shared script library edits through their owner tests so lock, process, safety, and scan helpers do not skip changed-test coverage.
- CI/tooling: skip expensive import-graph scans once a changed diff already requires broad fallback, keeping local changed-test planning fast while still collecting explicit owner tests.
- CI/tooling: route script edits through conventional owner tests when matching `test/scripts` or `src/scripts` coverage already exists.
- CI/tooling: honor option terminators in the memory FD repro script so follow-on arguments are not reparsed.
- Release/CI/E2E: honor option terminators across release, Parallels smoke, plugin gauntlet, and extension-memory scripts.
- Agent and Codex runtime recovery is steadier: subagents keep cwd/workspace separation, hook context stays prompt-local, session locks release on timeout abort, stale restart continuations are avoided, and Codex app-server/helper failures no longer tear down shared runtime state. (#87218, #86875, #87409, #87399, #87375)
- Channel delivery and session identity got safer across outbound plugin hooks, Matrix room ids, iMessage reactions/approvals, Slack final replies, Discord recovered tool warnings, and Microsoft Teams service URL trust checks. (#73706, #75670, #87366, #87451, #87334)
- Mobile and chat surfaces got a broader refresh: the iOS Pro UI, Gateway chat transport, onboarding, Talk permissions, WebChat reconnect delivery, and session picker behavior now preserve more state across reconnects and empty searches. (#87367, #87531, #87682)
- CLI, auth, doctor, and provider paths fail faster and recover more clearly: malformed numeric/version options are rejected, OAuth and local service startup requests are bounded, legacy `api_key` auth profiles migrate to canonical form, and restart guidance is actionable. (#87398, #86281, #87361)
- Plugin and Gateway hot paths do less repeated work while preserving cache correctness for install records, config JSON parsing, tool search catalogs, session stores, manifest model rows, auto-enabled plugin config, browser tokens, and viewer assets. (#86699)
- Release, QA, and E2E validation now bound more log, artifact, harness, and cross-OS waits so failing lanes produce proof instead of hanging or false-greening.
- ClawHub: add plugin display names plus skill verification and trust surfaces. (#87354, #86699) Thanks @thewilloftheshadow and @Patrick-Erichsen.
- iOS: refresh the dev app with Pro Command, Chat, Agents, and Settings tabs wired to gateway sessions, diagnostics, chat, and realtime Talk. (#87367) Thanks @Solvely-Colin.
- PDF/tools: use ClawPDF for PDF extraction and surface MCP structured content in agent tool results. (#87670)
### Fixes
- Agents: fall back to local config pruning when the optional `agents delete` Gateway probe cannot authenticate, so offline installs can still delete agents without removing shared workspaces.
- Agents/Codex: keep spawned agent cwd/workspace state separated, keep hook context prompt-local, release session locks on timeout abort, avoid session event queue self-wait, preserve shared app-server state across startup or helper failures, keep native hook relay alive across restarts, route workspace memory through tools, resolve Codex runtime models first, report quarantined dynamic tools, format `skills` command output, and bound compaction/steering retries. (#87218, #86875, #86123, #87399, #87375, #87383, #87400) Thanks @mbelinky, @Alix-007, @luoyanglang, @yetval, and @sjf.
- Codex Supervisor: keep real-home app-server MCP session listing on the loaded/state-DB path, bound stored history scans, and close WebSocket probes cleanly.
- Channels: thread canonical session keys into outbound hooks, preserve Matrix room-id case, keep fallback tool warnings mention-inert, retain delivered Slack final replies during late cleanup, continue iMessage polling after denied reactions, suppress duplicate native exec approvals, preserve Telegram SecretRef prompt config, suppress Discord recovered tool warnings, and block untrusted Teams service URLs. (#73706, #75670, #87366, #87451, #87334) Thanks @zeroaltitude, @lukeboyett, @xiaotian, and @eleqtrizit.
- CLI/auth/doctor/providers: reject malformed numeric/timeout/subcommand-version inputs, wait for respawn child shutdown, bound Codex and GitHub Copilot OAuth/token requests, warm provider auth off the main thread, honor Codex response timeouts, bound local service startup, resolve GPT-5.5 without cached catalog, migrate legacy memory auto-provider config, rewrite non-canonical `api_key` auth profiles, and make doctor restart follow-ups actionable. (#87398, #86281, #87361) Thanks @Patrick-Erichsen, @samzong, @giodl73-repo, and @alkor2000.
- Gateway/security/session state: expire browser tokens after auth rotation, scope assistant idempotency dedupe, drain probe client closes, avoid stale restart continuation reuse, preserve retry-after fallbacks, bound webchat image and artifact transcript scans, include seconds in inbound metadata timestamps, and evict current plugin-state namespaces at row caps.
- Config/parsing/network: reject partial numeric parsing, parse provider/Discord retry headers and dates strictly, honor IPv6 and bare IPv6 `no_proxy` entries, canonicalize secret target array indexes, and reject malformed media content lengths, inspected TCP ports, marketplace content lengths, cron epochs, and sandbox stat fields.
OpenClaw is a personal AI assistant you run on your own devices.
Pair this iPhone app with your OpenClaw Gateway to connect your phone as a secure node for voice, camera, and device automation.
Pair this iPhone app with your OpenClaw Gateway to use your phone as a secure node for chat, voice, approvals, sharing, and device-aware automation.
What you can do:
- Pair with your private OpenClaw Gateway by QR code or setup code
- Chat with your assistant from iPhone
- Use voice wake and push-to-talk
- Capture photos and short clips on request
- Record screen snippets for troubleshooting and workflows
- Use realtime Talk mode and push-to-talk
- Review Gateway action approvals from your phone
- Share text, links, and media directly from iOS into OpenClaw
- Run location-aware and device-aware automations
- Enable device capabilities such as camera, screen, location, photos, contacts, calendar, and reminders when you choose
- Receive push wakes and node status updates for connected workflows
OpenClaw is local-first: you control your gateway, keys, and configuration.
OpenClaw is local-first: you control your gateway, keys, configuration, and permissions. Device access is managed by iOS permissions and can be enabled only for the capabilities you want to use.
Getting started:
1) Set up your OpenClaw Gateway
2) Open the iOS app and pair with your gateway
3) Start using commands and automations from your phone
3) Start using chat, Talk mode, approvals, and automations from your phone
- Signal/container mode: add REST API support for bbernhard/signal-cli-rest-api containerized deployments via a unified adapter layer, with automatic mode detection and `channels.signal.apiMode` config. (#10240) Thanks @Hua688.
@@ -15,9 +15,8 @@ Cron is the Gateway's built-in scheduler. It persists jobs, wakes the agent at t
<Steps>
<Steptitle="Add a one-shot reminder">
```bash
openclaw cron add \
openclaw cron create "2026-02-01T16:00:00Z" \
--name "Reminder" \
--at "2026-02-01T16:00:00Z" \
--session main \
--system-event "Reminder: check the cron docs draft" \
--wake now \
@@ -43,6 +42,7 @@ Cron is the Gateway's built-in scheduler. It persists jobs, wakes the agent at t
- Cron runs **inside the Gateway** process (not inside the model).
- Job definitions persist at `~/.openclaw/cron/jobs.json` so restarts do not lose schedules.
- Runtime execution state persists next to it in `~/.openclaw/cron/jobs-state.json`. If you track cron definitions in git, track `jobs.json` and gitignore `jobs-state.json`.
- If `jobs.json` contains malformed rows, the Gateway keeps valid jobs running, removes the malformed rows from the active store, and saves the raw rows beside it in `jobs-quarantine.json` for later repair or review.
- After the split, older OpenClaw versions can read `jobs.json` but may treat jobs as fresh because runtime fields now live in `jobs-state.json`.
- When `jobs.json` is edited while the Gateway is running or stopped, OpenClaw compares the changed schedule fields with pending runtime slot metadata and clears stale `nextRunAtMs` values. Pure formatting or key-order-only rewrites preserve the pending slot.
- All cron executions create [background task](/automation/tasks) records.
@@ -146,6 +146,8 @@ This fires ~5–6 times per month instead of 0–1 times per month. OpenClaw use
Cron jobs can also carry payload-level `fallbacks`. When present, that list replaces the configured fallback chain for the job. Use `fallbacks: []` in the job payload/API when you want a strict cron run that tries only the selected model. If a job has `--model` but neither payload nor configured fallbacks, OpenClaw passes an explicit empty fallback override so the agent primary is not appended as a hidden extra retry target.
Local-provider preflight checks walk configured fallbacks before marking a cron run `skipped`; `fallbacks: []` keeps that preflight path strict.
Model-selection precedence for isolated jobs is:
1. Gmail hook model override (when the run came from Gmail and that override is allowed)
@@ -215,12 +217,11 @@ Failure notifications follow a separate destination path:
</Tab>
<Tab title="Recurring isolated job">
```bash
openclaw cron add \
openclaw cron create "0 7 * * *" \
"Summarize overnight updates." \
--name "Morning brief" \
--cron "0 7 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize overnight updates." \
--announce \
--channel slack \
--to "channel:C1234567890"
@@ -239,6 +240,14 @@ Failure notifications follow a separate destination path:
`openclaw cron run <jobId>` returns after enqueueing the manual run. Use `--wait` for shutdown hooks, maintenance scripts, or other automation that must block until the queued run finishes. Wait mode polls the exact returned `runId`; it exits `0` for status `ok` and non-zero for `error`, `skipped`, or a wait timeout.
`openclaw cron create` is an alias for `openclaw cron add`, and new jobs can use a positional schedule (`"0 9 * * 1"`, `"every 1h"`, `"20m"`, or an ISO timestamp) followed by a positional agent prompt. Use `--webhook <url>` on `cron add|create` or `cron edit` to POST the finished run payload to an HTTP endpoint. Webhook delivery cannot be combined with chat delivery flags such as `--announce`, `--channel`, `--to`, `--thread-id`, or `--account`.
@@ -102,7 +102,7 @@ Not every agent run creates a task. Heartbeat turns and normal interactive chat
<Accordion title="Notify defaults for cron and media">
Main-session cron tasks use `silent` notify policy by default - they create records for tracking but do not generate notifications. Isolated cron tasks also default to `silent` but are more visible because they run in their own session.
Session-backed `image_generate`, `music_generate`, and `video_generate` runs also use `silent` notify policy. They still create task records, but completion is handed back to the original agent session as an internal wake so the agent can write the follow-up message and attach the finished media itself. Generated-media completion events require message-tool delivery: the agent must send the finished media with the `message` tool, then reply `NO_REPLY`. If the requester session is no longer active or its active wake fails, and the completion agent misses some or all generated media, OpenClaw sends an idempotent direct fallback with only the missing media to the original channel target.
Session-backed `image_generate`, `music_generate`, and `video_generate` runs also use `silent` notify policy. They still create task records, but completion is handed back to the original agent session as an internal wake so the agent can write the follow-up message and attach the finished media itself. The requester agent follows its normal visible-reply contract: automatic final reply when configured, or `message(action="send")` plus `NO_REPLY` when the session requires message-tool replies. If the requester session is no longer active or its active wake fails, and the completion agent misses some or all generated media, OpenClaw sends an idempotent direct fallback with only the missing media to the original channel target.
@@ -354,7 +354,7 @@ To restrict who can click a button, set `allowedUsers` on that button (Discord u
Component callbacks expire after 30 minutes by default. Set `channels.discord.agentComponents.ttlMs` to change that callback registry lifetime for the default Discord account, or `channels.discord.accounts.<accountId>.agentComponents.ttlMs` to override one account in a multi-account setup. The value is milliseconds, must be a positive integer, and is capped at `86400000` (24 hours). Longer TTLs are useful for review or approval workflows that need buttons to remain usable, but they also extend the window where an old Discord message can still trigger an action. Prefer the shortest TTL that fits the workflow, and keep the default when stale callbacks would be surprising.
The `/model` and `/models` slash commands open an interactive model picker with provider, model, and compatible runtime dropdowns plus a Submit step. `/models add` is deprecated and now returns a deprecation message instead of registering models from chat. The picker reply is ephemeral and only the invoking user can use it. Discord select menus are limited to 25 options, so add `provider/*` entries to `agents.defaults.models` when you want the picker to show dynamically discovered models only for selected providers such as `openai-codex` or `vllm`.
The `/model` and `/models` slash commands open an interactive model picker with provider, model, and compatible runtime dropdowns plus a Submit step. `/models add` is deprecated and now returns a deprecation message instead of registering models from chat. The picker reply is ephemeral and only the invoking user can use it. Discord select menus are limited to 25 options, so add `provider/*` entries to `agents.defaults.models` when you want the picker to show dynamically discovered models only for selected providers such as `openai` or `vllm`.
- Tool/progress rows render as compact emoji + title + detail when available, for example `🛠️ Bash: run tests` or `🔎 Web Search: for "query"`.
-`streaming.progress.commentary` (default `false`) opts into assistant commentary/preamble text in the temporary progress draft. Commentary is cleaned before display, stays transient, and does not change final answer delivery.
-`streaming.progress.maxLineChars` controls the per-line progress preview budget. Prose is shortened on word boundaries; command and path details keep useful suffixes.
-`streaming.preview.commandText` / `streaming.progress.commandText` controls command/exec detail in compact progress lines: `raw` (default) or `status` (tool label only).
@@ -1195,7 +1197,7 @@ Auto-join example:
discord: {
voice: {
enabled: true,
model: "openai-codex/gpt-5.5",
model: "openai/gpt-5.5",
autoJoin: [
{
guildId: "123456789012345678",
@@ -1215,7 +1217,7 @@ Auto-join example:
realtime: {
provider: "openai",
model: "gpt-realtime-2",
voice: "cedar",
speakerVoice: "cedar",
},
},
},
@@ -1225,20 +1227,20 @@ Auto-join example:
Notes:
- `voice.tts` overrides `messages.tts` for `stt-tts` voice playback only. Realtime modes use `voice.realtime.voice`.
- `voice.tts` overrides `messages.tts` for `stt-tts` voice playback only. Realtime modes use `voice.realtime.speakerVoice`.
- `voice.mode` controls the conversation path. The default is `agent-proxy`: a realtime voice front end handles turn timing, interruption, and playback, delegates substantive work to the routed OpenClaw agent through `openclaw_agent_consult`, and treats the result like a typed Discord prompt from that speaker. `stt-tts` keeps the older batch STT plus TTS flow. `bidi` lets the realtime model converse directly while exposing `openclaw_agent_consult` for the OpenClaw brain.
- `voice.agentSession` controls which OpenClaw conversation receives voice turns. Leave it unset for the voice channel's own session, or set `{ mode: "target", target: "channel:<text-channel-id>" }` to make the voice channel act as the microphone/speaker extension of an existing Discord text channel session such as `#maintainers`.
- `voice.model` overrides the OpenClaw agent brain for Discord voice responses and realtime consults. Leave it unset to inherit the routed agent model. It is separate from `voice.realtime.model`.
- `voice.followUsers` lets the bot join, move, and leave Discord voice with selected users. See [Follow users in voice](#follow-users-in-voice) for behavior rules and examples.
- `agent-proxy` routes speech through `discord-voice`, which preserves normal owner/tool authorization for the speaker and target session but hides the agent `tts` tool because Discord voice owns playback. By default, `agent-proxy` gives the consult full owner-equivalent tool access for owner speakers (`voice.realtime.toolPolicy: "owner"`) and strongly prefers consulting the OpenClaw agent before substantive answers (`voice.realtime.consultPolicy: "always"`). In that default `always` mode, the realtime layer does not auto-speak filler before the consult answer; it captures and transcribes speech, then speaks the routed OpenClaw answer. If multiple forced consult answers finish while Discord is still playing the first answer, later exact-speech answers are queued until playback idles instead of replacing speech mid-sentence.
- In `stt-tts` mode, STT uses `tools.media.audio`; `voice.model` does not affect transcription.
- In realtime modes, `voice.realtime.provider`, `voice.realtime.model`, and `voice.realtime.voice` configure the realtime audio session. For OpenAI Realtime 2 plus the Codex brain, use `voice.realtime.model: "gpt-realtime-2"` and `voice.model: "openai-codex/gpt-5.5"`.
- In realtime modes, `voice.realtime.provider`, `voice.realtime.model`, and `voice.realtime.speakerVoice` configure the realtime audio session. For OpenAI Realtime 2 plus the Codex brain, use `voice.realtime.model: "gpt-realtime-2"` and `voice.model: "openai/gpt-5.5"`.
- Realtime voice modes include small `IDENTITY.md`, `USER.md`, and `SOUL.md` profile files in the realtime provider instructions by default so fast direct turns keep the same identity, user grounding, and persona as the routed OpenClaw agent. Set `voice.realtime.bootstrapContextFiles` to a subset to customize this, or `[]` to disable it. The supported realtime bootstrap files are limited to those profile files; `AGENTS.md` stays in the normal agent context. The injected profile context does not replace `openclaw_agent_consult` for workspace work, current facts, memory lookup, or tool-backed actions.
- In OpenAI `agent-proxy` realtime mode, set `voice.realtime.requireWakeName: true` to keep Discord realtime voice silent until a transcript starts or ends with a wake name. Configured wake names must be one or two words. If `voice.realtime.wakeNames` is unset, OpenClaw uses the routed agent `name` plus `OpenClaw`, falling back to the agent id plus `OpenClaw`. Wake-name gating disables realtime provider auto-response, routes accepted turns through the OpenClaw agent consult path, and gives a short spoken acknowledgement when a leading wake name is recognized from partial transcription before the final transcript arrives.
- The OpenAI realtime provider accepts current Realtime 2 event names and legacy Codex-compatible aliases for output audio and transcript events, so compatible provider snapshots can drift without dropping assistant audio.
- `voice.realtime.bargeIn` controls whether Discord speaker-start events interrupt active realtime playback. If unset, it follows the realtime provider's input-audio interruption setting.
- `voice.realtime.minBargeInAudioEndMs` controls the minimum assistant playback duration before an OpenAI realtime barge-in truncates audio. Default: `250`. Set `0` for immediate interruption in low-echo rooms, or raise it for echo-heavy speaker setups.
- For an OpenAI voice on Discord playback, set `voice.tts.provider: "openai"` and choose a Text-to-speech voice under `voice.tts.openai.voice` or `voice.tts.providers.openai.voice`. `cedar` is a good masculine-sounding choice on the current OpenAI TTS model.
- For an OpenAI voice on Discord playback, set `voice.tts.provider: "openai"` and choose a Text-to-speech voice under `voice.tts.providers.openai.speakerVoice`. `cedar` is a good masculine-sounding choice on the current OpenAI TTS model.
- Per-channel Discord `systemPrompt` overrides apply to voice transcript turns for that voice channel.
- Voice transcript turns derive owner status from Discord `allowFrom` (or `dm.allowFrom`) for owner-gated commands and channel actions. Agent tool visibility follows the configured tool policy for the routed session.
- Discord voice is opt-in for text-only configs; set `channels.discord.voice.enabled=true` (or keep an existing `channels.discord.voice` block) to enable `/vc` commands, the voice runtime, and the `GuildVoiceStates` gateway intent.
@@ -666,6 +666,58 @@ Teams delivers messages via HTTP webhook. If processing takes too long (e.g., sl
OpenClaw handles this by returning quickly and sending replies proactively, but very slow responses may still cause issues.
### Teams cloud and service URL support
This SDK-backed Teams path is live-validated for Microsoft Teams public cloud.
Inbound replies use the incoming Teams SDK turn context. Out-of-context proactive operations - sends, edits, deletes, cards, polls, file-consent messages, and queued long-running replies - use the stored conversation reference `serviceUrl`. Public cloud defaults to the Teams SDK public cloud environment and allows stored references on the public Teams Connector host: `https://smba.trafficmanager.net/`.
Public cloud is the default. You do not need to set `channels.msteams.cloud` or `channels.msteams.serviceUrl` for normal public-cloud bots.
For non-public Teams clouds, set `cloud` and the matching proactive boundary when Microsoft publishes one:
- `channels.msteams.cloud` selects the Teams SDK cloud preset for authentication, JWT validation, token services, and Graph scope.
- `channels.msteams.serviceUrl` selects the Bot Connector endpoint boundary used to validate stored conversation references before proactive sends, edits, deletes, cards, polls, file-consent messages, and queued long-running replies. It is required for USGov and DoD SDK clouds. For China/21Vianet, OpenClaw uses the SDK `China` preset and accepts stored/configured service URLs only on Azure China Bot Framework channel hosts.
Microsoft publishes the global proactive Bot Connector endpoints in the [Create the conversation](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages?tabs=dotnet#create-the-conversation) section of the Teams proactive messaging docs. Use the incoming activity's `serviceUrl` when available; if you need a global proactive endpoint, use Microsoft's table.
| Teams environment | OpenClaw config | Proactive `serviceUrl` |
`channels.msteams.serviceUrl` is restricted to supported Microsoft Teams Bot Connector hosts. When a service URL is configured, OpenClaw checks that the stored conversation `serviceUrl` uses the same host before proactive sends, edits, deletes, cards, polls, or queued long-running replies run. With the default public-cloud config, OpenClaw fails closed if a stored conversation points outside the public Teams Connector host. Receive a fresh message from the conversation after changing cloud/service URL settings so the stored conversation reference is current.
China/21Vianet does not have a separate global proactive `smba` URL in Microsoft's Teams proactive endpoint table. Configure `cloud: "China"` so the Teams SDK uses Azure China auth, token, and JWT endpoints. Proactive sends then require a stored conversation reference from an incoming China Teams activity, or an explicitly configured service URL, on the Azure China Bot Framework channel boundary (`*.botframework.azure.cn`). Graph-backed Teams helpers are currently disabled for `cloud: "China"` until OpenClaw routes Graph requests through the Azure China Graph endpoint.
### Formatting
Teams markdown is more limited than Slack or Discord:
@@ -680,6 +732,8 @@ Key settings (see `/gateway/configuration` for shared channel patterns):
- `channels.msteams.enabled`: enable/disable the channel.
- `channels.msteams.cloud`: Teams SDK cloud environment (`Public`, `USGov`, `USGovDoD`, or `China`; default `Public`). Set this with `serviceUrl` for USGov/DoD SDK clouds; China uses the SDK preset and stored Azure China Bot Framework conversation references, with Graph-backed helpers disabled until Azure China Graph routing is implemented.
- `channels.msteams.serviceUrl`: Bot Connector service URL boundary for SDK proactive operations. Public cloud uses the SDK default; set this for GCC (`https://smba.infra.gcc.teams.microsoft.com/teams`), GCC High, or DoD. China accepts Azure China Bot Framework channel hosts when the stored conversation reference comes from Teams operated by 21Vianet.
@@ -1120,6 +1120,8 @@ Hide raw command/exec text while keeping compact progress lines:
`channels.slack.streaming.nativeTransport` controls Slack native text streaming when `channels.slack.streaming.mode` is `partial` (default: `true`).
Slack native progress task cards are opt-in for progress mode. Set `channels.slack.streaming.progress.nativeTaskCards` to `true` with `channels.slack.streaming.mode="progress"` to send a Slack-native plan/task card while work is running, then update the same task card at completion. Without this flag, progress mode keeps the portable draft-preview behavior.
- A reply thread must be available for native text streaming and Slack assistant thread status to appear. Thread selection still follows `replyToMode`.
- Channel, group-chat, and top-level DM roots can still use the normal draft preview when native streaming is unavailable or no reply thread exists.
- Top-level Slack DMs stay off-thread by default, so they do not show Slack's thread-style native stream/status preview; OpenClaw posts and edits a draft preview in the DM instead.
@@ -1142,6 +1144,24 @@ Use draft preview instead of Slack native text streaming:
}
```
Opt in to Slack native progress task cards:
```json5
{
channels: {
slack: {
streaming: {
mode: "progress",
progress: {
nativeTaskCards: true,
render: "rich",
},
},
},
},
}
```
Legacy keys:
- `channels.slack.streamMode` (`replace | status_final | append`) is a legacy runtime alias for `channels.slack.streaming.mode`.
-`progress` keeps one editable status draft for tool progress, clears it at completion, and sends the final answer as a normal message
-`streaming.preview.toolProgress` controls whether tool/progress updates reuse the same edited preview message (default: `true` when preview streaming is active)
-`streaming.progress.commentary` (default `false`) opts into Codex preamble/commentary text in the temporary progress draft. Commentary is cleaned before display, stays transient, and does not change final answer delivery.
-`streaming.preview.commandText` controls command/exec detail inside those tool-progress lines: `raw` (default, preserves released behavior) or `status` (tool label only)
- legacy `channels.telegram.streamMode` and boolean `streaming` values are detected; run `openclaw doctor --fix` to migrate them to `channels.telegram.streaming.mode`
Tool-progress preview updates are the short status lines shown while tools run, for example command execution, file reads, planning updates, patch summaries, or Codex preamble/commentary text in Codex app-server mode. Telegram keeps these enabled by default to match released OpenClaw behavior from `v2026.4.22` and later.
Tool-progress preview updates are the short status lines shown while tools run, for example command execution, file reads, planning updates, and patch summaries. Telegram keeps these enabled by default to match released OpenClaw behavior from `v2026.4.22` and later.
Direct chats can use native Telegram drafts for these tool-progress lines without persisting tool chatter into chat history. Native drafts stop before answer text starts; final answers stay on the normal persistent delivery path. This lane is off by default and should be gated to trusted DM IDs first:
Preview streaming is separate from block streaming. When block streaming is explicitly enabled for Telegram, OpenClaw skips the preview stream to avoid double-streaming.
Telegram-only reasoning stream:
Reasoning stream behavior:
- `/reasoning stream` sends reasoning to the live preview while generating
- `/reasoning stream` uses a supported channel's reasoning-preview path; on Telegram, it streams reasoning into the live preview while generating
- the reasoning preview is deleted after final delivery; use `/reasoning on` when reasoning should remain visible
- final answer is sent without reasoning text
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.