diff --git a/docs/channels/feishu.md b/docs/channels/feishu.md index 4642dc262ad..716a47545dc 100644 --- a/docs/channels/feishu.md +++ b/docs/channels/feishu.md @@ -584,6 +584,10 @@ Full configuration: [Gateway configuration](/gateway/configuration) | `channels.feishu.blockStreaming` | Completed-block reply streaming | `false` | | `channels.feishu.typingIndicator` | Send typing reactions | `true` | | `channels.feishu.resolveSenderNames` | Resolve sender display names | `true` | +| `channels.feishu.tools.bitable` | Enable Bitable/Base tools | `true` | +| `channels.feishu.tools.base` | Alias for `channels.feishu.tools.bitable`; explicit `bitable` wins when both set | `true` | +| `channels.feishu.accounts..tools.bitable` | Per-account Bitable/Base tool gate | inherited | +| `channels.feishu.accounts..tools.base` | Per-account alias for `tools.bitable` | inherited | --- diff --git a/extensions/feishu/src/accounts.ts b/extensions/feishu/src/accounts.ts index b19d6466de6..05ef71319b0 100644 --- a/extensions/feishu/src/accounts.ts +++ b/extensions/feishu/src/accounts.ts @@ -198,7 +198,10 @@ function mergeFeishuAccountConfig(cfg: ClawdbotConfig, accountId: string): Feish if (merged.tools === undefined && topTools !== undefined) { return { ...merged, tools: topTools }; } - if (topTools?.bitable === false || topTools?.base === false) { + if ( + topTools?.bitable === false || + (topTools?.bitable === undefined && topTools?.base === false) + ) { return { ...merged, tools: { diff --git a/extensions/feishu/src/tool-account-routing.test.ts b/extensions/feishu/src/tool-account-routing.test.ts index a12740ffaf6..087155ae0b1 100644 --- a/extensions/feishu/src/tool-account-routing.test.ts +++ b/extensions/feishu/src/tool-account-routing.test.ts @@ -274,6 +274,21 @@ describe("feishu tool account routing", () => { ).toBe(0); }); + test("explicit top-level bitable enable wins over disabled base alias in account merge", async () => { + const { api, resolveTool } = createToolFactoryHarness( + createConfig({ + topTools: { bitable: true, base: false }, + toolsA: { bitable: true }, + }), + ); + registerFeishuBitableTools(api); + + const tool = resolveTool("feishu_bitable_get_meta", { agentAccountId: "a" }); + await tool.execute("call", { url: "invalid-url" }); + + expect(createFeishuClientMock.mock.calls.at(-1)?.[0]?.appId).toBe("app-a"); + }); + test("bitable tools are not registered when account bitable configs disable them", async () => { const { api, registered, resolveTool } = createToolFactoryHarness( createConfig({