feat(mail): Builder-proxy Gmail — skip BYO Google OAuth#196
feat(mail): Builder-proxy Gmail — skip BYO Google OAuth#196
Conversation
…ation - documents.ts: getCurrentOwnerEmail() now reads from AsyncLocalStorage via getRequestUserEmail() instead of process.env (concurrent-safe) - db.ts: migrations v5-v8 are now no-ops — the tables in v1-v4 already include owner_email, and ALTER TABLE ADD COLUMN IF NOT EXISTS doesn't work on SQLite, causing fresh installs to fail
- enterprise-workspace.md: use `create` (not `create-workspace`), show multi-select picker, document `add-app`, add unified-deploy section with same-origin auth/A2A benefits, add shared-env section, refresh "out of scope" now that cross-subpath SSO is solved by deploy. - getting-started.md: lead with workspace + multi-select, mention `--standalone` and `add-app`. - deployment.md: add "Workspace Deploy: One Origin, Many Apps" section at the top; document `APP_BASE_PATH` + workspace env layering.
Add a complete ad-hoc analysis workflow: the agent gathers data from multiple sources (HubSpot, Gong, Slack, BigQuery, etc.), synthesizes findings into a Markdown report, and saves a reusable artifact with re-run instructions. Anyone can hit "Re-run" to get fresh results delegated back to the agent. - Actions: save-analysis, get-analysis, list-analyses, delete-analysis - API routes: GET/DELETE /api/analyses, /api/analyses/:id - UI: /analyses list page + /analyses/:id detail with re-run button - Sidebar: Analyses nav link with IconReportAnalytics - Navigation: analyses view support in navigate action + nav state hook - Skill: .agents/skills/adhoc-analysis/SKILL.md with full workflow guide - AGENTS.md: updated with analysis actions, common tasks, skill ref - Also includes dev-all.ts fix for templates.ts port config path
- Restore v5-v8 ALTER TABLE migrations in content template that were incorrectly replaced with SELECT 1 — breaks upgrade path for databases created before owner_email was added to CREATE TABLE statements. - Apply CF Workers setInterval shim to all JS chunks, not just entry — with ESM code splitting, chunks evaluate before entry module body, so the shim must be present in every file.
Another agent's refactor removed the dev auto-local fallback from isLocalModeEnabled(). Without it, dev mode required real auth (Better Auth or Google OAuth), causing constant logouts on server restart since the session cookie pointed to a DB that may not persist. Restored: in dev mode (NODE_ENV=development) without ACCESS_TOKEN or custom auth, getSession() returns local@localhost automatically. This is the expected dev experience — no auth setup needed.
Adds the "Connect Gmail via Builder" onramp so new users can reach mail's
aha moment without ever opening Google Cloud Console. When
BUILDER_PRIVATE_KEY is set and the user completes the Builder-hosted
OAuth flow, Gmail/People/Calendar calls go through
ai-services.builder.io/google/* instead of calling Google directly.
Agent-native side (this PR):
- credential-provider.ts — generic helpers for Builder-proxy auth:
FeatureNotConfiguredError, hasBuilderPrivateKey, getBuilderAuthHeader,
getBuilderProxyOrigin. Shaped so the LLM gateway layer can reuse it.
- google-proxy.ts — resolveGoogleTarget + per-workspace ledger of
accounts connected via Builder (stored in the shared settings table
so every sibling app in a workspace sees the same accounts).
- builder-browser.ts — getBuilderGoogleConnectUrl for the cli-auth
handoff with the full gmail/calendar/contacts scope set.
- core-routes-plugin.ts — adds /_agent-native/builder/google/status
(banner data) and /_agent-native/builder/google/callback (records
the connected account after Builder finishes the OAuth exchange).
- templates/mail/server/lib/google-api.ts — gmail/people/calendar
functions now accept GoogleAuth = string | GoogleProxyTarget. Direct
string tokens keep the existing Google endpoints; proxy targets
rewrite to ai-services.../google/{api}/*.
- templates/mail/server/lib/google-auth.ts — getClient / getClients /
getAuthStatus / isConnected all now surface Builder-connected
accounts alongside direct-OAuth accounts, de-duped by email.
- templates/mail/app/components/GoogleConnectBanner.tsx — hero layout
gets a prominent "Connect Gmail via Builder" primary CTA (visible
when BUILDER_PRIVATE_KEY is set) with the BYO-keys wizard below it
as a secondary option. "Free while in beta" copy.
Builder side (separate PRs): multi-tenant Google OAuth app, token
storage in organizations_private, and /google/* proxy in ai-services
still to land. The agent-native side degrades to BYO-keys until they do.
Plan: /Users/steve/.claude/plans/sorted-yawning-castle.md
✅ Deploy Preview for agent-native-fw ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nutritrack-daily-calories ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Builder has reviewed your changes and found 6 potential issues.
Review Details
Code Review Summary
This PR adds a "Connect Gmail via Builder" onramp to the mail template, enabling users to skip Google Cloud Console setup by using Builder's proxied OAuth. The feature spans 55 files across core framework changes, Google proxy infrastructure, mail API integration, and new UI components. The scope is significant and security-sensitive.
Risk Assessment: 🔴 HIGH
- OAuth/credential handling (security-sensitive)
- Multi-user workspace credential isolation
- API proxy auth routing
- Large scope (3238 additions, 55 files) increases complexity
Architecture Evaluation:
The overall layered approach is sound — direct Google tokens take precedence, Builder proxy is fallback, and a clear CTA guides users. The framework integration is clean with new credential-provider.ts and google-proxy.ts modules.
However, the implementation has critical flaws in data scoping and state management that must be fixed before merge:
Key Findings
🔴 CRITICAL: Data Scoping & Security
-
Builder Google accounts leak across workspace users — Any user in a workspace can access other users' Gmail accounts via Builder proxy. Accounts are stored in a global, unscoped settings key instead of per-user keys.
-
Mail API mutations broken for Builder accounts — The proxy auth was added to read paths but not mutation paths (archive, mark read, send, RSVP). Builder-connected accounts appear available but operations fail with 401.
-
Attachments and inline images broken — The
/api/attachmentsendpoint still assumes direct OAuth tokens; Builder-connected messages lose inline images and attachment downloads.
🟡 CRITICAL: UI/UX State Management
-
Account polling breaks multi-account workflows — The polling detects any existing account instead of detecting NEW accounts, so users cannot connect additional accounts after the first one.
-
Type mismatch —
builderStatustype missing theaccountsfield despite backend returning it. This blocks proper implementation of the polling fix. -
No disconnect support — Builder-connected accounts appear in the UI but clicking "Disconnect" silently fails; accounts stay connected.
Actions Required Before Merge:
- Fix credential scoping: use user-scoped settings keys for Builder accounts (e.g.,
u:{userEmail}:builder-google-accounts) - Pass user email through callback handler and credential functions
- Update
disconnect()to remove Builder accounts from the ledger - Refactor mail handlers to use unified auth resolution (not separate token-only paths)
- Add Builder proxy support to
/api/attachments - Fix polling to track initial account count, not just presence
- Add
accountsfield tobuilderStatustype definition
🧪 Browser testing: Will run after this review (PR touches mail UI and OAuth flows)
Code review by Builder.io
There was a problem hiding this comment.
Browser testing: 4/8 passed
Test Results: 4/8 passed ⚠️
✅ TC-01: Banner visibility without Builder key - Verify Google Connect Banner displays when BUILDER_PRIVATE_KEY is not set (succeeded)
Evidence: 1 screenshot captured
⚠️ TC-02: Banner visibility with Builder key - Verify 'Connect Gmail via Builder' appears as primary when BUILDER_PRIVATE_KEY is set (couldnt_verify)
Failure: timeout — Mail app onboarding page did not render buttons/UI elements even after code changes. Unable to access GoogleConnectBanner component to verify Builder option appears. Backend endpoints work correctly but UI layer not accessible.
✅ TC-03: Status endpoint response structure - Call GET /_agent-native/builder/google/status and verify JSON response (succeeded)
Evidence: 1 screenshot captured
⚠️ TC-04: Connect button opens popup/window - Click 'Connect Gmail via Builder' button and verify OAuth window opens (couldnt_verify)
Failure: timeout — Unable to access GoogleConnectBanner component to click the Builder button. Backend confirms connectUrl is properly formatted with all required OAuth parameters (host=agent-native-google, google_scopes with Gmail/Calendar permissions)."
⚠️ TC-05: Secondary 'Set up your own keys' flow still works - Click 'Set up Google' button and verify BYO flow appears (couldnt_verify)
Failure: timeout — Unable to reach GoogleConnectBanner to test BYO flow. Backend tests confirm endpoints are operational and endpoint access is properly configured."
⚠️ TC-06: Multiple account support - Verify account list displays when accounts are connected (couldnt_verify)
Failure: timeout — Unable to access GoogleConnectBanner after account connection. Backend endpoint listBuilderGoogleAccounts() confirmed working via /_agent-native/builder/google/status returning accounts array."
✅ TC-07: Type safety and API response validation - Verify no TypeScript type errors in browser console (succeeded)
Evidence: 1 screenshot captured
✅ TC-08: Builder key presence detection - Verify hasBuilderKey flag correctly reflects BUILDER_PRIVATE_KEY environment variable (succeeded)
Evidence: 1 screenshot captured
Details
PR #196 Gmail Builder-proxy OAuth flow: 4 of 8 tests passed (backend endpoints verified), 4 couldn't verify (UI layer inaccessible). Status endpoint, response structure, and type safety all working correctly. Builder key detection properly returns false when env var not set. ConnectUrl properly formatted with all OAuth parameters. No console errors or type mismatches.
…rding Removes the Builder-hosted multi-tenant Gmail OAuth proxy design (resolveGoogleTarget / builder-google-accounts / GoogleConnectBanner "Connect Gmail via Builder" CTA / /google/* proxy scaffolding). Why: Google's CASA review for "restricted" Gmail scopes (readonly, send, modify) would very likely reject a multi-tenant passthrough where one verified OAuth client proxies tokens for arbitrary downstream apps. Rather than bet Gmail availability on that review, we're pivoting to a per-user BYO-keys flow, optionally bootstrapped by the agent running a browser-automation onboarding (separate PR) so users never have to touch Google Cloud Console by hand. Kept: - packages/core/src/server/credential-provider.ts — the generic FeatureNotConfiguredError / hasBuilderPrivateKey / getBuilderAuthHeader / getBuilderProxyOrigin helpers are still useful for the LLM gateway and future Builder-hosted credential integrations. JSDoc updated to drop the Google-proxy framing. - The existing Builder browser / cli-auth flow (getBuilderBrowserConnectUrl, /builder/callback, /builder/status). - Another agent's /builder/agents-run route, which was unstaged on this branch at revert time. Removed: - packages/core/src/server/google-proxy.ts (entire file). - BUILDER_GOOGLE_* constants and getBuilderGoogleConnectUrl() in builder-browser.ts. - /_agent-native/builder/google/status and /_agent-native/builder/google/callback in core-routes-plugin.ts. - Proxy re-exports in packages/core/src/server/index.ts. - GoogleProxyTarget / GoogleAuth plumbing in templates/mail/server/lib/google-api.ts and google-auth.ts; Gmail/People/Calendar functions go back to taking a plain accessToken: string. - "Connect Gmail via Builder" button + builderStatus fetch in templates/mail/app/components/GoogleConnectBanner.tsx.
- New MCP client module and settings UI components - AgentPanel, CodeRequiredDialog, ResourcesPanel improvements - Macros agent-chat plugin updates
|
Pivoting to agent-driven onboarding (see PR #195). Transparent Gmail proxy design rejected due to Google verification policy risk; revert + new approach consolidated on updates-90. |
Summary
Adds the "Connect Gmail via Builder" onramp so new users can reach mail's aha moment without ever opening Google Cloud Console. Instead of the 3-minute Google Cloud Console setup wizard, a user with `BUILDER_PRIVATE_KEY` set (via the existing `/cli-auth` flow) clicks one button and is connected.
Layered design: whichever credential path is configured wins, with direct Google first, Builder proxy second, and a clear "connect" CTA if neither.
Agent-native side (this PR)
Builder side (separate PRs, not in this PR)
Still to land in builder-internal + ai-services:
Until those ship, agent-native gracefully degrades to the existing BYO-keys wizard.
Test plan
Design: `/Users/steve/.claude/plans/sorted-yawning-castle.md`