Skip to content

Refactor inbound room events into core#82606

Merged
steipete merged 5 commits into
mainfrom
refactor/core-inbound-event-classification
May 16, 2026
Merged

Refactor inbound room events into core#82606
steipete merged 5 commits into
mainfrom
refactor/core-inbound-event-classification

Conversation

@steipete

Copy link
Copy Markdown
Contributor

Summary

  • Moves unmentioned group/channel room-event classification into core under src/channels/inbound-event/*, with shared classification, context, event-kind, and media helpers.
  • Migrates Telegram, Slack, Discord, Synology, gateway/tooling, prompt context, and MCP metadata from inbound-turn naming to InboundEventKind / inbound-event terminology.
  • Adds canonical config messages.groupChat.unmentionedInbound: "room_event" and deliberately removes the pre-ship ambientTurns spelling with no runtime alias or migration.
  • Bumps the gateway protocol to v5 for inboundEventKind, including MCP header/env rename to x-openclaw-inbound-event-kind / OPENCLAW_MCP_INBOUND_EVENT_KIND, plus Android/Swift generated model updates.
  • Keeps only public plugin SDK compatibility aliases in openclaw/plugin-sdk/channel-inbound; all bundled plugins are migrated to the modern API.

Fixes #81317.

Behavior addressed

Core, plugin-agnostic inbound room-event classification for unmentioned always-on group/channel chatter. Mentions, DMs, commands, abort requests, and native commands remain user_request; configured unmentioned group/channel chatter can become room_event, run as quiet context, avoid normal visible source replies, avoid Discord status/ack reactions, and speak visibly only through the message tool.

Implementation notes

  • src/channels/inbound-event/classification.ts owns classifyChannelInboundEvent and resolveUnmentionedGroupInboundPolicy.
  • src/channels/inbound-event/context.ts builds inbound event prompt context and emits InboundEventKind.
  • Discord, Slack, and Telegram now consume the same core classifier instead of duplicating plugin-local policy.
  • Room events set prompt metadata such as inbound_event_kind: room_event and visible_reply_contract: message_tool_only, suppress transcript-only assistant persistence, and force source reply delivery to message_tool_only.
  • Public SDK legacy helpers still map inboundTurnKind and return InboundTurnKind; this is the explicit public-plugin-API compat exception, not an internal compat layer.

Codex review

  • Ran /Users/steipete/Projects/agent-scripts/skills/codex-review/scripts/codex-review against the branch diff before the final fixup.
  • Accepted and fixed prior review findings: legacy public buildChannelTurnContext now returns InboundTurnKind, Slack/Discord room events stay message_tool_only, Slack/Discord abort phrases stay user_request, and Discord room events do not emit ack/status reactions.
  • Final rerun: /Users/steipete/Projects/agent-scripts/skills/codex-review/scripts/codex-review on the local fix diff returned codex-review clean: no accepted/actionable findings reported.
  • Rejected runtime config aliasing for ambientTurns: this branch has not shipped, repo policy keeps runtime config canonical, and legacy repair belongs in doctor/migration paths only.

Verification

  • git diff --check
  • pnpm docs:list
  • pnpm config:docs:check
  • pnpm plugin-sdk:api:check
  • pnpm protocol:check
  • pnpm check:test-types
  • pnpm build
  • pnpm test src/plugin-sdk/channel-inbound.test.ts extensions/discord/src/monitor/message-handler.process.test.ts
  • Focused full surface test command: pnpm test src/plugin-sdk/channel-inbound.test.ts src/channels/inbound-event/classification.test.ts src/channels/inbound-event/context.test.ts src/channels/inbound-event/media.test.ts src/config/zod-schema.visible-replies.test.ts src/auto-reply/reply/prompt-prelude.test.ts src/auto-reply/reply/get-reply-run.media-only.test.ts src/auto-reply/reply/dispatch-from-config.test.ts src/gateway/mcp-http.test.ts src/gateway/tool-resolution.test.ts src/gateway/server-methods/send.test.ts extensions/telegram/src/bot-message-context.require-mention.test.ts extensions/telegram/src/bot-message-context.reactions.test.ts extensions/telegram/src/inbound-event-delivery.test.ts extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/action-runtime.test.ts extensions/discord/src/monitor/message-handler.preflight.test.ts extensions/discord/src/monitor/message-handler.process.test.ts extensions/slack/src/monitor/message-handler/prepare.test.ts src/auto-reply/reply/source-reply-delivery-mode.test.ts
  • Blacksmith Testbox via Crabbox: tbx_01krrehj2sys4bmh2qxwh01z78, Actions run https://github.com/openclaw/openclaw/actions/runs/25962778077. pnpm check:changed passed through conflict-marker, changelog attribution, wildcard reexport, duplicate coverage, dependency/patch guard, typecheck core/core tests/extensions/extension tests, lint core/extensions/scripts. Testbox app lint stopped only because the image lacked swiftlint.
  • pnpm lint:swift locally passed. Existing non-fatal macOS type body length warnings remain; iOS lint was clean.

Real behavior proof

Behavior addressed: Unmentioned always-on group/channel chatter is classified in core as a quiet room_event, while explicit requests keep user_request behavior.

Real environment tested: Local macOS checkout plus Blacksmith Testbox through Crabbox (tbx_01krrehj2sys4bmh2qxwh01z78).

Exact steps or command run after this patch: Ran the local docs/config/protocol/SDK/type/build checks, focused Vitest coverage across core/Telegram/Slack/Discord/gateway reply behavior, final codex-review, and Testbox pnpm check:changed via node scripts/crabbox-wrapper.mjs.

Evidence after fix: Focused Vitest passed 603 tests before final review fixup; post-fix targeted pnpm test src/plugin-sdk/channel-inbound.test.ts extensions/discord/src/monitor/message-handler.process.test.ts passed 61 tests; Testbox check lanes were green until the missing swiftlint binary; local pnpm lint:swift passed.

Observed result after fix: Core emits InboundEventKind, bundled plugins use the core classifier, Discord no longer leaks visible reactions for room events even with explicit status reactions, and public legacy SDK callers still see InboundTurnKind.

What was not tested: Live Telegram, Slack, or Discord provider E2E. Testbox app lint could not complete because swiftlint is missing from that image; the same Swift lint lane was covered locally.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: discord Channel integration: discord channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: zalo Channel integration: zalo app: android App: android app: web-ui App: web-ui gateway Gateway runtime scripts Repository scripts agents Agent runtime and tooling channel: qqbot extensions: codex channel: synology-chat size: XL maintainer Maintainer-authored PR labels May 16, 2026
@clawsweeper

clawsweeper Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

Summary
The PR moves room-event classification into core, renames inbound-turn/ambient terminology to inbound-event/unmentionedInbound across channels, gateway protocol clients, docs, and SDK compatibility helpers.

Reproducibility: yes. for the review findings: source inspection shows current main exposes inboundTurnKind and the PR head only forwards the new inboundEventKind/action header names. The PR itself is a feature/refactor rather than a user bug report.

Real behavior proof
Override: A maintainer applied proof: override for this PR.

Next step before merge
The two blocking compatibility issues have narrow code/test repairs; final merge still needs maintainer approval for the protected-label protocol/config change.

Security
Cleared: No concrete security or supply-chain regression was found; the diff does not add dependencies, workflow permissions, secret handling, or external code execution.

Review findings

  • [P1] Preserve the legacy action context key — src/infra/outbound/message-action-runner.ts:1161
  • [P2] Accept the old MCP inbound header during upgrade — src/gateway/mcp-http.request.ts:183
Review details

Best possible solution:

Keep the core classifier direction, add compatibility for the public action context and MCP loopback header during the rename, then let maintainers decide the protocol/config break before merge.

Do we have a high-confidence way to reproduce the issue?

Yes for the review findings: source inspection shows current main exposes inboundTurnKind and the PR head only forwards the new inboundEventKind/action header names. The PR itself is a feature/refactor rather than a user bug report.

Is this the best way to solve the issue?

No as written: centralizing classification is a reasonable direction, but dropping public action-context and MCP loopback compatibility is not the safest migration path. Add deprecated aliases/fallbacks while keeping the new canonical names.

Full review comments:

  • [P1] Preserve the legacy action context key — src/infra/outbound/message-action-runner.ts:1161
    ChannelMessageActionContext currently exposes inboundTurnKind, but this dispatch now sends only inboundEventKind. Existing channel plugins that branch on ctx.inboundTurnKind will silently see undefined after upgrading, so pass both keys and mark the old one deprecated until the public plugin API break is explicitly versioned.
    Confidence: 0.91
  • [P2] Accept the old MCP inbound header during upgrade — src/gateway/mcp-http.request.ts:183
    Loopback MCP callers on current main send x-openclaw-inbound-turn-kind; this branch only reads the renamed header. Those callers will lose room_event tool scoping and source-reply behavior without an error, so read the new header first but fall back to the old header during the transition.
    Confidence: 0.87

Overall correctness: patch is incorrect
Overall confidence: 0.89

Acceptance criteria:

  • git diff --check
  • node scripts/run-vitest.mjs src/plugin-sdk/channel-inbound.test.ts src/infra/outbound/message-action-runner.plugin-dispatch.test.ts src/gateway/mcp-http.test.ts src/gateway/server-methods/send.test.ts src/gateway/tool-resolution.test.ts
  • pnpm plugin-sdk:api:check
  • pnpm protocol:check

What I checked:

Likely related people:

  • @steipete: Current-main history includes commit 903e246 on the room-event turn type surface, and the PR head is authored by the same contributor on the same refactor surface. (role: recent area contributor and branch proposer; confidence: high; commits: 903e246e871f, 6d00eab3d0d4; files: src/channels/turn/kind.ts, src/channels/turn/types.ts, src/channels/inbound-event/classification.ts)
  • @obviyus: The related merged room-event PR was opened by this contributor, and recent current-main commits under the same handle adjusted Telegram delivery behavior adjacent to this refactor. (role: related feature owner and recent Telegram delivery contributor; confidence: high; commits: 20c3580394e7, 0fb0b5197e0e; files: extensions/telegram/src/bot-message-dispatch.ts, extensions/telegram/src/lane-delivery.ts, extensions/telegram/src/bot-message-context.session.ts)

Remaining risk / open question:

  • The branch deliberately changes public config and protocol spelling; maintainers still need to approve that shipped-surface break even after compatibility aliases are fixed.
  • The proof gate is overridden by label, but this read-only pass did not independently run live Telegram, Slack, or Discord behavior.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 3b2cd0dd1a85.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87245f2fdb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

sessionKey: input.sessionKey,
sessionId: input.sessionId,
inboundTurnKind: input.inboundTurnKind,
inboundEventKind: input.inboundEventKind,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve inboundTurnKind in plugin action context

handlePluginAction now forwards only inboundEventKind to dispatchChannelMessageAction, so existing channel plugins that still read ctx.inboundTurnKind will silently see undefined after this update. That breaks backward compatibility for already-published plugins (for example, plugins that branch room-event delivery/idempotency by this field) even when they otherwise run unchanged on a newer host. Keep a compatibility alias in the dispatched context (send both keys) until the old key is fully deprecated.

Useful? React with 👍 / 👎.

normalizeMessageChannel(getHeader(req, "x-openclaw-message-channel")) ?? undefined,
accountId: normalizeOptionalString(getHeader(req, "x-openclaw-account-id")),
inboundTurnKind: normalizeMcpInboundTurnKind(getHeader(req, "x-openclaw-inbound-turn-kind")),
inboundEventKind: normalizeMcpInboundEventKind(getHeader(req, "x-openclaw-inbound-event-kind")),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept legacy MCP inbound-turn header during transition

resolveMcpRequestContext only reads x-openclaw-inbound-event-kind now. Any existing MCP client/proxy that still sends x-openclaw-inbound-turn-kind will lose room-event classification, which changes tool scoping and source-reply behavior (room events no longer enforced as message-tool-only). Add a fallback read of the old header to avoid silent behavior regressions during upgrade.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added the mantis: telegram-visible-proof Mantis should capture Telegram visible proof. label May 16, 2026
@steipete steipete force-pushed the refactor/core-inbound-event-classification branch from 87245f2 to 6d00eab Compare May 16, 2026 22:58
@steipete steipete added the proof: override Maintainer override for the external PR real behavior proof gate. label May 16, 2026
@clawsweeper clawsweeper Bot added the P1 High-priority user-facing bug, regression, or broken workflow. label May 16, 2026
@steipete

Copy link
Copy Markdown
Contributor Author

Maintainer rebase/fixup done for landing.

Proof:

  • git diff --check
  • pnpm docs:list
  • pnpm config:docs:check
  • pnpm plugin-sdk:api:check
  • pnpm protocol:check
  • pnpm check:test-types
  • pnpm build
  • focused inbound/auto-reply/gateway/plugin tests: 7 Vitest shards, 603 tests passed
  • codex-review --mode local: clean, no accepted/actionable findings
  • Testbox via Crabbox: tbx_01krsg14h0ah56y75pw4esdk8k, Actions run https://github.com/openclaw/openclaw/actions/runs/25975164532, pnpm check:changed green through selected lanes except runner image missing swiftlint
  • pnpm lint:swift: passed locally; only existing type-body-length warnings
  • CI run https://github.com/openclaw/openclaw/actions/runs/25975216740 was rerun twice; remaining red shards are unrelated order-sensitive tests in src/gateway/server.auth.browser-hardening.test.ts and src/pairing/setup-code.test.ts
  • local exact rerun of those red files passed: pnpm test src/gateway/server.auth.browser-hardening.test.ts src/pairing/setup-code.test.ts

Applied proof: override because this refactor was covered with source/focused/remote gates and no live channel E2E was run for the maintainer land request.

@steipete steipete merged commit 0190f4a into main May 16, 2026
268 of 278 checks passed
@steipete steipete deleted the refactor/core-inbound-event-classification branch May 16, 2026 23:10
galiniliev pushed a commit to galiniliev/openclaw that referenced this pull request May 20, 2026
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 24, 2026
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 24, 2026
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 24, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
galiniliev pushed a commit to galiniliev/openclaw that referenced this pull request May 25, 2026
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 26, 2026
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 26, 2026
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 26, 2026
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: android App: android app: web-ui App: web-ui channel: discord Channel integration: discord channel: qqbot channel: slack Channel integration: slack channel: synology-chat channel: telegram Channel integration: telegram channel: zalo Channel integration: zalo docs Improvements or additions to documentation extensions: codex gateway Gateway runtime maintainer Maintainer-authored PR mantis: telegram-visible-proof Mantis should capture Telegram visible proof. P1 High-priority user-facing bug, regression, or broken workflow. proof: override Maintainer override for the external PR real behavior proof gate. scripts Repository scripts size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant