openclaw - ✅(Solved) Fix feat: binding-level workspace override for per-conversation context isolation [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#52201Fetched 2026-04-08 01:14:23
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×1

Root Cause

The diff touches 55 files because there's no shared base class for channel handlers — each of the 19 extensions constructs MsgContext independently, so threading a new field requires a 1-line addition in each. 16 of the 55 files add exactly 1 line.

Fix Action

Fix / Workaround

Problem: When one agent is bound to multiple conversations (rooms/groups/DMs), they all share the same workspace. Memory and context from one conversation bleeds into another. The only workaround is duplicating the agent with a different config, which fragments identity and creates config sprawl.

PR fix notes

PR #61460: feat(messages): configurable outbound firewall with human-confirmation

Description (problem / solution / changelog)

Summary

Adds a configurable outbound messaging firewall. When messages.firewall.enabled is true, any outbound message tool send whose target is not listed in messages.firewall.selfTargets is held pending explicit human approval (allow-once / deny) via the gateway approval plugin before dispatch.

This addresses the trust boundary issue where an agent could autonomously message arbitrary external targets without operator knowledge.

Changes

  • src/config/types.messages.tsMessagingFirewallConfig type; firewall? field on MessagesConfig
  • src/config/zod-schema.session.ts — Zod schema for the new fields
  • src/config/schema.base.generated.ts — regenerated to include the new fields
  • src/infra/outbound/message-action-runner.tscheckMessageFirewall() async helper; called before every non-dryRun send dispatch; broadcast is covered because it recurses through runMessageAction with action: "send"
  • src/infra/outbound/message-firewall.test.ts — unit tests (8 cases): disabled, selfTarget bare, selfTarget channel-qualified, empty to short-circuit, non-self triggers approval, deny throws, allow-once resolves, unavailable infra throws
  • docs/gateway/configuration-reference.md + docs/concepts/messages.md — firewall config documented with example and field reference

Behaviour

{
  messages: {
    firewall: {
      enabled: true,
      selfTargets: [
        "-1001234567890",          // bare — matches any channel
        "telegram:-1009876543210"  // channel-qualified
      ]
    }
  }
}
  • Disabled by default (enabled: false) — no behaviour change for existing configs
  • selfTargets entries matched as bare value OR <channel>:<target>
  • Empty / missing to short-circuits without hitting approval (prevents false positives)
  • dryRun sends bypass the check (no real message dispatched)
  • Approval timeout: 120 s; abort signal propagated from caller

Closes #52201

Changed files

  • docs/concepts/messages.md (modified, +44/-0)
  • docs/gateway/configuration-reference.md (modified, +33/-0)
  • extensions/discord/src/doctor-contract.ts (modified, +3/-1)
  • extensions/matrix/src/matrix/client.test.ts (modified, +1/-3)
  • extensions/matrix/src/matrix/client/config.ts (modified, +1/-1)
  • extensions/matrix/src/matrix/monitor/handler.ts (modified, +1/-1)
  • extensions/slack/src/doctor-contract.ts (modified, +1/-4)
  • src/commands/doctor/shared/legacy-config-migrate.test.ts (modified, +1/-4)
  • src/config/schema.base.generated.ts (modified, +15/-0)
  • src/config/types.messages.ts (modified, +13/-0)
  • src/config/zod-schema.session.ts (modified, +7/-0)
  • src/infra/outbound/current-conversation-bindings.ts (modified, +1/-3)
  • src/infra/outbound/message-action-runner.firewall.test.ts (added, +199/-0)
  • src/infra/outbound/message-action-runner.ts (modified, +182/-1)
  • src/plugins/bundle-manifest.ts (modified, +1/-1)
  • ui/src/ui/app-render.helpers.ts (modified, +15/-11)
  • ui/src/ui/app-render.ts (modified, +1460/-1393)
  • ui/src/ui/chat/grouped-render.ts (modified, +47/-31)
  • ui/src/ui/views/agents-panels-overview.ts (modified, +15/-11)
  • ui/src/ui/views/agents-panels-status-files.ts (modified, +116/-78)
  • ui/src/ui/views/agents-panels-tools-skills.ts (modified, +135/-105)
  • ui/src/ui/views/agents.ts (modified, +173/-145)
  • ui/src/ui/views/channels.config.ts (modified, +20/-12)
  • ui/src/ui/views/channels.nostr-profile-form.ts (modified, +40/-24)
  • ui/src/ui/views/channels.nostr.ts (modified, +62/-38)
  • ui/src/ui/views/channels.shared.ts (modified, +5/-3)
  • ui/src/ui/views/channels.telegram.ts (modified, +20/-12)
  • ui/src/ui/views/channels.ts (modified, +26/-16)
  • ui/src/ui/views/channels.whatsapp.ts (modified, +10/-6)
  • ui/src/ui/views/chat.ts (modified, +109/-84)
  • ui/src/ui/views/config-form.node.ts (modified, +93/-76)
  • ui/src/ui/views/config-form.render.ts (modified, +49/-41)
  • ui/src/ui/views/config.ts (modified, +159/-115)
  • ui/src/ui/views/cron.ts (modified, +160/-102)
  • ui/src/ui/views/debug.ts (modified, +30/-16)
  • ui/src/ui/views/dreaming.ts (modified, +19/-15)
  • ui/src/ui/views/instances.ts (modified, +20/-12)
  • ui/src/ui/views/logs.ts (modified, +26/-18)
  • ui/src/ui/views/nodes-exec-approvals.ts (modified, +65/-39)
  • ui/src/ui/views/nodes.ts (modified, +73/-39)
  • ui/src/ui/views/overview.ts (modified, +25/-17)
  • ui/src/ui/views/sessions.ts (modified, +64/-47)
  • ui/src/ui/views/skills.ts (modified, +103/-61)

Code Example

bindings: [
  { agentId: "main", workspace: "~/projects/backend/",
    match: { channel: "matrix", peer: { kind: "group", id: "!room1" } } },
  { agentId: "main", workspace: "~/projects/infra/",
    match: { channel: "matrix", peer: { kind: "group", id: "!room2" } } }
]
RAW_BUFFERClick to expand / collapse

Feature Request

Problem: When one agent is bound to multiple conversations (rooms/groups/DMs), they all share the same workspace. Memory and context from one conversation bleeds into another. The only workaround is duplicating the agent with a different config, which fragments identity and creates config sprawl.

Proposed solution: Add an optional workspace field to route bindings:

bindings: [
  { agentId: "main", workspace: "~/projects/backend/",
    match: { channel: "matrix", peer: { kind: "group", id: "!room1" } } },
  { agentId: "main", workspace: "~/projects/infra/",
    match: { channel: "matrix", peer: { kind: "group", id: "!room2" } } }
]

Same agent, same model, same personality — different memory per conversation. Resolution: binding.workspace → agent.workspace → default.

Implementation Ready

We have a fully working implementation with:

  • workspaceOverride threaded through all 19 channel inbound handlers
  • Outbound persistence (Discord components retain workspace across interactions)
  • Rebind safety (override cleared on all 5 re-route paths)
  • Zod validation (.trim().min(1)) + runtime defense
  • 7 new tests (routing, precedence, coexistence, trimming, multi-binding, Discord rebind clearing)
  • 55 files changed, +329/−7 lines, single squashed commit on latest main

The diff touches 55 files because there's no shared base class for channel handlers — each of the 19 extensions constructs MsgContext independently, so threading a new field requires a 1-line addition in each. 16 of the 55 files add exactly 1 line.

Barnacle Auto-Close

We've opened this PR multiple times and it keeps getting auto-closed by barnacle bot, which flags it as a "dirty branch" due to the file count:

  • #52198 — clean rebase, detailed explanation of why 55 files, closed by barnacle
  • #52159 — clean rebase on latest main, all tests passing locally, Greptile gave 5/5 confidence ("safe to merge, no functional bugs found"), closed by barnacle

The branch is efe-arv:feat/binding-ws-v3 and is ready for review whenever a maintainer can override the auto-close or allowlist it.

Would love guidance on how to get this past barnacle — happy to split the PR if that helps, but it's genuinely one atomic feature.

extent analysis

Fix Plan

To implement the proposed solution, follow these steps:

  • Update the bindings schema to include an optional workspace field.
  • Modify the 19 channel inbound handlers to use the workspaceOverride field.
  • Implement outbound persistence to retain the workspace across interactions.
  • Ensure rebind safety by clearing the override on all re-route paths.
  • Add Zod validation and runtime defense for the workspace field.

Example code snippet to update the bindings schema:

const bindingsSchema = z.array(
  z.object({
    agentId: z.string(),
    workspace: z.string().optional(),
    match: z.object({
      channel: z.string(),
      peer: z.object({
        kind: z.string(),
        id: z.string(),
      }),
    }),
  })
);

Verification

To verify the fix, test the following scenarios:

  • Multiple conversations with the same agent and different workspaces.
  • Persistence of workspace across interactions.
  • Rebind safety and override clearing.

Extra Tips

  • Consider refactoring the channel handlers to use a shared base class to reduce code duplication.
  • Review the barnacle bot configuration to allowlist the efe-arv:feat/binding-ws-v3 branch or split the PR into smaller, more manageable pieces.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix feat: binding-level workspace override for per-conversation context isolation [1 pull requests, 1 participants]