Fix qa-channel final reply preview delivery when a harness or plugin path calls deliver without dispatcher metadata. Verified with remote Testbox proof plus hosted PR checks.
* feat(slack): add ignoreOtherMentions channel config
Mirrors the existing Discord `ignoreOtherMentions` option for Slack
channels. When set on a channel entry, drop channel/group/MPIM messages
that mention another user or subteam but not this bot — the inverse of
`requireMention`. Useful in busy channels where the bot would otherwise
reply to side conversations.
Implementation lives in `prepareSlackMessage` right after the
`allowBots: "mentions"` bot-drop gate and before the `shouldRequireMention`
non-mention drop, so it integrates with the new `messageIngress` ingress
pipeline. The gate is conditional on `canDetectMention` (botUserId
resolvable via `auth.test` or explicit mention regexes configured) to
avoid false drops when we have no reliable way to tell bot vs non-bot
mentions apart.
Slack implicit mentions (thread participation) are intentionally NOT
respected here — they fire for every message in a bot-participated
thread, so honoring them would defeat the feature in any active thread.
The gate matches on `wasMentioned` (explicit) and `hasAnyMention`, with
the existing `shouldBypassMention` override (e.g. authorized commands)
also respected.
Includes config types, zod schema, channel-config resolution,
prepare-message implementation, full test coverage in
`prepare.test.ts`, regenerated channel + docs baselines, and Slack
docs entry.
* refactor(slack): harden other-mention filtering
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Type.Record(Type.String(), Type.Any()) serializes via TypeBox to an empty nested value schema (patternProperties: { "^.*$": {} }). AWS Bedrock's strict tool-schema validation rejects the empty sub-schema, breaking all agent turns for Bedrock-backed Anthropic users with Feishu bitable tools enabled.
Replace the three affected free-form field maps (create_record, update_record, create_field) with an explicit non-empty FlexibleFieldValue value schema that still accepts any JSON value. Type.Unknown() is avoided because it also serializes to an empty schema. Adds regression coverage asserting the emitted value schema is non-empty.
Co-authored-by: TwinsLee <1460694+TwinsLee@users.noreply.github.com>
Tool calls in Control UI chat rendered as triple-nested cards: activity
group card + tool-shell bubble chrome + gradient summary pill with a
0 8px 22px drop shadow. Collapsed rows now render as flat single-line
rows with ellipsis, the activity group uses a flat header plus a left
rule, and expanded detail keeps soft tinted blocks without card chrome.
Also deletes the dead legacy .chat-tool-card__details/__output CSS and
the unused renderToolCardSidebar path with its never-taken
renderToolDataBlock branches.
Closes#99760
Keep moved Control UI browser layout tests routed in both UI Vitest configs and stabilize chat suite-order assertions. Verified with Testbox UI proof and changed gate; final PR CI remains the merge gate.
* refactor(models): remove redundant generation model ref alias
* refactor(types): remove redundant local aliases
* refactor(types): preserve shipped model ref type
The claude-cli (claude-stdio) live-session backend emits native
reasoning on stream-json (`thinking_delta` + a snapshot `thinking`
block), but the CLI parser had no thinking handling at all, so it was
silently dropped on every surface in every /reasoning mode.
- cli-output.ts: parse thinking_delta/snapshot into stream:"thinking"
with replace-style per-content-block-index dedupe
(assembleThinkingTextByIndex), robust to tool-interleaved multi-block
turns (snapshot replaces per-index rather than assuming a streamed
prefix). signature_delta/redacted_thinking stay skipped.
- agent-runner-cli-dispatch.ts: bridge stream:"thinking" into
onReasoningStream via a new createReasoningTextBridge (mirrors the
existing assistant-text bridge), replacing the old
assistant-text-as-reasoning heuristic. Track the final reasoning
snapshot and, when present, prepend a durable
`{text, isReasoning: true}` payload before the answer — mirroring
the embedded-runner durable reasoning payload
(embedded-agent-runner/run/payloads.ts).
- agent-runner-execution.ts: thread isReasoningSnapshot through to
onReasoningStream, still passing requiresReasoningProgressOptIn:true
so CLI reasoning rides the exact same channel gates as embedded
reasoning (Discord/Telegram): /reasoning off -> nothing on any
surface, stream -> window preview only, on -> durable payload too.
The bridge is suppressed under the same conditions as the assistant
bridge (e.g. silentExpected follow-ups), so no durable payload is
synthesized for turns that are expected to stay silent.
Supersedes this branch's prior commit (a straight port of
af7eb48e05's emit-always model, no gating and no durable mode) with
the above gated/durable design per product decision.
Also, in cli-output.ts: reset the per-content-block-index thinking
tracker on every new Anthropic message (message_start, or a fresh
top-level "assistant" snapshot's message.id) instead of leaving it
scoped to the whole CLI turn. Content-block indices restart at 0 per
Anthropic message, so a tool round-trip within one turn (message A:
thinking + tool_use; tool result fed back; message B: a fresh
message_start) was letting message A's stale index-0 thinking text
bleed into message B's index-0 delta.
And in agent-runner-cli-dispatch.ts: attach the durable reasoning
payload whenever any thinking was bridged, not only when the CLI also
produced a visible final answer, so a thinking-only turn (no visible
reply) doesn't silently drop its reasoning.
Round 2: the durable `{isReasoning: true}` payload synthesized above
was also reaching the QUEUED FOLLOW-UP delivery path
(followup-runner.ts -> resolveFollowupDeliveryPayloads) with no gate
at all, so a claude-cli queued follow-up with /reasoning off would
leak internal reasoning to the channel as an ordinary visible message.
resolveFollowupDeliveryPayloads (followup-delivery.ts) now takes a
reasoningPayloadsEnabled flag and drops isReasoning payloads unless
it is true; followup-runner.ts threads opts?.reasoningPayloadsEnabled
through at both call sites, from the same GetReplyOptions the direct
path already reads (dispatch-from-config.ts:2054). This makes the
queued-follow-up path apply the identical isReasoning/
reasoningPayloadsEnabled gate that direct dispatch already enforces
(dispatch-from-config.ts:3347, :3537) — no new mechanism, CLI
follow-ups now go through the exact same gate embedded follow-ups do.
Scope note: routeReply's own unconditional suppression of isReasoning
payloads on the origin-routing branch (route-reply.ts:131,
shouldSuppressReasoningPayload) is pre-existing, shared with the
embedded runner, and unchanged by this commit — that gate governs
whether a kept-and-routed reasoning payload is actually rendered on a
cross-channel queued follow-up, and is out of scope here. This commit
closes the leak (off -> never eligible on any path); it does not
change what happens to an eligible payload once it reaches routeReply.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(slack): preserve interaction thread status
Co-authored-by: Intern Dev <dev@wukongai.io>
* test(slack): type message thread timestamps
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Intern Dev <dev@wukongai.io>
The sessions.usage RPC loaded each visible session's summary via
loadSessionCostSummaryFromCache, which re-reads and re-parses the entire
.usage-cost-cache.json on every call. At the dashboard's default limit=50
that is ~50 full parses per connect, spiking RSS 200-600 MiB and tripping
the gateway rss_growth memory-pressure alarm with a false positive.
Unify the visible and hidden session paths onto the batched
loadSessionCostSummariesFromCache loader, which reads and parses the cache
at most once per agent regardless of limit.
Fixes#100041
Refactor the Control UI around route-owned page lifecycle and state while preserving existing behavior and design.
Prepared head SHA: bd51b6fa76
Co-authored-by: Shakker <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
Prevents opted-in plugin runtime dependencies from being published with stale
registry pins. The same freshness assertion now guards both npm and ClawHub
release paths, including the managed Codex runtime.
Prepared head SHA: d5d0ecba4b
Reviewed-by: @fuller-stack-dev
Closes#99951
* fix(telegram): resolve local Bot API container file paths against trustedLocalFileRoots
When the self-hosted telegram-bot-api server runs with --local inside a
container, getFile returns absolute file_path values rooted at the
container data dir (/var/lib/telegram-bot-api/...). The host process
mounts that volume at a different path, so the absolute path never
matches a trustedLocalFileRoots entry and inbound media fails to
materialize (messages reach the agent as bare <media:document>
placeholders without bytes).
Map container-absolute paths back to relative candidates and resolve
them under each trusted root, trying both with and without the
per-token directory segment the local server uses. Relative file_path
values are now also resolved against trusted roots before falling back
to the HTTP file endpoint, which keeps large-file (>20MB) local-mode
downloads working.
Path traversal protections: candidates are normalized, NUL bytes and
dot-segments are rejected, and reads stay behind the trusted-roots
file-access sandbox.
AI-assisted (Claude Code), validated with targeted vitest run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Dizesales <269209351+Dizesales@users.noreply.github.com>
* fix(telegram): map local Bot API container media paths
Co-authored-by: Lucas Magalhaes <ellucasrj@gmail.com>
* chore(config): refresh channel metadata
---------
Co-authored-by: Lucas Magalhaes <ellucasrj@gmail.com>
Co-authored-by: Dizesales <269209351+Dizesales@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The node_modules package-root boundary fix anchors bundled plugin
discovery to the running checkout in nested worktrees, so the wrapper
no longer needs to pin OPENCLAW_BUNDLED_PLUGINS_DIR for symlinked
node_modules layouts. Verified live in a .claude worktree: with the
boundary fix and no pin, vitest workers resolve the worktree's own
extensions in both no-node_modules and symlinked layouts, and linked
worktrees now follow the same canonical dist-preferred discovery as
normal checkouts. The trust env remains owned by test/setup.shared.ts.
Must land together with or after the boundary fix commit directly below.
Accept the documented package-manager separator for the web fetch benchmark CLI and add a process-level regression test. Verified with rebased Testbox check:changed, prior targeted Testbox benchmark/test smokes, and AWS macOS install/build/gateway probes.
Summary:
- The branch updates the shared duration helpers to branch on rounded millisecond values and adds 999.6ms expectations for compact, human, and precise duration output.
- PR surface: Source +3, Tests +2. Total +5 across 2 files.
- Reproducibility: yes. Current main source branches on raw `ms < 1000` before rounding in all three helpers, and the linked issue plus prior review include direct module proof that 999.6ms returned `1000ms`.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head 9e06973112.
- Required merge gates passed before the squash merge.
Prepared head SHA: 9e06973112
Review: https://github.com/openclaw/openclaw/pull/100006#issuecomment-4882739452
Co-authored-by: qingminlong <qing.minlong@xydigit.com>
Approved-by: takhoffman
The lineRangeOverlapsDreamingFence guard tracked insideFence state from
the marker lines but did not flag ranges that included the marker lines
themselves. A relocated promotion range that ended on a start marker,
began on an end marker, or covered only a marker line passed the guard,
and the snippet built from those raw lines carried the marker text into
MEMORY.md.
Treat marker lines as inside-fence content for range overlap purposes
and add unit + integration coverage. The integration test exercises the
real applyShortTermPromotions path: pre-patch a recall whose stored
snippet is the start-marker text produces a 'Promoted From Short-Term
Memory' entry containing the literal marker; post-patch the same input
yields applied=0 and no MEMORY.md write. Refs #80613.
Keep the legacy WhatsApp crontab Doctor lint check opt-in by marking the existing core check default-disabled. Default `doctor --lint` no longer reads the user crontab for this narrow legacy diagnostic, while `--all` and `--only core/doctor/legacy-whatsapp-crontab` still run the detector.
Validation:
- focused Doctor contribution Vitest passed 70/70
- changed-file oxfmt and oxlint passed
- git diff --check passed
- pnpm build passed before the final rebase
- hosted exact-head CI/Testbox gates passed on 8964ccb239 after QA Smoke rerun
- PR body includes real lint-module proof for default skip and explicit selected warning behavior
Maintainer note: accepted ClawSweeper's default-output compatibility tradeoff for this legacy crontab probe; no public SDK/plugin/config contract changes.
Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
* fix(cron): roll back live store when persist fails during add/update/remove
* fix(cron): restore catch-up deferral markers on failed persists
* fix(cron): defer rollback-sensitive notifications
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>