






















@@ -5,13 +5,19 @@ import register from "./index.js";
55describe("thread-ownership plugin", () => {
66const hooks: Record<string, Function> = {};
77const fetchMock = vi.fn() as unknown as typeof globalThis.fetch;
8+let configFile: Record<string, unknown> = {};
89const api = {
910pluginConfig: {},
1011config: {
1112agents: {
1213list: [{ id: "test-agent", default: true, identity: { name: "TestBot" } }],
1314},
1415},
16+runtime: {
17+config: {
18+loadConfig: () => configFile,
19+},
20+},
1521id: "thread-ownership",
1622name: "Thread Ownership",
1723logger: { info: vi.fn(), warn: vi.fn(), debug: vi.fn() },
@@ -26,6 +32,9 @@ describe("thread-ownership plugin", () => {
2632delete hooks[key];
2733}
2834api.pluginConfig = {};
35+configFile = {
36+agents: api.config.agents,
37+};
29383039process.env.SLACK_FORWARDER_URL = "http://localhost:8750";
3140process.env.SLACK_BOT_USER_ID = "U999";
@@ -173,6 +182,35 @@ describe("thread-ownership plugin", () => {
173182);
174183});
175184185+it("uses live runtime allowlists when deciding whether to claim ownership", async () => {
186+api.pluginConfig = { abTestChannels: ["C123"] };
187+configFile = {
188+ ...configFile,
189+plugins: {
190+entries: {
191+"thread-ownership": {
192+config: {
193+abTestChannels: ["C999"],
194+},
195+},
196+},
197+},
198+};
199+register.register(api as unknown as OpenClawPluginApi);
200+201+const result = await hooks.message_sending(
202+{
203+content: "hello",
204+replyToId: "1234.5678",
205+to: "C123",
206+},
207+{ channelId: "slack", conversationId: "C123" },
208+);
209+210+expect(result).toBeUndefined();
211+expect(globalThis.fetch).not.toHaveBeenCalled();
212+});
213+176214it("cancels when thread owned by another agent", async () => {
177215vi.mocked(globalThis.fetch).mockResolvedValue(
178216new Response(JSON.stringify({ owner: "other-agent" }), { status: 409 }),
@@ -326,6 +364,57 @@ describe("thread-ownership plugin", () => {
326364expect(globalThis.fetch).not.toHaveBeenCalled();
327365});
328366367+it("uses the live runtime agent identity for ownership claims", async () => {
368+configFile = {
369+ ...configFile,
370+agents: {
371+list: [{ id: "live-agent", default: true, identity: { name: "LiveBot" } }],
372+},
373+};
374+vi.mocked(globalThis.fetch).mockResolvedValue(
375+new Response(JSON.stringify({ owner: "live-agent" }), { status: 200 }),
376+);
377+378+await hooks.message_sending(
379+{ content: "On it!", replyToId: "8888.0005", metadata: { channelId: "C789" }, to: "C789" },
380+{ channelId: "slack", conversationId: "C789" },
381+);
382+383+expect(globalThis.fetch).toHaveBeenCalledWith(
384+"http://localhost:8750/api/v1/ownership/C789/8888.0005",
385+expect.objectContaining({
386+method: "POST",
387+body: JSON.stringify({ agent_id: "live-agent" }),
388+}),
389+);
390+});
391+392+it("uses the live runtime agent name for mention tracking", async () => {
393+configFile = {
394+ ...configFile,
395+agents: {
396+list: [{ id: "live-agent", default: true, identity: { name: "LiveBot" } }],
397+},
398+};
399+400+await hooks.message_received(
401+{
402+content: "hey @LiveBot help",
403+threadId: "8888.0006",
404+metadata: { channelId: "C789" },
405+},
406+{ channelId: "slack", conversationId: "C789" },
407+);
408+409+const result = await hooks.message_sending(
410+{ content: "On it!", replyToId: "8888.0006", metadata: { channelId: "C789" }, to: "C789" },
411+{ channelId: "slack", conversationId: "C789" },
412+);
413+414+expect(result).toBeUndefined();
415+expect(globalThis.fetch).not.toHaveBeenCalled();
416+});
417+329418it("does not treat superset handles as agent-name mentions", async () => {
330419await hooks.message_received(
331420{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。