andrewyng--aisuite
6.8 KiB
6.8 KiB
Google Docs & Sheets connector — by-URL reads, tiered Sheets writes
Status: agreed 2026-07-10 (Rohit + agent). Motivated by the competitor
use-case analysis (ocw-context docs/competitor-use-cases.md): Google
docs/spreadsheets appear in 6 of 8 flagship workflows, always as "THIS doc /
THIS spreadsheet" — a pasted link, never a Drive-wide search.
Decisions (locked)
- One combined connector
google_docs(title: "Google Docs & Sheets"). Both scope sets are Google sensitive tier, so combining costs nothing on the verification ladder — the gmail/google_calendar split existed to keep Calendar out of Gmail's restricted/CASA bucket, which doesn't apply here. One consent per Google account instead of two. - Consent tiers, HubSpot-style (chosen at connect time, named tiers only):
read(default):documents.readonly+spreadsheets.readonlywrite:documents.readonly+spreadsheets(full) — enables create-a-tracker, update-cells, append-rows. Docs stay read-only in both.
- No Drive scope, no Drive search. Documents are addressed by URL or id.
Drive-wide search (
drive.readonly, RESTRICTED → CASA) is wave 2, bundled with Gmail's CASA whenever that happens. - Multi-account from day one via the generic accounts layer
(CONNECTORS-BATCH2-SPEC §1):
account_field="@identity"— the account key is the Google email (broker sends it asaccountfrom the id_token).
Non-goals (wave 1)
- Docs WRITE (create/edit Google Docs) — briefs land as local artifacts or
Notion pages; revisit on demand (
documentsscope is also sensitive, so it's an easy later add). - Drive listing/search, Slides, Forms.
- Per-document privacy filters (nothing to filter without search).
Broker (ocw-connect)
googleproviderconnector_scopesgains"google_docs": ["documents.readonly", "spreadsheets.readonly"](full URLs). The existinggoogle_drivestub stays for wave 2.- Model extension: access tiers must be PER CONNECTOR on multi-connector
providers — today
access_scopesis provider-level (fine for HubSpot, a single-connector provider; wrong for google, where awritetier must not leak into gmail/calendar consents). Addconnector_access_scopes: dict[connector, dict[tier, list[scope]]];startresolves connector tier first, then provider-level (back-compat). Forgoogle_docs:writeREPLACESspreadsheets.readonlywithspreadsheets(not additive — avoid redundant scope pairs). - Callback: nothing new — google identity extraction (email from id_token)
already covers it;
account_idalready rides the loopback POST. - Prereq (Rohit): add the two sensitive scopes to the OAuth consent screen's scope list (same client, Testing mode: test users + 7-day refresh as usual). Publishing later = standard verification, no CASA.
Desktop
- Descriptor
google_docs:managed=True,account_field="@identity", brand color#4285f4; manual field =access_tokenpaste (same shape as gmail's — rarely used but keeps the local-only path alive). Validator:GET https://www.googleapis.com/oauth2/v2/userinfo→ email. PROVIDER_FOR_CONNECTOR["google_docs"] = "google". Managed connect passesaccesstier throughbegin_managed_connect(HubSpot precedent); the granted scope string is stored on the profile → connector_list exposesaccess: "read" | "write"per account (badge in the GUI, like HubSpot).- URL/id parsing (shared helper): accepts full URLs
(
docs.google.com/document/d/<id>/…,docs.google.com/spreadsheets/d/<id>/…) or bare ids; the doc-vs-sheet kind comes from the URL path when present and is validated against the tool being called.
Tools (all with account param + _acct_result stamping)
Read tier:
gdoc_read(url_or_id, account)— Docs APIdocuments.get, body flattened to readable text: headings prefixed (#/##), list items bulleted, table rows ascell | cell | celllines. Returns{title, text, truncated}(cap ~50k chars).gsheet_read(url_or_id, range="", account)— no range: return{title, sheets:[{name, rows, cols}]}+ the FIRST sheet's used range values; with range (Sheet1!A1:D50or justSheet1):values.get. Values come back as a compact rows array; cap ~2k cells per call (ask for a narrower range beyond that).
Write tier (approval-gated, only usable when the account's grant includes
spreadsheets; a read-tier account gets a clear "connected read-only —
reconnect with write access" error):
gsheet_update_cells(url_or_id, range, rows_json, account)—values.update(RAW input option; rows_json = JSON array of arrays).gsheet_append_rows(url_or_id, range, rows_json, account)—values.append.gsheet_create(title, rows_json="", account)—spreadsheets.create(+ optional initial values); returns the new sheet's URL.
tool_defs: 2 read + 3 write entries; write tools carry the tier note in their descriptions so the model self-selects correctly.
GUI
- AccountsDetail already covers it (generic accounts page) with one
addition: an
accessbadge per account row (read-onlytag when the grant lacksspreadsheets) — same affordance as HubSpot's portal rows. - Add-modal: One click | Manual pills with the read/write radio on the
one-click pane — generalize
HubSpotOneClickinto the access-tier variant ofGenericOneClickinstead of a third bespoke pane. - Row blurb: "Read docs and sheets by link; update sheets with approval."
Tests
- URL/id parser (doc URLs, sheet URLs, bare ids, gid fragments, rejects).
- Doc flattener (headings/lists/tables → text) on a canned documents.get payload; truncation.
- Sheet read: no-range metadata+first-sheet shape; ranged values; cell cap.
- Write tools: routing + approval pinned + read-only-grant error path.
- Broker: per-connector access tier resolution (google_docs write ≠ gmail consent pollution), scope replacement not addition.
- e2e: accounts page reuse (access badge render); modal radio → connect body.
- Live drill (together): connect read on account A + write on account B, run the training-followups flow against a real Sheet, verify the read-only account refuses writes and approvals name the email.
Build order
- Broker: per-connector access tiers + google_docs scopes (+tests).
- Desktop: descriptor/provider map/URL parser/read tools (+tests).
- Desktop: write tools + tier gating (+tests).
- GUI: access badge + one-click radio generalization (+e2e), visual review.
- Rohit: consent-screen scopes; deploy (terraform approval); joint drills.
Open questions (defaulted)
- Docs tabs (new Docs API tab structure): wave 1 flattens all tabs in order.
gsheet_createlands in My Drive root (no drive.file scope to place it in folders) — acceptable; the tool returns the URL.- CSV in/out helpers deferred; rows_json is enough for the model.