S289 Technical Handoff — OAuth-First Intelligent Task Execution
Session: 289.2
Date: 2026-03-31
Branch: session-280-290 (shared lane, no commits — concurrent S291)
Spec: docs/superpowers/specs/2026-03-30-oauth-first-task-execution-design.md
Plan: docs/superpowers/plans/2026-03-30-oauth-first-task-execution.md
Scope Completed
Phases 1-3 of 7: Google scope expansion + Gmail API executor + 3-tier routing + Vault OAuth UI.
Architecture
User goal → ComputerUseSupervisor.startSupervised()
│
├── Vault credential resolution (S287, unchanged)
│
├── [S289] OAuthRouterService.tryApiExecution()
│ ├── TaskIntentParser.parse() → { service, action, params, confidence }
│ ├── ApiExecutorRegistry.get(service) → GmailApiExecutor
│ ├── BackendApiClientService.getOAuthConnections(userId)
│ └── executor.execute() → BackendApiClientService.oauthProxy()
│ └── backend-api POST /auth/oauth/proxy → Gmail API
│
├── Tier 1 success? → return immediately (no VM)
│
└── Fallthrough → infra.computerUseExecute() (Tiers 2-3, unchanged)
Files Created
| File |
Lines |
Purpose |
engine/api-executors/api-executor.interface.ts |
35 |
ApiExecutor + ApiExecutionResult interfaces |
engine/api-executors/api-executor.registry.ts |
38 |
NestJS registry, auto-registers Gmail on init |
engine/api-executors/gmail-api.executor.ts |
215 |
5 Gmail actions, OAuthProxyResponse type, callProxy helper |
engine/task-intent-parser.service.ts |
260 |
10 patterns (Gmail 5, Calendar 2, GitHub 3), confidence scoring |
engine/oauth-router.service.ts |
155 |
Tier 1 orchestrator, SERVICE_PROVIDER_MAP, 0.7 threshold |
engine/__tests__/task-intent-parser.spec.ts |
270 |
37 tests |
engine/__tests__/gmail-api-executor.spec.ts |
260 |
18 tests |
engine/__tests__/oauth-router.spec.ts |
155 |
10 tests |
Files Modified
| File |
Change |
backend-api/.../google.provider.ts |
Scopes: +gmail.send, +gmail.readonly, +calendar.events, +calendar.events.readonly |
backend-api/.../oauth.service.ts |
handleCallback returns connectionId (MongoDB _id) |
backend-api/.../oauth.controller.ts |
buildPopupCloseHtml passes email + connectionId in postMessage |
backend-vitto-api/.../computer-use-supervisor.service.ts |
Injects OAuthRouterService, Tier 1 try-catch block before computerUseExecute |
backend-vitto-api/.../tools.module.ts |
Registers TaskIntentParser, OAuthRouterService, ApiExecutorRegistry, GmailApiExecutor |
vitto-hub/.../vault-page.component.ts |
Injects HttpClient + API_BASE_URL, adds connectOAuth(), supportsOAuth(), oauthError signal |
vitto-hub/.../vault-page.component.html |
OAuth connect button + error message in add-account modal, OAuth badge in account rows |
vitto-hub/.../vault-page.component.scss |
.oauth-connect-btn, .oauth-error, .oauth-badge, .divider styles |
vitto-core/.../vitto-api.service.ts |
createVaultAccount body type: +oauthConnectionId?: string |
MCP Decisions Logged
| ID |
Type |
Summary |
dev_..._ijrw3ww |
architecture |
3-tier routing hierarchy design |
dev_..._0ay163t |
architecture |
Token proxy pattern (backend-api owns all tokens) |
dev_..._9ug1c6u |
implementation |
Regex intent parser with confidence scoring |
dev_..._fleh4qd |
implementation |
Popup blocker fix (sync open, async redirect) |
dev_..._2pfhckj |
pattern |
API executor plugin architecture |
Conventions Added
- Security: OAuth tokens never leave backend-api. Proxy through
oauthProxy(). Redact userId in logs.
- Angular: Browser popups from async contexts: open
about:blank sync, redirect after. Validate postMessage origin.
- Backend: Pipeline intercepts must use try-catch fallthrough. New features must never block existing paths.
Test Coverage
3 test suites, 65 tests, all passing
- task-intent-parser.spec.ts: 37 tests (all Gmail/Calendar/GitHub patterns + edge cases)
- gmail-api-executor.spec.ts: 18 tests (5 actions + error handling + MIME encoding)
- oauth-router.spec.ts: 10 tests (routing logic, fallthrough, confidence, redaction)
What Remains (Phases 4-7)
| Phase |
Files to Create/Modify |
Effort |
| 4: Persistent VM onboarding |
Vault UI "Verify & Save Session" button, login-only CU endpoint, snapshotId on VaultAccount schema |
2 tasks |
| 5: Calendar executor |
google-calendar-api.executor.ts (4 actions: list, create, update, delete) |
1 task |
| 6: GitHub executor |
github-api.executor.ts (5 actions: create issue, list issues, create PR, list repos, star) |
1 task |
| 7: Metrics |
Log API vs snapshot vs cold boot routing, track cost savings |
1 task |
Deployment Requirements
- Environment variables:
GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, GITHUB_OAUTH_CLIENT_ID, GITHUB_OAUTH_CLIENT_SECRET, OAUTH_ENCRYPTION_KEY must be set in .env.local
- Google Cloud Console: Enable Gmail API + Calendar API. Add test users in OAuth consent screen (test mode).
- Existing OAuth connections: Users must disconnect and reconnect Google to get the new scopes (because
prompt: 'consent' forces a fresh consent screen).
- Restart: backend-api (scope change), backend-vitto-api (new services), frontend dev server.
Known Limitations
- No
connectOAuth() / getOAuthConnections() methods in VittoApiService — the vault component calls HttpClient directly to backend-api. Works but diverges from the "one service layer" pattern.
- Google requires verification for sensitive scopes (
gmail.send) in production. Test mode is fine for dev but production deployment requires Google's OAuth verification process (can take weeks).
- Gmail API rate limits: 250 quota units/day for free Gmail accounts. Heavy users may exhaust quota, causing fallthrough to CU.