refactor(hooks): centralize bundled subagent hook wiring

This commit is contained in:
Vincent Koc
2026-04-22 11:36:07 -07:00
parent d30f252c1b
commit bbcd185215
8 changed files with 97 additions and 91 deletions

View File

@@ -3,15 +3,7 @@ import {
loadBundledEntryExportSync,
} from "openclaw/plugin-sdk/channel-entry-contract";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/channel-entry-contract";
type FeishuSubagentHooksModule = typeof import("./api.js");
let feishuSubagentHooksPromise: Promise<FeishuSubagentHooksModule> | null = null;
function loadFeishuSubagentHooksModule() {
feishuSubagentHooksPromise ??= import("./api.js");
return feishuSubagentHooksPromise;
}
import { registerFeishuSubagentHooks } from "./subagent-hooks-api.js";
function registerFeishuDocTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
@@ -79,18 +71,7 @@ export default defineBundledChannelEntry({
exportName: "setFeishuRuntime",
},
registerFull(api) {
api.on("subagent_spawning", async (event, ctx) => {
const { handleFeishuSubagentSpawning } = await loadFeishuSubagentHooksModule();
return await handleFeishuSubagentSpawning(event, ctx);
});
api.on("subagent_delivery_target", async (event) => {
const { handleFeishuSubagentDeliveryTarget } = await loadFeishuSubagentHooksModule();
return handleFeishuSubagentDeliveryTarget(event);
});
api.on("subagent_ended", async (event) => {
const { handleFeishuSubagentEnded } = await loadFeishuSubagentHooksModule();
handleFeishuSubagentEnded(event);
});
registerFeishuSubagentHooks(api);
registerFeishuDocTools(api);
registerFeishuChatTools(api);
registerFeishuWikiTools(api);

View File

@@ -4,7 +4,7 @@ import {
registerHookHandlersForTest,
} from "../../../test/helpers/plugins/subagent-hooks.js";
import type { ClawdbotConfig, OpenClawPluginApi } from "../runtime-api.js";
import { registerFeishuSubagentHooks } from "./subagent-hooks.js";
import { registerFeishuSubagentHooks } from "../subagent-hooks-api.js";
import {
createFeishuThreadBindingManager,
__testing as threadBindingTesting,
@@ -51,7 +51,7 @@ describe("feishu subagent hook handlers", () => {
expect(result).toEqual({ status: "ok", threadBindingReady: true });
const deliveryTargetHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
expect(
await expect(
deliveryTargetHandler(
{
childSessionKey: "agent:main:subagent:child",
@@ -65,7 +65,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toEqual({
).resolves.toEqual({
origin: {
channel: "feishu",
accountId: "work",
@@ -89,7 +89,7 @@ describe("feishu subagent hook handlers", () => {
},
});
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:chat-dm-child",
@@ -103,7 +103,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toEqual({
).resolves.toEqual({
origin: {
channel: "feishu",
accountId: "work",
@@ -136,7 +136,7 @@ describe("feishu subagent hook handlers", () => {
);
expect(result).toEqual({ status: "ok", threadBindingReady: true });
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:topic-child",
@@ -151,7 +151,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toEqual({
).resolves.toEqual({
origin: {
channel: "feishu",
accountId: "work",
@@ -205,7 +205,7 @@ describe("feishu subagent hook handlers", () => {
parentConversationId: "oc_group_chat",
},
]);
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:sender-child",
@@ -220,7 +220,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toEqual({
).resolves.toEqual({
origin: {
channel: "feishu",
accountId: "work",
@@ -267,7 +267,7 @@ describe("feishu subagent hook handlers", () => {
{},
);
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:shared",
@@ -281,7 +281,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toEqual({
).resolves.toEqual({
origin: {
channel: "feishu",
accountId: "work",
@@ -335,7 +335,7 @@ describe("feishu subagent hook handlers", () => {
error: expect.stringContaining("direct messages or topic conversations"),
});
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:ambiguous-child",
@@ -350,7 +350,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toBeUndefined();
).resolves.toBeUndefined();
});
it("fails closed when both topic-level and sender-scoped requester bindings exist", async () => {
@@ -398,7 +398,7 @@ describe("feishu subagent hook handlers", () => {
error: expect.stringContaining("direct messages or topic conversations"),
});
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:mixed-topic-child",
@@ -413,7 +413,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toBeUndefined();
).resolves.toBeUndefined();
});
it("no-ops for non-Feishu channels and non-threaded spawns", async () => {
@@ -456,7 +456,7 @@ describe("feishu subagent hook handlers", () => {
),
).resolves.toBeUndefined();
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:child",
@@ -470,9 +470,9 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toBeUndefined();
).resolves.toBeUndefined();
expect(
await expect(
endedHandler(
{
targetSessionKey: "agent:main:subagent:child",
@@ -482,7 +482,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toBeUndefined();
).resolves.toBeUndefined();
});
it("returns an error for unsupported non-topic Feishu group conversations", async () => {
@@ -532,7 +532,7 @@ describe("feishu subagent hook handlers", () => {
{},
);
endedHandler(
await endedHandler(
{
targetSessionKey: "agent:main:subagent:child",
targetKind: "subagent",
@@ -542,7 +542,7 @@ describe("feishu subagent hook handlers", () => {
{},
);
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:child",
@@ -556,7 +556,7 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toBeUndefined();
).resolves.toBeUndefined();
});
it("fails closed when the Feishu monitor-owned binding manager is unavailable", async () => {
@@ -584,7 +584,7 @@ describe("feishu subagent hook handlers", () => {
error: expect.stringContaining("monitor is not active"),
});
expect(
await expect(
deliveryHandler(
{
childSessionKey: "agent:main:subagent:no-manager",
@@ -598,6 +598,6 @@ describe("feishu subagent hook handlers", () => {
},
{},
),
).toBeUndefined();
).resolves.toBeUndefined();
});
});

View File

@@ -2,7 +2,6 @@ import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "openclaw/plugin-sdk/text-runtime";
import type { OpenClawPluginApi } from "../runtime-api.js";
import { buildFeishuConversationId, parseFeishuConversationId } from "./conversation-id.js";
import { normalizeFeishuTarget } from "./targets.js";
import { getFeishuThreadBindingManager } from "./thread-bindings.js";
@@ -396,9 +395,3 @@ export function handleFeishuSubagentEnded(event: FeishuSubagentEndedEvent) {
const manager = getFeishuThreadBindingManager(event.accountId);
manager?.unbindBySessionKey(event.targetSessionKey);
}
export function registerFeishuSubagentHooks(api: OpenClawPluginApi) {
api.on("subagent_spawning", (event, ctx) => handleFeishuSubagentSpawning(event, ctx));
api.on("subagent_delivery_target", (event) => handleFeishuSubagentDeliveryTarget(event));
api.on("subagent_ended", (event) => handleFeishuSubagentEnded(event));
}

View File

@@ -0,0 +1,31 @@
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/channel-entry-contract";
type FeishuSubagentHooksModule = typeof import("./src/subagent-hooks.js");
let feishuSubagentHooksPromise: Promise<FeishuSubagentHooksModule> | null = null;
function loadFeishuSubagentHooksModule() {
feishuSubagentHooksPromise ??= import("./src/subagent-hooks.js");
return feishuSubagentHooksPromise;
}
export function registerFeishuSubagentHooks(api: OpenClawPluginApi): void {
api.on("subagent_spawning", async (event, ctx) => {
const { handleFeishuSubagentSpawning } = await loadFeishuSubagentHooksModule();
return await handleFeishuSubagentSpawning(event, ctx);
});
api.on("subagent_delivery_target", async (event) => {
const { handleFeishuSubagentDeliveryTarget } = await loadFeishuSubagentHooksModule();
return handleFeishuSubagentDeliveryTarget(event);
});
api.on("subagent_ended", async (event) => {
const { handleFeishuSubagentEnded } = await loadFeishuSubagentHooksModule();
handleFeishuSubagentEnded(event);
});
}
export {
handleFeishuSubagentDeliveryTarget,
handleFeishuSubagentEnded,
handleFeishuSubagentSpawning,
} from "./src/subagent-hooks.js";