




















1+import { afterEach, describe, expect, it } from "vitest";
2+import { getMatchingMessagingToolReplyTargets } from "../auto-reply/reply/reply-payloads-dedupe.js";
3+import { setActivePluginRegistry } from "../plugins/runtime.js";
4+import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
5+import {
6+extractMessagingToolSend,
7+extractMessagingToolSendResult,
8+} from "./embedded-agent-subscribe.tools.js";
9+10+const PARTIAL_RESULT_PROVIDER = "partialthreadprovider";
11+12+function createPartialResultPlugin(): unknown {
13+return {
14+ ...createChannelTestPluginBase({ id: PARTIAL_RESULT_PROVIDER }),
15+actions: {
16+extractToolSend: ({ args }: { args: Record<string, unknown> }) =>
17+args.action === "send" && typeof args.to === "string"
18+ ? { to: args.to, threadImplicit: true }
19+ : null,
20+extractToolSendResult: ({ result }: { result: unknown }) => {
21+const toolSend = (result as { details?: { toolSend?: Record<string, unknown> } })?.details
22+?.toolSend;
23+const to = typeof toolSend?.to === "string" ? toolSend.to : undefined;
24+if (!to) {
25+return null;
26+}
27+const threadId = typeof toolSend?.threadId === "string" ? toolSend.threadId : undefined;
28+return {
29+ to,
30+ ...(threadId ? { threadId } : {}),
31+ ...(toolSend?.threadImplicit === true ? { threadImplicit: true } : {}),
32+ ...(toolSend?.threadSuppressed === true ? { threadSuppressed: true } : {}),
33+};
34+},
35+},
36+threading: {
37+resolveAutoThreadId: ({ toolContext }: { toolContext?: { currentThreadTs?: string } }) =>
38+toolContext?.currentThreadTs,
39+},
40+};
41+}
42+43+function registerPartialResultProvider(): void {
44+setActivePluginRegistry(
45+createTestRegistry([
46+{ pluginId: PARTIAL_RESULT_PROVIDER, source: "test", plugin: createPartialResultPlugin() },
47+]),
48+);
49+}
50+51+describe("extractMessagingToolSendResult thread evidence", () => {
52+afterEach(() => {
53+setActivePluginRegistry(createTestRegistry());
54+});
55+56+it("preserves implicit thread evidence when the provider result omits it", () => {
57+registerPartialResultProvider();
58+59+const pending = extractMessagingToolSend(
60+"message",
61+{ action: "send", provider: PARTIAL_RESULT_PROVIDER, to: "channel:abc", message: "answer" },
62+{
63+currentChannelId: "channel:abc",
64+currentMessagingTarget: "channel:abc",
65+currentThreadId: "root-1",
66+replyToMode: "all",
67+},
68+);
69+expect(pending?.threadImplicit).toBe(true);
70+expect(pending?.threadId).toBe("root-1");
71+72+const confirmed = extractMessagingToolSendResult(pending!, {
73+details: { toolSend: { to: "channel:abc" } },
74+});
75+expect(confirmed.threadImplicit).toBe(true);
76+expect(confirmed.threadId).toBe("root-1");
77+78+const matches = getMatchingMessagingToolReplyTargets({
79+messageProvider: PARTIAL_RESULT_PROVIDER,
80+originatingTo: "channel:abc",
81+originatingThreadId: "root-1",
82+messagingToolSentTargets: [confirmed],
83+});
84+expect(matches).toHaveLength(1);
85+});
86+87+it("lets an explicit provider-reported thread override pending implicit evidence", () => {
88+registerPartialResultProvider();
89+90+const confirmed = extractMessagingToolSendResult(
91+{
92+tool: "message",
93+provider: PARTIAL_RESULT_PROVIDER,
94+to: "channel:abc",
95+threadImplicit: true,
96+},
97+{ details: { toolSend: { to: "channel:abc", threadId: "root-9" } } },
98+);
99+expect(confirmed.threadId).toBe("root-9");
100+expect(confirmed.threadImplicit).toBeUndefined();
101+});
102+103+it.each([
104+{
105+name: "provider suppression replaces pending implicit evidence",
106+pending: {
107+threadId: "root-1",
108+threadImplicit: true,
109+},
110+result: {
111+threadSuppressed: true,
112+},
113+expected: {
114+threadId: undefined,
115+threadImplicit: undefined,
116+threadSuppressed: true,
117+},
118+},
119+{
120+name: "provider implicit evidence replaces pending suppression",
121+pending: {
122+threadSuppressed: true,
123+},
124+result: {
125+threadImplicit: true,
126+},
127+expected: {
128+threadId: undefined,
129+threadImplicit: true,
130+threadSuppressed: undefined,
131+},
132+},
133+{
134+name: "a partial result preserves pending suppression",
135+pending: {
136+threadSuppressed: true,
137+},
138+result: {},
139+expected: {
140+threadId: undefined,
141+threadImplicit: undefined,
142+threadSuppressed: true,
143+},
144+},
145+])("$name", ({ pending, result, expected }) => {
146+registerPartialResultProvider();
147+148+const confirmed = extractMessagingToolSendResult(
149+{
150+tool: "message",
151+provider: PARTIAL_RESULT_PROVIDER,
152+to: "channel:abc",
153+ ...pending,
154+},
155+{ details: { toolSend: { to: "channel:abc", ...result } } },
156+);
157+158+expect({
159+threadId: confirmed.threadId,
160+threadImplicit: confirmed.threadImplicit,
161+threadSuppressed: confirmed.threadSuppressed,
162+}).toEqual(expected);
163+});
164+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。