Skip to content

Plugins: broaden plugin surface for Codex App Server#45318

Merged
vincentkoc merged 28 commits intoopenclaw:mainfrom
huntharo:codex/broaden-plugin-surface-for-agents
Mar 15, 2026
Merged

Plugins: broaden plugin surface for Codex App Server#45318
vincentkoc merged 28 commits intoopenclaw:mainfrom
huntharo:codex/broaden-plugin-surface-for-agents

Conversation

@huntharo
Copy link
Copy Markdown
Member

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: OpenClaw's plugin surface did not let a third-party plugin fully own bound channel conversations, interactive callbacks, or the small set of channel controls that Codex App Server needs.
  • Why it matters: Codex App Server could only be integrated by patching core behavior directly, which made it hard to ship as an independent community plugin and hard to validate the plugin model itself.
  • What changed: this PR adds a generic plugin-facing inbound-claim hook, public binding/runtime surfaces, plugin interactive callback routing, and first-class Telegram/Discord channel helpers so a Codex App Server plugin can bind conversations, claim inbound messages, render interactive approvals/questions, and manage channel UX without core-specific code paths.
  • What did NOT change (scope boundary): this does not add Codex App Server itself to core. The Codex behavior remains in the external plugin package; this PR only broadens the reusable plugin seam needed to support it.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #codex-app-server-redux

User-visible / Behavior Changes

  • Plugins can now claim inbound bound messages before core command/agent dispatch.
  • Plugins can use the public session-binding runtime surface instead of relying on core-private binding paths.
  • Plugins can register Telegram and Discord interactive callback handlers under their own namespace.
  • Plugins now have first-class Telegram and Discord runtime helpers for typing and conversation/message controls needed by interactive agents.
  • The new surface has been validated by building @pwrdrvr/openclaw-app-server as an external plugin and exercising the Codex App Server flows against it.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) Yes
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) Yes
  • Data access scope changed? (Yes/No) Yes
  • If any Yes, explain risk + mitigation:

This broadens what plugins are allowed to do inside the gateway: plugins can now claim inbound messages, register interactive handlers, and invoke a limited set of Telegram/Discord conversation controls. The mitigation is that these capabilities are explicit plugin APIs, stay within existing plugin loading/trust boundaries, remain channel-scoped, and are covered by focused tests around routing, dedupe, and control-path behavior.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 / pnpm workspace
  • Model/provider: Codex App Server via external plugin
  • Integration/channel (if any): Telegram and Discord
  • Relevant config (redacted): linked local plugin install for @pwrdrvr/openclaw-app-server

Steps

  1. Link the external Codex App Server plugin into an OpenClaw gateway built from this branch.
  2. Bind Telegram or Discord conversations to Codex threads and exercise resume, approvals, plan questions, review flows, and interactive callback actions.
  3. Verify that bound inbound messages are claimed by the plugin, interactive callbacks route to the plugin namespace, and channel controls used by the plugin behave without a core-specific Codex integration.

Expected

  • Codex App Server works as an external plugin using the plugin interface alone.
  • Bound conversations route to the plugin rather than falling back to the built-in Claw path.
  • Telegram and Discord interactive controls used by the Codex plugin work through the new generic plugin surface.

Actual

  • The external Codex App Server plugin was able to implement the required functionality so far on top of this branch's plugin APIs, including bound routing, interactive questionnaires/approvals, and Telegram/Discord control flows.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • Built and linked a standalone Codex App Server plugin against this branch's plugin surface.
    • Exercised Telegram thread binding, resume flows, plan questionnaires, approvals, and follow-up actions.
    • Exercised Discord callback and picker behavior through the new plugin interactive routing path.
  • Edge cases checked:
    • Bound inbound routing stops core handling when the plugin claims the event.
    • Interactive callback ownership is namespaced and deduped.
    • Telegram and Discord control helpers cover the plugin behaviors needed so far without reintroducing a direct core Codex path.
  • What you did not verify:
    • Broad compatibility with unrelated third-party plugins beyond the focused Codex App Server validation.
    • Long-tail live traffic behavior across every built-in channel.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert the two plugin-surface commits on this branch or disable the external Codex plugin and fall back to current core behavior.
  • Files/config to restore: src/plugins/*, src/telegram/*, src/discord/*, and src/auto-reply/reply/dispatch-from-config.ts
  • Known bad symptoms reviewers should watch for: plugins unexpectedly claiming inbound messages, callback handlers not reaching built-in fallback paths when they should, or Telegram/Discord interactive controls misrouting/duplicating actions.

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: the new inbound-claim seam could let a plugin intercept traffic earlier than intended.
    • Mitigation: the hook is explicit, sequential, first-claim-wins, and covered with routing tests.
  • Risk: plugin interactive namespaces could conflict with built-in callback handling.
    • Mitigation: callback registration is explicit and namespaced, with tests for deterministic ownership and fallback blocking.
  • Risk: Telegram/Discord channel helper APIs may be too narrow or slightly off for future plugins.
    • Mitigation: the surface was validated against a real Codex App Server plugin implementation rather than a toy example, and can be iterated further from that baseline.

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 13, 2026

🔒 Aisle Security Analysis

We found 3 potential security issue(s) in this PR:

# Severity Title
1 🟠 High Telegram plugin interactive callbacks marked authorized unconditionally (authorization bypass)
2 🟠 High Untrusted Discord component callbackData can invoke arbitrary plugin interactive handlers without enforced authorization
3 🟡 Medium Discord plugin interactive dispatch can fall through to built-in routing after plugin handled interaction

1. 🟠 Telegram plugin interactive callbacks marked authorized unconditionally (authorization bypass)

Property Value
Severity High
CWE CWE-863
Location src/telegram/bot-handlers.ts:1225-1241

Description

In the Telegram callback_query handler, plugin interactive callbacks are dispatched with auth.isAuthorizedSender hardcoded to true, regardless of the actual sender authorization state.

This creates an authorization bypass for any plugin that relies on ctx.auth.isAuthorizedSender to protect privileged interactive actions (e.g., approvals, admin operations, data access):

  • Input: callback.from (Telegram user id/username) and callback.data (button payload)
  • Gate: authorizeTelegramEventSender(...) is used only to decide whether callbacks are processed at all, but its result is not propagated to plugins.
  • Bypass: plugins always see isAuthorizedSender: true, so plugin-side checks like if (!ctx.auth.isAuthorizedSender) return { handled: true } become ineffective.

Vulnerable code:

const senderAuthorization = authorizeTelegramEventSender(...);
if (!senderAuthorization.allowed) {
  return;
}

const pluginCallback = await dispatchPluginInteractiveHandler({
  channel: "telegram",
  data,
  ...
  ctx: {
    ...
    auth: {
      isAuthorizedSender: true,
    },
    ...
  },
});

Concrete exploit scenario:

  • In a Telegram group where inline buttons are enabled and group policy is not strict, any group member who can click a bot’s inline button can trigger codex:<payload> callbacks.
  • If a plugin uses ctx.auth.isAuthorizedSender to restrict those actions to an allowlist/paired users, that restriction is bypassed because the framework always supplies true.

Note: Telegram callback queries generally require clicking an inline button (users can’t normally send arbitrary callback queries), but in shared chats any member can click the button and trigger the plugin handler.

Recommendation

Propagate the real authorization status into the plugin interactive context.

At minimum, compute an allowlist-based authorization result (separately from the broader callback-scope gating) and pass it through:

// existing gate (controls whether we process callbacks at all)
const senderAuthorization = authorizeTelegramEventSender({
  chatId,
  chatTitle: callbackMessage.chat.title,
  isGroup,
  senderId,
  senderUsername,
  mode: authorizationMode,
  context: eventAuthContext,
});
if (!senderAuthorization.allowed) return;// NEW: compute whether the sender is actually authorized (allowlist/dmPolicy),// even when callback scope allows broader interaction.
const allowlistAuthorization = authorizeTelegramEventSender({
  chatId,
  chatTitle: callbackMessage.chat.title,
  isGroup,
  senderId,
  senderUsername,
  mode: "callback-allowlist",
  context: eventAuthContext,
});

const pluginCallback = await dispatchPluginInteractiveHandler({
  channel: "telegram",
  data,
  callbackId: callback.id,
  ctx: {// ...
    auth: {
      isAuthorizedSender: allowlistAuthorization.allowed,
    },
  },
  respond: { /* ... */ },
});

Alternatively, gate plugin callback dispatch behind the strictest policy you want plugins to inherit (e.g., always require allowlist/paired access), but do not hardcode authorization to true.


2. 🟠 Untrusted Discord component callbackData can invoke arbitrary plugin interactive handlers without enforced authorization

Property Value
Severity High
CWE CWE-285
Location src/discord/monitor/agent-components.ts:1290-1306

Description

Discord component specs now support callbackData, which is persisted into DiscordComponentEntry/DiscordModalEntry and used to dispatch plugin interactive handlers on user interaction.

If a component spec can be influenced by untrusted input (notably LLM/tool output via the Discord sendMessage action), an attacker can cause the bot to send a message containing a component whose callbackData targets any registered plugin interactive namespace (e.g. admin:dangerousAction). Any user who is allowed to press the component (often anyone in an unrestricted channel) can then trigger the plugin handler.

Key points from the code:

  • Source of untrusted spec: the Discord actions tool parses raw JSON params.components into a component spec via readDiscordComponentSpec(...) (allowing callbackData).
  • Sink: on click/select, the bot calls dispatchPluginInteractiveHandler whenever consumed.callbackData exists.
  • No mandatory authorization gate: commandAuthorized is computed but only passed through as a boolean flag (auth.isAuthorizedSender); the framework does not block dispatch when commandAuthorized === false. If plugin handlers forget/omit checks, this becomes a privilege boundary bypass.

Vulnerable behavior (dispatch occurs regardless of commandAuthorized):

if (consumed.callbackData) {
  const pluginDispatch = await dispatchPluginDiscordInteractiveEvent({
    ...,
    isAuthorizedSender: commandAuthorized,
    data: consumed.callbackData,
    ...
  });
  if (pluginDispatch === "handled") {
    return;
  }
}

Concrete threat scenarios:

  • Prompt-injection to cross-plugin invocation: a user convinces the LLM/agent to call the Discord send tool with components containing callbackData: "sensitive_namespace:do". After the bot posts it to a channel, other users can click it to invoke that plugin handler.
  • Unauthorized member triggers sensitive handler in open channel: in channels with no users/roles restrictions, ensureGuildComponentMemberAllowed will pass for any member, and unless allowedUsers is set, anyone can trigger the handler even when commandAuthorized is false.

While allowedUsers checks do exist, they are optional and can be omitted from the spec; authorization then depends entirely on every plugin handler correctly interpreting ctx.auth.isAuthorizedSender.

Recommendation

Enforce an authorization decision in the framework before dispatching plugin interactive handlers, rather than relying on each plugin to check ctx.auth.isAuthorizedSender.

Recommended options (choose one or combine):

  1. Default-deny interactive dispatch when not authorized
if (consumed.callbackData) {
  if (!commandAuthorized) {
    await params.interaction.reply({
      content: "You are not authorized to use this action.",
      ephemeral: true,
    });
    return;
  }// then dispatch
}
  1. Bind callbackData to an origin (prevents cross-plugin/cross-context invocation):
  • Store { pluginId, namespace, payload } server-side when building components, rather than trusting a free-form string.
  • Or cryptographically sign callback data (HMAC) and verify signature + expiry + messageId before dispatch.
  1. Restrict callback namespaces
  • Maintain an allowlist of namespaces permitted for LLM-generated component specs.
  • Reject/strip callbackData in readDiscordComponentSpec unless explicitly allowed by configuration.

Also document clearly that allowedUsers should be set for any sensitive interactive component and that plugin handlers must not perform privileged operations unless ctx.auth.isAuthorizedSender is true.


3. 🟡 Discord plugin interactive dispatch can fall through to built-in routing after plugin handled interaction

Property Value
Severity Medium
CWE CWE-840
Location src/discord/monitor/agent-components.ts:868-879

Description

In dispatchPluginDiscordInteractiveEvent the return value depends on whether the plugin used the provided respond helpers.

  • The function tracks responded and only returns "handled" when dispatched.handled && !responded.
  • If a plugin responds (e.g., respond.reply() / respond.editMessage() / respond.acknowledge()) and returns { handled: true }, then responded === true and the function returns "unmatched".
  • Call sites interpret anything other than "handled" as fall-through and will proceed with built-in routing (reply("✓") and dispatchDiscordComponentEvent(...)), causing double-handling (plugin side effects + core agent routing).

This is security-relevant because built-in routing forwards an event to the agent runtime even when the plugin explicitly claimed to handle the interaction. That can lead to unintended duplicated actions (e.g., double approvals) or a plugin’s attempt to fully handle/contain an interaction being bypassed.

Vulnerable code:

if (!dispatched.matched) {
  return "unmatched";
}
if (dispatched.handled && !responded) {
  ...
  return "handled";
}
return "unmatched";

Call sites that continue built-in routing unless "handled" is returned:

  • handleDiscordComponentEvent (buttons/selects)
  • DiscordComponentModal.run (modals)

Recommendation

Return "handled" whenever the plugin reports handled: true, regardless of whether it already responded. Use responded only to decide whether an auto-ack is needed.

Suggested fix:

if (!dispatched.matched) return "unmatched";

if (dispatched.handled) {
  if (!responded) {
    try { await respond.acknowledge(); } catch {}
  }
  return "handled";
}

return "unmatched";

Also add a test covering the case where the plugin both:

  • calls respond.reply(...) (or respond.acknowledge()), and
  • returns { handled: true }

and assert that built-in routing does not call dispatchDiscordComponentEvent/dispatchReply.


Analyzed PR: #45318 at commit 5c7ac6d

Last updated on: 2026-03-14T02:58:04Z

Latest run failed. Keeping previous successful results. Trace ID: 019cedf05681f0e5327225cb7d3d376c.

Last updated on: 2026-03-14T23:53:04Z

Latest run failed. Keeping previous successful results. Trace ID: 019ceedd8c4ec29ae551a75a81945252.

Last updated on: 2026-03-15T02:59:31Z

Latest run failed. Keeping previous successful results. Trace ID: 019cef873f1713abec5c5f08dc4b0aee.

Last updated on: 2026-03-15T06:39:24Z

@openclaw-barnacle openclaw-barnacle bot added channel: discord Channel integration: discord channel: telegram Channel integration: telegram extensions: lobster Extension: lobster extensions: phone-control size: XL maintainer Maintainer-authored PR labels Mar 13, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR broadens the plugin surface of the OpenClaw gateway to let a third-party plugin (specifically the @pwrdrvr/openclaw-app-server Codex App Server plugin) fully own bound-conversation routing, interactive callbacks, and channel controls without touching core-private code paths. It adds:

  • A new inbound_claim plugin hook (first-claim-wins) and a plugin-targeted variant so plugins can intercept bound inbound messages before core command/agent dispatch.
  • src/plugins/conversation-binding.ts — a full approval-gated session-binding subsystem for plugins, with persistent per-channel "always allow" grants stored under ~/.openclaw/plugin-binding-approvals.json.
  • src/plugins/interactive.ts — namespaced, deduped interactive-callback routing for Telegram and Discord.
  • Telegram and Discord typing lease helpers and conversation action helpers (edit, delete, pin, unpin, thread management) surfaced on the plugin runtime.
  • Wiring in dispatch-from-config.ts that checks for plugin-owned bindings before core dispatch.

The design is well-scoped — the changes do not include the Codex App Server itself — and the new seams are explicitly guarded, sequential, and covered with focused unit tests.

Issues found:

  • pendingRequests memory leak (src/plugins/conversation-binding.ts:636): Every call to requestPluginConversationBinding that results in a "pending" status adds an entry to the pendingRequests map, but entries are only removed when a user taps an approval button. Ignored prompts accumulate indefinitely. requestedAt is stored on every entry, so adding a TTL sweep (e.g., 30 min) on entry into resolvePluginConversationBindingApproval, or replacing the plain Map with the createDedupeCache helper already used in interactive.ts, would close the leak.

  • auth.isAuthorizedSender hardcoded to true for all Telegram interactive callbacks (extensions/telegram/src/bot-handlers.ts:1256): When inlineButtonsScope is "group" or "dm" (i.e., authorizationMode === "callback-scope"), any user in the matching scope passes senderAuthorization.allowed and reaches the plugin dispatch. Setting isAuthorizedSender: true unconditionally will mislead plugin handlers that gate sensitive actions (conversation binding, agent invocation) on this field. The Discord path correctly derives this value from resolveComponentCommandAuthorized. The Telegram path should similarly reflect whether the sender passes the stricter allowlist check rather than just the scope check.

Confidence Score: 3/5

  • Safe to merge after addressing the pendingRequests TTL gap and the Telegram isAuthorizedSender hardcoding; both are real bugs but neither is a showstopper in a controlled deployment.
  • The PR is architecturally sound and well-tested. The two flagged issues — unbounded pendingRequests growth and the misleading isAuthorizedSender: true for non-allowlist Telegram callers — are genuine defects that could affect production correctness and plugin security reasoning, which prevents a higher score. The previously reported dedupe-before-handler and missing timer.unref() issues have both been addressed per the review thread.
  • src/plugins/conversation-binding.ts (memory leak) and extensions/telegram/src/bot-handlers.ts (auth field accuracy) need attention before merging.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/plugins/conversation-binding.ts
Line: 636

Comment:
**Unbounded `pendingRequests` map — no TTL or eviction**

`pendingRequests.set(request.id, request)` is called every time a user triggers a bind prompt that requires an approval, but an entry is only removed in `resolvePluginConversationBindingApproval` — when someone actually taps a button. If the user ignores the prompt (closes the message, revokes bot access, leaves the chat, etc.), the entry stays in memory for the lifetime of the process.

In a busy gateway this accumulates indefinitely. Each `PendingPluginBindingRequest` includes conversation references, sender IDs, plugin identifiers, and a summary string, so the growth is meaningful.

The simplest fix is to evict entries older than a reasonable window (e.g. 30 minutes) on each call to `resolvePluginConversationBindingApproval` and/or to run a periodic sweep. `requestedAt` is already stored on every entry, so the eviction predicate is already available:

```ts
// at the top of resolvePluginConversationBindingApproval
const now = Date.now();
const EXPIRY_MS = 30 * 60_000;
for (const [id, req] of pendingRequests) {
  if (now - req.requestedAt > EXPIRY_MS) {
    pendingRequests.delete(id);
  }
}
```

Alternatively, replace the plain `Map` with the existing `createDedupeCache` helper (already used in `interactive.ts`) which provides TTL + max-size eviction out of the box.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/telegram/src/bot-handlers.ts
Line: 1256

Comment:
**`isAuthorizedSender: true` hardcoded regardless of scope-vs-allowlist authorization mode**

At this point in the handler `senderAuthorization.allowed` is `true` (we returned early if it was `false`), but the authorization *mode* can be either `"callback-scope"` or `"callback-allowlist"`:

- **`"callback-allowlist"`**: only explicitly allow-listed users reach here → `isAuthorizedSender: true` is accurate.
- **`"callback-scope"`** (the default for most deployments): *any* user in the correct scope (DM or group, per `inlineButtonsScope`) reaches here, regardless of whether they are in the trusted sender allowlist. For a group-scoped bot this means every group member is handed `isAuthorizedSender: true`.

On the Discord side this is handled correctly: `isAuthorizedSender: params.isAuthorizedSender` is derived from `resolveComponentCommandAuthorized`, which checks the allowlist separately from the component ACL.

A plugin author who reads `auth.isAuthorizedSender` to decide whether to execute a sensitive action (bind a conversation, trigger an agent, etc.) will incorrectly trust arbitrary group members when the gateway is in `callback-scope` mode.

The fix is to derive the value from `senderAuthorization` rather than hardcoding `true`:

```suggestion
            isAuthorizedSender: senderAuthorization.isCommandAuthorized ?? senderAuthorization.allowed,
```

(The exact field name depends on what `authorizeTelegramEventSender` returns; the point is to use the allowlist-level result rather than the weaker scope check.)

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 732e710

Copy link
Copy Markdown

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

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: fd8f1dd8f7

ℹ️ 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".

@huntharo huntharo force-pushed the codex/broaden-plugin-surface-for-agents branch from e861256 to 99759ac Compare March 13, 2026 17:33
Copy link
Copy Markdown

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

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: 99759acc98

ℹ️ 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".

Copy link
Copy Markdown

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

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: 07714512e9

ℹ️ 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".

Copy link
Copy Markdown

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

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: 5c7ac6deca

ℹ️ 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".

Copy link
Copy Markdown

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

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: f9800f08c6

ℹ️ 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".

Copy link
Copy Markdown

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

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: 61213288f6

ℹ️ 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".

Copy link
Copy Markdown

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

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: 20bef89e57

ℹ️ 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".

@huntharo
Copy link
Copy Markdown
Member Author

@greptile-apps please re-review

Copy link
Copy Markdown

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

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: 732e710c30

ℹ️ 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".

Copy link
Copy Markdown

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

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: 311043ae93

ℹ️ 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".

@huntharo huntharo self-assigned this Mar 15, 2026
@Takhoffman
Copy link
Copy Markdown
Contributor

Two concrete risks:

  1. Existing legacy plugin-bound conversations can turn into dead chats.
    If a conversation is marked as bound to a plugin, this PR now skips normal agent handling and assumes the plugin will take it. If that plugin is no longer loaded, changed, or doesn’t actually claim the message, the message can just disappear instead of falling back to the normal bot flow.

  2. Plugins on non-Telegram/non-Discord channels can get stuck waiting forever for approval.
    The new binding approval flow can create a “pending approval” state for plugin bindings on generic channels, but only Telegram and Discord actually have approval handlers wired up. So on something like Slack or another plugin-backed channel, a bind request can be created with no real way to approve it.

Copy link
Copy Markdown

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

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: 7f7ad05a63

ℹ️ 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".

Copy link
Copy Markdown

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

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: d9ebe3f54c

ℹ️ 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".

@huntharo huntharo force-pushed the codex/broaden-plugin-surface-for-agents branch from 5cd0fc9 to 10e0ad9 Compare March 15, 2026 21:34
Copy link
Copy Markdown

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

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: 10e0ad915d

ℹ️ 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".

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

💡 Codex Review

const result = await command.handler(ctx);
logVerbose(
`Plugin command /${command.name} executed successfully for ${senderId || "unknown"}`,
);
return result;

P2 Badge Validate plugin command handler return payload

executePluginCommand returns the handler result verbatim, so a runtime JS plugin that accidentally returns undefined is treated as a successful command and forwarded to channel-specific responders. In the Discord native-command flow, that value is passed into hasRenderableReplyPayload, which dereferences payload.text and throws, causing the interaction to fail instead of returning a safe error reply. Since this boundary already validates other runtime command fields, it should also guard non-object/empty command results and fall back to an explicit error payload.

ℹ️ 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".

@vincentkoc vincentkoc self-assigned this Mar 15, 2026
@vincentkoc
Copy link
Copy Markdown
Member

Addressed the remaining review commentary in the latest push.

  • Telegram plugin callbacks now receive the real command-authorization signal instead of a hardcoded true, use the resolved topic identity for callback binding context, and keep bind approval buttons intact when a click resolves as expired.
  • Discord plugin callbacks are now framework-gated on command authorization before dispatch, and expired plugin bind approvals no longer clear the source controls.
  • Interactive handler registration now validates channel + handler shape up front, dedupe keys are scoped by channel/namespace, and declined callbacks no longer consume the dedupe key.
  • Plugin bind approvals now expire/prune stale pending requests and reject unsupported channels instead of creating unresolvable approvals.
  • Unbound inbound_claim hooks now run before core command/agent dispatch, and Discord group-DM conversation identity is normalized consistently in command binding and inbound-claim mapping.

Verified with pnpm check, pnpm test -- src/plugins/interactive.test.ts src/plugins/conversation-binding.test.ts src/plugins/commands.test.ts src/hooks/message-hook-mappers.test.ts src/auto-reply/reply/dispatch-from-config.test.ts, and pnpm test -- extensions/telegram/src/bot.test.ts extensions/discord/src/monitor/monitor.test.ts.

@vincentkoc vincentkoc merged commit aa1454d into openclaw:main Mar 15, 2026
30 of 32 checks passed
mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 15, 2026
* main: (66 commits)
  Commands: lazy-load auth choice plugin provider runtime (openclaw#47692)
  fix: tighten setup wizard typing
  docs: describe channel setup wizard surface
  refactor: move telegram onboarding to setup wizard
  refactor: decouple channel setup discovery
  Changelog: note plugin agent integrations
  fix(plugins): restore provider compatibility fallbacks
  build(plugins): add bundled provider plugin packages
  feat(plugins): move provider runtimes into bundled plugins
  feat(plugins): add compatible bundle support
  Plugins: broaden plugin surface for Codex App Server (openclaw#45318)
  Channels: use owned helper imports
  Channels: move onboarding adapters into extensions
  build(plugins): add bundled provider plugin manifests
  feat(plugins): move provider runtimes into bundled plugins
  Plugins: clean stale bundled skill outputs
  Plugins: skip nested node_modules in bundled skills
  Plugins: relocate bundled skill assets
  Commands: lazy-load non-interactive plugin provider runtime (openclaw#47593)
  fix(dev): align gateway watch with tsdown wrapper (openclaw#47636)
  ...
romeroej2 pushed a commit to romeroej2/openclaw that referenced this pull request Mar 16, 2026
* Plugins: add inbound claim and Telegram interaction seams

* Plugins: add Discord interaction surface

* Chore: fix formatting after plugin rebase

* fix(hooks): preserve observers after inbound claim

* test(hooks): cover claimed inbound observer delivery

* fix(plugins): harden typing lease refreshes

* fix(discord): pass real auth to plugin interactions

* fix(plugins): remove raw session binding runtime exposure

* fix(plugins): tighten interactive callback handling

* Plugins: gate conversation binding with approvals

* Plugins: migrate legacy plugin binding records

* Plugins/phone-control: update test command context

* Plugins: migrate legacy binding ids

* Plugins: migrate legacy codex session bindings

* Discord: fix plugin interaction handling

* Discord: support direct plugin conversation binds

* Plugins: preserve Discord command bind targets

* Tests: fix plugin binding and interactive fallout

* Discord: stabilize directory lookup tests

* Discord: route bound DMs to plugins

* Discord: restore plugin bindings after restart

* Telegram: persist detached plugin bindings

* Plugins: limit binding APIs to Telegram and Discord

* Plugins: harden bound conversation routing

* Plugins: fix extension target imports

* Plugins: fix Telegram runtime extension imports

* Plugins: format rebased binding handlers

* Discord: bind group DM interactions by channel

---------

Co-authored-by: Vincent Koc <[email protected]>
guiramos added a commit to butley/openclaw that referenced this pull request Mar 22, 2026
* feat: make compaction timeout configurable via agents.defaults.compaction.timeoutSeconds (openclaw#46889)

* feat: make compaction timeout configurable via agents.defaults.compaction.timeoutSeconds

The hardcoded 5-minute (300s) compaction timeout causes large sessions
to enter a death spiral where compaction repeatedly fails and the
session grows indefinitely. This adds agents.defaults.compaction.timeoutSeconds
to allow operators to override the compaction safety timeout.

Default raised to 900s (15min) which is sufficient for sessions up to
~400k tokens. The resolved timeout is also used for the session write
lock duration so locks don't expire before compaction completes.

Fixes openclaw#38233

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* test: add resolveCompactionTimeoutMs tests

Cover config resolution edge cases: undefined config, missing
compaction section, valid seconds, fractional values, zero,
negative, NaN, and Infinity.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: add timeoutSeconds to compaction Zod schema

The compaction object schema uses .strict(), so setting the new
timeoutSeconds config option would fail validation at startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: enforce integer constraint on compaction timeoutSeconds schema

Prevents sub-second values like 0.5 which would floor to 0ms and
cause immediate compaction timeout. Matches pattern of other
integer timeout fields in the schema.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: clamp compaction timeout to Node timer-safe maximum

Values above ~2.1B ms overflow Node's setTimeout to 1ms, causing
immediate timeout. Clamp to MAX_SAFE_TIMEOUT_MS matching the
pattern in agents/timeout.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: add FIELD_LABELS entry for compaction timeoutSeconds

Maintains label/help parity invariant enforced by
schema.help.quality.test.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: align compaction timeouts with abort handling

* fix: land compaction timeout handling (openclaw#46889) (thanks @asyncjason)

---------

Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* fix: harden compaction timeout follow-ups

* Docs: fix stale Clawdbot branding in agent workflow file (openclaw#46963)

Co-authored-by: webdevpraveen <[email protected]>

* docs: replace outdated Clawdbot references with OpenClaw in skill docs (openclaw#41563)

Update 5 references to the old "Clawdbot" name in
skills/apple-reminders/SKILL.md and skills/imsg/SKILL.md.

Co-authored-by: imanisynapse <[email protected]>

* Docs: switch README logo to SVG assets (openclaw#47049)

* fix: Disable strict mode tools for non-native openai-completions compatible APIs (openclaw#45497)

Merged via squash.

Prepared head SHA: 20fe05f
Co-authored-by: sahancava <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* fix: forward forceDocument through sendPayload path (follow-up to openclaw#45111) (openclaw#47119)

Merged via squash.

Prepared head SHA: d791190
Co-authored-by: thepagent <[email protected]>
Reviewed-by: @frankekn

* fix(android): support android node  `calllog.search` (openclaw#44073)

* fix(android): support android node  `calllog.search`

* fix(android): support android node calllog.search

* fix(android): wire callLog through shared surfaces

* fix: land Android callLog support (openclaw#44073) (thanks @lxk7280)

---------

Co-authored-by: lixuankai <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob

* fix(web): handle 515 Stream Error during WhatsApp QR pairing (openclaw#27910)

* fix(web): handle 515 Stream Error during WhatsApp QR pairing

getStatusCode() never unwrapped the lastDisconnect wrapper object,
so login.errorStatus was always undefined and the 515 restart path
in restartLoginSocket was dead code.

- Add err.error?.output?.statusCode fallback to getStatusCode()
- Export waitForCredsSaveQueue() so callers can await pending creds
- Await creds flush in restartLoginSocket before creating new socket

Fixes openclaw#3942

* test: update session mock for getStatusCode unwrap + waitForCredsSaveQueue

Mirror the getStatusCode fix (err.error?.output?.statusCode fallback)
in the test mock and export waitForCredsSaveQueue so restartLoginSocket
tests work correctly.

* fix(web): scope creds save queue per-authDir to avoid cross-account blocking

The credential save queue was a single global promise chain shared by all
WhatsApp accounts. In multi-account setups, a slow save on one account
blocked credential writes and 515 restart recovery for unrelated accounts.

Replace the global queue with a per-authDir Map so each account's creds
serialize independently. waitForCredsSaveQueue() now accepts an optional
authDir to wait on a single account's queue, or waits on all when omitted.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* test: use real Baileys v7 error shape in 515 restart test

The test was using { output: { statusCode: 515 } } which was already
handled before the fix. Updated to use the actual Baileys v7 shape
{ error: { output: { statusCode: 515 } } } to cover the new fallback
path in getStatusCode.

Co-Authored-By: Claude Code (Opus 4.6) <[email protected]>

* fix(web): bound credential-queue wait during 515 restart

Prevents restartLoginSocket from blocking indefinitely if a queued
saveCreds() promise stalls (e.g. hung filesystem write).

Co-Authored-By: Claude <[email protected]>

* fix: clear flush timeout handle and assert creds queue in test

Co-Authored-By: Claude <[email protected]>

* fix: evict settled credsSaveQueues entries to prevent unbounded growth

Co-Authored-By: Claude <[email protected]>

* fix: share WhatsApp 515 creds flush handling (openclaw#27910) (thanks @asyncjason)

---------

Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* Deduplicate repeated tool call IDs for OpenAI-compatible APIs (openclaw#40996)

Merged via squash.

Prepared head SHA: 38d8048
Co-authored-by: xaeon2026 <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* fix(gateway): skip Control UI pairing when auth.mode=none (closes openclaw#42931) (openclaw#47148)

When auth is completely disabled (mode=none), requiring device pairing
for Control UI operator sessions adds friction without security value
since any client can already connect without credentials.

Add authMode parameter to shouldSkipControlUiPairing so the bypass
fires only for Control UI + operator role + auth.mode=none. This avoids
the openclaw#43478 regression where a top-level OR disabled pairing for ALL
websocket clients.

* fix: preserve Telegram word boundaries when rechunking HTML (openclaw#47274)

* fix: preserve Telegram chunk word boundaries

* fix: address Telegram chunking review feedback

* fix: preserve Telegram retry separators

* fix: preserve Telegram chunking boundaries (openclaw#47274)

* test(whatsapp): fix stale append inbox expectation

* chore(gateway): ignore `.test.ts` changes in `gateway:watch` (openclaw#36211)

* fix: harden remote cdp probes

* feat(feishu): add ACP and subagent session binding (openclaw#46819)

* feat(feishu): add ACP session support

* fix(feishu): preserve sender-scoped ACP rebinding

* fix(feishu): recover sender scope from bound ACP sessions

* fix(feishu): support DM ACP binding placement

* feat(feishu): add current-conversation session binding

* fix(feishu): avoid DM parent binding fallback

* fix(feishu): require canonical topic sender ids

* fix(feishu): honor sender-scoped ACP bindings

* fix(feishu): allow user-id ACP DM bindings

* fix(feishu): recover user-id ACP DM bindings

* ACP: fail closed on conflicting tool identity hints (openclaw#46817)

* ACP: fail closed on conflicting tool identity hints

* ACP: restore rawInput fallback for safe tool resolution

* ACP tests: cover rawInput-only safe tool approval

* fix: harden mention pattern regex compilation

* Nodes: recheck queued actions before delivery (openclaw#46815)

* Nodes: recheck queued actions before delivery

* Nodes tests: cover pull-time policy recheck

* Nodes tests: type node policy mocks explicitly

* refactor: drop deprecated whatsapp mention pattern sdk helper

* added a fix for memory leak on 2gb ram (openclaw#46522)

* Nodes tests: prove pull-time policy revalidation

* fix: harden device token rotation denial paths

* style: format imported model helpers

* Plugins: preserve scoped ids and reserve bundled duplicates (openclaw#47413)

* Plugins: preserve scoped ids and reserve bundled duplicates

* Changelog: add plugin scoped id note

* Plugins: harden scoped install ids

* Plugins: reserve scoped install dirs

* Plugins: migrate legacy scoped update ids

* CLI: reduce channels add startup memory (openclaw#46784)

* CLI: lazy-load channel subcommand handlers

* Channels: defer add command dependencies

* CLI: skip status JSON plugin preload

* CLI: cover status JSON route preload

* Status: trim JSON security audit path

* Status: update JSON fast-path tests

* CLI: cover root help fast path

* CLI: fast-path root help

* Status: keep JSON security parity

* Status: restore JSON security tests

* CLI: document status plugin preload

* Channels: reuse Telegram account import

* Integrations: tighten inbound callback and allowlist checks (openclaw#46787)

* Integrations: harden inbound callback and allowlist handling

* Integrations: address review follow-ups

* Update CHANGELOG.md

* Mattermost: avoid command-gating open button callbacks

* ACP: require admin scope for mutating internal actions (openclaw#46789)

* ACP: require admin scope for mutating internal actions

* ACP: cover operator admin mutating actions

* ACP: gate internal status behind admin scope

* Changelog: add missing PR credits

* Changelog: add more unreleased PR numbers

* Subagents: restrict follow-up messaging scope (openclaw#46801)

* Subagents: restrict follow-up messaging scope

* Subagents: cover foreign-session follow-up sends

* Update CHANGELOG.md

* Webhooks: tighten pre-auth body handling (openclaw#46802)

* Webhooks: tighten pre-auth body handling

* Webhooks: clean up request body guards

* Tools: revalidate workspace-only patch targets (openclaw#46803)

* Tools: revalidate workspace-only patch targets

* Tests: narrow apply-patch delete-path assertion

* CLI: trim onboarding provider startup imports (openclaw#47467)

* Scope Control UI sessions per gateway (openclaw#47453)

* Scope Control UI sessions per gateway

Signed-off-by: sallyom <[email protected]>

* Add changelog for Control UI session scoping

Signed-off-by: sallyom <[email protected]>

---------

Signed-off-by: sallyom <[email protected]>

* Gateway: scrub credentials from endpoint snapshots (openclaw#46799)

* Gateway: scrub credentials from endpoint snapshots

* Gateway: scrub raw endpoint credentials in snapshots

* Gateway: preserve config redaction round-trips

* Gateway: restore redacted endpoint URLs on apply

* fix(config): avoid failing startup on implicit memory slot (openclaw#47494)

* fix(config): avoid failing on implicit memory slot

* fix(config): satisfy build for memory slot guard

* docs(changelog): note implicit memory slot startup fix (openclaw#47494)

* CLI: lazy-load auth choice provider fallback (openclaw#47495)

* CLI: lazy-load auth choice provider fallback

* CLI: cover lazy auth choice provider fallback

* fix(ci): config drift found and documented

* Gateway: tighten forwarded client and pairing guards (openclaw#46800)

* Gateway: tighten forwarded client and pairing guards

* Gateway: make device approval scope checks atomic

* Gateway: preserve device approval baseDir compatibility

* Changelog: note CLI OOM startup fixes (openclaw#47525)

* Commands: lazy-load model picker provider runtime (openclaw#47536)

* Commands: lazy-load model picker provider runtime

* Tests: cover model picker runtime boundary

* docs: fork rebase spec + per-patch diffs for upstream v2026.3.13 merge

Generated after failed merge attempt (2026-03-15). Contains:
- FORK-PATCHES-SPEC.md: implementation instructions per patch group (249 lines)
- FORK-REBASE-SPEC.md: technical context, errors, SSE protocol (292 lines)
- fork-patches/by-patch/: 31 per-patch git diffs (consultable on demand)
- fork-patches/fork-vs-upstream-src-only.patch: full squashed diff (5813 lines)

Co-authored-by: Bob

* docs: add merge plan from feat/upstream-merge-3.13 branch

Co-authored-by: Bob

* docs: remove old merge plan — superseded by FORK-PATCHES-SPEC + FORK-REBASE-SPEC

Co-authored-by: Bob

* chore(fmt): format changes and broken types

* Commands: split static onboard auth choice help (openclaw#47545)

* Commands: split static onboard auth choice help

* Tests: cover static onboard auth choice help

* Changelog: note static onboard auth choice help

* CLI/completion: fix generator OOM and harden plugin registries (openclaw#45537)

* fix: avoid OOM during completion script generation

* CLI/completion: fix PowerShell nested command paths

* CLI/completion: cover generated shell scripts

* Changelog: note completion generator follow-up

* Plugins: reserve shared registry names

---------

Co-authored-by: Xiaoyi <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>

* fix(plugins): load bundled extensions from dist (openclaw#47560)

* fix(models): preserve stream usage compat opt-ins (openclaw#45733)

Preserves explicit `supportsUsageInStreaming` overrides from built-in provider
catalogs and user config instead of unconditionally forcing `false` on non-native
openai-completions endpoints.

Adds `applyNativeStreamingUsageCompat()` to set `supportsUsageInStreaming: true`
on ModelStudio (DashScope) and Moonshot models at config build time so their
native streaming usage works out of the box.

Closes openclaw#46142

Co-authored-by: pezy <[email protected]>

* Plugins: reserve context engine ownership

* docs(zalo): document current Marketplace bot behavior (openclaw#47552)

Verified:
- pnpm check:docs

Co-authored-by: Tomáš Dinh <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* Docs: move release runbook to maintainer repo (openclaw#47532)

* Docs: redact private release setup

* Docs: tighten release order

* Docs: move release runbook to maintainer repo

* Docs: delete public mac release page

* Docs: remove zh-CN mac release page

* Docs: turn release checklist into release policy

* Docs: point release policy to private docs

* Docs: regenerate zh-CN release policy pages

* Docs: preserve Doctor in zh-CN hubs

* Docs: fix zh-CN polls label

* Docs: tighten docs i18n term guardrails

* Docs: enforce zh-CN glossary coverage

* Scripts: rebuild on extension and tsdown config changes (openclaw#47571)

Merged via squash.

Prepared head SHA: edd8ed8
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras

* fix: reset chat buffer on tool-start to prevent intermediary text accumulation

The Pi SDK resets lastStreamedAssistantCleaned between tool calls, but
the gateway chatRunState.buffers was not reset — causing mergedText to
accumulate text from ALL prior turns. The SSE subscriber (which resets
lastTextLen=0 on tool-start) then re-emitted the entire conversation.

Co-authored-by: Bob

* fix(release): block oversized npm packs that regress low-memory startup (openclaw#46850)

* fix(release): guard npm pack size regressions

* fix(release): fail closed when npm omits pack size

* Plugins: reserve context engine ownership (openclaw#47595)

* Plugins: reserve context engine ownership

* Update src/context-engine/registry.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: restore Patch #4 (chat mirror) and Patch #5 (inbound push) lost in Codex merge

Patch #4: webchat-originated replies on WA-scoped sessions now mirror
to WhatsApp via sendMessageWhatsApp(). The Codex merge kept the
runContext.mirror registration but lost the delivery block.

Patch #5: inbound messages (WA/Slack/etc.) now broadcast to WS/SSE
clients via message.inbound event, restoring real-time cross-channel
message display in the webchat dashboard.

Co-authored-by: Bob

* fix: mirror delivery in emitChatFinal (embedded runner path)

The previous commit placed mirror only in the !agentRunStarted branch
of server-methods/chat.ts, but the embedded runner sets agentRunStarted=true
and delivers via emitChatFinal in server-chat.ts instead. This restores
the mirror block in the correct location — matching the alpha.

Co-authored-by: Bob

* Gateway: sync runtime post-build artifacts

* Plugins: harden context engine ownership

* fix: complete Patch #5 inbound push + fix mirror static import

- Add emitInboundMessageEvent() call in dispatch-from-config.ts (was
  only defined but never called — WA messages never reached SSE/webchat)
- Switch mirror from dynamic import() to static import (dynamic import
  failed silently in bundled build)

Co-authored-by: Bob

* fix: globalThis singleton for WA listeners to survive chunk duplication

The bundler splits active-listener.ts into a different chunk than
server-chat.ts (mirror) and auto-reply/monitor.ts (listener registration).
Static/dynamic imports resolve to different module instances, so mirror
always sees an empty listeners Map. Using globalThis ensures all chunks
share the same Map instance.

Co-authored-by: Bob

* fix(plugins): fix bundled plugin roots and skill assets (openclaw#47601)

* fix(acpx): resolve bundled plugin root correctly

* fix(plugins): copy bundled plugin skill assets

* fix(plugins): tolerate missing bundled skill paths

* chore: remove temporary mirror debug logs

Co-authored-by: Bob

* fix: globalThis singleton for inbound event listeners (chunk duplication)

Same root cause as the WA listener fix: dispatch-from-config.ts emits
inbound events in one chunk, server.impl.ts subscribes in another.
Module-level Set gets duplicated across chunks.

Co-authored-by: Bob

* fix: emit inbound events from WA process-message path (not dispatch-from-config)

dispatch-from-config.ts is NOT in the WA message processing chain.
WA messages go through process-message.ts → provider-dispatcher.ts.
Moved emitInboundMessageEvent to process-message.ts where WA messages
are actually processed.

Co-authored-by: Bob

* fix(ci): restore config baseline release-check output (openclaw#47629)

* Docs: regenerate config baseline

* Chore: ignore generated config baseline

* Update .prettierignore

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* CLI: support package-manager installs from GitHub main (openclaw#47630)

* CLI: resolve package-manager main install specs

* CLI: skip registry resolution for raw package specs

* CLI: support main package target updates

* CLI: document package update specs in help

* Tests: cover package install spec resolution

* Tests: cover npm main-package updates

* Tests: cover update --tag main

* Installer: support main package targets

* Installer: support main package targets on Windows

* Docs: document package-manager main updates

* Docs: document installer main targets

* Docs: document npm and pnpm main installs

* Docs: document update --tag main

* Changelog: note package-manager main installs

* Update src/infra/update-global.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: emit message.inbound directly on gatewayEventBus

Bypass inbound-events.ts entirely — its module-level Set suffers from
chunk duplication even with globalThis (timing/ordering issues).
gatewayEventBus already uses globalThis singleton and is proven to work
for chat/agent events. SSE listens on gatewayEventBus for message.inbound.

Co-authored-by: Bob

* fix(dev): align gateway watch with tsdown wrapper (openclaw#47636)

* Commands: lazy-load non-interactive plugin provider runtime (openclaw#47593)

* Commands: lazy-load non-interactive plugin provider runtime

* Tests: cover non-interactive plugin provider ordering

* Update src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.runtime.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Plugins: relocate bundled skill assets

* Plugins: skip nested node_modules in bundled skills

* Plugins: clean stale bundled skill outputs

* feat(plugins): move provider runtimes into bundled plugins

* build(plugins): add bundled provider plugin manifests

* Channels: move onboarding adapters into extensions

* Channels: use owned helper imports

* Plugins: broaden plugin surface for Codex App Server (openclaw#45318)

* Plugins: add inbound claim and Telegram interaction seams

* Plugins: add Discord interaction surface

* Chore: fix formatting after plugin rebase

* fix(hooks): preserve observers after inbound claim

* test(hooks): cover claimed inbound observer delivery

* fix(plugins): harden typing lease refreshes

* fix(discord): pass real auth to plugin interactions

* fix(plugins): remove raw session binding runtime exposure

* fix(plugins): tighten interactive callback handling

* Plugins: gate conversation binding with approvals

* Plugins: migrate legacy plugin binding records

* Plugins/phone-control: update test command context

* Plugins: migrate legacy binding ids

* Plugins: migrate legacy codex session bindings

* Discord: fix plugin interaction handling

* Discord: support direct plugin conversation binds

* Plugins: preserve Discord command bind targets

* Tests: fix plugin binding and interactive fallout

* Discord: stabilize directory lookup tests

* Discord: route bound DMs to plugins

* Discord: restore plugin bindings after restart

* Telegram: persist detached plugin bindings

* Plugins: limit binding APIs to Telegram and Discord

* Plugins: harden bound conversation routing

* Plugins: fix extension target imports

* Plugins: fix Telegram runtime extension imports

* Plugins: format rebased binding handlers

* Discord: bind group DM interactions by channel

---------

Co-authored-by: Vincent Koc <[email protected]>

* feat(plugins): add compatible bundle support

* feat(plugins): move provider runtimes into bundled plugins

* build(plugins): add bundled provider plugin packages

* fix(plugins): restore provider compatibility fallbacks

* Changelog: note plugin agent integrations

* chore: remove inbound-push debug logs

Co-authored-by: Bob

* refactor: decouple channel setup discovery

* refactor: move telegram onboarding to setup wizard

* docs: describe channel setup wizard surface

* fix: tighten setup wizard typing

* fix: deduplicate inbound events + use raw body instead of envelope

- Remove emitInboundMessageEvent from dispatch-from-config.ts (WA uses
  process-message.ts path, causing double emit)
- Use params.msg.body (clean) instead of combinedBody (with envelope
  prefix) to avoid showing [WhatsApp ...] metadata in chat UI

Co-authored-by: Bob

* Commands: lazy-load auth choice plugin provider runtime (openclaw#47692)

* Commands: lazy-load auth choice plugin provider runtime

* Tests: cover auth choice plugin provider runtime

* refactor: expand setup wizard flow

* refactor: move discord and slack to setup wizard

* refactor: drop onboarding adapter sdk exports

* docs: update setup wizard capabilities

* feat(plugins): test bundle MCP end to end

* fix(onboarding): use scoped plugin snapshots to prevent OOM on low-memory hosts (openclaw#46763)

* fix(onboarding): use scoped plugin snapshots to prevent OOM on low-memory hosts

Onboarding and channel-add flows previously loaded the full plugin registry,
which caused OOM crashes on memory-constrained hosts. This patch introduces
scoped, non-activating plugin registry snapshots that load only the selected
channel plugin without replacing the running gateway's global state.

Key changes:
- Add onlyPluginIds and activate options to loadOpenClawPlugins for scoped loads
- Add suppressGlobalCommands to plugin registry to avoid leaking commands
- Replace full registry reloads in onboarding with per-channel scoped snapshots
- Validate command definitions in snapshot loads without writing global registry
- Preload configured external plugins via scoped discovery during onboarding

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(test): add return type annotation to hoisted mock to resolve TS2322

* fix(plugins): enforce cache:false invariant for non-activating snapshot loads

* Channels: preserve lazy scoped snapshot import after rebase

* Onboarding: scope channel snapshots by plugin id

* Catalog: trust manifest ids for channel plugin mapping

* Onboarding: preserve scoped setup channel loading

* Onboarding: restore built-in adapter fallback

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>

* feat(plugins): add provider usage runtime hooks

* feat(plugins): move bundled providers behind plugin hooks

* docs(plugins): document provider runtime usage hooks

* docs(plugins): unify bundle format explainer

* fix: repair onboarding adapter registry imports

* refactor: expand setup wizard input flow

* refactor: move signal imessage mattermost to setup wizard

* docs: document richer setup wizard prompts

* feat(plugins): move anthropic and openai vendors to plugins

* fix: repair onboarding setup-wizard imports

* test(discord): cover startup phase logging

* fix: reduce plugin and discord warning noise

* chore: raise plugin registry cache cap

* build: suppress protobufjs eval warning in tsdown

* refactor: tighten setup wizard onboarding bridge

* refactor: move bluebubbles to setup wizard

* refactor: move nextcloud talk to setup wizard

* CLI: restore lightweight root help and scoped status plugin preload

* Matrix: lazy-load runtime-heavy channel paths

* CI: add CLI startup memory regression check

* MSTeams: lazy-load runtime-heavy channel paths

* refactor: expand setup wizard flow

* refactor: move whatsapp to setup wizard

* refactor: move irc to setup wizard

* refactor: move tlon to setup wizard

* refactor: move googlechat to setup wizard

* refactor: expose setup wizard sdk surfaces

* Feishu: lazy-load runtime-heavy channel paths

* Google Chat: lazy-load runtime-heavy channel paths

* fix: gate setup-only plugin side effects

* feat(web-search): add plugin-backed search providers

* fix(web-search): restore build after plugin rebase

* refactor(web-search): move providers into company plugins

* WhatsApp: lazy-load setup wizard surface

* fix: align channel adapters with plugin sdk

* fix: repair node24 ci type drift

* refactor(google): merge gemini auth into google plugin

* feat(plugins): merge openai vendor seams into one plugin

* refactor(plugins): lazy load provider runtime shims

* perf(cli): trim help startup imports

* perf(status): defer heavy startup loading

* fix(matrix): assert outbound runtime hooks

* refactor: extend setup wizard account resolution

* refactor: move feishu zalo zalouser to setup wizard

* refactor: move matrix msteams twitch to setup wizard

* refactor: drop channel onboarding fallback

* fix: quiet discord startup logs

* Slack: lazy-load setup wizard surface

* Feishu: drop stale runtime onboarding export

* Discord: lazy-load setup wizard surface

* Signal: lazy-load setup wizard surface

* perf(plugins): lazy-load setup surfaces

* fix(cli): repair preaction merge typo

* Signal: restore setup surface helper exports

* iMessage: lazy-load setup wizard surface

* Nextcloud Talk: split setup adapter helpers

* fix: remove stale dist plugin dirs

* BlueBubbles: split setup adapter helpers

* test(plugins): cover retired google auth compatibility

* refactor(tests): share plugin registration helpers

* refactor(plugins): share bundled compat transforms

* refactor(google): split oauth flow modules

* refactor(plugin-sdk): centralize entrypoint manifest

* fix(docs): harden i18n prompt failures

* docs(i18n): sync zh-CN google plugin references

* fix(docs): run i18n through a local rpc client

* build(plugin-sdk): enforce export sync in check

* docs(google): remove stale plugin references

* IRC: split setup adapter helpers

* refactor: move line to setup wizard

* refactor: trim onboarding sdk exports

* Telegram: split setup adapter helpers

* fix: allow plugin package id hints

* Tlon: split setup adapter helpers

* LINE: split setup adapter helpers

* fix: restore ci type checks

* fix: resolve line setup rebase drift

* Mattermost: split setup adapter helpers

* refactor: merge minimax bundled plugins

* docs: refresh zh-CN model providers

* perf(plugins): lazy-load channel setup entrypoints

* Google Chat: split setup adapter helpers

* Matrix: split setup adapter helpers

* MSTeams: split setup adapter helpers

* feat(telegram): add topic-edit action

* fix(telegram): normalize topic-edit targets

* fix: add Telegram topic-edit action (openclaw#47798)

* Feishu: split setup adapter helpers

* fixed main?

* Zalo: split setup adapter helpers

* refactor(plugins): split lightweight channel setup modules

* Zalouser: split setup adapter helpers

* Status: skip unused channel issue scan in JSON mode

* fix(plugins): tighten lazy setup typing

* fix: tighten outbound channel/plugin resolution

* fix(ci): repair security and route test fixtures

* secrets: harden read-only SecretRef command paths and diagnostics (openclaw#47794)

* secrets: harden read-only SecretRef resolution for status and audit

* CLI: add SecretRef degrade-safe regression coverage

* Docs: align SecretRef status and daemon probe semantics

* Security audit: close SecretRef review gaps

* Security audit: preserve source auth SecretRef configuredness

* changelog

Signed-off-by: joshavant <[email protected]>

---------

Signed-off-by: joshavant <[email protected]>

* Gateway: add presence-only probe mode for status

* refactor: move group access into setup wizard

* feat: add nostr setup and unify channel setup discovery

* fix: drop duplicate channel setup import

* feat: add openshell sandbox backend

* feat(system-prompt): replace hardcoded identity with butley-system-prompt.md

Cherry-picked from work (9e1137a). Guilherme's PR #4.
Co-authored-by: Bob

* fix: suppress SSE finalization on retryable rate-limit errors (openclaw#32)

Cherry-picked from work (456f091). Retryable provider errors (429,
overload) no longer kill the SSE stream — keeps it open during
gateway retries/failover so text flows when retry succeeds.

Co-authored-by: Bob

* Status: scope JSON plugin preload to configured channels

* feat: persist previousSessionId chain across session resets (openclaw#34)

Cherry-picked from work (b465233). Adapted: upstream already has
previousSessionEntry — only added previousSessionIdForChain for
fallback chain persistence on reset/new/idle-expiry.

Co-authored-by: Bob

* Status: lazy-load read-only account inspectors

* refactor(core): land plugin auth and startup cleanup

* chore: restore butley-api + clickup-api custom plugins from alpha

These custom extensions were missing from the rebase branch.
Copied from alpha verbatim.

Co-authored-by: Bob

* CLI: route gateway status before program registration

* feat: add remote openshell sandbox mode

* docs: expand openshell sandbox docs

* feat: add firecrawl onboarding search plugin

* Gateway: lazy-load SSH status helpers

* refactor: rename channel setup flow seam

* refactor: move setup fallback into setup registry

* feat: add synology chat setup wizard

* build: add setup entrypoints for migrated channel plugins

* docs: update channel setup docs

* fix: update feishu setup adapter import

* Status: lazy-load channel summary helpers

* Agents: skip eager context warmup for status commands

* Status: route JSON through lean command

* refactor(plugins): move auth and model policy to providers

* fix: control UI sends correct provider prefix when switching models

The model selector was using just the model ID (e.g. "gpt-5.2") as the
option value. When sent to sessions.patch, the server would fall back to
the session's current provider ("anthropic") yielding "anthropic/gpt-5.2"
instead of "openai/gpt-5.2".

Now option values use "provider/model" format, and resolveModelOverrideValue
and resolveDefaultModelValue also return the full provider-prefixed key so
selected state stays consistent.

* fix: format default model label as 'model · provider' for consistency

The default option showed 'Default (openai/gpt-5.2)' while individual
options used the friendlier 'gpt-5.2 · openai' format.

* Nostr: break setup-surface import cycle

* Tests: stabilize bundle MCP env on Windows

* Status: lazy-load channel security and summaries

* Docs: refresh generated config baseline

* test: silence vitest warning noise

* Status: lazy-load text scan helpers

* refactor: rename setup helper surfaces

* test: fix fetch mock typing

* docs: update channel setup wording

* Security: lazy-load channel audit provider helpers

* fix(ui): centralize control model ref handling

* CLI: route gateway status through daemon status

* Status: restore lazy scan runtime typing

* feat: token usage tracking via llm_output hook

* fix: remove duplicate previousSessionEntry declaration

* fix: butley-api extension imports — use openclaw/plugin-sdk instead of relative source paths

* fix: ensure llm_output hook is included in butley-api build output

- Add optional bundled cluster filtering to listBundledPluginBuildEntries()
  to skip extensions with native dependencies (matrix, whatsapp, etc.)
  that cannot be bundled by rolldown on all platforms
- Filter plugin-sdk entries for optional clusters to prevent native
  .node binary bundling failures
- Matches upstream's shouldBuildBundledCluster() pattern
- butley-api dist output now correctly contains both the tool
  registration AND the llm_output hook in the register() default export

* Revert "fix: ensure llm_output hook is included in butley-api build output"

This reverts commit 4558c9d.

* fix: revert tsdown changes, copy butley-api to dist via Dockerfile

* feat: [FORK-PATCH-37] token usage tracking via direct Convex POST

* fix: improve FORK-PATCH-37 logging and add cache token tracking

---------

Signed-off-by: sallyom <[email protected]>
Signed-off-by: joshavant <[email protected]>
Co-authored-by: Jason <[email protected]>
Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Praveen K  Singh <[email protected]>
Co-authored-by: webdevpraveen <[email protected]>
Co-authored-by: SkunkWorks0x <[email protected]>
Co-authored-by: imanisynapse <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: Sahan <[email protected]>
Co-authored-by: frankekn <[email protected]>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: thepagent <[email protected]>
Co-authored-by: Ace Lee <[email protected]>
Co-authored-by: lixuankai <[email protected]>
Co-authored-by: Ted Li <[email protected]>
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: 助爪 <[email protected]>
Co-authored-by: xaeon2026 <[email protected]>
Co-authored-by: Andrew Demczuk <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Harold Hunt <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: Aditya Chaudhary <[email protected]>
Co-authored-by: Sally O'Malley <[email protected]>
Co-authored-by: Nimrod Gutman <[email protected]>
Co-authored-by: Lucas Machado <[email protected]>
Co-authored-by: xiaoyi <[email protected]>
Co-authored-by: Xiaoyi <[email protected]>
Co-authored-by: peizhe.chen <[email protected]>
Co-authored-by: Tomáš Dinh <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Mason <[email protected]>
Co-authored-by: Josh Avant <[email protected]>
Co-authored-by: Christopher Chamaletsos <[email protected]>
sbezludny pushed a commit to sbezludny/openclaw that referenced this pull request Mar 27, 2026
* Plugins: add inbound claim and Telegram interaction seams

* Plugins: add Discord interaction surface

* Chore: fix formatting after plugin rebase

* fix(hooks): preserve observers after inbound claim

* test(hooks): cover claimed inbound observer delivery

* fix(plugins): harden typing lease refreshes

* fix(discord): pass real auth to plugin interactions

* fix(plugins): remove raw session binding runtime exposure

* fix(plugins): tighten interactive callback handling

* Plugins: gate conversation binding with approvals

* Plugins: migrate legacy plugin binding records

* Plugins/phone-control: update test command context

* Plugins: migrate legacy binding ids

* Plugins: migrate legacy codex session bindings

* Discord: fix plugin interaction handling

* Discord: support direct plugin conversation binds

* Plugins: preserve Discord command bind targets

* Tests: fix plugin binding and interactive fallout

* Discord: stabilize directory lookup tests

* Discord: route bound DMs to plugins

* Discord: restore plugin bindings after restart

* Telegram: persist detached plugin bindings

* Plugins: limit binding APIs to Telegram and Discord

* Plugins: harden bound conversation routing

* Plugins: fix extension target imports

* Plugins: fix Telegram runtime extension imports

* Plugins: format rebased binding handlers

* Discord: bind group DM interactions by channel

---------

Co-authored-by: Vincent Koc <[email protected]>
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 28, 2026
* Plugins: add inbound claim and Telegram interaction seams

* Plugins: add Discord interaction surface

* Chore: fix formatting after plugin rebase

* fix(hooks): preserve observers after inbound claim

* test(hooks): cover claimed inbound observer delivery

* fix(plugins): harden typing lease refreshes

* fix(discord): pass real auth to plugin interactions

* fix(plugins): remove raw session binding runtime exposure

* fix(plugins): tighten interactive callback handling

* Plugins: gate conversation binding with approvals

* Plugins: migrate legacy plugin binding records

* Plugins/phone-control: update test command context

* Plugins: migrate legacy binding ids

* Plugins: migrate legacy codex session bindings

* Discord: fix plugin interaction handling

* Discord: support direct plugin conversation binds

* Plugins: preserve Discord command bind targets

* Tests: fix plugin binding and interactive fallout

* Discord: stabilize directory lookup tests

* Discord: route bound DMs to plugins

* Discord: restore plugin bindings after restart

* Telegram: persist detached plugin bindings

* Plugins: limit binding APIs to Telegram and Discord

* Plugins: harden bound conversation routing

* Plugins: fix extension target imports

* Plugins: fix Telegram runtime extension imports

* Plugins: format rebased binding handlers

* Discord: bind group DM interactions by channel

---------

Co-authored-by: Vincent Koc <[email protected]>
(cherry picked from commit aa1454d)

# Conflicts:
#	extensions/discord/src/components.test.ts
#	extensions/discord/src/directory-live.test.ts
#	extensions/discord/src/monitor/message-handler.preflight.test.ts
#	extensions/discord/src/monitor/monitor.test.ts
#	extensions/discord/src/monitor/thread-bindings.lifecycle.test.ts
#	extensions/discord/src/targets.test.ts
#	extensions/lobster/src/lobster-tool.test.ts
#	extensions/telegram/src/bot.test.ts
#	extensions/telegram/src/send.test-harness.ts
#	extensions/telegram/src/send.test.ts
#	extensions/test-utils/plugin-api.ts
#	src/auto-reply/reply/dispatch-from-config.test.ts
#	src/auto-reply/reply/dispatch-from-config.ts
#	src/hooks/message-hook-mappers.test.ts
#	src/plugin-sdk/index.ts
#	src/plugins/commands.ts
#	src/plugins/hooks.ts
#	src/plugins/registry.ts
#	src/plugins/types.ts
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 28, 2026
* Plugins: add inbound claim and Telegram interaction seams

* Plugins: add Discord interaction surface

* Chore: fix formatting after plugin rebase

* fix(hooks): preserve observers after inbound claim

* test(hooks): cover claimed inbound observer delivery

* fix(plugins): harden typing lease refreshes

* fix(discord): pass real auth to plugin interactions

* fix(plugins): remove raw session binding runtime exposure

* fix(plugins): tighten interactive callback handling

* Plugins: gate conversation binding with approvals

* Plugins: migrate legacy plugin binding records

* Plugins/phone-control: update test command context

* Plugins: migrate legacy binding ids

* Plugins: migrate legacy codex session bindings

* Discord: fix plugin interaction handling

* Discord: support direct plugin conversation binds

* Plugins: preserve Discord command bind targets

* Tests: fix plugin binding and interactive fallout

* Discord: stabilize directory lookup tests

* Discord: route bound DMs to plugins

* Discord: restore plugin bindings after restart

* Telegram: persist detached plugin bindings

* Plugins: limit binding APIs to Telegram and Discord

* Plugins: harden bound conversation routing

* Plugins: fix extension target imports

* Plugins: fix Telegram runtime extension imports

* Plugins: format rebased binding handlers

* Discord: bind group DM interactions by channel

---------

Co-authored-by: Vincent Koc <[email protected]>
(cherry picked from commit aa1454d)

# Conflicts:
#	extensions/lobster/src/lobster-tool.test.ts
#	extensions/test-utils/plugin-api.ts
flowolforg added a commit to flowolforg/openclaw that referenced this pull request Apr 1, 2026
Add a broadcast call to hookRunner.runInboundClaim() after the
plugin-bound block in dispatch-from-config.ts. This lets global
plugin handlers (spam filters, rate limiters, group-chat observers
like listen-only) receive all unbound inbound events.

- Uses existing infrastructure from PR openclaw#45318
- hasHooks("inbound_claim") guard matches adjacent hook patterns
- Early-return when any listener returns { handled: true }

Closes openclaw#48434
flowolforg added a commit to flowolforg/openclaw that referenced this pull request Apr 1, 2026
Add a broadcast call to hookRunner.runInboundClaim() after the
plugin-bound block in dispatch-from-config.ts. This lets global
plugin handlers (spam filters, rate limiters, group-chat observers
like listen-only) receive all unbound inbound events.

- Uses existing infrastructure from PR openclaw#45318
- hasHooks("inbound_claim") guard matches adjacent hook patterns
- Early-return when any listener returns { handled: true }

Closes openclaw#48434
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: discord Channel integration: discord channel: telegram Channel integration: telegram extensions: lobster Extension: lobster extensions: phone-control maintainer Maintainer-authored PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants