Skip to content

Commit 89e3969

Browse files
authored
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
1 parent a472f98 commit 89e3969

21 files changed

+2988
-48
lines changed

docs/channels/feishu.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,75 @@ Feishu supports streaming replies via interactive cards. When enabled, the bot u
532532

533533
Set `streaming: false` to wait for the full reply before sending.
534534

535+
### ACP sessions
536+
537+
Feishu supports ACP for:
538+
539+
- DMs
540+
- group topic conversations
541+
542+
Feishu ACP is text-command driven. There are no native slash-command menus, so use `/acp ...` messages directly in the conversation.
543+
544+
#### Persistent ACP bindings
545+
546+
Use top-level typed ACP bindings to pin a Feishu DM or topic conversation to a persistent ACP session.
547+
548+
```json5
549+
{
550+
agents: {
551+
list: [
552+
{
553+
id: "codex",
554+
runtime: {
555+
type: "acp",
556+
acp: {
557+
agent: "codex",
558+
backend: "acpx",
559+
mode: "persistent",
560+
cwd: "/workspace/openclaw",
561+
},
562+
},
563+
},
564+
],
565+
},
566+
bindings: [
567+
{
568+
type: "acp",
569+
agentId: "codex",
570+
match: {
571+
channel: "feishu",
572+
accountId: "default",
573+
peer: { kind: "direct", id: "ou_1234567890" },
574+
},
575+
},
576+
{
577+
type: "acp",
578+
agentId: "codex",
579+
match: {
580+
channel: "feishu",
581+
accountId: "default",
582+
peer: { kind: "group", id: "oc_group_chat:topic:om_topic_root" },
583+
},
584+
acp: { label: "codex-feishu-topic" },
585+
},
586+
],
587+
}
588+
```
589+
590+
#### Thread-bound ACP spawn from chat
591+
592+
In a Feishu DM or topic conversation, you can spawn and bind an ACP session in place:
593+
594+
```text
595+
/acp spawn codex --thread here
596+
```
597+
598+
Notes:
599+
600+
- `--thread here` works for DMs and Feishu topics.
601+
- Follow-up messages in the bound DM/topic route directly to that ACP session.
602+
- v1 does not target generic non-topic group chats.
603+
535604
### Multi-agent routing
536605

537606
Use `bindings` to route Feishu DMs or groups to different agents.

extensions/feishu/index.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/feishu";
2+
import { describe, expect, it, vi } from "vitest";
3+
4+
const registerFeishuDocToolsMock = vi.hoisted(() => vi.fn());
5+
const registerFeishuChatToolsMock = vi.hoisted(() => vi.fn());
6+
const registerFeishuWikiToolsMock = vi.hoisted(() => vi.fn());
7+
const registerFeishuDriveToolsMock = vi.hoisted(() => vi.fn());
8+
const registerFeishuPermToolsMock = vi.hoisted(() => vi.fn());
9+
const registerFeishuBitableToolsMock = vi.hoisted(() => vi.fn());
10+
const setFeishuRuntimeMock = vi.hoisted(() => vi.fn());
11+
const registerFeishuSubagentHooksMock = vi.hoisted(() => vi.fn());
12+
13+
vi.mock("./src/docx.js", () => ({
14+
registerFeishuDocTools: registerFeishuDocToolsMock,
15+
}));
16+
17+
vi.mock("./src/chat.js", () => ({
18+
registerFeishuChatTools: registerFeishuChatToolsMock,
19+
}));
20+
21+
vi.mock("./src/wiki.js", () => ({
22+
registerFeishuWikiTools: registerFeishuWikiToolsMock,
23+
}));
24+
25+
vi.mock("./src/drive.js", () => ({
26+
registerFeishuDriveTools: registerFeishuDriveToolsMock,
27+
}));
28+
29+
vi.mock("./src/perm.js", () => ({
30+
registerFeishuPermTools: registerFeishuPermToolsMock,
31+
}));
32+
33+
vi.mock("./src/bitable.js", () => ({
34+
registerFeishuBitableTools: registerFeishuBitableToolsMock,
35+
}));
36+
37+
vi.mock("./src/runtime.js", () => ({
38+
setFeishuRuntime: setFeishuRuntimeMock,
39+
}));
40+
41+
vi.mock("./src/subagent-hooks.js", () => ({
42+
registerFeishuSubagentHooks: registerFeishuSubagentHooksMock,
43+
}));
44+
45+
describe("feishu plugin register", () => {
46+
it("registers the Feishu channel, tools, and subagent hooks", async () => {
47+
const { default: plugin } = await import("./index.js");
48+
const registerChannel = vi.fn();
49+
const api = {
50+
runtime: { log: vi.fn() },
51+
registerChannel,
52+
on: vi.fn(),
53+
config: {},
54+
} as unknown as OpenClawPluginApi;
55+
56+
plugin.register(api);
57+
58+
expect(setFeishuRuntimeMock).toHaveBeenCalledWith(api.runtime);
59+
expect(registerChannel).toHaveBeenCalledTimes(1);
60+
expect(registerFeishuSubagentHooksMock).toHaveBeenCalledWith(api);
61+
expect(registerFeishuDocToolsMock).toHaveBeenCalledWith(api);
62+
expect(registerFeishuChatToolsMock).toHaveBeenCalledWith(api);
63+
expect(registerFeishuWikiToolsMock).toHaveBeenCalledWith(api);
64+
expect(registerFeishuDriveToolsMock).toHaveBeenCalledWith(api);
65+
expect(registerFeishuPermToolsMock).toHaveBeenCalledWith(api);
66+
expect(registerFeishuBitableToolsMock).toHaveBeenCalledWith(api);
67+
});
68+
});

extensions/feishu/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { registerFeishuDocTools } from "./src/docx.js";
77
import { registerFeishuDriveTools } from "./src/drive.js";
88
import { registerFeishuPermTools } from "./src/perm.js";
99
import { setFeishuRuntime } from "./src/runtime.js";
10+
import { registerFeishuSubagentHooks } from "./src/subagent-hooks.js";
1011
import { registerFeishuWikiTools } from "./src/wiki.js";
1112

1213
export { monitorFeishuProvider } from "./src/monitor.js";
@@ -53,6 +54,7 @@ const plugin = {
5354
register(api: OpenClawPluginApi) {
5455
setFeishuRuntime(api.runtime);
5556
api.registerChannel({ plugin: feishuPlugin });
57+
registerFeishuSubagentHooks(api);
5658
registerFeishuDocTools(api);
5759
registerFeishuChatTools(api);
5860
registerFeishuWikiTools(api);

0 commit comments

Comments
 (0)