# Google Services Patterns This folder contains patterns for integrating with Google services (Gmail, Calendar, Docs) via OAuth authentication. ## Directory Structure ``` packages/patterns/google/ ├── core/ # Core, engineering-supported patterns │ ├── util/ # Shared utilities (clients, auth manager) │ │ ├── gmail-client.ts │ │ ├── gmail-send-client.ts │ │ ├── calendar-write-client.ts │ │ ├── google-auth-manager.tsx │ │ ├── google-docs-client.ts │ │ ├── google-docs-markdown.ts │ │ └── agentic-tools.ts │ │ │ ├── google-auth.tsx # Core OAuth2 │ ├── google-auth-personal.tsx # Personal account wrapper │ ├── google-auth-work.tsx # Work account wrapper │ ├── gmail-importer.tsx # Email fetching (heavily used) │ ├── google-calendar-importer.tsx │ ├── imported-calendar.tsx │ ├── processing-status.tsx │ │ │ └── experimental/ # Less hardened │ ├── google-auth-switcher.tsx │ ├── gmail-agentic-search.tsx │ ├── gmail-sender.tsx │ ├── gmail-label-manager.tsx │ ├── gmail-search-registry.tsx │ ├── calendar-event-manager.tsx │ ├── calendar-viewer.tsx │ └── google-docs-comment-orchestrator.tsx │ ├── extractors/ # End-user patterns │ ├── usps-informed-delivery.tsx │ ├── email-notes.tsx │ ├── chase-bill-tracker.tsx │ ├── bofa-bill-tracker.tsx │ ├── pge-bill-tracker.tsx │ ├── berkeley-library.tsx │ ├── united-flight-tracker.tsx │ ├── hotel-membership-gmail-agent.tsx │ ├── favorite-foods-gmail-agent.tsx │ ├── email-pattern-launcher.tsx │ └── ... │ └── WIP/ # Work in progress └── google-docs-importer.tsx ``` ## Quick Start ### For Staging/Production (Recommended) OAuth is pre-configured. Just: 1. Visit your homespace (e.g., `https://toolshed.common.tools/`) 2. Deploy `google-auth.tsx` 3. Click "Sign in with Google" and complete OAuth 4. Click the star to favorite it (tags it as `#googleAuth`) 5. Deploy any Google pattern - it auto-discovers your auth via `wish()` ### For Local Development You need your own Google OAuth credentials: 1. Create project at [Google Cloud Console](https://console.cloud.google.com) 2. Create OAuth 2.0 Client ID (Web application) 3. Add redirect URI: `http://localhost:8000/api/integrations/google-oauth/callback` 4. Enable APIs: Gmail, Calendar, Drive (as needed) 5. Add to `packages/toolshed/.env`: ``` GOOGLE_CLIENT_ID=your-client-id GOOGLE_CLIENT_SECRET=your-client-secret ``` 6. Follow the staging/production steps above ### Token Refresh Tokens expire after ~1 hour. Refresh happens automatically in two layers: **Layer 1: Automatic (background-charm-service)** When google-auth is registered with background-charm-service, the `bgUpdater` handler is polled every ~60 seconds. If the token has < 10 minutes remaining, it refreshes proactively — no user action needed. To enable: register your google-auth piece with background-charm-service. **Layer 2: One-click (UI fallback)** If background refresh isn't running (e.g., local dev), consuming patterns show a "Refresh Session" button when the token expires. Clicking it calls the google-auth piece's `refreshToken` stream to refresh the token inline. **Layer 3: Manual (last resort)** If refresh fails (e.g., refresh token revoked): 1. Find your google-auth piece (in favorites or homespace) 2. Click "Sign in with Google" to re-authenticate 3. Other patterns automatically get the new token ## Pattern Architectures This folder contains two main architectural approaches for building Gmail-based patterns: ### Smart Importers Patterns that fetch specific emails and process them with LLM vision or text analysis. **Example:** `usps-informed-delivery.tsx` **How it works:** 1. Embeds `gmail-importer` with a hardcoded search query 2. Filters and extracts data (e.g., mail piece images) 3. Uses `generateObject()` with vision to analyze each item 4. Exposes enriched results + aggregate counts to other patterns **Architecture:** ``` Your Pattern └─ Instantiates GmailImporter({ gmailFilterQuery: "from:..." }) └─ Gets raw emails └─ computed() chain for filtering/extraction └─ .map() with generateObject() for LLM analysis └─ Exports: enriched data, counts, previewUI ``` **Key code (usps-informed-delivery.tsx):** - Line 294-295: Hardcoded query `from:USPSInformeddelivery@email.informeddelivery.usps.com` - Lines 362-442: Per-item LLM analysis with vision using `.map()` and `generateObject()` - Lines 460-495: Aggregate category counts computed from analysis results ### Agentic Search Patterns Patterns where an LLM strategizes and loops to find information dynamically. **Example:** `hotel-membership-gmail-agent.tsx` **How it works:** 1. Instantiates `GmailAgenticSearch` with goal, schema, prompts 2. LLM decides which searches to run 3. LLM extracts results matching the schema 4. Loops until goal is satisfied or limits reached **Architecture:** ``` Your Pattern └─ Defines: goal, resultSchema, systemPrompt, additionalTools └─ Instantiates GmailAgenticSearch({...}) └─ generateObject() with tools: searchGmail + your tools └─ LLM loop: search → analyze → extract → repeat └─ Composes UI: searcher.ui.{auth, controls, progress} └─ Exports: extracted results ``` **Key code (hotel-membership-gmail-agent.tsx):** - Lines 54-88: Schema definition with `defineItemSchema()` - Lines 287-391: Dynamic goal generation based on scan mode - Lines 408-429: System prompt with workflow instructions ## Pattern Categories ### core/ — Core Auth | Pattern | Description | | ------------------------------- | ------------------------------------------- | | `core/google-auth.tsx` | OAuth2 authentication flow for Google APIs | | `core/google-auth-personal.tsx` | Wrapper that adds `#googleAuthPersonal` tag | | `core/google-auth-work.tsx` | Wrapper that adds `#googleAuthWork` tag | ### core/ — Gmail & Calendar | Pattern | Description | | ----------------------------------- | -------------------------------------------- | | `core/gmail-importer.tsx` | Import emails from Gmail with search queries | | `core/google-calendar-importer.tsx` | Import events from Google Calendar | | `core/imported-calendar.tsx` | Display local calendar events | | `core/processing-status.tsx` | Loading/progress UI component | ### core/experimental/ — Less Hardened | Pattern | Description | | -------------------------------------------------------- | ---------------------------------------------- | | `core/experimental/google-auth-switcher.tsx` | Post-login account type classification | | `core/experimental/gmail-sender.tsx` | Send emails via Gmail API | | `core/experimental/gmail-label-manager.tsx` | Add/remove labels from emails | | `core/experimental/gmail-agentic-search.tsx` | Base pattern for Gmail-based agentic searchers | | `core/experimental/gmail-search-registry.tsx` | Community query database for Gmail searches | | `core/experimental/calendar-event-manager.tsx` | Create, update, delete calendar events | | `core/experimental/calendar-viewer.tsx` | View calendar events | | `core/experimental/google-docs-comment-orchestrator.tsx` | AI assistant for Google Docs comments | | `core/experimental/google-docs-comment-confirm.ts` | Side effects handler for Docs comments | ### extractors/ — End-User Patterns | Pattern | Description | | --------------------------------------------- | ---------------------------------------- | | `extractors/usps-informed-delivery.tsx` | USPS mail analyzer with LLM vision | | `extractors/email-notes.tsx` | Task notes sent to self | | `extractors/chase-bill-tracker.tsx` | Chase credit card bill tracker | | `extractors/bofa-bill-tracker.tsx` | Bank of America bill tracker | | `extractors/pge-bill-tracker.tsx` | PGE utility bill tracker | | `extractors/berkeley-library.tsx` | Library holds and due dates | | `extractors/united-flight-tracker.tsx` | United Airlines flight tracking | | `extractors/hotel-membership-gmail-agent.tsx` | Extract hotel loyalty numbers from Gmail | | `extractors/favorite-foods-gmail-agent.tsx` | Extract food preferences from emails | | `extractors/email-pattern-launcher.tsx` | Auto-launch patterns based on emails | | `extractors/calendar-change-detector.tsx` | Detect schedule changes | ### WIP/ — Work In Progress | Pattern | Description | | ------------------------------ | -------------------------- | | `WIP/google-docs-importer.tsx` | Import Google Docs content | > **Note:** `google-docs-importer.tsx` imports from `../../notes/note.tsx` and > requires deploying with `--root packages/patterns` to resolve cross-folder > imports. ## OAuth Scopes The patterns request various scopes depending on their needs: - `email`, `profile` - Basic user info (OpenID Connect) - `https://www.googleapis.com/auth/gmail.readonly` - Read emails - `https://www.googleapis.com/auth/gmail.send` - Send emails - `https://www.googleapis.com/auth/gmail.modify` - Modify labels - `https://www.googleapis.com/auth/calendar.readonly` - Read calendar - `https://www.googleapis.com/auth/calendar.events` - Manage calendar events - `https://www.googleapis.com/auth/documents.readonly` - Read Google Docs ## Manual Piece Linking When `wish()` isn't working (e.g., favorites disabled), you can manually link pieces via CLI. ### Steps #### 1. Deploy both pieces ```bash # Deploy google-auth ct piece new google-auth.tsx # Deploy gmail-importer ct piece new gmail-importer.tsx ``` #### 2. Authenticate with Google Auth piece Navigate to the google-auth piece in browser and complete OAuth flow. #### 3. Link the pieces ```bash # Format: source/path target/path ct piece link \ GOOGLE_AUTH_PIECE_ID/auth \ GMAIL_IMPORTER_PIECE_ID/overrideAuth ``` **Critical paths:** - Source: `GOOGLE_AUTH_PIECE_ID/auth` - the auth result from google-auth - Target: `GMAIL_IMPORTER_PIECE_ID/overrideAuth` - the overrideAuth input of gmail-importer #### 4. Verify the link ```bash # Check that overrideAuth is populated ct piece inspect --piece GMAIL_IMPORTER_PIECE_ID ``` You should see `overrideAuth` in the Source (Inputs) with token, user info, etc. ### Important Notes 1. **Path format**: Use forward slashes, e.g., `pieceId/auth` not `pieceId.auth` 2. **Link direction**: Source -> Target. The target piece "reads from" the source. 3. **The pattern must support overrideAuth**: The gmail-importer has: ```typescript overrideAuth?: Auth; ``` This optional input is what receives the linked auth data. 4. **Check "Reading From" in inspect**: After linking, `ct piece inspect` shows: ``` --- Reading From --- - sourcePieceId (Google Auth (email@example.com)) ``` ## Troubleshooting ### Link exists but auth not working in UI - The pattern might be showing both the "Connect Google Account" UI AND using overrideAuth - Check that the pattern's logic correctly uses overrideAuth when available - The piece name should show the email if overrideAuth is working (e.g., "GMail Importer email@example.com") ### Settings not being read in handler If pattern defaults aren't reaching the handler, ensure the handler's type definition includes all fields: ```typescript const myHandler = handler; }>(...); ``` Missing fields in the handler's type definition can cause them to be unavailable when calling `.get()`. ## Fork Status The `google-auth.tsx` pattern in this folder is a **fork** of the original from the community-patterns repository. This version includes additional features and enhancements, but may also have introduced regressions or bugs. **Long-term goal:** Rationalize these two implementations to maintain a single, well-tested version. ## Origin These patterns were originally developed by jkomoros in the [community-patterns](https://github.com/user/community-patterns) repository.