@slates-integrations/google-chat 0.1.4 → 0.3.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.
package/docs/SPEC.md CHANGED
@@ -1,25 +1,26 @@
1
- # Slates Specification for Google Chat
1
+ # Google Chat integration specification
2
2
 
3
3
  ## Overview
4
4
 
5
- Google Chat is Google Workspace's team messaging service. This integration exposes 26 tools for messages, spaces, memberships, reactions, direct messages, attachments, space events, the signed-in user's read state, space notification settings, sidebar sections, custom emoji, and organization-wide admin space search. It has no triggers.
5
+ Google Chat is Google Workspace's team messaging service. This integration exposes 27 tools for messages, spaces, memberships, reactions, direct messages, attachments, space events, the signed-in user's read state, space notification settings, sidebar sections, custom emoji, and organization-wide admin space search. It also implements the normalized chat adapter for the Google Chat app and one manual webhook trigger group for Chat app interaction events (see the README's Chat app adapter section).
6
6
 
7
7
  ## Tool surface
8
8
 
9
9
  | Tool | Authentication | API behavior |
10
10
  |---|---|---|
11
- | `send_message` | User OAuth or Chat app | `spaces.messages.create`, including thread replies and uploaded attachment tokens |
11
+ | `send_message` | User OAuth or Chat app | `spaces.messages.create`, including thread replies, `requestId` idempotency, client-assigned message IDs, and uploaded attachment tokens (user OAuth) |
12
12
  | `list_messages` | User OAuth | `spaces.messages.list` with thread/time filtering, `createTime ASC`/`DESC` ordering, and pagination |
13
13
  | `search_messages` | User OAuth | `POST /v1/spaces/-/messages:search`, with a `spaces.messages.list` keyword fallback when the search API is unavailable for the account or project |
14
14
  | `search_conversations` | User OAuth or Chat app | `spaces.list`, followed by client-side name matching on each returned page |
15
15
  | `manage_space` | User OAuth | Create, setup, get, update, and delete space workflows |
16
- | `manage_member` | User OAuth | Add, get, list, update, and remove memberships |
17
16
  | `manage_message` | User OAuth or Chat app | Get, patch, and delete messages; apps can mutate only app-authored messages |
18
17
  | `manage_reaction` | User OAuth | Create, list, and delete reactions |
18
+ | `list_members` | User OAuth or Chat app | `spaces.members.list` with role/member-type filters, `showGroups`, `showInvited` (user OAuth), and pagination; app auth omits app memberships |
19
+ | `manage_membership` | User OAuth | `spaces.members.get`, `create` (user by ID or email, or Google Group), `patch` (`updateMask=role`), and `delete` |
19
20
  | `find_direct_message` | User OAuth or Chat app | `spaces.findDirectMessage` |
20
21
  | `get_attachment` | Chat app only | `spaces.messages.attachments.get` metadata |
21
22
  | `download_attachment` | User OAuth or Chat app | `media.download`; returns a downloadable file |
22
- | `upload_attachment` | User OAuth or Chat app | Multipart `media.upload`; returns an upload token, not a posted message |
23
+ | `upload_attachment` | User OAuth | Multipart `media.upload` (user authentication only); returns an upload token, not a posted message |
23
24
  | `list_space_events` | User OAuth | List events or get one event by ID |
24
25
  | `get_space_read_state` | User OAuth | `users.spaces.getSpaceReadState` for `users/me` |
25
26
  | `get_thread_read_state` | User OAuth | `users.spaces.threads.getThreadReadState` for `users/me` |
@@ -41,25 +42,24 @@ The four official-named core tools are `send_message`, `list_messages`, `search_
41
42
 
42
43
  ### User OAuth
43
44
 
44
- The OAuth authorization-code flow requests offline access and incremental authorization. Callback and refresh handling persist the access token, expiry, and refresh token, preserving the existing refresh token when Google does not rotate it. Profile lookup uses Google Account identity scopes when either identity scope is granted.
45
+ The OAuth authorization-code flow requests offline access with a consent prompt; it does not request incremental authorization (`include_granted_scopes`). Callback and refresh handling persist the access token, expiry, and refresh token, preserving the existing refresh token when Google does not rotate it. Profile lookup uses Google Account identity scopes when either identity scope is granted.
45
46
 
46
47
  The consent surface contains:
47
48
 
48
- - `chat.messages`, `chat.messages.readonly`, and `chat.messages.create`
49
- - `chat.spaces`, `chat.spaces.readonly`, and the separate `chat.delete` scope
50
- - `chat.memberships`, `chat.memberships.readonly`, and `chat.memberships.app`
51
- - `chat.messages.reactions`
49
+ - `chat.messages` (also covers reactions)
50
+ - `chat.spaces` and the separate `chat.delete` scope
51
+ - `chat.memberships`
52
52
  - `chat.users.readstate`, `chat.users.spacesettings`, `chat.users.sections`, and `chat.customemojis`
53
53
  - `chat.admin.spaces.readonly`
54
54
  - `userinfo.email` and `userinfo.profile`
55
55
 
56
- Connections request every scope declared on the OAuth method. Consolidated tools use relaxed `anyOf` static gates and document the per-action scope Google actually enforces in their instructions: `manage_space` accepts `chat.spaces`, `chat.spaces.readonly`, or `chat.delete` (get works read-only; create/setup/update need `chat.spaces`; delete needs `chat.delete`, and without it the tool reports that the connection must be reauthorized). `manage_message` accepts a message read scope so `action=get` works with read-only grants, while update/delete need `chat.messages`. `manage_member` accepts `chat.memberships` or `chat.memberships.app`; the app scope is required only for adding or removing the calling Chat app itself. `list_space_events` accepts any one message, reaction, membership, or space read scope because Google enforces the scope per requested `eventTypes` family. Read-only tools for read state, sections, and custom emoji also accept the matching `.readonly` scope; their write tools require the full scope. `search_spaces_admin` accepts `chat.admin.spaces.readonly` or `chat.admin.spaces`.
56
+ Connections request every scope declared on the OAuth method, so read-only and narrower variants (`chat.messages.readonly`, `chat.messages.create`, `chat.messages.reactions`, `chat.spaces.readonly`, `chat.memberships.readonly`) are not declared; tool gates still accept them for connections that granted them earlier. Consolidated tools use relaxed `anyOf` static gates and document the per-action scope Google actually enforces in their instructions: `manage_space` accepts `chat.spaces`, `chat.spaces.readonly`, or `chat.delete` (get works read-only; create/setup/update need `chat.spaces`; delete needs `chat.delete`, and without it the tool reports that the connection must be reauthorized). `manage_message` accepts a message read scope so `action=get` works with read-only grants, while update/delete need `chat.messages`. `manage_membership` accepts either membership scope so `action=get` works read-only, while add/update/remove need `chat.memberships`. `list_space_events` accepts any one message, reaction, membership, or space read scope because Google enforces the scope per requested `eventTypes` family. Read-only tools for read state, sections, and custom emoji also accept the matching `.readonly` scope; their write tools require the full scope. `search_spaces_admin` accepts `chat.admin.spaces.readonly` or `chat.admin.spaces`.
57
57
 
58
58
  ### Chat app service account
59
59
 
60
- App authentication accepts the JSON key for the service account configured as the Google Chat app, exchanges a signed JWT for an access token, and refreshes by signing a new assertion. It requests only the app-only `chat.bot` scope; `chat.bot` is not presented on the user OAuth consent screen.
60
+ App authentication accepts the JSON key for the service account configured as the Google Chat app and an optional numeric `projectNumber`, exchanges a signed JWT for an access token, and refreshes by signing a new assertion. It requests only the app-only `chat.bot` scope; `chat.bot` is not presented on the user OAuth consent screen.
61
61
 
62
- The Google Cloud project must have a configured Chat app, and that app must be installed in each space it accesses. App authentication does not substitute for user authentication on user-only endpoints. Attachment metadata (`get_attachment`) is app-only, and attachment upload (`upload_attachment`) supports app authentication in addition to user OAuth, matching `media.upload`. Mixed-auth tools still follow Google restrictions; in particular, a Chat app can update or delete only its own messages.
62
+ The Google Cloud project must have a configured Chat app, and that app must be installed in each space it accesses. App authentication does not substitute for user authentication on user-only endpoints. Attachment metadata (`get_attachment`) is app-only, while attachment upload (`upload_attachment`) is user-only because `media.upload` does not support app authentication. Mixed-auth tools still follow Google restrictions; in particular, a Chat app can update or delete only its own messages.
63
63
 
64
64
  ## Search behavior
65
65
 
@@ -84,4 +84,10 @@ Google Drive download flow.
84
84
 
85
85
  ## Live verification boundary
86
86
 
87
- The private E2E suite defines scenarios for all 26 tools. A real run requires both a fully consented user OAuth profile and a Chat app service-account profile, a Workspace tenant where the app can be installed, a disposable member fixture, existing OAuth/app direct-message fixtures, and stable user/app attachment fixtures. Where the search API is available the suite exercises the primary `messages.search` path; otherwise `search_messages` exercises its `spaces.messages.list` fallback instead. The read state, notification setting, section, and custom emoji scenarios need their user scopes, and `search_spaces_admin` runs only for a Workspace administrator profile. Scenarios whose profile, scope, or fixture is missing skip individually rather than claiming live provider coverage.
87
+ The private E2E suite defines scenarios for all 27 tools. A real run requires both a fully consented user OAuth profile and a Chat app service-account profile, a Workspace tenant where the app can be installed, a disposable member fixture, existing OAuth/app direct-message fixtures, and stable user/app attachment fixtures. Where the search API is available the suite exercises the primary `messages.search` path; otherwise `search_messages` exercises its `spaces.messages.list` fallback instead. The read state, notification setting, section, and custom emoji scenarios need their user scopes, and `search_spaces_admin` runs only for a Workspace administrator profile. Scenarios whose profile, scope, or fixture is missing skip individually rather than claiming live provider coverage.
88
+
89
+ ## Chat app adapter and events
90
+
91
+ The `service_account` method is the only chat-adapter-eligible method (`adapters: ['chat']`); user OAuth declares `adapters: []`. Its auth output persists the non-secret app identity (`clientEmail`, `clientId`, `projectId`, `projectNumber`). Chat actions model the app as one workspace, `projects/{projectId}`, and stamp it on every channel. Each implemented action was checked against its Google Chat API reference page for `chat.bot` app authentication without administrator approval; the README lists the supported matrix and exclusions.
92
+
93
+ The `chat_app_events` trigger group uses manual registration. Its saved registration holds the endpoint URL, the Authentication Audience (`project_number`, recommended, or `endpoint_url`), the project number, and the project ID. `process` verifies the bearer token as described in [Verify requests from Google Chat](https://developers.google.com/workspace/chat/verify-requests-from-chat): RS256 signatures against `https://www.googleapis.com/oauth2/v3/certs` (ID tokens, issuer `accounts.google.com`, email `chat@system.gserviceaccount.com`) or `https://www.googleapis.com/service_accounts/v1/jwk/chat@system.gserviceaccount.com` (self-signed JWTs, issuer `chat@system.gserviceaccount.com`), audience, and expiry with five minutes of clock skew. Routing matchers are `{ installType: 'chat_app', projectNumber, projectId }` on both sides; the connection's project ID comes from its service account email, which the token exchange proves, so a connection cannot claim another project's events by entering its project number. In `endpoint_url` mode the token does not identify the project, so the endpoint URL must stay private. Chat apps built as Google Workspace add-ons save an optional `addOnServiceAccountEmail`, which must be the project's Google-managed add-on service agent (`service-{projectNumber}@gcp-sa-gsuiteaddons.iam.gserviceaccount.com`) and requires `endpoint_url`; such registrations accept only ID tokens issued to that account, binding requests to the project. Add-on bodies (`chat.<kind>Payload`) are converted to the Chat event shape before selection, and `authorizationEventObject` and `commonEventObject` are never forwarded. `MESSAGE` and `APP_COMMAND` events are forwarded with idempotency keys `message:{name}` and `command:{name}`; other event types are acknowledged and skipped.
package/package.json CHANGED
@@ -9,16 +9,17 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "@lowerdeck/error": "^1.1.0",
12
+ "@slates/adapter-chat": "1.0.0-rc.13",
12
13
  "@types/node": "^20",
13
- "slates": "1.0.0-rc.25",
14
+ "slates": "1.0.0-rc.27",
14
15
  "zod": "^4.2"
15
16
  },
16
17
  "devDependencies": {
17
- "@slates/test": "1.0.0-rc.23",
18
+ "@slates/test": "1.0.0-rc.24",
18
19
  "typescript": "^5",
19
20
  "vitest": "^3.1.2"
20
21
  },
21
- "version": "0.1.4",
22
+ "version": "0.3.0",
22
23
  "files": [
23
24
  "dist/**",
24
25
  "docs/**",