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

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
@@ -0,0 +1,682 @@
1
+ import { a as resolveDefaultGoogleChatAccountId, i as listGoogleChatAccountIds, n as googlechatSetupAdapter, o as resolveGoogleChatAccount, r as listEnabledGoogleChatAccounts, s as resolveGoogleChatConfigAccessorAccount, t as googlechatSetupWizard } from "./setup-surface-CofP-Gg9.js";
2
+ import { E as resolveChannelMediaMaxBytes, L as getGoogleChatRuntime, a as buildChannelConfigSchema, i as PAIRING_APPROVED_MESSAGE, m as fetchRemoteMedia, o as chunkTextForOutbound, r as GoogleChatConfigSchema, t as DEFAULT_ACCOUNT_ID, v as loadOutboundMediaFromUrl$1, y as missingTargetError } from "./runtime-api-wkIdfwqY.js";
3
+ import { a as findGoogleChatDirectMessage, c as sendGoogleChatMessage, o as listGoogleChatReactions, r as deleteGoogleChatReaction, t as createGoogleChatReaction, u as uploadGoogleChatAttachment } from "./api-C67jv0ow.js";
4
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-CG1sLToP.js";
5
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DtQ_IO7J.js";
6
+ import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
7
+ import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
8
+ import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
9
+ import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
10
+ import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
11
+ import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
12
+ import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
13
+ import { createActionGate, jsonResult, readNumberParam, readReactionParams, readStringParam } from "openclaw/plugin-sdk/channel-actions";
14
+ import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
15
+ import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
16
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
17
+ import { createAccountStatusSink, runPassiveAccountLifecycle } from "openclaw/plugin-sdk/channel-lifecycle";
18
+ import { composeAccountWarningCollectors, createAllowlistProviderOpenWarningCollector, createDangerousNameMatchingMutableAllowlistWarningCollector, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
19
+ import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
20
+ import { createChannelDirectoryAdapter, listResolvedDirectoryGroupEntriesFromMapKeys, listResolvedDirectoryUserEntriesFromAllowFrom } from "openclaw/plugin-sdk/directory-runtime";
21
+ import { sanitizeForPlainText } from "openclaw/plugin-sdk/outbound-runtime";
22
+ //#region extensions/googlechat/src/targets.ts
23
+ function normalizeGoogleChatTarget(raw) {
24
+ const trimmed = raw?.trim();
25
+ if (!trimmed) return;
26
+ const normalized = trimmed.replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:(users\/)?/i, "users/").replace(/^space:(spaces\/)?/i, "spaces/");
27
+ if (isGoogleChatUserTarget(normalized)) {
28
+ const suffix = normalized.slice(6);
29
+ return suffix.includes("@") ? `users/${normalizeLowercaseStringOrEmpty(suffix)}` : normalized;
30
+ }
31
+ if (isGoogleChatSpaceTarget(normalized)) return normalized;
32
+ if (normalized.includes("@")) return `users/${normalizeLowercaseStringOrEmpty(normalized)}`;
33
+ return normalized;
34
+ }
35
+ function isGoogleChatUserTarget(value) {
36
+ return normalizeLowercaseStringOrEmpty(value).startsWith("users/");
37
+ }
38
+ function isGoogleChatSpaceTarget(value) {
39
+ return normalizeLowercaseStringOrEmpty(value).startsWith("spaces/");
40
+ }
41
+ function stripMessageSuffix(target) {
42
+ const index = target.indexOf("/messages/");
43
+ if (index === -1) return target;
44
+ return target.slice(0, index);
45
+ }
46
+ async function resolveGoogleChatOutboundSpace(params) {
47
+ const normalized = normalizeGoogleChatTarget(params.target);
48
+ if (!normalized) throw new Error("Missing Google Chat target.");
49
+ const base = stripMessageSuffix(normalized);
50
+ if (isGoogleChatSpaceTarget(base)) return base;
51
+ if (isGoogleChatUserTarget(base)) {
52
+ const dm = await findGoogleChatDirectMessage({
53
+ account: params.account,
54
+ userName: base
55
+ });
56
+ if (!dm?.name) throw new Error(`No Google Chat DM found for ${base}`);
57
+ return dm.name;
58
+ }
59
+ return base;
60
+ }
61
+ //#endregion
62
+ //#region extensions/googlechat/src/actions.ts
63
+ const providerId = "googlechat";
64
+ function listEnabledAccounts(cfg) {
65
+ return listEnabledGoogleChatAccounts(cfg).filter((account) => account.enabled && account.credentialSource !== "none");
66
+ }
67
+ function isReactionsEnabled(accounts) {
68
+ for (const account of accounts) if (createActionGate(account.config.actions)("reactions")) return true;
69
+ return false;
70
+ }
71
+ function resolveAppUserNames(account) {
72
+ return new Set(["users/app", account.config.botUser?.trim()].filter(Boolean));
73
+ }
74
+ async function loadGoogleChatActionMedia(params) {
75
+ const runtime = getGoogleChatRuntime();
76
+ return /^https?:\/\//i.test(params.mediaUrl) ? await runtime.channel.media.fetchRemoteMedia({
77
+ url: params.mediaUrl,
78
+ maxBytes: params.maxBytes
79
+ }) : await loadOutboundMediaFromUrl(params.mediaUrl, {
80
+ maxBytes: params.maxBytes,
81
+ mediaAccess: params.mediaAccess,
82
+ mediaLocalRoots: params.mediaLocalRoots,
83
+ mediaReadFile: params.mediaReadFile
84
+ });
85
+ }
86
+ const googlechatMessageActions = {
87
+ describeMessageTool: ({ cfg, accountId }) => {
88
+ const accounts = accountId ? [resolveGoogleChatAccount({
89
+ cfg,
90
+ accountId
91
+ })].filter((account) => account.enabled && account.credentialSource !== "none") : listEnabledAccounts(cfg);
92
+ if (accounts.length === 0) return null;
93
+ const actions = /* @__PURE__ */ new Set([]);
94
+ actions.add("send");
95
+ actions.add("upload-file");
96
+ if (isReactionsEnabled(accounts)) {
97
+ actions.add("react");
98
+ actions.add("reactions");
99
+ }
100
+ return { actions: Array.from(actions) };
101
+ },
102
+ extractToolSend: ({ args }) => {
103
+ return extractToolSend(args, "sendMessage");
104
+ },
105
+ handleAction: async ({ action, params, cfg, accountId, mediaAccess, mediaLocalRoots, mediaReadFile }) => {
106
+ const account = resolveGoogleChatAccount({
107
+ cfg,
108
+ accountId
109
+ });
110
+ if (account.credentialSource === "none") throw new Error("Google Chat credentials are missing.");
111
+ if (action === "send" || action === "upload-file") {
112
+ const to = readStringParam(params, "to", { required: true });
113
+ const content = readStringParam(params, "message", {
114
+ required: action === "send",
115
+ allowEmpty: true
116
+ }) ?? readStringParam(params, "initialComment", { allowEmpty: true }) ?? "";
117
+ const mediaUrl = readStringParam(params, "media", { trim: false }) ?? readStringParam(params, "filePath", { trim: false }) ?? readStringParam(params, "path", { trim: false });
118
+ const threadId = readStringParam(params, "threadId") ?? readStringParam(params, "replyTo");
119
+ const space = await resolveGoogleChatOutboundSpace({
120
+ account,
121
+ target: to
122
+ });
123
+ if (mediaUrl) {
124
+ const loaded = await loadGoogleChatActionMedia({
125
+ mediaUrl,
126
+ maxBytes: (account.config.mediaMaxMb ?? 20) * 1024 * 1024,
127
+ mediaAccess,
128
+ mediaLocalRoots,
129
+ mediaReadFile
130
+ });
131
+ const uploadFileName = readStringParam(params, "filename") ?? readStringParam(params, "title") ?? loaded.fileName ?? "attachment";
132
+ const upload = await uploadGoogleChatAttachment({
133
+ account,
134
+ space,
135
+ filename: uploadFileName,
136
+ buffer: loaded.buffer,
137
+ contentType: loaded.contentType
138
+ });
139
+ await sendGoogleChatMessage({
140
+ account,
141
+ space,
142
+ text: content,
143
+ thread: threadId ?? void 0,
144
+ attachments: upload.attachmentUploadToken ? [{
145
+ attachmentUploadToken: upload.attachmentUploadToken,
146
+ contentName: uploadFileName
147
+ }] : void 0
148
+ });
149
+ return jsonResult({
150
+ ok: true,
151
+ to: space
152
+ });
153
+ }
154
+ if (action === "upload-file") throw new Error("upload-file requires media, filePath, or path");
155
+ await sendGoogleChatMessage({
156
+ account,
157
+ space,
158
+ text: content,
159
+ thread: threadId ?? void 0
160
+ });
161
+ return jsonResult({
162
+ ok: true,
163
+ to: space
164
+ });
165
+ }
166
+ if (action === "react") {
167
+ const messageName = readStringParam(params, "messageId", { required: true });
168
+ const { emoji, remove, isEmpty } = readReactionParams(params, { removeErrorMessage: "Emoji is required to remove a Google Chat reaction." });
169
+ if (remove || isEmpty) {
170
+ const reactions = await listGoogleChatReactions({
171
+ account,
172
+ messageName
173
+ });
174
+ const appUsers = resolveAppUserNames(account);
175
+ const toRemove = reactions.filter((reaction) => {
176
+ const userName = reaction.user?.name?.trim();
177
+ if (appUsers.size > 0 && !appUsers.has(userName ?? "")) return false;
178
+ if (emoji) return reaction.emoji?.unicode === emoji;
179
+ return true;
180
+ });
181
+ for (const reaction of toRemove) {
182
+ if (!reaction.name) continue;
183
+ await deleteGoogleChatReaction({
184
+ account,
185
+ reactionName: reaction.name
186
+ });
187
+ }
188
+ return jsonResult({
189
+ ok: true,
190
+ removed: toRemove.length
191
+ });
192
+ }
193
+ return jsonResult({
194
+ ok: true,
195
+ reaction: await createGoogleChatReaction({
196
+ account,
197
+ messageName,
198
+ emoji
199
+ })
200
+ });
201
+ }
202
+ if (action === "reactions") return jsonResult({
203
+ ok: true,
204
+ reactions: await listGoogleChatReactions({
205
+ account,
206
+ messageName: readStringParam(params, "messageId", { required: true }),
207
+ limit: readNumberParam(params, "limit", { integer: true }) ?? void 0
208
+ })
209
+ });
210
+ throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
211
+ }
212
+ };
213
+ //#endregion
214
+ //#region extensions/googlechat/src/approval-auth.ts
215
+ function normalizeGoogleChatApproverId(value) {
216
+ const normalized = normalizeGoogleChatTarget(String(value));
217
+ if (!normalized || !isGoogleChatUserTarget(normalized)) return;
218
+ const suffix = normalizeLowercaseStringOrEmpty(normalized.slice(6));
219
+ if (!suffix || suffix.includes("@")) return;
220
+ return `users/${suffix}`;
221
+ }
222
+ const googleChatApprovalAuth = createResolvedApproverActionAuthAdapter({
223
+ channelLabel: "Google Chat",
224
+ resolveApprovers: ({ cfg, accountId }) => {
225
+ const account = resolveGoogleChatAccount({
226
+ cfg,
227
+ accountId
228
+ }).config;
229
+ return resolveApprovalApprovers({
230
+ allowFrom: account.dm?.allowFrom,
231
+ defaultTo: account.defaultTo,
232
+ normalizeApprover: normalizeGoogleChatApproverId
233
+ });
234
+ },
235
+ normalizeSenderId: (value) => normalizeGoogleChatApproverId(value)
236
+ });
237
+ //#endregion
238
+ //#region extensions/googlechat/src/group-policy.ts
239
+ function resolveGoogleChatGroupRequireMention(params) {
240
+ return resolveChannelGroupRequireMention({
241
+ cfg: params.cfg,
242
+ channel: "googlechat",
243
+ groupId: params.groupId,
244
+ accountId: params.accountId
245
+ });
246
+ }
247
+ //#endregion
248
+ //#region extensions/googlechat/src/channel.adapters.ts
249
+ const loadGoogleChatChannelRuntime$2 = createLazyRuntimeNamedExport(() => import("./channel.runtime-CoSLc-4R.js"), "googleChatChannelRuntime");
250
+ const formatAllowFromEntry = (entry) => normalizeLowercaseStringOrEmpty(entry.trim().replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:/i, "").replace(/^users\//i, ""));
251
+ const collectGoogleChatSecurityWarnings = composeAccountWarningCollectors(createAllowlistProviderOpenWarningCollector({
252
+ providerConfigPresent: (cfg) => cfg.channels?.googlechat !== void 0,
253
+ resolveGroupPolicy: (account) => account.config.groupPolicy,
254
+ buildOpenWarning: {
255
+ surface: "Google Chat spaces",
256
+ openBehavior: "allows any space to trigger (mention-gated)",
257
+ remediation: "Set channels.googlechat.groupPolicy=\"allowlist\" and configure channels.googlechat.groups"
258
+ }
259
+ }), (account) => account.config.dm?.policy === "open" && "- Google Chat DMs are open to anyone. Set channels.googlechat.dm.policy=\"pairing\" or \"allowlist\".");
260
+ const googlechatGroupsAdapter = { resolveRequireMention: resolveGoogleChatGroupRequireMention };
261
+ const googlechatDirectoryAdapter = createChannelDirectoryAdapter({
262
+ listPeers: async (params) => listResolvedDirectoryUserEntriesFromAllowFrom({
263
+ ...params,
264
+ resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount),
265
+ resolveAllowFrom: (account) => account.config.dm?.allowFrom,
266
+ normalizeId: (entry) => normalizeGoogleChatTarget(entry) ?? entry
267
+ }),
268
+ listGroups: async (params) => listResolvedDirectoryGroupEntriesFromMapKeys({
269
+ ...params,
270
+ resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount),
271
+ resolveGroups: (account) => account.config.groups
272
+ })
273
+ });
274
+ const googlechatSecurityAdapter = {
275
+ dm: {
276
+ channelKey: "googlechat",
277
+ resolvePolicy: (account) => account.config.dm?.policy,
278
+ resolveAllowFrom: (account) => account.config.dm?.allowFrom,
279
+ allowFromPathSuffix: "dm.",
280
+ normalizeEntry: (raw) => formatAllowFromEntry(raw)
281
+ },
282
+ collectWarnings: collectGoogleChatSecurityWarnings
283
+ };
284
+ const googlechatThreadingAdapter = { scopedAccountReplyToMode: {
285
+ resolveAccount: (cfg, accountId) => resolveGoogleChatAccount({
286
+ cfg,
287
+ accountId
288
+ }),
289
+ resolveReplyToMode: (account, _chatType) => account.config.replyToMode,
290
+ fallback: "off"
291
+ } };
292
+ const googlechatPairingTextAdapter = {
293
+ idLabel: "googlechatUserId",
294
+ message: PAIRING_APPROVED_MESSAGE,
295
+ normalizeAllowEntry: (entry) => formatAllowFromEntry(entry),
296
+ notify: async ({ cfg, id, message, accountId }) => {
297
+ const account = resolveGoogleChatAccount({
298
+ cfg,
299
+ accountId
300
+ });
301
+ if (account.credentialSource === "none") return;
302
+ const user = normalizeGoogleChatTarget(id) ?? id;
303
+ const space = await resolveGoogleChatOutboundSpace({
304
+ account,
305
+ target: isGoogleChatUserTarget(user) ? user : `users/${user}`
306
+ });
307
+ const { sendGoogleChatMessage } = await loadGoogleChatChannelRuntime$2();
308
+ await sendGoogleChatMessage({
309
+ account,
310
+ space,
311
+ text: message
312
+ });
313
+ }
314
+ };
315
+ const googlechatOutboundAdapter = {
316
+ base: {
317
+ deliveryMode: "direct",
318
+ chunker: chunkTextForOutbound,
319
+ chunkerMode: "markdown",
320
+ textChunkLimit: 4e3,
321
+ sanitizeText: ({ text }) => sanitizeForPlainText(text),
322
+ resolveTarget: ({ to }) => {
323
+ const trimmed = normalizeOptionalString(to) ?? "";
324
+ if (trimmed) {
325
+ const normalized = normalizeGoogleChatTarget(trimmed);
326
+ if (!normalized) return {
327
+ ok: false,
328
+ error: missingTargetError("Google Chat", "<spaces/{space}|users/{user}>")
329
+ };
330
+ return {
331
+ ok: true,
332
+ to: normalized
333
+ };
334
+ }
335
+ return {
336
+ ok: false,
337
+ error: missingTargetError("Google Chat", "<spaces/{space}|users/{user}>")
338
+ };
339
+ }
340
+ },
341
+ attachedResults: {
342
+ channel: "googlechat",
343
+ sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => {
344
+ const account = resolveGoogleChatAccount({
345
+ cfg,
346
+ accountId
347
+ });
348
+ const space = await resolveGoogleChatOutboundSpace({
349
+ account,
350
+ target: to
351
+ });
352
+ const thread = typeof threadId === "number" ? String(threadId) : threadId ?? replyToId ?? void 0;
353
+ const { sendGoogleChatMessage } = await loadGoogleChatChannelRuntime$2();
354
+ return {
355
+ messageId: (await sendGoogleChatMessage({
356
+ account,
357
+ space,
358
+ text,
359
+ thread
360
+ }))?.messageName ?? "",
361
+ chatId: space
362
+ };
363
+ },
364
+ sendMedia: async ({ cfg, to, text, mediaUrl, mediaAccess, mediaLocalRoots, mediaReadFile, accountId, replyToId, threadId }) => {
365
+ if (!mediaUrl) throw new Error("Google Chat mediaUrl is required.");
366
+ const account = resolveGoogleChatAccount({
367
+ cfg,
368
+ accountId
369
+ });
370
+ const space = await resolveGoogleChatOutboundSpace({
371
+ account,
372
+ target: to
373
+ });
374
+ const thread = typeof threadId === "number" ? String(threadId) : threadId ?? replyToId ?? void 0;
375
+ const effectiveMaxBytes = resolveChannelMediaMaxBytes({
376
+ cfg,
377
+ resolveChannelLimitMb: ({ cfg, accountId }) => (cfg.channels?.googlechat)?.accounts?.[accountId]?.mediaMaxMb ?? (cfg.channels?.googlechat)?.mediaMaxMb,
378
+ accountId
379
+ }) ?? (account.config.mediaMaxMb ?? 20) * 1024 * 1024;
380
+ const loaded = /^https?:\/\//i.test(mediaUrl) ? await fetchRemoteMedia({
381
+ url: mediaUrl,
382
+ maxBytes: effectiveMaxBytes
383
+ }) : await loadOutboundMediaFromUrl$1(mediaUrl, {
384
+ maxBytes: effectiveMaxBytes,
385
+ mediaAccess,
386
+ mediaLocalRoots,
387
+ mediaReadFile
388
+ });
389
+ const { sendGoogleChatMessage, uploadGoogleChatAttachment } = await loadGoogleChatChannelRuntime$2();
390
+ const upload = await uploadGoogleChatAttachment({
391
+ account,
392
+ space,
393
+ filename: loaded.fileName ?? "attachment",
394
+ buffer: loaded.buffer,
395
+ contentType: loaded.contentType
396
+ });
397
+ return {
398
+ messageId: (await sendGoogleChatMessage({
399
+ account,
400
+ space,
401
+ text,
402
+ thread,
403
+ attachments: upload.attachmentUploadToken ? [{
404
+ attachmentUploadToken: upload.attachmentUploadToken,
405
+ contentName: loaded.fileName
406
+ }] : void 0
407
+ }))?.messageName ?? "",
408
+ chatId: space
409
+ };
410
+ }
411
+ }
412
+ };
413
+ //#endregion
414
+ //#region extensions/googlechat/src/doctor.ts
415
+ function asObjectRecord(value) {
416
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
417
+ }
418
+ function isGoogleChatMutableAllowEntry(raw) {
419
+ const text = raw.trim();
420
+ if (!text || text === "*") return false;
421
+ const withoutPrefix = text.replace(/^(googlechat|google-chat|gchat):/i, "").trim();
422
+ if (!withoutPrefix) return false;
423
+ return withoutPrefix.replace(/^users\//i, "").includes("@");
424
+ }
425
+ const collectGoogleChatMutableAllowlistWarnings = createDangerousNameMatchingMutableAllowlistWarningCollector({
426
+ channel: "googlechat",
427
+ detector: isGoogleChatMutableAllowEntry,
428
+ collectLists: (scope) => {
429
+ const lists = [{
430
+ pathLabel: `${scope.prefix}.groupAllowFrom`,
431
+ list: scope.account.groupAllowFrom
432
+ }];
433
+ const dm = asObjectRecord(scope.account.dm);
434
+ if (dm) lists.push({
435
+ pathLabel: `${scope.prefix}.dm.allowFrom`,
436
+ list: dm.allowFrom
437
+ });
438
+ const groups = asObjectRecord(scope.account.groups);
439
+ if (groups) for (const [groupKey, groupRaw] of Object.entries(groups)) {
440
+ const group = asObjectRecord(groupRaw);
441
+ if (!group) continue;
442
+ lists.push({
443
+ pathLabel: `${scope.prefix}.groups.${groupKey}.users`,
444
+ list: group.users
445
+ });
446
+ }
447
+ return lists;
448
+ }
449
+ });
450
+ //#endregion
451
+ //#region extensions/googlechat/src/gateway.ts
452
+ const loadGoogleChatChannelRuntime$1 = createLazyRuntimeNamedExport(() => import("./channel.runtime-CoSLc-4R.js"), "googleChatChannelRuntime");
453
+ async function startGoogleChatGatewayAccount(ctx) {
454
+ const account = ctx.account;
455
+ const statusSink = createAccountStatusSink({
456
+ accountId: account.accountId,
457
+ setStatus: ctx.setStatus
458
+ });
459
+ ctx.log?.info?.(`[${account.accountId}] starting Google Chat webhook`);
460
+ const { resolveGoogleChatWebhookPath, startGoogleChatMonitor } = await loadGoogleChatChannelRuntime$1();
461
+ statusSink({
462
+ running: true,
463
+ lastStartAt: Date.now(),
464
+ webhookPath: resolveGoogleChatWebhookPath({ account }),
465
+ audienceType: account.config.audienceType,
466
+ audience: account.config.audience
467
+ });
468
+ await runPassiveAccountLifecycle({
469
+ abortSignal: ctx.abortSignal,
470
+ start: async () => await startGoogleChatMonitor({
471
+ account,
472
+ config: ctx.cfg,
473
+ runtime: ctx.runtime,
474
+ abortSignal: ctx.abortSignal,
475
+ webhookPath: account.config.webhookPath,
476
+ webhookUrl: account.config.webhookUrl,
477
+ statusSink
478
+ }),
479
+ stop: async (unregister) => {
480
+ unregister?.();
481
+ },
482
+ onStop: async () => {
483
+ statusSink({
484
+ running: false,
485
+ lastStopAt: Date.now()
486
+ });
487
+ }
488
+ });
489
+ }
490
+ //#endregion
491
+ //#region extensions/googlechat/src/channel.ts
492
+ const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CoSLc-4R.js"), "googleChatChannelRuntime");
493
+ const meta = {
494
+ id: "googlechat",
495
+ label: "Google Chat",
496
+ selectionLabel: "Google Chat (Chat API)",
497
+ docsPath: "/channels/googlechat",
498
+ docsLabel: "googlechat",
499
+ blurb: "Google Workspace Chat app with HTTP webhook.",
500
+ aliases: ["gchat", "google-chat"],
501
+ order: 55,
502
+ detailLabel: "Google Chat",
503
+ systemImage: "message.badge",
504
+ markdownCapable: true
505
+ };
506
+ const googleChatConfigAdapter = createScopedChannelConfigAdapter({
507
+ sectionKey: "googlechat",
508
+ listAccountIds: listGoogleChatAccountIds,
509
+ resolveAccount: adaptScopedAccountAccessor(resolveGoogleChatAccount),
510
+ resolveAccessorAccount: resolveGoogleChatConfigAccessorAccount,
511
+ defaultAccountId: resolveDefaultGoogleChatAccountId,
512
+ clearBaseFields: [
513
+ "serviceAccount",
514
+ "serviceAccountFile",
515
+ "audienceType",
516
+ "audience",
517
+ "webhookPath",
518
+ "webhookUrl",
519
+ "botUser",
520
+ "name"
521
+ ],
522
+ resolveAllowFrom: (account) => account.config.dm?.allowFrom,
523
+ formatAllowFrom: (allowFrom) => formatNormalizedAllowFromEntries({
524
+ allowFrom,
525
+ normalizeEntry: formatAllowFromEntry
526
+ }),
527
+ resolveDefaultTo: (account) => account.config.defaultTo
528
+ });
529
+ const googlechatActions = {
530
+ describeMessageTool: (ctx) => googlechatMessageActions.describeMessageTool?.(ctx) ?? null,
531
+ extractToolSend: (ctx) => googlechatMessageActions.extractToolSend?.(ctx) ?? null,
532
+ handleAction: async (ctx) => {
533
+ if (!googlechatMessageActions.handleAction) throw new Error("Google Chat actions are not available.");
534
+ return await googlechatMessageActions.handleAction(ctx);
535
+ }
536
+ };
537
+ const googlechatPlugin = createChatChannelPlugin({
538
+ base: {
539
+ id: "googlechat",
540
+ meta: { ...meta },
541
+ setup: googlechatSetupAdapter,
542
+ setupWizard: googlechatSetupWizard,
543
+ capabilities: {
544
+ chatTypes: [
545
+ "direct",
546
+ "group",
547
+ "thread"
548
+ ],
549
+ reactions: true,
550
+ threads: true,
551
+ media: true,
552
+ nativeCommands: false,
553
+ blockStreaming: true
554
+ },
555
+ streaming: { blockStreamingCoalesceDefaults: {
556
+ minChars: 1500,
557
+ idleMs: 1e3
558
+ } },
559
+ reload: { configPrefixes: ["channels.googlechat"] },
560
+ configSchema: buildChannelConfigSchema(GoogleChatConfigSchema),
561
+ config: {
562
+ ...googleChatConfigAdapter,
563
+ isConfigured: (account) => account.credentialSource !== "none",
564
+ describeAccount: (account) => describeAccountSnapshot({
565
+ account,
566
+ configured: account.credentialSource !== "none",
567
+ extra: { credentialSource: account.credentialSource }
568
+ })
569
+ },
570
+ approvalCapability: googleChatApprovalAuth,
571
+ secrets: {
572
+ secretTargetRegistryEntries,
573
+ collectRuntimeConfigAssignments
574
+ },
575
+ groups: googlechatGroupsAdapter,
576
+ messaging: {
577
+ targetPrefixes: [
578
+ "googlechat",
579
+ "google-chat",
580
+ "gchat"
581
+ ],
582
+ normalizeTarget: normalizeGoogleChatTarget,
583
+ targetResolver: {
584
+ looksLikeId: (raw, normalized) => {
585
+ const value = normalized ?? raw.trim();
586
+ return isGoogleChatSpaceTarget(value) || isGoogleChatUserTarget(value);
587
+ },
588
+ hint: "<spaces/{space}|users/{user}>"
589
+ }
590
+ },
591
+ directory: googlechatDirectoryAdapter,
592
+ resolver: { resolveTargets: async ({ inputs, kind }) => {
593
+ return inputs.map((input) => {
594
+ const normalized = normalizeGoogleChatTarget(input);
595
+ if (!normalized) return {
596
+ input,
597
+ resolved: false,
598
+ note: "empty target"
599
+ };
600
+ if (kind === "user" && isGoogleChatUserTarget(normalized)) return {
601
+ input,
602
+ resolved: true,
603
+ id: normalized
604
+ };
605
+ if (kind === "group" && isGoogleChatSpaceTarget(normalized)) return {
606
+ input,
607
+ resolved: true,
608
+ id: normalized
609
+ };
610
+ return {
611
+ input,
612
+ resolved: false,
613
+ note: "use spaces/{space} or users/{user}"
614
+ };
615
+ });
616
+ } },
617
+ actions: googlechatActions,
618
+ doctor: {
619
+ dmAllowFromMode: "nestedOnly",
620
+ groupModel: "route",
621
+ groupAllowFromFallbackToAllowFrom: false,
622
+ warnOnEmptyGroupSenderAllowlist: false,
623
+ legacyConfigRules,
624
+ normalizeCompatibilityConfig,
625
+ collectMutableAllowlistWarnings: collectGoogleChatMutableAllowlistWarnings
626
+ },
627
+ status: createComputedAccountStatusAdapter({
628
+ defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
629
+ collectStatusIssues: (accounts) => accounts.flatMap((entry) => {
630
+ const accountId = entry.accountId ?? DEFAULT_ACCOUNT_ID;
631
+ const enabled = entry.enabled !== false;
632
+ const configured = entry.configured === true;
633
+ if (!enabled || !configured) return [];
634
+ const issues = [];
635
+ if (!entry.audience) issues.push({
636
+ channel: "googlechat",
637
+ accountId,
638
+ kind: "config",
639
+ message: "Google Chat audience is missing (set channels.googlechat.audience).",
640
+ fix: "Set channels.googlechat.audienceType and channels.googlechat.audience."
641
+ });
642
+ if (!entry.audienceType) issues.push({
643
+ channel: "googlechat",
644
+ accountId,
645
+ kind: "config",
646
+ message: "Google Chat audienceType is missing (app-url or project-number).",
647
+ fix: "Set channels.googlechat.audienceType and channels.googlechat.audience."
648
+ });
649
+ return issues;
650
+ }),
651
+ buildChannelSummary: ({ snapshot }) => buildPassiveProbedChannelStatusSummary(snapshot, {
652
+ credentialSource: snapshot.credentialSource ?? "none",
653
+ audienceType: snapshot.audienceType ?? null,
654
+ audience: snapshot.audience ?? null,
655
+ webhookPath: snapshot.webhookPath ?? null,
656
+ webhookUrl: snapshot.webhookUrl ?? null
657
+ }),
658
+ probeAccount: async ({ account }) => (await loadGoogleChatChannelRuntime()).probeGoogleChat(account),
659
+ resolveAccountSnapshot: ({ account }) => ({
660
+ accountId: account.accountId,
661
+ name: account.name,
662
+ enabled: account.enabled,
663
+ configured: account.credentialSource !== "none",
664
+ extra: {
665
+ credentialSource: account.credentialSource,
666
+ audienceType: account.config.audienceType,
667
+ audience: account.config.audience,
668
+ webhookPath: account.config.webhookPath,
669
+ webhookUrl: account.config.webhookUrl,
670
+ dmPolicy: account.config.dm?.policy ?? "pairing"
671
+ }
672
+ })
673
+ }),
674
+ gateway: { startAccount: startGoogleChatGatewayAccount }
675
+ },
676
+ pairing: { text: googlechatPairingTextAdapter },
677
+ security: googlechatSecurityAdapter,
678
+ threading: googlechatThreadingAdapter,
679
+ outbound: googlechatOutboundAdapter
680
+ });
681
+ //#endregion
682
+ export { googlechatPlugin as t };
@@ -0,0 +1,5 @@
1
+ import { a as buildChannelConfigSchema, r as GoogleChatConfigSchema } from "./runtime-api-wkIdfwqY.js";
2
+ //#region extensions/googlechat/src/config-schema.ts
3
+ const GoogleChatChannelConfigSchema = buildChannelConfigSchema(GoogleChatConfigSchema);
4
+ //#endregion
5
+ export { GoogleChatChannelConfigSchema };
@@ -0,0 +1,2 @@
1
+ import { t as googlechatPlugin } from "./channel-DE2hroMn.js";
2
+ export { googlechatPlugin };