@openclaw/googlechat 2026.5.2-beta.2 → 2026.5.3-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/api-C67jv0ow.js +627 -0
  2. package/dist/api.js +3 -0
  3. package/dist/channel-DE2hroMn.js +682 -0
  4. package/dist/channel-config-api.js +5 -0
  5. package/dist/channel-plugin-api.js +2 -0
  6. package/dist/channel.runtime-CoSLc-4R.js +920 -0
  7. package/dist/contract-api.js +3 -0
  8. package/dist/doctor-contract-CG1sLToP.js +151 -0
  9. package/dist/doctor-contract-api.js +2 -0
  10. package/dist/index.js +22 -0
  11. package/dist/runtime-api-wkIdfwqY.js +30 -0
  12. package/dist/runtime-api.js +2 -0
  13. package/dist/secret-contract-DtQ_IO7J.js +99 -0
  14. package/dist/secret-contract-api.js +2 -0
  15. package/dist/setup-entry.js +15 -0
  16. package/dist/setup-plugin-api.js +75 -0
  17. package/dist/setup-surface-CofP-Gg9.js +309 -0
  18. package/dist/test-api.js +3 -0
  19. package/package.json +14 -6
  20. package/api.ts +0 -3
  21. package/channel-config-api.ts +0 -1
  22. package/channel-plugin-api.ts +0 -1
  23. package/contract-api.ts +0 -5
  24. package/doctor-contract-api.ts +0 -1
  25. package/index.ts +0 -20
  26. package/runtime-api.ts +0 -60
  27. package/secret-contract-api.ts +0 -5
  28. package/setup-entry.ts +0 -13
  29. package/setup-plugin-api.ts +0 -3
  30. package/src/accounts.ts +0 -169
  31. package/src/actions.test.ts +0 -265
  32. package/src/actions.ts +0 -227
  33. package/src/api.ts +0 -322
  34. package/src/approval-auth.test.ts +0 -24
  35. package/src/approval-auth.ts +0 -32
  36. package/src/auth.ts +0 -209
  37. package/src/channel-config.test.ts +0 -39
  38. package/src/channel.adapters.ts +0 -293
  39. package/src/channel.deps.runtime.ts +0 -28
  40. package/src/channel.runtime.ts +0 -17
  41. package/src/channel.setup.ts +0 -98
  42. package/src/channel.test.ts +0 -695
  43. package/src/channel.ts +0 -256
  44. package/src/config-schema.test.ts +0 -16
  45. package/src/config-schema.ts +0 -3
  46. package/src/doctor-contract.test.ts +0 -75
  47. package/src/doctor-contract.ts +0 -182
  48. package/src/doctor.ts +0 -57
  49. package/src/gateway.ts +0 -63
  50. package/src/google-auth.runtime.test.ts +0 -462
  51. package/src/google-auth.runtime.ts +0 -539
  52. package/src/group-policy.ts +0 -17
  53. package/src/monitor-access.test.ts +0 -443
  54. package/src/monitor-access.ts +0 -405
  55. package/src/monitor-reply-delivery.ts +0 -156
  56. package/src/monitor-routing.ts +0 -55
  57. package/src/monitor-types.ts +0 -33
  58. package/src/monitor-webhook.test.ts +0 -446
  59. package/src/monitor-webhook.ts +0 -285
  60. package/src/monitor.reply-delivery.test.ts +0 -139
  61. package/src/monitor.ts +0 -428
  62. package/src/monitor.webhook-routing.test.ts +0 -252
  63. package/src/runtime.ts +0 -9
  64. package/src/secret-contract.test.ts +0 -60
  65. package/src/secret-contract.ts +0 -161
  66. package/src/sender-allow.ts +0 -46
  67. package/src/setup-core.ts +0 -40
  68. package/src/setup-surface.ts +0 -236
  69. package/src/setup.test.ts +0 -560
  70. package/src/targets.test.ts +0 -421
  71. package/src/targets.ts +0 -66
  72. package/src/types.config.ts +0 -3
  73. package/src/types.ts +0 -73
  74. package/test-api.ts +0 -2
  75. package/tsconfig.json +0 -16
@@ -1,443 +0,0 @@
1
- import { beforeAll, describe, expect, it, vi } from "vitest";
2
-
3
- const createChannelPairingController = vi.hoisted(() => vi.fn());
4
- const evaluateGroupRouteAccessForPolicy = vi.hoisted(() => vi.fn());
5
- const isDangerousNameMatchingEnabled = vi.hoisted(() => vi.fn());
6
- const resolveAllowlistProviderRuntimeGroupPolicy = vi.hoisted(() => vi.fn());
7
- const resolveDefaultGroupPolicy = vi.hoisted(() => vi.fn());
8
- const resolveDmGroupAccessWithLists = vi.hoisted(() => vi.fn());
9
- const resolveInboundMentionDecision = vi.hoisted(() => vi.fn());
10
- const resolveSenderScopedGroupPolicy = vi.hoisted(() => vi.fn());
11
- const warnMissingProviderGroupPolicyFallbackOnce = vi.hoisted(() => vi.fn());
12
- const sendGoogleChatMessage = vi.hoisted(() => vi.fn());
13
-
14
- vi.mock("openclaw/plugin-sdk/channel-inbound", () => ({
15
- resolveInboundMentionDecision,
16
- }));
17
-
18
- vi.mock("../runtime-api.js", () => ({
19
- GROUP_POLICY_BLOCKED_LABEL: { space: "space" },
20
- createChannelPairingController,
21
- evaluateGroupRouteAccessForPolicy,
22
- isDangerousNameMatchingEnabled,
23
- resolveAllowlistProviderRuntimeGroupPolicy,
24
- resolveDefaultGroupPolicy,
25
- resolveDmGroupAccessWithLists,
26
- resolveSenderScopedGroupPolicy,
27
- warnMissingProviderGroupPolicyFallbackOnce,
28
- }));
29
-
30
- vi.mock("./api.js", () => ({
31
- sendGoogleChatMessage,
32
- }));
33
-
34
- function createCore() {
35
- return {
36
- channel: {
37
- commands: {
38
- shouldComputeCommandAuthorized: vi.fn(() => false),
39
- resolveCommandAuthorizedFromAuthorizers: vi.fn(() => false),
40
- shouldHandleTextCommands: vi.fn(() => false),
41
- isControlCommandMessage: vi.fn(() => false),
42
- },
43
- text: {
44
- hasControlCommand: vi.fn(() => false),
45
- },
46
- },
47
- };
48
- }
49
-
50
- function primeCommonDefaults() {
51
- isDangerousNameMatchingEnabled.mockReturnValue(false);
52
- resolveDefaultGroupPolicy.mockReturnValue("allowlist");
53
- resolveAllowlistProviderRuntimeGroupPolicy.mockReturnValue({
54
- groupPolicy: "allowlist",
55
- providerMissingFallbackApplied: false,
56
- });
57
- resolveSenderScopedGroupPolicy.mockImplementation(({ groupPolicy }) => groupPolicy);
58
- evaluateGroupRouteAccessForPolicy.mockReturnValue({
59
- allowed: true,
60
- });
61
- warnMissingProviderGroupPolicyFallbackOnce.mockReturnValue(undefined);
62
- }
63
-
64
- const baseAccessConfig = {
65
- channels: { googlechat: {} },
66
- commands: { useAccessGroups: true },
67
- } as const;
68
-
69
- const defaultSender = {
70
- senderId: "users/alice",
71
- senderName: "Alice",
72
- senderEmail: "alice@example.com",
73
- } as const;
74
-
75
- let applyGoogleChatInboundAccessPolicy: typeof import("./monitor-access.js").applyGoogleChatInboundAccessPolicy;
76
-
77
- function allowInboundGroupTraffic(options?: {
78
- effectiveGroupAllowFrom?: string[];
79
- effectiveWasMentioned?: boolean;
80
- }) {
81
- createChannelPairingController.mockReturnValue({
82
- readAllowFromStore: vi.fn(async () => []),
83
- issueChallenge: vi.fn(),
84
- });
85
- resolveDmGroupAccessWithLists.mockReturnValue({
86
- decision: "allow",
87
- effectiveAllowFrom: [],
88
- effectiveGroupAllowFrom: options?.effectiveGroupAllowFrom ?? ["users/alice"],
89
- });
90
- resolveInboundMentionDecision.mockReturnValue({
91
- shouldSkip: false,
92
- effectiveWasMentioned: options?.effectiveWasMentioned ?? true,
93
- });
94
- }
95
-
96
- async function applyInboundAccessPolicy(
97
- overrides: Partial<Parameters<typeof applyGoogleChatInboundAccessPolicy>[0]>,
98
- ) {
99
- return applyGoogleChatInboundAccessPolicy({
100
- account: {
101
- accountId: "default",
102
- config: {},
103
- } as never,
104
- config: baseAccessConfig as never,
105
- core: createCore() as never,
106
- space: { name: "spaces/AAA", displayName: "Team Room" } as never,
107
- message: { annotations: [] } as never,
108
- isGroup: true,
109
- rawBody: "hello team",
110
- logVerbose: vi.fn(),
111
- ...defaultSender,
112
- ...overrides,
113
- } as never);
114
- }
115
-
116
- describe("googlechat inbound access policy", () => {
117
- beforeAll(async () => {
118
- ({ applyGoogleChatInboundAccessPolicy } = await import("./monitor-access.js"));
119
- });
120
-
121
- it("issues a pairing challenge for unauthorized DMs in pairing mode", async () => {
122
- primeCommonDefaults();
123
- const issueChallenge = vi.fn(async ({ onCreated, sendPairingReply }) => {
124
- onCreated?.();
125
- await sendPairingReply("pairing text");
126
- });
127
- createChannelPairingController.mockReturnValue({
128
- readAllowFromStore: vi.fn(async () => []),
129
- issueChallenge,
130
- });
131
- resolveDmGroupAccessWithLists.mockReturnValue({
132
- decision: "pairing",
133
- reason: "pairing_required",
134
- effectiveAllowFrom: [],
135
- effectiveGroupAllowFrom: [],
136
- });
137
- sendGoogleChatMessage.mockResolvedValue({ ok: true });
138
-
139
- const statusSink = vi.fn();
140
- const logVerbose = vi.fn();
141
-
142
- await expect(
143
- applyGoogleChatInboundAccessPolicy({
144
- account: {
145
- accountId: "default",
146
- config: {
147
- dm: { policy: "pairing" },
148
- },
149
- } as never,
150
- config: {
151
- channels: { googlechat: {} },
152
- } as never,
153
- core: createCore() as never,
154
- space: { name: "spaces/AAA", displayName: "DM" } as never,
155
- message: { annotations: [] } as never,
156
- isGroup: false,
157
- senderId: "users/abc",
158
- senderName: "Alice",
159
- senderEmail: "alice@example.com",
160
- rawBody: "hello",
161
- statusSink,
162
- logVerbose,
163
- }),
164
- ).resolves.toEqual({ ok: false });
165
-
166
- expect(issueChallenge).toHaveBeenCalledTimes(1);
167
- expect(sendGoogleChatMessage).toHaveBeenCalledWith({
168
- account: expect.anything(),
169
- space: "spaces/AAA",
170
- text: "pairing text",
171
- });
172
- expect(statusSink).toHaveBeenCalledWith(
173
- expect.objectContaining({
174
- lastOutboundAt: expect.any(Number),
175
- }),
176
- );
177
- });
178
-
179
- it("allows group traffic when sender and mention gates pass", async () => {
180
- primeCommonDefaults();
181
- allowInboundGroupTraffic();
182
- const core = createCore();
183
- core.channel.commands.shouldComputeCommandAuthorized.mockReturnValue(true);
184
- core.channel.commands.resolveCommandAuthorizedFromAuthorizers.mockReturnValue(true);
185
-
186
- await expect(
187
- applyInboundAccessPolicy({
188
- account: {
189
- accountId: "default",
190
- config: {
191
- botUser: "users/app-bot",
192
- groups: {
193
- "spaces/AAA": {
194
- users: ["users/alice"],
195
- requireMention: true,
196
- systemPrompt: " group prompt ",
197
- },
198
- },
199
- },
200
- } as never,
201
- core: core as never,
202
- message: {
203
- annotations: [
204
- {
205
- type: "USER_MENTION",
206
- userMention: { user: { name: "users/app-bot" } },
207
- },
208
- ],
209
- } as never,
210
- }),
211
- ).resolves.toEqual({
212
- ok: true,
213
- commandAuthorized: true,
214
- effectiveWasMentioned: true,
215
- groupSystemPrompt: "group prompt",
216
- });
217
- });
218
-
219
- it("allows group traffic from generic message sender access groups", async () => {
220
- primeCommonDefaults();
221
- allowInboundGroupTraffic();
222
-
223
- await expect(
224
- applyInboundAccessPolicy({
225
- config: {
226
- ...baseAccessConfig,
227
- accessGroups: {
228
- operators: {
229
- type: "message.senders",
230
- members: {
231
- googlechat: ["users/alice"],
232
- },
233
- },
234
- },
235
- } as never,
236
- account: {
237
- accountId: "default",
238
- config: {
239
- groups: {
240
- "spaces/AAA": {
241
- users: ["accessGroup:operators"],
242
- requireMention: false,
243
- },
244
- },
245
- },
246
- } as never,
247
- }),
248
- ).resolves.toMatchObject({
249
- ok: true,
250
- });
251
- });
252
-
253
- it("expands generic message sender access groups before DM access checks", async () => {
254
- primeCommonDefaults();
255
- const readAllowFromStore = vi.fn(async () => []);
256
- createChannelPairingController.mockReturnValue({
257
- readAllowFromStore,
258
- issueChallenge: vi.fn(),
259
- });
260
- resolveDmGroupAccessWithLists.mockReturnValue({
261
- decision: "allow",
262
- effectiveAllowFrom: ["accessGroup:operators", "users/alice"],
263
- effectiveGroupAllowFrom: [],
264
- });
265
-
266
- await expect(
267
- applyInboundAccessPolicy({
268
- isGroup: false,
269
- config: {
270
- ...baseAccessConfig,
271
- accessGroups: {
272
- operators: {
273
- type: "message.senders",
274
- members: {
275
- googlechat: ["users/alice"],
276
- },
277
- },
278
- },
279
- } as never,
280
- account: {
281
- accountId: "default",
282
- config: {
283
- dm: {
284
- policy: "allowlist",
285
- allowFrom: ["accessGroup:operators"],
286
- },
287
- },
288
- } as never,
289
- }),
290
- ).resolves.toMatchObject({
291
- ok: true,
292
- });
293
-
294
- expect(resolveDmGroupAccessWithLists).toHaveBeenCalledWith(
295
- expect.objectContaining({
296
- allowFrom: ["accessGroup:operators", "users/alice"],
297
- }),
298
- );
299
- expect(readAllowFromStore).not.toHaveBeenCalled();
300
- });
301
-
302
- it("preserves allowlist group policy when a routed space has no sender allowlist", async () => {
303
- primeCommonDefaults();
304
- allowInboundGroupTraffic({
305
- effectiveGroupAllowFrom: [],
306
- effectiveWasMentioned: false,
307
- });
308
- resolveSenderScopedGroupPolicy.mockReturnValue("open");
309
- resolveSenderScopedGroupPolicy.mockClear();
310
- resolveDmGroupAccessWithLists.mockClear();
311
-
312
- await expect(
313
- applyInboundAccessPolicy({
314
- account: {
315
- accountId: "default",
316
- config: {
317
- groups: {
318
- "spaces/AAA": {
319
- enabled: true,
320
- },
321
- },
322
- },
323
- } as never,
324
- }),
325
- ).resolves.toEqual({
326
- ok: true,
327
- commandAuthorized: undefined,
328
- effectiveWasMentioned: false,
329
- groupSystemPrompt: undefined,
330
- });
331
-
332
- expect(resolveSenderScopedGroupPolicy).not.toHaveBeenCalled();
333
- expect(resolveDmGroupAccessWithLists).toHaveBeenCalledWith(
334
- expect.objectContaining({
335
- groupPolicy: "allowlist",
336
- groupAllowFrom: [],
337
- }),
338
- );
339
- });
340
-
341
- it("drops unauthorized group control commands", async () => {
342
- primeCommonDefaults();
343
- allowInboundGroupTraffic({
344
- effectiveGroupAllowFrom: [],
345
- effectiveWasMentioned: false,
346
- });
347
- const core = createCore();
348
- core.channel.commands.shouldComputeCommandAuthorized.mockReturnValue(true);
349
- core.channel.commands.resolveCommandAuthorizedFromAuthorizers.mockReturnValue(false);
350
- core.channel.commands.isControlCommandMessage.mockReturnValue(true);
351
- const logVerbose = vi.fn();
352
-
353
- await expect(
354
- applyInboundAccessPolicy({
355
- core: core as never,
356
- rawBody: "/admin",
357
- logVerbose,
358
- }),
359
- ).resolves.toEqual({ ok: false });
360
-
361
- expect(logVerbose).toHaveBeenCalledWith("googlechat: drop control command from users/alice");
362
- });
363
-
364
- it("does not match group policy by mutable space displayName when the stable id differs", async () => {
365
- primeCommonDefaults();
366
- allowInboundGroupTraffic();
367
- const logVerbose = vi.fn();
368
-
369
- await expect(
370
- applyInboundAccessPolicy({
371
- account: {
372
- accountId: "default",
373
- config: {
374
- groups: {
375
- "Finance Ops": {
376
- users: ["users/alice"],
377
- requireMention: true,
378
- systemPrompt: "finance-only prompt",
379
- },
380
- },
381
- },
382
- } as never,
383
- core: createCore() as never,
384
- space: { name: "spaces/BBB", displayName: "Finance Ops" } as never,
385
- message: {
386
- annotations: [
387
- {
388
- type: "USER_MENTION",
389
- userMention: { user: { name: "users/app" } },
390
- },
391
- ],
392
- } as never,
393
- rawBody: "show quarter close status",
394
- logVerbose,
395
- }),
396
- ).resolves.toEqual({ ok: false });
397
-
398
- expect(logVerbose).toHaveBeenCalledWith(
399
- "Deprecated Google Chat group key detected: group routing now requires stable space ids (spaces/<spaceId>). Update channels.googlechat.groups keys: Finance Ops",
400
- );
401
- expect(logVerbose).toHaveBeenCalledWith(
402
- "drop group message (deprecated mutable group key matched, space=spaces/BBB)",
403
- );
404
- });
405
-
406
- it("fails closed instead of falling back to wildcard when a deprecated room key matches", async () => {
407
- primeCommonDefaults();
408
- resolveAllowlistProviderRuntimeGroupPolicy.mockReturnValue({
409
- groupPolicy: "open",
410
- providerMissingFallbackApplied: false,
411
- });
412
- allowInboundGroupTraffic();
413
- const logVerbose = vi.fn();
414
-
415
- await expect(
416
- applyInboundAccessPolicy({
417
- account: {
418
- accountId: "default",
419
- config: {
420
- groupPolicy: "open",
421
- groups: {
422
- "*": {
423
- users: ["users/alice"],
424
- },
425
- "Finance Ops": {
426
- enabled: false,
427
- users: ["users/bob"],
428
- },
429
- },
430
- },
431
- } as never,
432
- core: createCore() as never,
433
- space: { name: "spaces/BBB", displayName: "Finance Ops" } as never,
434
- rawBody: "show quarter close status",
435
- logVerbose,
436
- }),
437
- ).resolves.toEqual({ ok: false });
438
-
439
- expect(logVerbose).toHaveBeenCalledWith(
440
- "drop group message (deprecated mutable group key matched, space=spaces/BBB)",
441
- );
442
- });
443
- });