@openclaw/googlechat 2026.5.7 → 2026.5.9-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.
@@ -0,0 +1,96 @@
1
+ import { safeParseJsonWithSchema, safeParseWithSchema } from "openclaw/plugin-sdk/extension-shared";
2
+ import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
3
+ import { DEFAULT_ACCOUNT_ID, createAccountListHelpers, normalizeAccountId, resolveAccountEntry, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
4
+ import { isSecretRef } from "openclaw/plugin-sdk/secret-input";
5
+ import { z } from "openclaw/plugin-sdk/zod";
6
+ //#region extensions/googlechat/src/accounts.ts
7
+ const ENV_SERVICE_ACCOUNT = "GOOGLE_CHAT_SERVICE_ACCOUNT";
8
+ const ENV_SERVICE_ACCOUNT_FILE = "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE";
9
+ const JsonRecordSchema = z.record(z.string(), z.unknown());
10
+ const { listAccountIds: listGoogleChatAccountIds, resolveDefaultAccountId: resolveDefaultGoogleChatAccountId } = createAccountListHelpers("googlechat");
11
+ function mergeGoogleChatAccountConfig(cfg, accountId) {
12
+ const raw = cfg.channels?.["googlechat"] ?? {};
13
+ const base = resolveMergedAccountConfig({
14
+ channelConfig: raw,
15
+ accounts: raw.accounts,
16
+ accountId,
17
+ omitKeys: ["defaultAccount"]
18
+ });
19
+ const defaultAccountConfig = resolveAccountEntry(raw.accounts, DEFAULT_ACCOUNT_ID) ?? {};
20
+ if (accountId === DEFAULT_ACCOUNT_ID) return base;
21
+ const { enabled: _ignoredEnabled, dangerouslyAllowNameMatching: _ignoredDangerouslyAllowNameMatching, serviceAccount: _ignoredServiceAccount, serviceAccountRef: _ignoredServiceAccountRef, serviceAccountFile: _ignoredServiceAccountFile, ...defaultAccountShared } = defaultAccountConfig;
22
+ return {
23
+ ...defaultAccountShared,
24
+ ...base
25
+ };
26
+ }
27
+ function resolveGoogleChatConfigAccessorAccount(params) {
28
+ const accountId = normalizeAccountId(params.accountId ?? params.cfg.channels?.googlechat?.defaultAccount);
29
+ return { config: mergeGoogleChatAccountConfig(params.cfg, accountId) };
30
+ }
31
+ function parseServiceAccount(value) {
32
+ if (isSecretRef(value)) return null;
33
+ if (typeof value === "string") {
34
+ const trimmed = value.trim();
35
+ if (!trimmed) return null;
36
+ return safeParseJsonWithSchema(JsonRecordSchema, trimmed);
37
+ }
38
+ return safeParseWithSchema(JsonRecordSchema, value);
39
+ }
40
+ function resolveCredentialsFromConfig(params) {
41
+ const { account, accountId } = params;
42
+ const inline = parseServiceAccount(account.serviceAccount);
43
+ if (inline) return {
44
+ credentials: inline,
45
+ source: "inline"
46
+ };
47
+ if (isSecretRef(account.serviceAccount)) throw new Error(`channels.googlechat.accounts.${accountId}.serviceAccount: unresolved SecretRef "${account.serviceAccount.source}:${account.serviceAccount.provider}:${account.serviceAccount.id}". Resolve this command against an active gateway runtime snapshot before reading it.`);
48
+ if (isSecretRef(account.serviceAccountRef)) throw new Error(`channels.googlechat.accounts.${accountId}.serviceAccount: unresolved SecretRef "${account.serviceAccountRef.source}:${account.serviceAccountRef.provider}:${account.serviceAccountRef.id}". Resolve this command against an active gateway runtime snapshot before reading it.`);
49
+ const file = normalizeOptionalString(account.serviceAccountFile);
50
+ if (file) return {
51
+ credentialsFile: file,
52
+ source: "file"
53
+ };
54
+ if (accountId === DEFAULT_ACCOUNT_ID) {
55
+ const envJson = process.env[ENV_SERVICE_ACCOUNT];
56
+ const envInline = parseServiceAccount(envJson);
57
+ if (envInline) return {
58
+ credentials: envInline,
59
+ source: "env"
60
+ };
61
+ const envFile = normalizeOptionalString(process.env[ENV_SERVICE_ACCOUNT_FILE]);
62
+ if (envFile) return {
63
+ credentialsFile: envFile,
64
+ source: "env"
65
+ };
66
+ }
67
+ return { source: "none" };
68
+ }
69
+ function resolveGoogleChatAccount(params) {
70
+ const accountId = normalizeAccountId(params.accountId ?? params.cfg.channels?.["googlechat"]?.defaultAccount);
71
+ const baseEnabled = params.cfg.channels?.["googlechat"]?.enabled !== false;
72
+ const merged = mergeGoogleChatAccountConfig(params.cfg, accountId);
73
+ const accountEnabled = merged.enabled !== false;
74
+ const enabled = baseEnabled && accountEnabled;
75
+ const credentials = resolveCredentialsFromConfig({
76
+ accountId,
77
+ account: merged
78
+ });
79
+ return {
80
+ accountId,
81
+ name: normalizeOptionalString(merged.name),
82
+ enabled,
83
+ config: merged,
84
+ credentialSource: credentials.source,
85
+ credentials: credentials.credentials,
86
+ credentialsFile: credentials.credentialsFile
87
+ };
88
+ }
89
+ function listEnabledGoogleChatAccounts(cfg) {
90
+ return listGoogleChatAccountIds(cfg).map((accountId) => resolveGoogleChatAccount({
91
+ cfg,
92
+ accountId
93
+ })).filter((account) => account.enabled);
94
+ }
95
+ //#endregion
96
+ export { resolveGoogleChatConfigAccessorAccount as a, resolveGoogleChatAccount as i, listGoogleChatAccountIds as n, resolveDefaultGoogleChatAccountId as r, listEnabledGoogleChatAccounts as t };
@@ -0,0 +1,160 @@
1
+ import { i as resolveGoogleChatAccount, t as listEnabledGoogleChatAccounts } from "./accounts-BbM9zX1S.js";
2
+ import { L as getGoogleChatRuntime } from "./runtime-api-JpbBRVpx.js";
3
+ import { c as sendGoogleChatMessage, o as listGoogleChatReactions, r as deleteGoogleChatReaction, t as createGoogleChatReaction, u as uploadGoogleChatAttachment } from "./api-BwM7NTay.js";
4
+ import { i as resolveGoogleChatOutboundSpace } from "./targets-14IVp2Yg.js";
5
+ import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
6
+ import { createActionGate, jsonResult, readNumberParam, readReactionParams, readStringParam } from "openclaw/plugin-sdk/channel-actions";
7
+ import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
8
+ //#region extensions/googlechat/src/actions.ts
9
+ const providerId = "googlechat";
10
+ function listEnabledAccounts(cfg) {
11
+ return listEnabledGoogleChatAccounts(cfg).filter((account) => account.enabled && account.credentialSource !== "none");
12
+ }
13
+ function isReactionsEnabled(accounts) {
14
+ for (const account of accounts) if (createActionGate(account.config.actions)("reactions")) return true;
15
+ return false;
16
+ }
17
+ function resolveAppUserNames(account) {
18
+ return new Set(["users/app", account.config.botUser?.trim()].filter(Boolean));
19
+ }
20
+ async function loadGoogleChatActionMedia(params) {
21
+ const runtime = getGoogleChatRuntime();
22
+ return /^https?:\/\//i.test(params.mediaUrl) ? await runtime.channel.media.fetchRemoteMedia({
23
+ url: params.mediaUrl,
24
+ maxBytes: params.maxBytes
25
+ }) : await loadOutboundMediaFromUrl(params.mediaUrl, {
26
+ maxBytes: params.maxBytes,
27
+ mediaAccess: params.mediaAccess,
28
+ mediaLocalRoots: params.mediaLocalRoots,
29
+ mediaReadFile: params.mediaReadFile
30
+ });
31
+ }
32
+ const googlechatMessageActions = {
33
+ describeMessageTool: ({ cfg, accountId }) => {
34
+ const accounts = accountId ? [resolveGoogleChatAccount({
35
+ cfg,
36
+ accountId
37
+ })].filter((account) => account.enabled && account.credentialSource !== "none") : listEnabledAccounts(cfg);
38
+ if (accounts.length === 0) return null;
39
+ const actions = /* @__PURE__ */ new Set([]);
40
+ actions.add("send");
41
+ actions.add("upload-file");
42
+ if (isReactionsEnabled(accounts)) {
43
+ actions.add("react");
44
+ actions.add("reactions");
45
+ }
46
+ return { actions: Array.from(actions) };
47
+ },
48
+ extractToolSend: ({ args }) => {
49
+ return extractToolSend(args, "sendMessage");
50
+ },
51
+ handleAction: async ({ action, params, cfg, accountId, mediaAccess, mediaLocalRoots, mediaReadFile }) => {
52
+ const account = resolveGoogleChatAccount({
53
+ cfg,
54
+ accountId
55
+ });
56
+ if (account.credentialSource === "none") throw new Error("Google Chat credentials are missing.");
57
+ if (action === "send" || action === "upload-file") {
58
+ const to = readStringParam(params, "to", { required: true });
59
+ const content = readStringParam(params, "message", {
60
+ required: action === "send",
61
+ allowEmpty: true
62
+ }) ?? readStringParam(params, "initialComment", { allowEmpty: true }) ?? "";
63
+ const mediaUrl = readStringParam(params, "media", { trim: false }) ?? readStringParam(params, "filePath", { trim: false }) ?? readStringParam(params, "path", { trim: false });
64
+ const threadId = readStringParam(params, "threadId") ?? readStringParam(params, "replyTo");
65
+ const space = await resolveGoogleChatOutboundSpace({
66
+ account,
67
+ target: to
68
+ });
69
+ if (mediaUrl) {
70
+ const loaded = await loadGoogleChatActionMedia({
71
+ mediaUrl,
72
+ maxBytes: (account.config.mediaMaxMb ?? 20) * 1024 * 1024,
73
+ mediaAccess,
74
+ mediaLocalRoots,
75
+ mediaReadFile
76
+ });
77
+ const uploadFileName = readStringParam(params, "filename") ?? readStringParam(params, "title") ?? loaded.fileName ?? "attachment";
78
+ const upload = await uploadGoogleChatAttachment({
79
+ account,
80
+ space,
81
+ filename: uploadFileName,
82
+ buffer: loaded.buffer,
83
+ contentType: loaded.contentType
84
+ });
85
+ await sendGoogleChatMessage({
86
+ account,
87
+ space,
88
+ text: content,
89
+ thread: threadId ?? void 0,
90
+ attachments: upload.attachmentUploadToken ? [{
91
+ attachmentUploadToken: upload.attachmentUploadToken,
92
+ contentName: uploadFileName
93
+ }] : void 0
94
+ });
95
+ return jsonResult({
96
+ ok: true,
97
+ to: space
98
+ });
99
+ }
100
+ if (action === "upload-file") throw new Error("upload-file requires media, filePath, or path");
101
+ await sendGoogleChatMessage({
102
+ account,
103
+ space,
104
+ text: content,
105
+ thread: threadId ?? void 0
106
+ });
107
+ return jsonResult({
108
+ ok: true,
109
+ to: space
110
+ });
111
+ }
112
+ if (action === "react") {
113
+ const messageName = readStringParam(params, "messageId", { required: true });
114
+ const { emoji, remove, isEmpty } = readReactionParams(params, { removeErrorMessage: "Emoji is required to remove a Google Chat reaction." });
115
+ if (remove || isEmpty) {
116
+ const reactions = await listGoogleChatReactions({
117
+ account,
118
+ messageName
119
+ });
120
+ const appUsers = resolveAppUserNames(account);
121
+ const toRemove = reactions.filter((reaction) => {
122
+ const userName = reaction.user?.name?.trim();
123
+ if (appUsers.size > 0 && !appUsers.has(userName ?? "")) return false;
124
+ if (emoji) return reaction.emoji?.unicode === emoji;
125
+ return true;
126
+ });
127
+ for (const reaction of toRemove) {
128
+ if (!reaction.name) continue;
129
+ await deleteGoogleChatReaction({
130
+ account,
131
+ reactionName: reaction.name
132
+ });
133
+ }
134
+ return jsonResult({
135
+ ok: true,
136
+ removed: toRemove.length
137
+ });
138
+ }
139
+ return jsonResult({
140
+ ok: true,
141
+ reaction: await createGoogleChatReaction({
142
+ account,
143
+ messageName,
144
+ emoji
145
+ })
146
+ });
147
+ }
148
+ if (action === "reactions") return jsonResult({
149
+ ok: true,
150
+ reactions: await listGoogleChatReactions({
151
+ account,
152
+ messageName: readStringParam(params, "messageId", { required: true }),
153
+ limit: readNumberParam(params, "limit", { integer: true }) ?? void 0
154
+ })
155
+ });
156
+ throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
157
+ }
158
+ };
159
+ //#endregion
160
+ export { googlechatMessageActions };
@@ -1,4 +1,4 @@
1
- import { h as fetchWithSsrFGuard$1 } from "./runtime-api-wkIdfwqY.js";
1
+ import { h as fetchWithSsrFGuard$1 } from "./runtime-api-JpbBRVpx.js";
2
2
  import { normalizeLowercaseStringOrEmpty, resolveUserPath } from "openclaw/plugin-sdk/text-runtime";
3
3
  import crypto from "node:crypto";
4
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
package/dist/api.js CHANGED
@@ -1,3 +1,3 @@
1
- import { n as googlechatSetupAdapter, t as googlechatSetupWizard } from "./setup-surface-CofP-Gg9.js";
2
- import { t as googlechatPlugin } from "./channel-CHniJUSZ.js";
1
+ import { t as googlechatPlugin } from "./channel-DHHatGQ1.js";
2
+ import { n as googlechatSetupAdapter, t as googlechatSetupWizard } from "./setup-surface-S4ArLnR8.js";
3
3
  export { googlechatPlugin, googlechatSetupAdapter, googlechatSetupWizard };
@@ -1,8 +1,9 @@
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-C8OqyWsW.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";
1
+ import { a as resolveGoogleChatConfigAccessorAccount, i as resolveGoogleChatAccount, n as listGoogleChatAccountIds, r as resolveDefaultGoogleChatAccountId } from "./accounts-BbM9zX1S.js";
2
+ import { E as resolveChannelMediaMaxBytes, 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, y as missingTargetError } from "./runtime-api-JpbBRVpx.js";
3
+ import { i as resolveGoogleChatOutboundSpace, n as isGoogleChatUserTarget, r as normalizeGoogleChatTarget, t as isGoogleChatSpaceTarget } from "./targets-14IVp2Yg.js";
4
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-BsERSjSe.js";
5
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DLAG2_NI.js";
6
+ import { n as googlechatSetupAdapter, t as googlechatSetupWizard } from "./setup-surface-S4ArLnR8.js";
6
7
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
7
8
  import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
8
9
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
@@ -10,207 +11,14 @@ import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
10
11
  import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
11
12
  import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
12
13
  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
14
  import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
15
+ import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
16
16
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
17
17
  import { createAccountStatusSink, runPassiveAccountLifecycle } from "openclaw/plugin-sdk/channel-lifecycle";
18
+ import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
18
19
  import { composeAccountWarningCollectors, createAllowlistProviderOpenWarningCollector, createDangerousNameMatchingMutableAllowlistWarningCollector, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
19
- import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
20
20
  import { createChannelDirectoryAdapter, listResolvedDirectoryGroupEntriesFromMapKeys, listResolvedDirectoryUserEntriesFromAllowFrom } from "openclaw/plugin-sdk/directory-runtime";
21
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
22
  //#region extensions/googlechat/src/approval-auth.ts
215
23
  function normalizeGoogleChatApproverId(value) {
216
24
  const normalized = normalizeGoogleChatTarget(String(value));
@@ -246,7 +54,20 @@ function resolveGoogleChatGroupRequireMention(params) {
246
54
  }
247
55
  //#endregion
248
56
  //#region extensions/googlechat/src/channel.adapters.ts
249
- const loadGoogleChatChannelRuntime$2 = createLazyRuntimeNamedExport(() => import("./channel.runtime-Bu7xf0wo.js"), "googleChatChannelRuntime");
57
+ const loadGoogleChatChannelRuntime$2 = createLazyRuntimeNamedExport(() => import("./channel.runtime-CRRchMB8.js"), "googleChatChannelRuntime");
58
+ function createGoogleChatSendReceipt(params) {
59
+ const messageId = params.messageId?.trim();
60
+ return createMessageReceiptFromOutboundResults({
61
+ results: messageId ? [{
62
+ channel: "googlechat",
63
+ messageId,
64
+ chatId: params.chatId,
65
+ conversationId: params.chatId
66
+ }] : [],
67
+ threadId: params.chatId,
68
+ kind: params.kind
69
+ });
70
+ }
250
71
  const formatAllowFromEntry = (entry) => normalizeLowercaseStringOrEmpty(entry.trim().replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:/i, "").replace(/^users\//i, ""));
251
72
  const collectGoogleChatSecurityWarnings = composeAccountWarningCollectors(createAllowlistProviderOpenWarningCollector({
252
73
  providerConfigPresent: (cfg) => cfg.channels?.googlechat !== void 0,
@@ -351,14 +172,20 @@ const googlechatOutboundAdapter = {
351
172
  });
352
173
  const thread = typeof threadId === "number" ? String(threadId) : threadId ?? replyToId ?? void 0;
353
174
  const { sendGoogleChatMessage } = await loadGoogleChatChannelRuntime$2();
175
+ const messageId = (await sendGoogleChatMessage({
176
+ account,
177
+ space,
178
+ text,
179
+ thread
180
+ }))?.messageName ?? "";
354
181
  return {
355
- messageId: (await sendGoogleChatMessage({
356
- account,
357
- space,
358
- text,
359
- thread
360
- }))?.messageName ?? "",
361
- chatId: space
182
+ messageId,
183
+ chatId: space,
184
+ receipt: createGoogleChatSendReceipt({
185
+ messageId,
186
+ chatId: space,
187
+ kind: "text"
188
+ })
362
189
  };
363
190
  },
364
191
  sendMedia: async ({ cfg, to, text, mediaUrl, mediaAccess, mediaLocalRoots, mediaReadFile, accountId, replyToId, threadId }) => {
@@ -380,7 +207,7 @@ const googlechatOutboundAdapter = {
380
207
  const loaded = /^https?:\/\//i.test(mediaUrl) ? await fetchRemoteMedia({
381
208
  url: mediaUrl,
382
209
  maxBytes: effectiveMaxBytes
383
- }) : await loadOutboundMediaFromUrl$1(mediaUrl, {
210
+ }) : await loadOutboundMediaFromUrl(mediaUrl, {
384
211
  maxBytes: effectiveMaxBytes,
385
212
  mediaAccess,
386
213
  mediaLocalRoots,
@@ -394,22 +221,41 @@ const googlechatOutboundAdapter = {
394
221
  buffer: loaded.buffer,
395
222
  contentType: loaded.contentType
396
223
  });
224
+ const messageId = (await sendGoogleChatMessage({
225
+ account,
226
+ space,
227
+ text,
228
+ thread,
229
+ attachments: upload.attachmentUploadToken ? [{
230
+ attachmentUploadToken: upload.attachmentUploadToken,
231
+ contentName: loaded.fileName
232
+ }] : void 0
233
+ }))?.messageName ?? "";
397
234
  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
235
+ messageId,
236
+ chatId: space,
237
+ receipt: createGoogleChatSendReceipt({
238
+ messageId,
239
+ chatId: space,
240
+ kind: "media"
241
+ })
409
242
  };
410
243
  }
411
244
  }
412
245
  };
246
+ const googlechatMessageAdapter = defineChannelMessageAdapter({
247
+ id: "googlechat",
248
+ durableFinal: { capabilities: {
249
+ text: true,
250
+ media: true,
251
+ thread: true,
252
+ messageSendingHooks: true
253
+ } },
254
+ send: {
255
+ text: googlechatOutboundAdapter.attachedResults.sendText,
256
+ media: googlechatOutboundAdapter.attachedResults.sendMedia
257
+ }
258
+ });
413
259
  //#endregion
414
260
  //#region extensions/googlechat/src/doctor.ts
415
261
  function asObjectRecord(value) {
@@ -449,7 +295,7 @@ const collectGoogleChatMutableAllowlistWarnings = createDangerousNameMatchingMut
449
295
  });
450
296
  //#endregion
451
297
  //#region extensions/googlechat/src/gateway.ts
452
- const loadGoogleChatChannelRuntime$1 = createLazyRuntimeNamedExport(() => import("./channel.runtime-Bu7xf0wo.js"), "googleChatChannelRuntime");
298
+ const loadGoogleChatChannelRuntime$1 = createLazyRuntimeNamedExport(() => import("./channel.runtime-CRRchMB8.js"), "googleChatChannelRuntime");
453
299
  async function startGoogleChatGatewayAccount(ctx) {
454
300
  const account = ctx.account;
455
301
  const statusSink = createAccountStatusSink({
@@ -489,7 +335,7 @@ async function startGoogleChatGatewayAccount(ctx) {
489
335
  }
490
336
  //#endregion
491
337
  //#region extensions/googlechat/src/channel.ts
492
- const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-Bu7xf0wo.js"), "googleChatChannelRuntime");
338
+ const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CRRchMB8.js"), "googleChatChannelRuntime");
493
339
  const meta = {
494
340
  id: "googlechat",
495
341
  label: "Google Chat",
@@ -527,9 +373,25 @@ const googleChatConfigAdapter = createScopedChannelConfigAdapter({
527
373
  resolveDefaultTo: (account) => account.config.defaultTo
528
374
  });
529
375
  const googlechatActions = {
530
- describeMessageTool: (ctx) => googlechatMessageActions.describeMessageTool?.(ctx) ?? null,
531
- extractToolSend: (ctx) => googlechatMessageActions.extractToolSend?.(ctx) ?? null,
376
+ describeMessageTool: ({ cfg, accountId }) => {
377
+ const accounts = accountId ? [resolveGoogleChatAccount({
378
+ cfg,
379
+ accountId
380
+ })].filter((account) => account.enabled && account.credentialSource !== "none") : listGoogleChatAccountIds(cfg).map((id) => resolveGoogleChatAccount({
381
+ cfg,
382
+ accountId: id
383
+ })).filter((account) => account.enabled && account.credentialSource !== "none");
384
+ if (accounts.length === 0) return null;
385
+ const actions = new Set(["send", "upload-file"]);
386
+ if (accounts.some((account) => account.config.actions?.reactions !== false)) {
387
+ actions.add("react");
388
+ actions.add("reactions");
389
+ }
390
+ return { actions: Array.from(actions) };
391
+ },
392
+ extractToolSend: ({ args }) => extractToolSend(args, "sendMessage"),
532
393
  handleAction: async (ctx) => {
394
+ const { googlechatMessageActions } = await import("./actions-CSUbe7VX.js");
533
395
  if (!googlechatMessageActions.handleAction) throw new Error("Google Chat actions are not available.");
534
396
  return await googlechatMessageActions.handleAction(ctx);
535
397
  }
@@ -589,6 +451,7 @@ const googlechatPlugin = createChatChannelPlugin({
589
451
  }
590
452
  },
591
453
  directory: googlechatDirectoryAdapter,
454
+ message: googlechatMessageAdapter,
592
455
  resolver: { resolveTargets: async ({ inputs, kind }) => {
593
456
  return inputs.map((input) => {
594
457
  const normalized = normalizeGoogleChatTarget(input);
@@ -1,4 +1,4 @@
1
- import { a as buildChannelConfigSchema, r as GoogleChatConfigSchema } from "./runtime-api-wkIdfwqY.js";
1
+ import { a as buildChannelConfigSchema, r as GoogleChatConfigSchema } from "./runtime-api-JpbBRVpx.js";
2
2
  //#region extensions/googlechat/src/config-schema.ts
3
3
  const GoogleChatChannelConfigSchema = buildChannelConfigSchema(GoogleChatConfigSchema);
4
4
  //#endregion
@@ -1,2 +1,2 @@
1
- import { t as googlechatPlugin } from "./channel-CHniJUSZ.js";
1
+ import { t as googlechatPlugin } from "./channel-DHHatGQ1.js";
2
2
  export { googlechatPlugin };
@@ -1,5 +1,5 @@
1
- import { A as resolveInboundRouteEnvelopeBuilderWithRuntime, D as resolveDefaultGroupPolicy, F as warnMissingProviderGroupPolicyFallbackOnce, L as getGoogleChatRuntime, M as resolveWebhookPath, O as resolveDmGroupAccessWithLists, T as resolveAllowlistProviderRuntimeGroupPolicy, f as evaluateGroupRouteAccessForPolicy, g as isDangerousNameMatchingEnabled, j as resolveSenderScopedGroupPolicy, l as createChannelPairingController, n as GROUP_POLICY_BLOCKED_LABEL, u as createChannelReplyPipeline } from "./runtime-api-wkIdfwqY.js";
2
- import { c as sendGoogleChatMessage, d as verifyGoogleChatRequest, i as downloadGoogleChatMedia, l as updateGoogleChatMessage, n as deleteGoogleChatMessage, s as probeGoogleChat, u as uploadGoogleChatAttachment } from "./api-C8OqyWsW.js";
1
+ import { A as resolveInboundRouteEnvelopeBuilderWithRuntime, D as resolveDefaultGroupPolicy, F as warnMissingProviderGroupPolicyFallbackOnce, L as getGoogleChatRuntime, M as resolveWebhookPath, O as resolveDmGroupAccessWithLists, T as resolveAllowlistProviderRuntimeGroupPolicy, f as evaluateGroupRouteAccessForPolicy, g as isDangerousNameMatchingEnabled, j as resolveSenderScopedGroupPolicy, n as GROUP_POLICY_BLOCKED_LABEL, u as createChannelPairingController } from "./runtime-api-JpbBRVpx.js";
2
+ import { c as sendGoogleChatMessage, d as verifyGoogleChatRequest, i as downloadGoogleChatMedia, l as updateGoogleChatMessage, n as deleteGoogleChatMessage, s as probeGoogleChat, u as uploadGoogleChatAttachment } from "./api-BwM7NTay.js";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
4
4
  import { resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-inbound";
5
5
  import { registerWebhookTargetWithPluginRoute, resolveWebhookTargetWithAuthOrReject, withResolvedWebhookRequestPipeline } from "openclaw/plugin-sdk/webhook-targets";
@@ -281,6 +281,19 @@ async function applyGoogleChatInboundAccessPolicy(params) {
281
281
  };
282
282
  }
283
283
  //#endregion
284
+ //#region extensions/googlechat/src/monitor-durable.ts
285
+ function resolveGoogleChatDurableReplyOptions(params) {
286
+ if (params.infoKind !== "final" || params.typingMessageName) return false;
287
+ const threadId = params.payload.replyToId?.trim() || void 0;
288
+ return {
289
+ to: params.spaceId,
290
+ ...threadId ? {
291
+ replyToId: threadId,
292
+ threadId
293
+ } : {}
294
+ };
295
+ }
296
+ //#endregion
284
297
  //#region extensions/googlechat/src/monitor-reply-delivery.ts
285
298
  async function deliverGoogleChatReply(params) {
286
299
  const { payload, account, spaceId, runtime, core, config, statusSink } = params;
@@ -791,12 +804,6 @@ async function processMessageWithPipeline(params) {
791
804
  } catch (err) {
792
805
  runtime.error?.(`Failed sending typing message: ${String(err)}`);
793
806
  }
794
- const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
795
- cfg: config,
796
- agentId: route.agentId,
797
- channel: "googlechat",
798
- accountId: route.accountId
799
- });
800
807
  await core.channel.turn.run({
801
808
  channel: "googlechat",
802
809
  accountId: route.accountId,
@@ -821,6 +828,12 @@ async function processMessageWithPipeline(params) {
821
828
  recordInboundSession: core.channel.session.recordInboundSession,
822
829
  dispatchReplyWithBufferedBlockDispatcher: core.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
823
830
  delivery: {
831
+ durable: (payload, info) => resolveGoogleChatDurableReplyOptions({
832
+ payload,
833
+ infoKind: info.kind,
834
+ spaceId,
835
+ typingMessageName
836
+ }),
824
837
  deliver: async (payload) => {
825
838
  await deliverGoogleChatReply({
826
839
  payload,
@@ -834,12 +847,14 @@ async function processMessageWithPipeline(params) {
834
847
  });
835
848
  typingMessageName = void 0;
836
849
  },
850
+ onDelivered: () => {
851
+ statusSink?.({ lastOutboundAt: Date.now() });
852
+ },
837
853
  onError: (err, info) => {
838
854
  runtime.error?.(`[${account.accountId}] Google Chat ${info.kind} reply failed: ${String(err)}`);
839
855
  }
840
856
  },
841
- dispatcherOptions: replyPipeline,
842
- replyOptions: { onModelSelected },
857
+ replyPipeline: {},
843
858
  record: { onRecordError: (err) => {
844
859
  runtime.error?.(`googlechat: failed updating session meta: ${String(err)}`);
845
860
  } }
@@ -1,3 +1,3 @@
1
- import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-CG1sLToP.js";
2
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DtQ_IO7J.js";
1
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-BsERSjSe.js";
2
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DLAG2_NI.js";
3
3
  export { collectRuntimeConfigAssignments, legacyConfigRules, normalizeCompatibilityConfig, secretTargetRegistryEntries };
@@ -1,2 +1,2 @@
1
- import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-CG1sLToP.js";
1
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-BsERSjSe.js";
2
2
  export { legacyConfigRules, normalizeCompatibilityConfig };
@@ -1,13 +1,12 @@
1
- import { createActionGate as createActionGate$1, jsonResult as jsonResult$1, readNumberParam as readNumberParam$1, readReactionParams as readReactionParams$1, readStringParam as readStringParam$1 } from "openclaw/plugin-sdk/channel-actions";
2
- import { loadOutboundMediaFromUrl as loadOutboundMediaFromUrl$1 } from "openclaw/plugin-sdk/outbound-media";
3
1
  import { extractToolSend as extractToolSend$1 } from "openclaw/plugin-sdk/tool-send";
4
2
  import { fetchWithSsrFGuard as fetchWithSsrFGuard$1 } from "openclaw/plugin-sdk/ssrf-runtime";
5
3
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
4
+ import { createActionGate as createActionGate$1, jsonResult as jsonResult$1, readNumberParam as readNumberParam$1, readReactionParams as readReactionParams$1, readStringParam as readStringParam$1 } from "openclaw/plugin-sdk/channel-actions";
6
5
  import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-primitives";
7
6
  import { missingTargetError } from "openclaw/plugin-sdk/channel-feedback";
8
7
  import { createAccountStatusSink as createAccountStatusSink$1, runPassiveAccountLifecycle as runPassiveAccountLifecycle$1 } from "openclaw/plugin-sdk/channel-lifecycle";
9
8
  import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
10
- import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
9
+ import { createChannelMessageReplyPipeline } from "openclaw/plugin-sdk/channel-message";
11
10
  import { evaluateGroupRouteAccessForPolicy, resolveDmGroupAccessWithLists, resolveSenderScopedGroupPolicy } from "openclaw/plugin-sdk/channel-policy";
12
11
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
13
12
  import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
@@ -15,6 +14,7 @@ import { GoogleChatConfigSchema } from "openclaw/plugin-sdk/bundled-channel-conf
15
14
  import { GROUP_POLICY_BLOCKED_LABEL, resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
16
15
  import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
17
16
  import { fetchRemoteMedia, resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
17
+ import { loadOutboundMediaFromUrl as loadOutboundMediaFromUrl$1 } from "openclaw/plugin-sdk/outbound-media";
18
18
  import { resolveInboundMentionDecision as resolveInboundMentionDecision$1 } from "openclaw/plugin-sdk/channel-inbound";
19
19
  import { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";
20
20
  import { resolveWebhookPath } from "openclaw/plugin-sdk/webhook-path";
@@ -27,4 +27,4 @@ const { setRuntime: setGoogleChatRuntime, getRuntime: getGoogleChatRuntime } = c
27
27
  errorMessage: "Google Chat runtime not initialized"
28
28
  });
29
29
  //#endregion
30
- export { resolveInboundRouteEnvelopeBuilderWithRuntime as A, readStringParam$1 as C, resolveDefaultGroupPolicy as D, resolveChannelMediaMaxBytes as E, warnMissingProviderGroupPolicyFallbackOnce as F, withResolvedWebhookRequestPipeline$1 as I, getGoogleChatRuntime as L, resolveWebhookPath as M, resolveWebhookTargetWithAuthOrReject$1 as N, resolveDmGroupAccessWithLists as O, runPassiveAccountLifecycle$1 as P, setGoogleChatRuntime as R, readReactionParams$1 as S, resolveAllowlistProviderRuntimeGroupPolicy as T, jsonResult$1 as _, buildChannelConfigSchema as a, readJsonWebhookBodyOrReject$1 as b, createActionGate$1 as c, createWebhookInFlightLimiter$1 as d, evaluateGroupRouteAccessForPolicy as f, isDangerousNameMatchingEnabled as g, fetchWithSsrFGuard$1 as h, PAIRING_APPROVED_MESSAGE as i, resolveSenderScopedGroupPolicy as j, resolveInboundMentionDecision$1 as k, createChannelPairingController as l, fetchRemoteMedia as m, GROUP_POLICY_BLOCKED_LABEL as n, chunkTextForOutbound as o, extractToolSend$1 as p, GoogleChatConfigSchema as r, createAccountStatusSink$1 as s, DEFAULT_ACCOUNT_ID as t, createChannelReplyPipeline as u, loadOutboundMediaFromUrl$1 as v, registerWebhookTargetWithPluginRoute$1 as w, readNumberParam$1 as x, missingTargetError as y };
30
+ export { resolveInboundRouteEnvelopeBuilderWithRuntime as A, readStringParam$1 as C, resolveDefaultGroupPolicy as D, resolveChannelMediaMaxBytes as E, warnMissingProviderGroupPolicyFallbackOnce as F, withResolvedWebhookRequestPipeline$1 as I, getGoogleChatRuntime as L, resolveWebhookPath as M, resolveWebhookTargetWithAuthOrReject$1 as N, resolveDmGroupAccessWithLists as O, runPassiveAccountLifecycle$1 as P, setGoogleChatRuntime as R, readReactionParams$1 as S, resolveAllowlistProviderRuntimeGroupPolicy as T, jsonResult$1 as _, buildChannelConfigSchema as a, readJsonWebhookBodyOrReject$1 as b, createActionGate$1 as c, createWebhookInFlightLimiter$1 as d, evaluateGroupRouteAccessForPolicy as f, isDangerousNameMatchingEnabled as g, fetchWithSsrFGuard$1 as h, PAIRING_APPROVED_MESSAGE as i, resolveSenderScopedGroupPolicy as j, resolveInboundMentionDecision$1 as k, createChannelMessageReplyPipeline as l, fetchRemoteMedia as m, GROUP_POLICY_BLOCKED_LABEL as n, chunkTextForOutbound as o, extractToolSend$1 as p, GoogleChatConfigSchema as r, createAccountStatusSink$1 as s, DEFAULT_ACCOUNT_ID as t, createChannelPairingController as u, loadOutboundMediaFromUrl$1 as v, registerWebhookTargetWithPluginRoute$1 as w, readNumberParam$1 as x, missingTargetError as y };
@@ -1,2 +1,2 @@
1
- import { A as resolveInboundRouteEnvelopeBuilderWithRuntime, C as readStringParam, D as resolveDefaultGroupPolicy, E as resolveChannelMediaMaxBytes, F as warnMissingProviderGroupPolicyFallbackOnce, I as withResolvedWebhookRequestPipeline, M as resolveWebhookPath, N as resolveWebhookTargetWithAuthOrReject, O as resolveDmGroupAccessWithLists, P as runPassiveAccountLifecycle, R as setGoogleChatRuntime, S as readReactionParams, T as resolveAllowlistProviderRuntimeGroupPolicy, _ as jsonResult, a as buildChannelConfigSchema, b as readJsonWebhookBodyOrReject, c as createActionGate, d as createWebhookInFlightLimiter, f as evaluateGroupRouteAccessForPolicy, g as isDangerousNameMatchingEnabled, h as fetchWithSsrFGuard, i as PAIRING_APPROVED_MESSAGE, j as resolveSenderScopedGroupPolicy, k as resolveInboundMentionDecision, l as createChannelPairingController, m as fetchRemoteMedia, n as GROUP_POLICY_BLOCKED_LABEL, o as chunkTextForOutbound, p as extractToolSend, r as GoogleChatConfigSchema, s as createAccountStatusSink, t as DEFAULT_ACCOUNT_ID, u as createChannelReplyPipeline, v as loadOutboundMediaFromUrl, w as registerWebhookTargetWithPluginRoute, x as readNumberParam, y as missingTargetError } from "./runtime-api-wkIdfwqY.js";
2
- export { DEFAULT_ACCOUNT_ID, GROUP_POLICY_BLOCKED_LABEL, GoogleChatConfigSchema, PAIRING_APPROVED_MESSAGE, buildChannelConfigSchema, chunkTextForOutbound, createAccountStatusSink, createActionGate, createChannelPairingController, createChannelReplyPipeline, createWebhookInFlightLimiter, evaluateGroupRouteAccessForPolicy, extractToolSend, fetchRemoteMedia, fetchWithSsrFGuard, isDangerousNameMatchingEnabled, jsonResult, loadOutboundMediaFromUrl, missingTargetError, readJsonWebhookBodyOrReject, readNumberParam, readReactionParams, readStringParam, registerWebhookTargetWithPluginRoute, resolveAllowlistProviderRuntimeGroupPolicy, resolveChannelMediaMaxBytes, resolveDefaultGroupPolicy, resolveDmGroupAccessWithLists, resolveInboundMentionDecision, resolveInboundRouteEnvelopeBuilderWithRuntime, resolveSenderScopedGroupPolicy, resolveWebhookPath, resolveWebhookTargetWithAuthOrReject, runPassiveAccountLifecycle, setGoogleChatRuntime, warnMissingProviderGroupPolicyFallbackOnce, withResolvedWebhookRequestPipeline };
1
+ import { A as resolveInboundRouteEnvelopeBuilderWithRuntime, C as readStringParam, D as resolveDefaultGroupPolicy, E as resolveChannelMediaMaxBytes, F as warnMissingProviderGroupPolicyFallbackOnce, I as withResolvedWebhookRequestPipeline, M as resolveWebhookPath, N as resolveWebhookTargetWithAuthOrReject, O as resolveDmGroupAccessWithLists, P as runPassiveAccountLifecycle, R as setGoogleChatRuntime, S as readReactionParams, T as resolveAllowlistProviderRuntimeGroupPolicy, _ as jsonResult, a as buildChannelConfigSchema, b as readJsonWebhookBodyOrReject, c as createActionGate, d as createWebhookInFlightLimiter, f as evaluateGroupRouteAccessForPolicy, g as isDangerousNameMatchingEnabled, h as fetchWithSsrFGuard, i as PAIRING_APPROVED_MESSAGE, j as resolveSenderScopedGroupPolicy, k as resolveInboundMentionDecision, l as createChannelMessageReplyPipeline, m as fetchRemoteMedia, n as GROUP_POLICY_BLOCKED_LABEL, o as chunkTextForOutbound, p as extractToolSend, r as GoogleChatConfigSchema, s as createAccountStatusSink, t as DEFAULT_ACCOUNT_ID, u as createChannelPairingController, v as loadOutboundMediaFromUrl, w as registerWebhookTargetWithPluginRoute, x as readNumberParam, y as missingTargetError } from "./runtime-api-JpbBRVpx.js";
2
+ export { DEFAULT_ACCOUNT_ID, GROUP_POLICY_BLOCKED_LABEL, GoogleChatConfigSchema, PAIRING_APPROVED_MESSAGE, buildChannelConfigSchema, chunkTextForOutbound, createAccountStatusSink, createActionGate, createChannelMessageReplyPipeline, createChannelPairingController, createWebhookInFlightLimiter, evaluateGroupRouteAccessForPolicy, extractToolSend, fetchRemoteMedia, fetchWithSsrFGuard, isDangerousNameMatchingEnabled, jsonResult, loadOutboundMediaFromUrl, missingTargetError, readJsonWebhookBodyOrReject, readNumberParam, readReactionParams, readStringParam, registerWebhookTargetWithPluginRoute, resolveAllowlistProviderRuntimeGroupPolicy, resolveChannelMediaMaxBytes, resolveDefaultGroupPolicy, resolveDmGroupAccessWithLists, resolveInboundMentionDecision, resolveInboundRouteEnvelopeBuilderWithRuntime, resolveSenderScopedGroupPolicy, resolveWebhookPath, resolveWebhookTargetWithAuthOrReject, runPassiveAccountLifecycle, setGoogleChatRuntime, warnMissingProviderGroupPolicyFallbackOnce, withResolvedWebhookRequestPipeline };
@@ -1,2 +1,2 @@
1
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-DtQ_IO7J.js";
1
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-DLAG2_NI.js";
2
2
  export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
@@ -1,4 +1,5 @@
1
- import { a as resolveDefaultGoogleChatAccountId, i as listGoogleChatAccountIds, n as googlechatSetupAdapter, o as resolveGoogleChatAccount, s as resolveGoogleChatConfigAccessorAccount, t as googlechatSetupWizard } from "./setup-surface-CofP-Gg9.js";
1
+ import { a as resolveGoogleChatConfigAccessorAccount, i as resolveGoogleChatAccount, n as listGoogleChatAccountIds, r as resolveDefaultGoogleChatAccountId } from "./accounts-BbM9zX1S.js";
2
+ import { n as googlechatSetupAdapter, t as googlechatSetupWizard } from "./setup-surface-S4ArLnR8.js";
2
3
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
3
4
  import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
4
5
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
@@ -1,99 +1,7 @@
1
- import { safeParseJsonWithSchema, safeParseWithSchema } from "openclaw/plugin-sdk/extension-shared";
2
- import { DEFAULT_ACCOUNT_ID, createAccountListHelpers, normalizeAccountId, resolveAccountEntry, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
3
- import { isSecretRef } from "openclaw/plugin-sdk/secret-input";
1
+ import { i as resolveGoogleChatAccount, r as resolveDefaultGoogleChatAccountId } from "./accounts-BbM9zX1S.js";
4
2
  import { normalizeOptionalString, normalizeStringifiedOptionalString } from "openclaw/plugin-sdk/text-runtime";
5
- import { z } from "zod";
6
3
  import { createPatchedAccountSetupAdapter, createSetupInputPresenceValidator } from "openclaw/plugin-sdk/setup-runtime";
7
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, addWildcardAllowFrom, applySetupAccountConfigPatch, createPromptParsedAllowFromForAccount, createStandardChannelSetupStatus, formatDocsLink, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, splitSetupEntries } from "openclaw/plugin-sdk/setup";
8
- //#region extensions/googlechat/src/accounts.ts
9
- const ENV_SERVICE_ACCOUNT$1 = "GOOGLE_CHAT_SERVICE_ACCOUNT";
10
- const ENV_SERVICE_ACCOUNT_FILE$1 = "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE";
11
- const JsonRecordSchema = z.record(z.string(), z.unknown());
12
- const { listAccountIds: listGoogleChatAccountIds, resolveDefaultAccountId: resolveDefaultGoogleChatAccountId } = createAccountListHelpers("googlechat");
13
- function mergeGoogleChatAccountConfig(cfg, accountId) {
14
- const raw = cfg.channels?.["googlechat"] ?? {};
15
- const base = resolveMergedAccountConfig({
16
- channelConfig: raw,
17
- accounts: raw.accounts,
18
- accountId,
19
- omitKeys: ["defaultAccount"]
20
- });
21
- const defaultAccountConfig = resolveAccountEntry(raw.accounts, DEFAULT_ACCOUNT_ID) ?? {};
22
- if (accountId === DEFAULT_ACCOUNT_ID) return base;
23
- const { enabled: _ignoredEnabled, dangerouslyAllowNameMatching: _ignoredDangerouslyAllowNameMatching, serviceAccount: _ignoredServiceAccount, serviceAccountRef: _ignoredServiceAccountRef, serviceAccountFile: _ignoredServiceAccountFile, ...defaultAccountShared } = defaultAccountConfig;
24
- return {
25
- ...defaultAccountShared,
26
- ...base
27
- };
28
- }
29
- function resolveGoogleChatConfigAccessorAccount(params) {
30
- const accountId = normalizeAccountId(params.accountId ?? params.cfg.channels?.googlechat?.defaultAccount);
31
- return { config: mergeGoogleChatAccountConfig(params.cfg, accountId) };
32
- }
33
- function parseServiceAccount(value) {
34
- if (isSecretRef(value)) return null;
35
- if (typeof value === "string") {
36
- const trimmed = value.trim();
37
- if (!trimmed) return null;
38
- return safeParseJsonWithSchema(JsonRecordSchema, trimmed);
39
- }
40
- return safeParseWithSchema(JsonRecordSchema, value);
41
- }
42
- function resolveCredentialsFromConfig(params) {
43
- const { account, accountId } = params;
44
- const inline = parseServiceAccount(account.serviceAccount);
45
- if (inline) return {
46
- credentials: inline,
47
- source: "inline"
48
- };
49
- if (isSecretRef(account.serviceAccount)) throw new Error(`channels.googlechat.accounts.${accountId}.serviceAccount: unresolved SecretRef "${account.serviceAccount.source}:${account.serviceAccount.provider}:${account.serviceAccount.id}". Resolve this command against an active gateway runtime snapshot before reading it.`);
50
- if (isSecretRef(account.serviceAccountRef)) throw new Error(`channels.googlechat.accounts.${accountId}.serviceAccount: unresolved SecretRef "${account.serviceAccountRef.source}:${account.serviceAccountRef.provider}:${account.serviceAccountRef.id}". Resolve this command against an active gateway runtime snapshot before reading it.`);
51
- const file = normalizeOptionalString(account.serviceAccountFile);
52
- if (file) return {
53
- credentialsFile: file,
54
- source: "file"
55
- };
56
- if (accountId === DEFAULT_ACCOUNT_ID) {
57
- const envJson = process.env[ENV_SERVICE_ACCOUNT$1];
58
- const envInline = parseServiceAccount(envJson);
59
- if (envInline) return {
60
- credentials: envInline,
61
- source: "env"
62
- };
63
- const envFile = normalizeOptionalString(process.env[ENV_SERVICE_ACCOUNT_FILE$1]);
64
- if (envFile) return {
65
- credentialsFile: envFile,
66
- source: "env"
67
- };
68
- }
69
- return { source: "none" };
70
- }
71
- function resolveGoogleChatAccount(params) {
72
- const accountId = normalizeAccountId(params.accountId ?? params.cfg.channels?.["googlechat"]?.defaultAccount);
73
- const baseEnabled = params.cfg.channels?.["googlechat"]?.enabled !== false;
74
- const merged = mergeGoogleChatAccountConfig(params.cfg, accountId);
75
- const accountEnabled = merged.enabled !== false;
76
- const enabled = baseEnabled && accountEnabled;
77
- const credentials = resolveCredentialsFromConfig({
78
- accountId,
79
- account: merged
80
- });
81
- return {
82
- accountId,
83
- name: normalizeOptionalString(merged.name),
84
- enabled,
85
- config: merged,
86
- credentialSource: credentials.source,
87
- credentials: credentials.credentials,
88
- credentialsFile: credentials.credentialsFile
89
- };
90
- }
91
- function listEnabledGoogleChatAccounts(cfg) {
92
- return listGoogleChatAccountIds(cfg).map((accountId) => resolveGoogleChatAccount({
93
- cfg,
94
- accountId
95
- })).filter((account) => account.enabled);
96
- }
4
+ import { DEFAULT_ACCOUNT_ID, addWildcardAllowFrom, applySetupAccountConfigPatch, createPromptParsedAllowFromForAccount, createStandardChannelSetupStatus, formatDocsLink, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, splitSetupEntries } from "openclaw/plugin-sdk/setup";
97
5
  const googlechatSetupAdapter = createPatchedAccountSetupAdapter({
98
6
  channelKey: "googlechat",
99
7
  validateInput: createSetupInputPresenceValidator({
@@ -130,7 +38,7 @@ const googlechatDmPolicy = {
130
38
  channel,
131
39
  policyKey: "channels.googlechat.dm.policy",
132
40
  allowFromKey: "channels.googlechat.dm.allowFrom",
133
- resolveConfigKeys: (cfg, accountId) => (accountId ?? resolveDefaultGoogleChatAccountId(cfg)) !== DEFAULT_ACCOUNT_ID$1 ? {
41
+ resolveConfigKeys: (cfg, accountId) => (accountId ?? resolveDefaultGoogleChatAccountId(cfg)) !== DEFAULT_ACCOUNT_ID ? {
134
42
  policyKey: `channels.googlechat.accounts.${accountId ?? resolveDefaultGoogleChatAccountId(cfg)}.dm.policy`,
135
43
  allowFromKey: `channels.googlechat.accounts.${accountId ?? resolveDefaultGoogleChatAccountId(cfg)}.dm.allowFrom`
136
44
  } : {
@@ -221,7 +129,7 @@ const googlechatSetupWizard = {
221
129
  ]
222
130
  },
223
131
  prepare: async ({ cfg, accountId, credentialValues, prompter }) => {
224
- if (accountId === DEFAULT_ACCOUNT_ID$1 && (Boolean(process.env[ENV_SERVICE_ACCOUNT]) || Boolean(process.env[ENV_SERVICE_ACCOUNT_FILE]))) {
132
+ if (accountId === DEFAULT_ACCOUNT_ID && (Boolean(process.env[ENV_SERVICE_ACCOUNT]) || Boolean(process.env[ENV_SERVICE_ACCOUNT_FILE]))) {
225
133
  if (await prompter.confirm({
226
134
  message: "Use GOOGLE_CHAT_SERVICE_ACCOUNT env vars?",
227
135
  initialValue: true
@@ -306,4 +214,4 @@ const googlechatSetupWizard = {
306
214
  dmPolicy: googlechatDmPolicy
307
215
  };
308
216
  //#endregion
309
- export { resolveDefaultGoogleChatAccountId as a, listGoogleChatAccountIds as i, googlechatSetupAdapter as n, resolveGoogleChatAccount as o, listEnabledGoogleChatAccounts as r, resolveGoogleChatConfigAccessorAccount as s, googlechatSetupWizard as t };
217
+ export { googlechatSetupAdapter as n, googlechatSetupWizard as t };
@@ -0,0 +1,43 @@
1
+ import { a as findGoogleChatDirectMessage } from "./api-BwM7NTay.js";
2
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
3
+ //#region extensions/googlechat/src/targets.ts
4
+ function normalizeGoogleChatTarget(raw) {
5
+ const trimmed = raw?.trim();
6
+ if (!trimmed) return;
7
+ const normalized = trimmed.replace(/^(googlechat|google-chat|gchat):/i, "").replace(/^user:(users\/)?/i, "users/").replace(/^space:(spaces\/)?/i, "spaces/");
8
+ if (isGoogleChatUserTarget(normalized)) {
9
+ const suffix = normalized.slice(6);
10
+ return suffix.includes("@") ? `users/${normalizeLowercaseStringOrEmpty(suffix)}` : normalized;
11
+ }
12
+ if (isGoogleChatSpaceTarget(normalized)) return normalized;
13
+ if (normalized.includes("@")) return `users/${normalizeLowercaseStringOrEmpty(normalized)}`;
14
+ return normalized;
15
+ }
16
+ function isGoogleChatUserTarget(value) {
17
+ return normalizeLowercaseStringOrEmpty(value).startsWith("users/");
18
+ }
19
+ function isGoogleChatSpaceTarget(value) {
20
+ return normalizeLowercaseStringOrEmpty(value).startsWith("spaces/");
21
+ }
22
+ function stripMessageSuffix(target) {
23
+ const index = target.indexOf("/messages/");
24
+ if (index === -1) return target;
25
+ return target.slice(0, index);
26
+ }
27
+ async function resolveGoogleChatOutboundSpace(params) {
28
+ const normalized = normalizeGoogleChatTarget(params.target);
29
+ if (!normalized) throw new Error("Missing Google Chat target.");
30
+ const base = stripMessageSuffix(normalized);
31
+ if (isGoogleChatSpaceTarget(base)) return base;
32
+ if (isGoogleChatUserTarget(base)) {
33
+ const dm = await findGoogleChatDirectMessage({
34
+ account: params.account,
35
+ userName: base
36
+ });
37
+ if (!dm?.name) throw new Error(`No Google Chat DM found for ${base}`);
38
+ return dm.name;
39
+ }
40
+ return base;
41
+ }
42
+ //#endregion
43
+ export { resolveGoogleChatOutboundSpace as i, isGoogleChatUserTarget as n, normalizeGoogleChatTarget as r, isGoogleChatSpaceTarget as t };
package/dist/test-api.js CHANGED
@@ -1,3 +1,3 @@
1
- import { R as setGoogleChatRuntime } from "./runtime-api-wkIdfwqY.js";
2
- import { t as googlechatPlugin } from "./channel-CHniJUSZ.js";
1
+ import { R as setGoogleChatRuntime } from "./runtime-api-JpbBRVpx.js";
2
+ import { t as googlechatPlugin } from "./channel-DHHatGQ1.js";
3
3
  export { googlechatPlugin, setGoogleChatRuntime };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/googlechat",
3
- "version": "2026.5.7",
3
+ "version": "2026.5.9-beta.1",
4
4
  "description": "OpenClaw Google Chat channel plugin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,15 +9,14 @@
9
9
  "type": "module",
10
10
  "dependencies": {
11
11
  "gaxios": "7.1.4",
12
- "google-auth-library": "10.6.2",
13
- "zod": "^4.4.3"
12
+ "google-auth-library": "10.6.2"
14
13
  },
15
14
  "devDependencies": {
16
15
  "@openclaw/plugin-sdk": "workspace:*",
17
16
  "openclaw": "workspace:*"
18
17
  },
19
18
  "peerDependencies": {
20
- "openclaw": ">=2026.5.7"
19
+ "openclaw": ">=2026.5.9-beta.1"
21
20
  },
22
21
  "peerDependenciesMeta": {
23
22
  "openclaw": {
@@ -75,10 +74,10 @@
75
74
  "minHostVersion": ">=2026.4.10"
76
75
  },
77
76
  "compat": {
78
- "pluginApi": ">=2026.5.7"
77
+ "pluginApi": ">=2026.5.9-beta.1"
79
78
  },
80
79
  "build": {
81
- "openclawVersion": "2026.5.7"
80
+ "openclawVersion": "2026.5.9-beta.1"
82
81
  },
83
82
  "release": {
84
83
  "publishToClawHub": true,