@open-everywhere/provider-google-chat 0.0.0-stage → 0.7.0

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 (76) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +51 -0
  3. package/README.md +40 -2
  4. package/agent/channels/google-chat-auth.ts +145 -0
  5. package/agent/channels/google-chat-front.ts +337 -0
  6. package/agent/channels/google-chat-identity.ts +97 -0
  7. package/agent/channels/google-chat-message-ops.ts +30 -0
  8. package/agent/channels/google-chat-origin-codec.ts +32 -0
  9. package/agent/channels/google-chat-post-arm.ts +36 -0
  10. package/agent/channels/google-chat-rest.ts +87 -0
  11. package/agent/channels/google-chat.ts +193 -0
  12. package/agent/contributions.ts +16 -0
  13. package/agent/docs-generated.ts +15 -0
  14. package/agent/policy.ts +101 -0
  15. package/agent/provider-events.ts +18 -0
  16. package/agent/register.ts +48 -0
  17. package/agent/surface-parity.ts +63 -0
  18. package/dist/agent/channels/google-chat-auth.d.ts +39 -0
  19. package/dist/agent/channels/google-chat-auth.d.ts.map +1 -0
  20. package/dist/agent/channels/google-chat-auth.js +118 -0
  21. package/dist/agent/channels/google-chat-auth.js.map +1 -0
  22. package/dist/agent/channels/google-chat-front.d.ts +101 -0
  23. package/dist/agent/channels/google-chat-front.d.ts.map +1 -0
  24. package/dist/agent/channels/google-chat-front.js +258 -0
  25. package/dist/agent/channels/google-chat-front.js.map +1 -0
  26. package/dist/agent/channels/google-chat-identity.d.ts +28 -0
  27. package/dist/agent/channels/google-chat-identity.d.ts.map +1 -0
  28. package/dist/agent/channels/google-chat-identity.js +70 -0
  29. package/dist/agent/channels/google-chat-identity.js.map +1 -0
  30. package/dist/agent/channels/google-chat-message-ops.d.ts +10 -0
  31. package/dist/agent/channels/google-chat-message-ops.d.ts.map +1 -0
  32. package/dist/agent/channels/google-chat-message-ops.js +25 -0
  33. package/dist/agent/channels/google-chat-message-ops.js.map +1 -0
  34. package/dist/agent/channels/google-chat-origin-codec.d.ts +6 -0
  35. package/dist/agent/channels/google-chat-origin-codec.d.ts.map +1 -0
  36. package/dist/agent/channels/google-chat-origin-codec.js +31 -0
  37. package/dist/agent/channels/google-chat-origin-codec.js.map +1 -0
  38. package/dist/agent/channels/google-chat-post-arm.d.ts +11 -0
  39. package/dist/agent/channels/google-chat-post-arm.d.ts.map +1 -0
  40. package/dist/agent/channels/google-chat-post-arm.js +31 -0
  41. package/dist/agent/channels/google-chat-post-arm.js.map +1 -0
  42. package/dist/agent/channels/google-chat-rest.d.ts +21 -0
  43. package/dist/agent/channels/google-chat-rest.d.ts.map +1 -0
  44. package/dist/agent/channels/google-chat-rest.js +81 -0
  45. package/dist/agent/channels/google-chat-rest.js.map +1 -0
  46. package/dist/agent/channels/google-chat.d.ts +3 -0
  47. package/dist/agent/channels/google-chat.d.ts.map +1 -0
  48. package/dist/agent/channels/google-chat.js +156 -0
  49. package/dist/agent/channels/google-chat.js.map +1 -0
  50. package/dist/agent/contributions.d.ts +12 -0
  51. package/dist/agent/contributions.d.ts.map +1 -0
  52. package/dist/agent/contributions.js +16 -0
  53. package/dist/agent/contributions.js.map +1 -0
  54. package/dist/agent/docs-generated.d.ts +3 -0
  55. package/dist/agent/docs-generated.d.ts.map +1 -0
  56. package/dist/agent/docs-generated.js +13 -0
  57. package/dist/agent/docs-generated.js.map +1 -0
  58. package/dist/agent/policy.d.ts +38 -0
  59. package/dist/agent/policy.d.ts.map +1 -0
  60. package/dist/agent/policy.js +78 -0
  61. package/dist/agent/policy.js.map +1 -0
  62. package/dist/agent/provider-events.d.ts +3 -0
  63. package/dist/agent/provider-events.d.ts.map +1 -0
  64. package/dist/agent/provider-events.js +16 -0
  65. package/dist/agent/provider-events.js.map +1 -0
  66. package/dist/agent/register.d.ts +3 -0
  67. package/dist/agent/register.d.ts.map +1 -0
  68. package/dist/agent/register.js +37 -0
  69. package/dist/agent/register.js.map +1 -0
  70. package/dist/agent/surface-parity.d.ts +3 -0
  71. package/dist/agent/surface-parity.d.ts.map +1 -0
  72. package/dist/agent/surface-parity.js +59 -0
  73. package/dist/agent/surface-parity.js.map +1 -0
  74. package/package.json +116 -4
  75. package/tsconfig.build.json +22 -0
  76. package/tsconfig.json +15 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-chat-post-arm.d.ts","sourceRoot":"","sources":["../../../agent/channels/google-chat-post-arm.ts"],"names":[],"mappings":"AAQA,OAAO,EAAkB,KAAK,OAAO,EAAiD,MAAM,4CAA4C,CAAC;AACzI,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAA2B,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjG,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,gBAAgB,CAAC;CAC1C;AAED,mGAAmG;AACnG,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUjI;AAED,eAAO,MAAM,iBAAiB,EAAE,OAK/B,CAAC"}
@@ -0,0 +1,31 @@
1
+ // google-chat-post-arm — the Google Chat surface's arm of the post-arms registry. One verb: answer the event that
2
+ // woke the seat, in the same space and thread, as the app (google-chat-rest.ts). The space and thread come from
3
+ // the stored origin, which the front froze from the token-verified event; the model never names one.
4
+ //
5
+ // 🔴 THE POLICY IS RE-READ AT SEND TIME. A surface turned off between the wake and the reply sends nothing, and
6
+ // an origin minted under another workspace segment is refused.
7
+ // NO EDIT, NO REACTION yet: the arm registers neither, and the capability manifest says so.
8
+ import { markNotApplied } from "@open-everywhere/core/agent/adapter-api.js";
9
+ import { googleChatPolicy } from "../policy.js";
10
+ import { createGoogleChatMessage } from "./google-chat-rest.js";
11
+ import { googleChatWakeOriginCodec } from "./google-chat-origin-codec.js";
12
+ /** Send one reply for a stored origin. Throws a token-free error when the reply did not go out. */
13
+ export async function sendGoogleChatReply(origin, text, deps = {}) {
14
+ if (!googleChatWakeOriginCodec.validate(origin)) {
15
+ throw markNotApplied(new Error("reply couldn't post to Google Chat — the stored reply target is not a valid Google Chat origin."));
16
+ }
17
+ const policy = (deps.policy ?? googleChatPolicy)();
18
+ if (!policy.enabled)
19
+ throw markNotApplied(new Error("reply couldn't post to Google Chat — the surface is turned off in agent/google-chat/policy.ts."));
20
+ if (origin.workspace !== policy.workspace) {
21
+ throw markNotApplied(new Error("reply couldn't post to Google Chat — the stored reply target belongs to another workspace."));
22
+ }
23
+ return createGoogleChatMessage({ space: origin.space, ...(origin.thread !== undefined ? { thread: origin.thread } : {}), text }, deps);
24
+ }
25
+ export const googleChatPostArm = {
26
+ platform: "google-chat",
27
+ dispatchUnified: (origin, text, _deps) => sendGoogleChatReply(origin, text),
28
+ describeRedriveTarget: (origin) => `Google Chat ${origin.space}`,
29
+ redriveContainerId: (origin) => origin.space,
30
+ };
31
+ //# sourceMappingURL=google-chat-post-arm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-chat-post-arm.js","sourceRoot":"","sources":["../../../agent/channels/google-chat-post-arm.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAClH,gHAAgH;AAChH,qGAAqG;AACrG,EAAE;AACF,gHAAgH;AAChH,+DAA+D;AAC/D,4FAA4F;AAE5F,OAAO,EAAE,cAAc,EAA+D,MAAM,4CAA4C,CAAC;AACzI,OAAO,EAAE,gBAAgB,EAAyB,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAA2B,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,yBAAyB,EAAyB,MAAM,+BAA+B,CAAC;AAMjG,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAwB,EAAE,IAAY,EAAE,IAAI,GAAwB,EAAE;IAC9G,IAAI,CAAC,yBAAyB,CAAC,QAAQ,CAAC,MAA4C,CAAC,EAAE,CAAC;QACtF,MAAM,cAAc,CAAC,IAAI,KAAK,CAAC,iGAAiG,CAAC,CAAC,CAAC;IACrI,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,gBAAgB,CAAC,EAAE,CAAC;IACnD,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,cAAc,CAAC,IAAI,KAAK,CAAC,gGAAgG,CAAC,CAAC,CAAC;IACvJ,IAAI,MAAM,CAAC,SAAS,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QAC1C,MAAM,cAAc,CAAC,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAC,CAAC;IAChI,CAAC;IACD,OAAO,uBAAuB,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;AACzI,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAY;IACxC,QAAQ,EAAE,aAAa;IACvB,eAAe,EAAE,CAAC,MAAyB,EAAE,IAAY,EAAE,KAAuB,EAAE,EAAE,CAAC,mBAAmB,CAAC,MAA0B,EAAE,IAAI,CAAC;IAC5I,qBAAqB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,eAAgB,MAA2B,CAAC,KAAK,EAAE;IACtF,kBAAkB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAE,MAA2B,CAAC,KAAK;CACnE,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type GoogleChatTokenDeps } from "./google-chat-auth.js";
2
+ /** One Chat message's text limit is 4,096 characters; leave room for nothing else. */
3
+ export declare const GOOGLE_CHAT_MAX_TEXT = 4000;
4
+ export declare function isSpaceName(v: unknown): v is string;
5
+ /** A thread name that belongs to `space` (a thread of another space is not a thread of this one). */
6
+ export declare function isThreadNameIn(v: unknown, space: string): v is string;
7
+ export declare function isMessageNameIn(v: unknown, space: string): v is string;
8
+ /** Split `text` into Chat-sized pieces, preferring a newline near the limit, never inside a surrogate pair. */
9
+ export declare function splitGoogleChatText(text: string, max?: number): readonly string[];
10
+ export interface GoogleChatPostDeps extends GoogleChatTokenDeps {
11
+ }
12
+ /**
13
+ * Post `text` into `space`, threaded under `thread` when given (a thread Google no longer knows falls back to
14
+ * a new thread). Returns the LAST created message name. A pre-flight refusal is definitive (`markNotApplied`).
15
+ */
16
+ export declare function createGoogleChatMessage(input: {
17
+ readonly space: string;
18
+ readonly thread?: string;
19
+ readonly text: string;
20
+ }, deps?: GoogleChatPostDeps): Promise<string>;
21
+ //# sourceMappingURL=google-chat-rest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-chat-rest.d.ts","sourceRoot":"","sources":["../../../agent/channels/google-chat-rest.ts"],"names":[],"mappings":"AAQA,OAAO,EAAyB,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAGxF,sFAAsF;AACtF,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAMzC,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAEnD;AAED,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,IAAI,MAAM,CAErE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,IAAI,MAAM,CAEtE;AAED,+GAA+G;AAC/G,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAuB,GAAG,SAAS,MAAM,EAAE,CAc/F;AAED,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;CAAG;AAElE;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAClF,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,MAAM,CAAC,CA6BjB"}
@@ -0,0 +1,81 @@
1
+ // google-chat-rest — the ONE outbound call of the Google Chat surface: create a message in a space, as the app,
2
+ // optionally inside a thread. The host is pinned (`chat.googleapis.com`), the space and thread names are
3
+ // validated against Google's resource grammar before they reach a URL, and errors carry the HTTP status only.
4
+ //
5
+ // The app can post only into a space it is a member of, so this call cannot reach a space nobody added the app
6
+ // to. A text longer than one Chat message is sent as consecutive messages in the same thread.
7
+ import { friendlyOutboundText, markNotApplied } from "@open-everywhere/core/agent/adapter-api.js";
8
+ import { googleChatAccessToken } from "./google-chat-auth.js";
9
+ const CHAT_API = "https://chat.googleapis.com/v1";
10
+ /** One Chat message's text limit is 4,096 characters; leave room for nothing else. */
11
+ export const GOOGLE_CHAT_MAX_TEXT = 4000;
12
+ const SPACE_NAME = /^spaces\/[A-Za-z0-9_-]{1,128}$/u;
13
+ const THREAD_NAME = /^spaces\/[A-Za-z0-9_-]{1,128}\/threads\/[A-Za-z0-9_.-]{1,128}$/u;
14
+ const MESSAGE_NAME = /^spaces\/[A-Za-z0-9_-]{1,128}\/messages\/[A-Za-z0-9_.-]{1,128}$/u;
15
+ export function isSpaceName(v) {
16
+ return typeof v === "string" && SPACE_NAME.test(v);
17
+ }
18
+ /** A thread name that belongs to `space` (a thread of another space is not a thread of this one). */
19
+ export function isThreadNameIn(v, space) {
20
+ return typeof v === "string" && THREAD_NAME.test(v) && v.startsWith(`${space}/threads/`);
21
+ }
22
+ export function isMessageNameIn(v, space) {
23
+ return typeof v === "string" && MESSAGE_NAME.test(v) && v.startsWith(`${space}/messages/`);
24
+ }
25
+ /** Split `text` into Chat-sized pieces, preferring a newline near the limit, never inside a surrogate pair. */
26
+ export function splitGoogleChatText(text, max = GOOGLE_CHAT_MAX_TEXT) {
27
+ const out = [];
28
+ let rest = text;
29
+ while (rest.length > max) {
30
+ const nl = rest.lastIndexOf("\n", max);
31
+ let cut = nl > max / 2 ? nl : max;
32
+ // Never cut through a surrogate pair: a lone half is not valid text, and Google may refuse the message.
33
+ const before = rest.charCodeAt(cut - 1);
34
+ if (before >= 0xd800 && before <= 0xdbff)
35
+ cut -= 1;
36
+ out.push(rest.slice(0, cut));
37
+ rest = rest.slice(cut).replace(/^\n/u, "");
38
+ }
39
+ out.push(rest);
40
+ return out;
41
+ }
42
+ /**
43
+ * Post `text` into `space`, threaded under `thread` when given (a thread Google no longer knows falls back to
44
+ * a new thread). Returns the LAST created message name. A pre-flight refusal is definitive (`markNotApplied`).
45
+ */
46
+ export async function createGoogleChatMessage(input, deps = {}) {
47
+ if (!isSpaceName(input.space))
48
+ throw markNotApplied(new Error("the Google Chat space name is not valid."));
49
+ if (input.thread !== undefined && !isThreadNameIn(input.thread, input.space)) {
50
+ throw markNotApplied(new Error("the Google Chat thread does not belong to the space."));
51
+ }
52
+ if (input.text.trim() === "")
53
+ throw markNotApplied(new Error("a Google Chat message needs text."));
54
+ // THE FRIENDLY-NAMES GUARD (OPE-2482), on the one Google Chat write: every raw id renders as a name or
55
+ // plain words before the text is split and sent, so a post outside the registered arms is covered too.
56
+ const text = await friendlyOutboundText(input.text, "google-chat");
57
+ const token = await googleChatAccessToken(deps);
58
+ const doFetch = deps.fetch ?? fetch;
59
+ let thread = input.thread;
60
+ let last = "";
61
+ for (const piece of splitGoogleChatText(text)) {
62
+ const url = `${CHAT_API}/${input.space}/messages${thread !== undefined ? "?messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD" : ""}`;
63
+ const res = await doFetch(url, {
64
+ method: "POST",
65
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json; charset=utf-8" },
66
+ body: JSON.stringify({ text: piece, ...(thread !== undefined ? { thread: { name: thread } } : {}) }),
67
+ });
68
+ if (!res.ok)
69
+ throw new Error(`Google Chat refused the message (HTTP ${res.status}).`);
70
+ const body = (await res.json().catch(() => ({})));
71
+ if (typeof body.name !== "string")
72
+ throw new Error("Google Chat returned no message name.");
73
+ last = body.name;
74
+ // The pieces after the first follow the first one's thread, so a long answer stays together.
75
+ const created = body.thread?.name;
76
+ if (thread === undefined && isThreadNameIn(created, input.space))
77
+ thread = created;
78
+ }
79
+ return last;
80
+ }
81
+ //# sourceMappingURL=google-chat-rest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-chat-rest.js","sourceRoot":"","sources":["../../../agent/channels/google-chat-rest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("eve/channels").Channel<undefined, Record<string, unknown>, Record<string, unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=google-chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-chat.d.ts","sourceRoot":"","sources":["../../../agent/channels/google-chat.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
1
+ // The GOOGLE CHAT surface — the agent as a Google Chat app (OPE-2442). A custom `defineChannel` HTTP transport
2
+ // (eve ships no Google Chat channel): `POST /eve/v1/google-chat` is the app's HTTP endpoint. The eve-free
3
+ // orchestration is google-chat-front.ts; this file is the live wiring — the store, the config, and the two seat
4
+ // dispatches.
5
+ //
6
+ // THE LOOP:
7
+ // POST /eve/v1/google-chat
8
+ // → handleGoogleChatRequest (google-chat-front.ts) holds the route's order, and a test pins it:
9
+ // → the surface SHIPS OFF: a disabled policy (or one with no audience) answers 503 before the body is read
10
+ // → the bearer token is verified against Google's keys and the policy's audience (fail closed: 401)
11
+ // → the body is read ONCE, capped (413), and the route acknowledges `{}` (no synchronous message) so a slow seat
12
+ // never makes Google time the event out; the rest runs in the background
13
+ // → parse, dedupe on the message name, route: a mention in a space diverts into the standing seat of the
14
+ // domain whose `reach` lists `google-chat/<workspace>/<space id>` — the same
15
+ // `divertInboundToUnifiedWindow` every chat surface's front door uses; the person's own DM with the app
16
+ // diverts into their DM seat (channels/dm-seat.ts)
17
+ // → the seat answers with `reply`, which the post arm (google-chat-post-arm.ts) sends into the same space
18
+ // and thread, as the app.
19
+ //
20
+ // Spend, the wake ingress row, provider-selection admission, rate-limit retry and the audit of the wake all ride
21
+ // the shared divert. Nothing here re-implements them.
22
+ import { defineChannel, POST } from "eve/channels";
23
+ import { appendAudit, claimRuntimeRotation, carryTailForward, deploymentOrg, divertInboundToUnifiedWindow, divertToUnifiedWindow, dmWindowRegistryKey, getConfig, getOperationalStore, getOrCreateDmSeat, grainUsesUnifiedWindow, mintRotationToken, mintWakeId, receiptEventTime, recordWakeTail, resolveWakeTrustFromContext, shouldRotate, spreadVendorTs, stampMintedRuntime, supervisorGrainFor, tailAuthorGrain, touchDmSeat, withEndsTurnFinalChannel, } from "@open-everywhere/core/agent/adapter-api.js";
24
+ import { googleChatPolicy } from "../policy.js";
25
+ import { handleGoogleChatRequest, renderGoogleChatWake } from "./google-chat-front.js";
26
+ const ROUTE = "/eve/v1/google-chat";
27
+ /** How long a message name stays claimed — longer than Google's retry window. */
28
+ const DEDUPE_TTL_MS = 24 * 60 * 60 * 1000;
29
+ function log(msg) {
30
+ console.log(`[open-everywhere:google-chat] ${msg}`);
31
+ }
32
+ function authContext(auth) {
33
+ return { channel: { kind: "google-chat" }, session: { auth: { current: auth } } };
34
+ }
35
+ /** The tail row both seats file after the wake landed: the trigger only. */
36
+ function tailRow(plan, atIso) {
37
+ return {
38
+ kind: "wake",
39
+ at: atIso,
40
+ from: "human",
41
+ trust: plan.trigger.trust,
42
+ ...tailAuthorGrain(plan.ingested[0] ?? { trust: plan.trigger.trust }),
43
+ author: plan.trigger.author,
44
+ authorId: plan.trigger.authorId ?? "",
45
+ id: plan.trigger.id,
46
+ platform: "google-chat",
47
+ container: plan.surfaceKey,
48
+ ...(plan.origin.thread !== undefined ? { threadRoot: plan.origin.thread } : {}),
49
+ text: plan.trigger.body,
50
+ };
51
+ }
52
+ /** The ROOM dispatch: the domain's standing seat, through the shared inbound divert. */
53
+ async function liveDispatchRoom(plan, to) {
54
+ const org = deploymentOrg();
55
+ const store = getOperationalStore();
56
+ const grain = supervisorGrainFor(plan.surfaceKey, getConfig().loadDomains());
57
+ const atIso = new Date().toISOString();
58
+ const perContainer = (gen) => `google-chat:${plan.surfaceKey}:oe-sup:g${gen}`;
59
+ // OPE-599: claim the runtime-change rotation BEFORE the row read, and unconditionally.
60
+ const runtimeChanged = await claimRuntimeRotation({ org, registryKey: grain.registryKey, surfaceKey: plan.surfaceKey, domainName: grain.domainName, atIso });
61
+ const existing = await store.getChannelSession(org, grain.registryKey);
62
+ let token;
63
+ let tail = [];
64
+ if (!existing) {
65
+ const row = await store.rotateChannelSession(org, grain.registryKey, mintRotationToken(grain, 1, () => perContainer(1)), atIso);
66
+ await stampMintedRuntime({ org, registryKey: grain.registryKey, surfaceKey: plan.surfaceKey, domainName: grain.domainName, atIso });
67
+ token = row.token;
68
+ }
69
+ else if (shouldRotate(existing, atIso, { runtimeChanged })) {
70
+ const gen = existing.generation + 1;
71
+ tail = await carryTailForward(org, grain.registryKey, existing.token);
72
+ token = (await store.rotateChannelSession(org, grain.registryKey, mintRotationToken(grain, gen, () => perContainer(gen)), atIso)).token;
73
+ }
74
+ else {
75
+ token = existing.token;
76
+ }
77
+ const wakeId = mintWakeId();
78
+ const rendered = renderGoogleChatWake(plan, wakeId, tail);
79
+ const window = { wakeId, origin: plan.origin, addressing: "direct-mention", ...spreadVendorTs(receiptEventTime(Date.now())) };
80
+ await divertInboundToUnifiedWindow(to, {
81
+ message: rendered.message,
82
+ ...(rendered.context.length > 0 ? { context: rendered.context } : {}),
83
+ token,
84
+ window,
85
+ surfaceKey: plan.surfaceKey,
86
+ auth: plan.auth,
87
+ });
88
+ await store.touchChannelSession(org, grain.registryKey, atIso).catch(() => undefined);
89
+ await recordWakeTail(org, grain.registryKey, token, tailRow(plan, atIso)).catch(() => undefined);
90
+ }
91
+ /** The DM dispatch: the person's own DM seat. */
92
+ async function liveDispatchDm(plan, to) {
93
+ const org = deploymentOrg();
94
+ const atIso = new Date().toISOString();
95
+ const seat = await getOrCreateDmSeat(org, { surfaceKey: plan.surfaceKey }, atIso);
96
+ const wakeId = mintWakeId();
97
+ const rendered = renderGoogleChatWake(plan, wakeId, seat.freshGeneration ? seat.tail : undefined);
98
+ await divertToUnifiedWindow(to, {
99
+ message: rendered.message,
100
+ ...(rendered.context.length > 0 ? { context: rendered.context } : {}),
101
+ token: seat.token,
102
+ window: { wakeId, origin: plan.origin, addressing: "direct-mention", ...spreadVendorTs(receiptEventTime(Date.now())) },
103
+ // Byte-exactly the surface named inside the DM token — the privacy fence both admission gates match on.
104
+ surfaceKey: plan.surfaceKey,
105
+ auth: plan.auth,
106
+ });
107
+ await touchDmSeat(org, plan.surfaceKey, atIso).catch(() => undefined);
108
+ await recordWakeTail(org, dmWindowRegistryKey(plan.surfaceKey), seat.token, tailRow(plan, atIso)).catch(() => undefined);
109
+ }
110
+ /** The live front deps over this request's `to`. */
111
+ function liveGoogleChatFrontDeps(to) {
112
+ return {
113
+ policy: googleChatPolicy,
114
+ markSeen: (key) => getOperationalStore().markEventSeen(deploymentOrg(), key, new Date().toISOString(), DEDUPE_TTL_MS),
115
+ roomHasSeat: (surfaceKey) => {
116
+ try {
117
+ return grainUsesUnifiedWindow(supervisorGrainFor(surfaceKey, getConfig().loadDomains()));
118
+ }
119
+ catch {
120
+ return false;
121
+ }
122
+ },
123
+ wakeTrust: (auth, sender) => resolveWakeTrustFromContext(authContext(auth), { id: sender, isBot: false }, { principalFallback: true }),
124
+ dispatchRoom: (plan) => liveDispatchRoom(plan, to),
125
+ dispatchDm: (plan) => liveDispatchDm(plan, to),
126
+ audit: (row) => appendAudit({
127
+ org: deploymentOrg(),
128
+ platform: "google-chat",
129
+ workspace: row.workspace,
130
+ ...(row.surface ? { surface: row.surface } : {}),
131
+ ...(row.principalId ? { principalId: row.principalId } : {}),
132
+ action: row.action,
133
+ detail: row.detail,
134
+ }).catch(() => undefined),
135
+ };
136
+ }
137
+ const channel = defineChannel({
138
+ routes: [
139
+ POST(ROUTE, (req, { to, waitUntil }) => handleGoogleChatRequest(req, waitUntil, liveGoogleChatFrontDeps(to), {
140
+ log,
141
+ // The event was acknowledged and its name claimed, so a retry is a duplicate: this row is the only trace
142
+ // of the lost message. The error class only: an error message can carry message content.
143
+ onFailure: (err, policy) => appendAudit({
144
+ org: deploymentOrg(),
145
+ platform: "google-chat",
146
+ workspace: policy.workspace,
147
+ action: "google-chat.wake_failed",
148
+ detail: { error: err instanceof Error ? err.name : "error" },
149
+ })
150
+ .then(() => undefined)
151
+ .catch(() => undefined),
152
+ })),
153
+ ],
154
+ });
155
+ export default withEndsTurnFinalChannel(channel);
156
+ //# sourceMappingURL=google-chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-chat.js","sourceRoot":"","sources":["../../../agent/channels/google-chat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare const CONTRIBUTIONS: {
2
+ readonly schemaVersion: 1;
3
+ readonly package: "@open-everywhere/provider-google-chat";
4
+ readonly register: "agent/register.js#registerGoogleChatProvider";
5
+ readonly contributions: readonly [{
6
+ readonly kind: "channel";
7
+ readonly name: "google-chat";
8
+ readonly module: "agent/channels/google-chat.js";
9
+ readonly summary: "Google Chat — the agent as a Chat app: @mentions in spaces and direct messages in, threaded replies out.";
10
+ }];
11
+ };
12
+ //# sourceMappingURL=contributions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../agent/contributions.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,aAAa;aACxB,aAAa,EAAE,CAAC;aAChB,OAAO,EAAE,uCAAuC;aAChD,QAAQ,EAAE,8CAA8C;aACxD,aAAa;iBAET,IAAI,EAAE,SAAS;iBAAE,IAAI,EAAE,aAAa;iBAAE,MAAM,EAAE,+BAA+B;iBAAE,OAAO,EAAE,0GAA0G;;CAE9L,CAAC"}
@@ -0,0 +1,16 @@
1
+ // THE CONTRIBUTION MANIFEST for `@open-everywhere/provider-google-chat` (OPE-2442) — the curated catalogue of
2
+ // everything this package OFFERS an app's eve slot tree. `oe build` reads it; the app's `roster.config.ts` decides
3
+ // about it, and `roster.lock.json` records the review. See packages/providers/github/agent/contributions.ts for why
4
+ // the manifest is curated, import-free data.
5
+ //
6
+ // ERASABLE SYNTAX ONLY (no `enum`, no `namespace`) — `oe build` loads this with a bare import.
7
+ export const CONTRIBUTIONS = {
8
+ schemaVersion: 1,
9
+ package: "@open-everywhere/provider-google-chat",
10
+ register: "agent/register.js#registerGoogleChatProvider",
11
+ contributions: [
12
+ // ── channels ─────────────────────────────────────────────────────────────────────────────────
13
+ { kind: "channel", name: "google-chat", module: "agent/channels/google-chat.js", summary: "Google Chat — the agent as a Chat app: @mentions in spaces and direct messages in, threaded replies out." },
14
+ ],
15
+ };
16
+ //# sourceMappingURL=contributions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contributions.js","sourceRoot":"","sources":["../../agent/contributions.ts"],"names":[],"mappings":"AAAA,8GAA8G;AAC9G,mHAAmH;AACnH,oHAAoH;AACpH,6CAA6C;AAC7C,EAAE;AACF,+FAA+F;AAE/F,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,aAAa,EAAE,CAAC;IAChB,OAAO,EAAE,uCAAuC;IAChD,QAAQ,EAAE,8CAA8C;IACxD,aAAa,EAAE;QACb,gGAAgG;QAChG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,+BAA+B,EAAE,OAAO,EAAE,0GAA0G,EAAE;KACvM;CACO,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DocEntry } from "@open-everywhere/core/agent/adapter-api.js";
2
+ export declare const PROVIDER_DOCS: readonly DocEntry[];
3
+ //# sourceMappingURL=docs-generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-generated.d.ts","sourceRoot":"","sources":["../../agent/docs-generated.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AAE3E,eAAO,MAAM,aAAa,EAAE,SAAS,QAAQ,EAO5C,CAAC"}
@@ -0,0 +1,13 @@
1
+ // GENERATED by `npx tsx scripts/docs-generate.ts` from docs/deploy/surfaces/google-chat.md — DO NOT HAND-EDIT.
2
+ // Edit the markdown under docs/ and re-run the generator; `npm run lint:docs` fails the
3
+ // build if this file and its sources ever disagree. See scripts/docs-generate.ts for why the
4
+ // manual is compiled in at all (docs/ does not exist on disk in a hosted deployment).
5
+ export const PROVIDER_DOCS = [
6
+ {
7
+ path: "docs/deploy/surfaces/google-chat.md",
8
+ title: "Google Chat",
9
+ summary: "Put the agent in Google Chat as a Chat app. People @mention it in a space or send it a direct message, and it answers in the same thread. Google Chat reaches the same two chiefs of staff as every other chat surface:",
10
+ markdown: "# Google Chat\n\nPut the agent in Google Chat as a Chat app. People @mention it in a space or send it a direct message, and it\nanswers in the same thread. Google Chat reaches the same two chiefs of staff as every other chat surface:\n\n- A **space** belongs to a domain. An @mention of the app in a space wakes that domain's standing seat, exactly as a\n message in one of the domain's Slack or Teams channels would.\n- A **direct message** with the app wakes the person's own DM seat.\n\nGoogle Chat is the surface for an organisation that runs Google Workspace. It ships **off**: until a policy enables\nit, the endpoint answers every delivery with `503`.\n\n## What it does and does not do\n\n- **Every event is verified before it is read.** Google Chat sends each event with a bearer token. The route checks\n the token's signature against Google's published keys, its issuer, its expiry, and its audience against the\n policy's `audience`. A token for any other audience, or (in endpoint-URL mode) from any Google account other than\n `chat@system.gserviceaccount.com`, is refused `401` and writes no audit row.\n- **Addressed only.** The HTTP endpoint gets a space message only when it @-mentions the app, or invokes one of its\n slash commands. The surface has no ambient leg: room chatter never reaches it. (A Workspace Events subscription\n would deliver the rest; this surface does not use one yet.)\n- **Trust comes from the policy, never from a name.** A sender binds `internal` only when Google's event names a\n Workspace domain id (`sender.domainId`) that the policy lists in `internalDomainIds`. Everyone else binds\n `external`: shared memory routes to the read-only `public` layer, and the turn is marked as carrying a\n non-member's words. ⚠ The mapping from your Workspace to its domain id is not documented as stable by Google.\n Read the id from a real event: every `google-chat.admitted` and `google-chat.refused` audit row records the\n sender's `domainId`. A DM-only deployment can start with an empty list: the first internal person's DM is refused\n `external-dm`, and its refused row carries their domain id.\n- **Direct messages are for internal people only.** A DM seat reads its person's private layer, so a DM from an\n `external` sender is refused (`external-dm`). Set `directMessages: false` to refuse every DM (`dm-off`).\n- **Group chats are refused** (`group-chat`). A group chat has no domain to own it and is not one person's DM.\n- **A space with no seat is refused**, not dropped in silence (`no-domain`): put its surface key in a domain's\n `reach`, or run a standing-seat window that gives an undeclared space its own solo seat.\n- **Bots never wake a seat.** A message from any bot, the app's own included, is dropped. That is the loop guard.\n- **Replies only, into the conversation that woke the seat.** The reply posts into the trigger's space and thread,\n as the app. The space and thread are frozen at the gate from the verified event; the model never names one. The\n app can post only into a space it is a member of. A reply longer than one Chat message goes out as consecutive\n messages in the same thread.\n\n## How it fits the planes\n\n| Plane | What Google Chat uses |\n|---|---|\n| Surface | The `google-chat` provider bundle (`packages/providers/google-chat`): identity mapper, capability manifest, post arm, wake-origin codec, host-notice transport. A space's surface key is `google-chat/<workspace>/<space id>` and goes in a domain's `reach` like any channel. |\n| Seat | A space mention diverts into the domain's standing seat through the same `divertInboundToUnifiedWindow` every chat surface uses; a DM diverts into the person's DM seat (`channels/dm-seat.ts`). Spend caps, the wake ingress row, provider selection and rate-limit retry ride the shared divert. |\n| Identity | The principal is the sender's Chat user resource name (`users/<id>`). The workspace is the policy's `workspace` segment. Visibility is always `private`. |\n| Audit | `google-chat.admitted` (space, seat kind, trust, the sender's domain id, whether it was a thread reply), `google-chat.refused` (the reason and the sender's domain id), and `google-chat.wake_failed`. The reply itself writes the shared `reply.post` row. |\n| Egress | The reply and a host notice (an approval outcome note, the judge-lift notice) are the only outbound calls, to `chat.googleapis.com`, with a `chat.bot` token minted from the app's service-account key at `oauth2.googleapis.com`. The token endpoint is pinned: the key file's own `token_uri` is ignored. |\n\n## What an operator must provision\n\nNothing below is created by Open Everywhere.\n\n1. **A Google Cloud project** with the Google Chat API enabled.\n2. **A service account** in that project, and a JSON key for it. The app posts its replies as this account.\n3. **The Chat app configuration** (Google Chat API → Configuration): give the app a name and avatar, turn on\n \"Receive 1:1 messages\" and \"Join spaces and group conversations\", choose **HTTP endpoint URL** as the connection\n setting, and set it to `https://<your deployment host>/eve/v1/google-chat`. Choose the **Authentication audience**:\n \"HTTP endpoint URL\" (recommended) or \"Project number\". Make the app visible to the people or groups who should\n use it.\n4. **The secret**, in the secret store, never in a file or the environment of a commit:\n ```sh\n npx oe secret set GOOGLE_CHAT_SERVICE_ACCOUNT_KEY # the service account's JSON key, whole\n ```\n (The resolver falls back to an environment variable of the same name, and warns when it does.)\n5. **The policy**, in `agent/google-chat/policy.ts`, by pull request: set `enabled: true`, the `audience` that\n matches step 3 (`{ kind: \"endpoint-url\", url: \"https://<host>/eve/v1/google-chat\" }` or\n `{ kind: \"project-number\", projectNumber: \"<number>\" }`), your `workspace` segment, and your\n `internalDomainIds`.\n6. **The seats.** For each space, add its surface key (`google-chat/<workspace>/<space id>`, the id after\n `spaces/` in the space's URL) to the owning domain's `reach` in `agent/memory/config/domains/`. A multi-source\n window requires every source vouched, so also declare the space in `agent/identity/surfaces.ts`. 🔴 The vouch\n does **not** make an outside sender internal: the mapper states each message's trust itself, from the policy.\n7. **Add the app** to each space (`@<app name>` → Add).\n\n## Settings\n\n| Field | Meaning |\n|---|---|\n| `enabled` | The master switch. Off answers `503` to every delivery and refuses every reply. |\n| `audience` | The audience every event's token must carry. `null` refuses every delivery; an enabled policy without one fails the boot. |\n| `workspace` | The workspace segment of every surface key (lowercase, `a-z`, `0-9`, `-`). |\n| `internalDomainIds` | The Workspace domain ids whose people bind `internal`. Empty: everybody binds `external`. |\n| `directMessages` | May an internal person wake their own DM seat? |\n\n## What is not built yet\n\n- **Cards**: an approval asked from Google Chat takes the venue walk to a card-capable room in the same domain. A\n Chat card with a button needs the `CARD_CLICKED` tap answered.\n- **Reading a space** (`read_channel`, `read_thread`) and a seat catch-up: listing a space's messages needs user\n authentication or the admin-approved `chat.app.messages.readonly` app scope, which the app does not request.\n- **Ambient messages, name-drops and edits**: they need a Workspace Events subscription.\n- **Edit, reactions, stop, files, proactive posts and routines.** Each is declared, with its reason, in the\n [chat-surface contract](../../extending/chat-surface-contract.md)'s parity tables.\n- **The Workspace add-on event format.** The route reads the classic Chat app event; an app built as a Google\n Workspace add-on wraps its events differently and is not supported.\n\n## Verification\n\nThe unit packs (`test:google-chat-auth`, `test:google-chat-front`, `test:google-chat-post-arm`) and the app-level\n`test:google-chat-integration` drive the token check, the gate, the reply and the binding offline. There is no\nlive e2e leg yet: one needs a Google Workspace test tenant with the app published to it, a fixture this repository\ncannot create.\n",
11
+ },
12
+ ];
13
+ //# sourceMappingURL=docs-generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-generated.js","sourceRoot":"","sources":["../../agent/docs-generated.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAC/G,wFAAwF;AACxF,6FAA6F;AAC7F,sFAAsF;AAItF,MAAM,CAAC,MAAM,aAAa,GAAwB;IAChD;QACE,IAAI,EAAE,qCAAqC;QAC3C,KAAK,EAAE,aAAa;QACpB,OAAO,EAAE,yNAAyN;QAClO,QAAQ,EAAE,+oQAA+oQ;KAC1pQ;CACF,CAAC"}
@@ -0,0 +1,38 @@
1
+ /** Which audience Google Chat puts in the bearer token of each event — the app's "Authentication audience"
2
+ * setting in the Google Cloud console. */
3
+ export type GoogleChatAudience =
4
+ /** "Project number": the token is self-signed by `chat@system.gserviceaccount.com` for the project number. */
5
+ {
6
+ readonly kind: "project-number";
7
+ readonly projectNumber: string;
8
+ }
9
+ /** "HTTP endpoint URL": the token is a Google OIDC id token for the exact endpoint URL. */
10
+ | {
11
+ readonly kind: "endpoint-url";
12
+ readonly url: string;
13
+ };
14
+ export interface GoogleChatPolicy {
15
+ /** The master switch. Off answers 503 to every delivery and refuses every reply. */
16
+ readonly enabled: boolean;
17
+ /** The audience every event's token must carry. Null refuses every delivery (fail closed). */
18
+ readonly audience: GoogleChatAudience | null;
19
+ /** The workspace segment of every Google Chat surface key (`google-chat/<workspace>/<space id>`). */
20
+ readonly workspace: string;
21
+ /**
22
+ * The Google Workspace domain ids (`sender.domainId` on a Chat event) whose people bind `internal`. Everyone
23
+ * else binds `external`. Empty: everybody binds external.
24
+ */
25
+ readonly internalDomainIds: readonly string[];
26
+ /** May a person wake their own DM seat by messaging the app directly? An `external` person never can. */
27
+ readonly directMessages: boolean;
28
+ }
29
+ export declare const DEFAULT_GOOGLE_CHAT_POLICY: GoogleChatPolicy;
30
+ /** Every problem with `policy`, or empty when it is valid. Pure. */
31
+ export declare function validateGoogleChatPolicy(policy: GoogleChatPolicy): readonly string[];
32
+ /** Register this deployment's policy. Throws on an invalid policy, so a bad file fails the boot. */
33
+ export declare function registerGoogleChatPolicy(policy: GoogleChatPolicy): void;
34
+ /** The registered policy (the shipped default — off — until the app registers one). */
35
+ export declare function googleChatPolicy(): GoogleChatPolicy;
36
+ /** The surface key of one space: `google-chat/<workspace>/<space id>`. */
37
+ export declare function googleChatSurfaceKey(policy: GoogleChatPolicy, space: string): string;
38
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../agent/policy.ts"],"names":[],"mappings":"AAaA;2CAC2C;AAC3C,MAAM,MAAM,kBAAkB;AAC5B,8GAA8G;AAC5G;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAAE;AACrE,2FAA2F;GACzF;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,8FAA8F;IAC9F,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC7C,qGAAqG;IACrG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,yGAAyG;IACzG,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED,eAAO,MAAM,0BAA0B,EAAE,gBAMxC,CAAC;AAMF,oEAAoE;AACpE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,MAAM,EAAE,CA0BpF;AAID,oGAAoG;AACpG,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAIvE;AAED,uFAAuF;AACvF,wBAAgB,gBAAgB,IAAI,gBAAgB,CAEnD;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpF"}
@@ -0,0 +1,78 @@
1
+ // THE GOOGLE CHAT POLICY (OPE-2442) — the schema, its validation and the registry. The app's own
2
+ // `agent/google-chat/policy.ts` declares the values and calls `registerGoogleChatPolicy` at boot (through
3
+ // `agent/lib/register-config.ts`), so every change is a reviewed pull request.
4
+ //
5
+ // 🔴 THE SURFACE SHIPS OFF. `DEFAULT_GOOGLE_CHAT_POLICY.enabled` is false, and the route answers 503 to every
6
+ // delivery until a policy enables it AND names the audience Google signs each event for. An external ingress
7
+ // that nobody configured must not be reachable.
8
+ //
9
+ // 🔴 TRUST IS TYPED CODE, NEVER AN ENVIRONMENT VARIABLE. `internalDomainIds` is the list of Google Workspace
10
+ // domain ids whose people bind `internal`; it is a trust declaration, so it lives here and changes by pull
11
+ // request. Nothing here is a secret: the app's service-account key lives in the secret store
12
+ // (`npx oe secret set GOOGLE_CHAT_SERVICE_ACCOUNT_KEY`).
13
+ export const DEFAULT_GOOGLE_CHAT_POLICY = {
14
+ enabled: false,
15
+ audience: null,
16
+ workspace: "workspace",
17
+ internalDomainIds: [],
18
+ directMessages: true,
19
+ };
20
+ const WORKSPACE_SEGMENT = /^[a-z0-9][a-z0-9-]{0,62}$/u;
21
+ const PROJECT_NUMBER = /^[0-9]{1,20}$/u;
22
+ const DOMAIN_ID = /^[A-Za-z0-9_-]{1,64}$/u;
23
+ /** Every problem with `policy`, or empty when it is valid. Pure. */
24
+ export function validateGoogleChatPolicy(policy) {
25
+ const problems = [];
26
+ if (!WORKSPACE_SEGMENT.test(policy.workspace)) {
27
+ problems.push(`workspace "${policy.workspace}" must be a lowercase segment (a-z, 0-9, "-"), at most 63 characters.`);
28
+ }
29
+ for (const id of policy.internalDomainIds) {
30
+ if (!DOMAIN_ID.test(id))
31
+ problems.push(`internalDomainIds entry "${id}" is not a Google Workspace domain id.`);
32
+ }
33
+ const a = policy.audience;
34
+ if (a !== null) {
35
+ if (a.kind === "project-number") {
36
+ if (!PROJECT_NUMBER.test(a.projectNumber))
37
+ problems.push("audience.projectNumber must be the numeric Google Cloud project number.");
38
+ }
39
+ else if (a.kind === "endpoint-url") {
40
+ let url;
41
+ try {
42
+ url = new URL(a.url);
43
+ }
44
+ catch {
45
+ url = undefined;
46
+ }
47
+ if (url?.protocol !== "https:")
48
+ problems.push("audience.url must be the app's https endpoint URL.");
49
+ }
50
+ else {
51
+ problems.push(`audience.kind must be "project-number" or "endpoint-url".`);
52
+ }
53
+ }
54
+ if (policy.enabled && a === null)
55
+ problems.push("an enabled policy must name its audience — without it no event can be verified.");
56
+ return problems;
57
+ }
58
+ let registered = DEFAULT_GOOGLE_CHAT_POLICY;
59
+ /** Register this deployment's policy. Throws on an invalid policy, so a bad file fails the boot. */
60
+ export function registerGoogleChatPolicy(policy) {
61
+ const problems = validateGoogleChatPolicy(policy);
62
+ if (problems.length > 0)
63
+ throw new Error(`agent/google-chat/policy.ts is invalid:\n - ${problems.join("\n - ")}`);
64
+ registered = policy;
65
+ }
66
+ /** The registered policy (the shipped default — off — until the app registers one). */
67
+ export function googleChatPolicy() {
68
+ return registered;
69
+ }
70
+ /** The surface key of one space: `google-chat/<workspace>/<space id>`. */
71
+ export function googleChatSurfaceKey(policy, space) {
72
+ return `google-chat/${policy.workspace}/${spaceId(space)}`;
73
+ }
74
+ /** The id after `spaces/`. */
75
+ function spaceId(space) {
76
+ return space.slice(space.indexOf("/") + 1);
77
+ }
78
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../agent/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ProviderEventDeclaration } from "@open-everywhere/core/agent/adapter-api.js";
2
+ export declare const GOOGLE_CHAT_PROVIDER_EVENTS: ProviderEventDeclaration;
3
+ //# sourceMappingURL=provider-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-events.d.ts","sourceRoot":"","sources":["../../agent/provider-events.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAE3F,eAAO,MAAM,2BAA2B,EAAE,wBAQzC,CAAC"}
@@ -0,0 +1,16 @@
1
+ // GOOGLE CHAT's event inventory (OPE-2442). See the core module's header
2
+ // (packages/core/agent/routines/provider-events.ts) for what the three routing states mean.
3
+ //
4
+ // 🔴 THE GOOGLE CHAT BUNDLE REGISTERS NO EVENT NORMALIZER, so every row is `routing: "none"` and the declaration
5
+ // names no `source`. A message event is CONVERSATION — it wakes a seat through the surface's gate — and a routine
6
+ // subscribed to raw Chat events would bypass that gate.
7
+ export const GOOGLE_CHAT_PROVIDER_EVENTS = {
8
+ doc: "https://developers.google.com/workspace/chat/receive-respond-interactions",
9
+ events: [
10
+ { event: "MESSAGE", routing: "none", match: [], scope: [], note: "an @mention in a space or a direct message — the front's conversation leg, through the gate, never a routine" },
11
+ { event: "ADDED_TO_SPACE", routing: "none", match: [], scope: [], note: "the app was added to a space — acknowledged and dropped; a space wakes nothing until a domain's `reach` lists it" },
12
+ { event: "REMOVED_FROM_SPACE", routing: "none", match: [], scope: [], note: "the app was removed from a space — acknowledged and dropped" },
13
+ { event: "CARD_CLICKED", routing: "none", match: [], scope: [], note: "a card button tap — acknowledged and dropped; the surface posts no card yet" },
14
+ ],
15
+ };
16
+ //# sourceMappingURL=provider-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-events.js","sourceRoot":"","sources":["../../agent/provider-events.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,4FAA4F;AAC5F,EAAE;AACF,iHAAiH;AACjH,kHAAkH;AAClH,wDAAwD;AAIxD,MAAM,CAAC,MAAM,2BAA2B,GAA6B;IACnE,GAAG,EAAE,2EAA2E;IAChF,MAAM,EAAE;QACN,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,8GAA8G,EAAE;QACjL,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,kHAAkH,EAAE;QAC5L,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,6DAA6D,EAAE;QAC3I,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,6EAA6E,EAAE;KACtJ;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Perform ALL of the Google Chat bundle's registrations. Idempotent (each register call replaces). */
2
+ export declare function registerGoogleChatProvider(): void;
3
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"AAoCA,uGAAuG;AACvG,wBAAgB,0BAA0B,IAAI,IAAI,CAUjD"}