- puterProxyService with storeToken (create/rotate idempotent), resolveToken, chatStream
- chatStream relays to Puter OpenAI-compat endpoint with SSE streaming
- Cost recording with provider=puter, billingType=subscription_included, costCents=0
- Cost recording skipped when agentId is null/undefined (no FK violation)
- puterProxyRoutes with POST /puter-proxy/token and POST /puter-proxy/chat
- Board auth (assertBoard + assertCompanyAccess) on all routes
- All 10 TDD tests passing
- POST /oauth/google/authorize: returns {url, stateId}, stores PKCE verifier only (no companyId)
- GET /oauth/google/callback: exchanges code, parks tokens in pendingTokens by stateId
- POST /oauth/google/claim: moves tokens from pendingTokens to secretService with real companyId
- POST /api-keys/store: upserts provider API keys (openai/anthropic/groq) via secretService
- Cleanup of entries older than 10 minutes on each request
- Mounted in app.ts via api.use(googleOAuthRoutes(db))
- generatePkce() using crypto.randomBytes base64url verifier and SHA256 challenge
- generateAuthUrl() builds Google OAuth URL with PKCE params for Gemini scopes
- exchangeCode() POSTs to Google token endpoint with code_verifier
- storeTokens() upserts google_gemini_oauth_token via secretService
- resolveTokens() retrieves and parses stored tokens by companyId
- 30-02-SUMMARY.md: checkpoint section updated to reflect visual verification approved
- STATE.md: status changed from verifying to complete, session updated
- SUMMARY.md for 30-02 (hardware API client, ModeSelector, HardwareSummaryStep, wizard refactor)
- STATE.md updated with position, decisions, and session info
- ROADMAP.md updated with phase 30 plan progress
- REQUIREMENTS.md: ONBD-07 marked complete
- Refactor to 3-step flow: hardware detection, mode selection, root directory
- Add step indicator 'Step N of 3'
- Add HardwareSummaryStep on step 1 with dynamic heading
- Add ModeSelector on step 2 with 'both' pre-selected
- Add Back buttons on steps 2 and 3
- Persist selected mode via updateNexusSettings on wizard completion
- Reset step and mode on wizard close
- Add hardwareRoutes with unauthenticated GET /system/providers
- Add hardwareRoutes with GET /system/providers/recommendation
- Add nexusSettingsRoutes with board-auth GET/PATCH /nexus/settings
- Mount hardwareRoutes on app before boardMutationGuard (unauthenticated)
- Mount nexusSettingsRoutes on api router (board-auth gated)
- Add hardwareService with Apple Silicon / GPU / cpu_only tier detection
- Add 3s Promise.race timeout for si.graphics() with cpu_only fallback
- Add nexusSettingsService with Zod validation and file-backed persistence
- Extend ollama-model-catalog.json with tier arrays on every variant
- Add qwen3:8b family to catalog
- Update getRecommendedModel to accept optional hardwareTier parameter
- All 13 unit tests pass (TDD green)
28-02: ollamaApi client, model dropdown in config, skill badge
28-03: stateJson merge after heartbeat, HermesRuntimeCard in AgentOverview
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add ui/src/api/ollama.ts with ollamaApi.status() and ollamaApi.models()
- Replace free-text Model input with hybrid dropdown/fallback in HermesLocalConfigFields
- Dropdown shows pulled Ollama models with * prefix for recommended entries
- Install callout shown when Ollama is absent (with link to installUrl)
- Edit mode: selecting an Ollama model atomically sets model + provider:custom + base_url
- Manual entry fallback via "Other (manual entry)..." option or when Ollama absent
- Uses useCompany() hook for companyId (consistent with AgentConfigForm pattern)