* fix(reply): deliver final reply when queued follow-up claims session; scope dedupe to routed thread
Two core bugs caused composed replies to be silently dropped (no delivery,
no error) when a second message arrived in the same thread mid-run:
1. dispatch-from-config: ensureDispatchReplyOperation only kept the
dispatch-owned operation authoritative while it had no result. Once
runReplyAgent completed the operation to drain queued follow-ups, a
second same-thread inbound could claim the session and the first final
reply would try to re-acquire the lane instead of finishing delivery,
deadlocking behind the queued work. Keep the dispatch-owned operation
authoritative through final delivery.
2. reply-payloads-dedupe: messaging-tool reply dedupe compared only the
channel target, not the routed thread, so a send in one thread could
suppress a later reply in a different thread. Thread the routed thread
id through buildReplyPayloads + follow-up delivery and only fall back to
channel-only matching for providers without a thread-aware suppression
matcher when neither side carries thread evidence.
Adds regression tests; existing Telegram topic-suppression behavior is
preserved by gating the thread guard to providers lacking a plugin matcher.
* fix(reply): preserve threaded message delivery evidence
* fix(reply): dedupe final payloads by delivery route
* fix(slack): preserve native send thread evidence
* fix(reply): preserve explicit reply thread evidence
* fix(reply): align explicit reply route dedupe
* fix(reply): preserve delivery lane through final dispatch
* fix(mattermost): preserve threaded tool send routes
* chore(plugin-sdk): refresh API baseline
* fix(reply): align final delivery route dedupe
* fix(reply): gate followups on final delivery
* fix(reply): keep send receipts private
* fix(reply): infer implicit message provider
* fix(reply): align routed threading policy
* fix(reply): preserve queued delivery context
* fix(reply): hydrate queued system event routes
* fix(reply): hydrate queued execution routes
* fix(reply): scope final delivery barriers
* fix(slack): preserve DM target aliases
* fix(reply): mirror resolved source thread routes
* fix(mattermost): retain delayed delivery barrier
* fix(codex): separate message routing from tool policy
* fix(reply): consume normalized Slack DM targets once
* fix(slack): remove stale target alias
* style(reply): satisfy changed lint gates
* fix(mattermost): preserve explicit reply targets
* test: align Slack reply branch checks
* fix(reply): persist overflow summaries to admitted session
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
diagnostics.otel.captureContent.{toolInputs,toolOutputs} were documented
and config-wired but never produced any span content. Emit tool args and
results over the trusted private-data diagnostic channel (mirroring the
model-content path), and have the OTel exporter bound/redact/truncate them
before span export. Raw tool content never rides the public event bus.
Scope: core embedded-runner tool path (canonical producer). Codex
(async-batched) and Claude CLI remain follow-ups tracked by the issue.
Refs #77391
Move Zalo hosted outbound media metadata and expiry into plugin state, add SDK chunked hosted media storage, and keep CI/type/lint gates green after rebase.
Fixes#85124.
Anthropic standard API keys no longer resolve as provider usage auth for `openclaw status --usage`, so valid inference keys are not sent to Anthropic's OAuth usage endpoint and surfaced as misleading invalid bearer-token errors.
The provider usage-auth SDK result now has an explicit handled/no-token shape so provider hooks can suppress generic fallback without widening the OAuth helper contract. Docs, Plugin SDK API baseline, and extension package-boundary cache inputs were updated with the new contract.
Thanks @zhangguiping-xydt.
Proof:
- node scripts/run-vitest.mjs src/infra/provider-usage.auth.normalizes-keys.test.ts src/infra/provider-usage.auth.plugin.test.ts extensions/anthropic/index.test.ts
- pnpm plugin-sdk:api:check
- pnpm plugin-sdk:check-exports
- git diff --check origin/main...HEAD
- pnpm docs:list
- pnpm run test:extensions:package-boundary:compile
- autoreview clean: no accepted/actionable findings
- PR CI rollup green: 131 success, 22 skipped, 1 neutral, 0 failures
Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>
Show DeepSeek API-key account balance in status/auth-status usage surfaces by adding a summary-only provider usage snapshot path, a DeepSeek balance fetcher, SDK/docs coverage, and focused regression tests.
Maintainer verification accepted the additive provider-usage/status contract and the DeepSeek balance visibility boundary for authenticated status surfaces.
Proof:
- Live DeepSeek balance proof via 1Password-backed DEEPSEEK_API_KEY against https://api.deepseek.com/user/balance; key and balance amount redacted.
- GitHub CI run 26717953383 passed on the current head.
- Real behavior proof run 26718215605 passed after the PR body was refreshed.
- Local clean PR clone: git diff --check; node --max-old-space-size=8192 --import tsx scripts/generate-plugin-sdk-api-baseline.ts --check; node scripts/run-vitest.mjs run src/agents/bash-tools.exec.path.test.ts.
Co-authored-by: Alex Tang <tangli1987118@hotmail.com>
Co-authored-by: litang9 <141409885+litang9@users.noreply.github.com>