


















@@ -30,6 +30,34 @@ beforeEach(() => {
3030accessMocks.applyGoogleChatInboundAccessPolicy.mockReset();
3131});
323233+function createInboundClassificationHarness() {
34+const resolveAgentRoute = vi.fn(() => ({
35+agentId: "agent-1",
36+accountId: "work",
37+sessionKey: "session-1",
38+}));
39+const buildContext = vi.fn((payload: unknown) => payload);
40+const runTurn = vi.fn();
41+const core = {
42+logging: { shouldLogVerbose: () => false },
43+channel: {
44+routing: { resolveAgentRoute },
45+session: {
46+resolveStorePath: () => "/tmp/openclaw-googlechat-test",
47+readSessionUpdatedAt: () => undefined,
48+recordInboundSession: vi.fn(),
49+},
50+reply: {
51+resolveEnvelopeFormatOptions: () => ({}),
52+formatAgentEnvelope: ({ body }: { body: string }) => body,
53+dispatchReplyWithBufferedBlockDispatcher: vi.fn(),
54+},
55+inbound: { buildContext, run: runTurn },
56+},
57+} as unknown as GoogleChatCoreRuntime;
58+return { buildContext, core, resolveAgentRoute, runTurn };
59+}
60+3361describe("googlechat monitor bot loop protection", () => {
3462it("maps accepted bot-authored messages to shared channel-turn facts", () => {
3563expect(
@@ -159,6 +187,74 @@ describe("googlechat monitor bot loop protection", () => {
159187});
160188});
161189190+describe("googlechat monitor inbound space classification", () => {
191+const cases = [
192+{ name: "legacy DM", space: { type: "DM" }, peerKind: "direct" },
193+{ name: "modern direct message", space: { spaceType: "DIRECT_MESSAGE" }, peerKind: "direct" },
194+{ name: "single-user bot DM", space: { singleUserBotDm: true }, peerKind: "direct" },
195+{ name: "modern space", space: { spaceType: "SPACE" }, peerKind: "group" },
196+{ name: "modern group chat", space: { spaceType: "GROUP_CHAT" }, peerKind: "group" },
197+{
198+name: "modern space over legacy DM",
199+space: { type: "DM", spaceType: "SPACE" },
200+peerKind: "group",
201+},
202+] as const;
203+204+it.each(cases)("$name uses the expected access and route branch", async ({ space, peerKind }) => {
205+const { buildContext, core, resolveAgentRoute, runTurn } = createInboundClassificationHarness();
206+const account = {
207+accountId: "work",
208+config: {},
209+credentialSource: "inline",
210+} as ResolvedGoogleChatAccount;
211+const event = {
212+type: "MESSAGE",
213+space: { name: "spaces/CLASSIFY", ...space },
214+message: {
215+name: "spaces/CLASSIFY/messages/1",
216+text: "hello",
217+sender: { name: "users/alice", displayName: "Alice", type: "HUMAN" },
218+},
219+} satisfies GoogleChatEvent;
220+221+accessMocks.applyGoogleChatInboundAccessPolicy.mockResolvedValue({
222+ok: true,
223+commandAuthorized: undefined,
224+effectiveWasMentioned: undefined,
225+groupBotLoopProtection: undefined,
226+groupSystemPrompt: undefined,
227+});
228+229+await testing.processMessageWithPipeline({
230+ event,
231+ account,
232+config: {},
233+runtime: { error: vi.fn(), log: vi.fn() },
234+ core,
235+mediaMaxMb: 10,
236+});
237+238+const isGroup = peerKind === "group";
239+expect(accessMocks.applyGoogleChatInboundAccessPolicy).toHaveBeenCalledWith(
240+expect.objectContaining({ isGroup }),
241+);
242+expect(resolveAgentRoute).toHaveBeenCalledWith({
243+cfg: {},
244+channel: "googlechat",
245+accountId: "work",
246+peer: { kind: peerKind, id: "spaces/CLASSIFY" },
247+});
248+expect(buildContext).toHaveBeenCalledWith(
249+expect.objectContaining({
250+conversation: expect.objectContaining({ kind: isGroup ? "channel" : "direct" }),
251+extra: expect.objectContaining({ ChatType: isGroup ? "channel" : "direct" }),
252+}),
253+);
254+expect(runTurn).toHaveBeenCalledOnce();
255+});
256+});
257+162258describe("googlechat monitor direct messages", () => {
163259it("creates typing messages by default", async () => {
164260const runTurn = vi.fn();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。