mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 23:14:31 +00:00
fix(routing): treat group/channel peer.kind as equivalent (land #31135 by @Sid-Qin)
Landed-from: #31135 Contributor: @Sid-Qin Co-authored-by: Sid <sidqin0410@gmail.com>
This commit is contained in:
@@ -102,6 +102,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Routing/Binding peer-kind parity: treat `peer.kind` `group` and `channel` as equivalent for binding scope matching (while keeping `direct` separate) so Slack/public channel bindings do not silently fall through. Landed from contributor PR #31135 by @Sid-Qin. Thanks @Sid-Qin.
|
||||||
- Agents/FS workspace default: honor documented host file-tool default `tools.fs.workspaceOnly=false` when unset so host `write`/`edit` calls are not incorrectly workspace-restricted unless explicitly enabled. Landed from contributor PR #31128 by @SaucePackets. Thanks @SaucePackets.
|
- Agents/FS workspace default: honor documented host file-tool default `tools.fs.workspaceOnly=false` when unset so host `write`/`edit` calls are not incorrectly workspace-restricted unless explicitly enabled. Landed from contributor PR #31128 by @SaucePackets. Thanks @SaucePackets.
|
||||||
- Gateway/CLI session recovery: handle expired CLI session IDs gracefully by clearing stale session state and retrying without crashing gateway runs. Landed from contributor PR #31090 by @frankekn. Thanks @frankekn.
|
- Gateway/CLI session recovery: handle expired CLI session IDs gracefully by clearing stale session state and retrying without crashing gateway runs. Landed from contributor PR #31090 by @frankekn. Thanks @frankekn.
|
||||||
- Slack/Subagent completion delivery: stop forcing bound conversation IDs into `threadId` so Slack completion announces do not send invalid `thread_ts` for DMs/top-level channels. Landed from contributor PR #31105 by @stakeswky. Thanks @stakeswky.
|
- Slack/Subagent completion delivery: stop forcing bound conversation IDs into `threadId` so Slack completion announces do not send invalid `thread_ts` for DMs/top-level channels. Landed from contributor PR #31105 by @stakeswky. Thanks @stakeswky.
|
||||||
|
|||||||
@@ -547,6 +547,74 @@ describe("backward compatibility: peer.kind dm → direct", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("backward compatibility: peer.kind group ↔ channel", () => {
|
||||||
|
test("config group binding matches runtime channel scope", () => {
|
||||||
|
const cfg: OpenClawConfig = {
|
||||||
|
bindings: [
|
||||||
|
{
|
||||||
|
agentId: "slack-group-agent",
|
||||||
|
match: {
|
||||||
|
channel: "slack",
|
||||||
|
peer: { kind: "group", id: "C123456" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const route = resolveAgentRoute({
|
||||||
|
cfg,
|
||||||
|
channel: "slack",
|
||||||
|
accountId: null,
|
||||||
|
peer: { kind: "channel", id: "C123456" },
|
||||||
|
});
|
||||||
|
expect(route.agentId).toBe("slack-group-agent");
|
||||||
|
expect(route.matchedBy).toBe("binding.peer");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("config channel binding matches runtime group scope", () => {
|
||||||
|
const cfg: OpenClawConfig = {
|
||||||
|
bindings: [
|
||||||
|
{
|
||||||
|
agentId: "slack-channel-agent",
|
||||||
|
match: {
|
||||||
|
channel: "slack",
|
||||||
|
peer: { kind: "channel", id: "C123456" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const route = resolveAgentRoute({
|
||||||
|
cfg,
|
||||||
|
channel: "slack",
|
||||||
|
accountId: null,
|
||||||
|
peer: { kind: "group", id: "C123456" },
|
||||||
|
});
|
||||||
|
expect(route.agentId).toBe("slack-channel-agent");
|
||||||
|
expect(route.matchedBy).toBe("binding.peer");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("group/channel compatibility does not match direct peer kind", () => {
|
||||||
|
const cfg: OpenClawConfig = {
|
||||||
|
bindings: [
|
||||||
|
{
|
||||||
|
agentId: "group-only-agent",
|
||||||
|
match: {
|
||||||
|
channel: "slack",
|
||||||
|
peer: { kind: "group", id: "C123456" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const route = resolveAgentRoute({
|
||||||
|
cfg,
|
||||||
|
channel: "slack",
|
||||||
|
accountId: null,
|
||||||
|
peer: { kind: "direct", id: "C123456" },
|
||||||
|
});
|
||||||
|
expect(route.agentId).toBe("main");
|
||||||
|
expect(route.matchedBy).toBe("default");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("role-based agent routing", () => {
|
describe("role-based agent routing", () => {
|
||||||
type DiscordBinding = NonNullable<OpenClawConfig["bindings"]>[number];
|
type DiscordBinding = NonNullable<OpenClawConfig["bindings"]>[number];
|
||||||
|
|
||||||
|
|||||||
@@ -262,12 +262,24 @@ function hasRolesConstraint(match: NormalizedBindingMatch): boolean {
|
|||||||
return Boolean(match.roles);
|
return Boolean(match.roles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function peerKindMatches(bindingKind: ChatType, scopeKind: ChatType): boolean {
|
||||||
|
if (bindingKind === scopeKind) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const both = new Set([bindingKind, scopeKind]);
|
||||||
|
return both.has("group") && both.has("channel");
|
||||||
|
}
|
||||||
|
|
||||||
function matchesBindingScope(match: NormalizedBindingMatch, scope: BindingScope): boolean {
|
function matchesBindingScope(match: NormalizedBindingMatch, scope: BindingScope): boolean {
|
||||||
if (match.peer.state === "invalid") {
|
if (match.peer.state === "invalid") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (match.peer.state === "valid") {
|
if (match.peer.state === "valid") {
|
||||||
if (!scope.peer || scope.peer.kind !== match.peer.kind || scope.peer.id !== match.peer.id) {
|
if (
|
||||||
|
!scope.peer ||
|
||||||
|
!peerKindMatches(match.peer.kind, scope.peer.kind) ||
|
||||||
|
scope.peer.id !== match.peer.id
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user