Source Of Truth
src/app/api/v1/**is the public backend surface for web, mobile, desktop, and extension clients.src/server/app-api/v1/**holds the route orchestration behind those Next route wrappers.src/server/**holds server-only auth, billing, storage, AI, tools, route services, and provider adapters.src/shared/**holds isomorphic contracts and client-safe helpers.src/contexts/**holds app-wide client providers (AuthContext,WorkspaceContext);src/hooks/**holds app-wide client hooks (useWorkspaceChanged). Both may import@/sharedonly.src/features/**holds web feature containers and feature-local helpers. Cross-feature imports are banned; shared primitives belong in@/shared,@/contexts, or@/hooks(e.g. the workspace provider, routing helpers, and types that used to sit insidefeatures/workspaces).src/server/workflows/**holds Vercel Workflow SDK durable orchestration (automation-run,automation-schedule,personal-chat-work,workspace-agent-turn). Workflow bodies bundle for the workflow runtime — keep their imports to"use step"functions and@/alias modules only.convex/**owns durable app state, domain mutations, scheduled work, and Stripe webhook handling.scripts/is split by purpose:scripts/ci/(checks, boundary rules, baselines consumers),scripts/qa/(smoke tests, rehearsal/readiness harnesses),scripts/db/(migrations, backfills, ops),scripts/lib/(shared script helpers).scripts/dev-setup.shandscripts/vercel-ignore-build.shstay at root — the latter is pinned by the Vercel project ignore-build command.packages/overlay-app-core/**holds shared cross-surface contracts and UI settings types.packages/overlay-api-client/**holds typed transport wrappers for/api/v1/**.packages/overlay-ui/**holds the shared presentation primitives (buttons, inputs, dialogs, and the list-page system:Tile/TileGrid/TileIcon/TileSkeleton/CreateTilefor grid tiles,ListRowfor list rows, andHeaderSearchfor collapsible header search). List pages (Projects, Knowledge, Files, Agents, Extensions) must render their tiles, rows, and headers through these primitives instead of hand-rolled markup so spacing, radii, hover language, and dark-mode surfaces stay uniform.packages/overlay-agent-bridge-protocol/**owns the vendor-neutral, versioned Zod wire protocol between Overlay and connected execution environments.packages/overlay-agent-host/**is the portable outbound-only host executable. It owns local device keys, SQLite delivery state, filesystem grants, and fake/ACP adapters; it does not own workspace identity, transcripts, approvals, policy, or billing.packages/overlay-sandbox-runtime/**is the provider-neutral managed execution boundary. It owns sandbox lifecycle, command/file/port/snapshot/network/usage contracts and Vercel/Daytona translations. Provider SDK types and raw handles do not cross into public routes, billing, or connected-agent persistence.
src/server/app-data/capabilities.ts
and src/server/app-data/route-support.ts are the source of truth for what
Postgres mode exposes. Unsupported routes must return the structured
app_data_route_not_supported response before domain services or Convex are
called. Workspace collaboration and connector mappings are currently Convex
only; Postgres must not silently fall back to Convex for them.
Connected-agent persistence is provider-neutral through
ConnectedAgentRepository. Environment, binding, command, remote-session,
approval, sandbox-lease, enrollment, proof-challenge, short-lived credential, and
request-replay records have matching Convex and PostgreSQL schemas.
Remote output advances a per-session contiguous event cursor transactionally;
it projects into the existing conversation and AgentRun lifecycle rather than
creating a second transcript.
A connected-agent environment is a machine or runtime boundary, not a working-directory or
single-agent boundary. Bindings choose an agent and adapter; each command chooses an explicitly
granted working directory. The host can grant multiple roots or explicit OS-user-wide access.
Protocol versioning, validation, sequencing, enrollment proof canonicalization, and signed HTTP
transport remain in the bridge/host
packages so neither Convex nor PostgreSQL becomes a public host API.
ConnectedAgentControlPlaneService is the server-only policy boundary for enrollment and host
traffic. Browser management routes use the normal authenticated BFF and active-workspace
resolution. Host routes are explicit BFF exceptions because they use outbound machine auth:
method-scoped opaque credentials plus Ed25519 request proofs and atomic nonces. Route handlers
never select a provider directly; both modes use ConnectedAgentRepository from app data.
ManagedAgentSandboxService provisions the private runtime selected by operators, starts the
same Agent Host image used by every environment, and records the provider-neutral sandbox lease.
The browser sees Overlay Cloud, an environment, and lease state, not a Vercel or Daytona object.
Clients should not call model providers, billing providers, identity admin APIs, object storage, or Convex directly unless a route explicitly exists for that surface.
src/app/_components/AppShellLayout.tsx is the reusable web shell boundary.
Canonical /app/** routes, ordinary auth flows, and public full-page product
destinations compose it from their route-group layouts. The shell accepts an
explicit showcase mode so public routes do not depend on hidden rewrite query
parameters. Protocol callbacks, integration popups, public share links, and
visual fixtures remain standalone by contract.
Domain Decomposition
Workspace behavior stays behind the existing provider/repository contracts while large seams are split by responsibility. TheWorkspaceSwitcher controller owns
navigation and lifecycle state; WorkspaceSwitcherView owns the trigger, menu,
and dialog presentation. Postgres workspace governance writes build their SQL in
src/server/workspaces/workspace-sharing-policy-sql.ts, keeping policy patch
semantics separate from repository orchestration without changing the contract.
Personal and organization workspaces use the same collaboration capabilities:
people, agents, direct messages, channels, teams, invitations, and resource
grants all follow the same service and repository contracts. Personal differs
only in lifecycle: it is the account’s permanent default workspace, cannot be
archived through workspace lifecycle APIs, and its creating principal remains
the canonical active owner. Permanent account-deletion erasure is a separate
audited compliance path.
Personal Chat execution lifecycle
Interactive Personal Chat runs use the provider-neutralAgentRun contract in
src/shared/agents/agent-run.ts. AgentRun.status is the only persisted
authority for queued, running, approval-waiting, completed, failed, or cancelled
execution; conversationMessages.status is a rendering projection. Convex and
Postgres create the assistant placeholder and AgentRun together, and complete,
fail, or cancel both records in one storage transaction.
Chat mode continues to use the AI SDK ToolLoopAgent. It streams directly to
the connected browser, while Next after() drains a tee of the stream for a
persistent conversation. Overlay does not write token deltas for this path and
does not use the Cloudflare chat relay. The final assistant content, parts,
usage, routed model, and terminal AgentRun state are written once when the run
ends. On reconnect, the client polls GET /api/v1/conversations/run and reloads
the final message; a non-terminal run shows the disconnected generating state.
ToolLoop runs tolerate browser disconnection but not a process crash. Their
lease expires to a structured tool_loop_lease_expired failure through the
Convex cron or Postgres maintenance worker. Stop first cancels AgentRun, then
best-effort aborts the in-process AbortController; late completion cannot
overwrite cancellation.
Work mode uses @ai-sdk/workflow’s WorkflowAgent behind the same AgentRun
contract. The request route performs the same authorization, context and memory
construction, model policy, tool filtering, billing reservation, and user-message
persistence as Chat mode, then starts workflows/personal-chat-work.ts. Model
calls and tool executions are durable Workflow steps; the workflow ID is stored
on AgentRun. Work mode exposes the workflow’s writable stream as a UI message
SSE stream: the WorkflowAgent writes ModelCallStreamPart chunks to the run
stream and the route pipes them through createModelCallToUIChunkTransform. The
client renders tokens, tool calls, and approval requests in real time, then
receives the final persisted message through the normal conversation data path.
Approval-required tools transition AgentRun to waiting_for_approval with a
durable hook token and tool-call summary. POST /api/v1/conversations/run/approval resumes the hook; the workflow returns the
same run to running. Stop cancels both AgentRun and the Workflow run. Internal
Overlay tool mutations receive agent-run:<runId>:tool:<toolCallId> as their
idempotency key. Connector providers outside Overlay must still honor their own
idempotency semantics; Workflow step replay alone cannot make an external API
idempotent.
Both runners write comparable observations onto the terminal AgentRun. Chat
records first-token and completion latency from its direct stream; Work records
first-token latency from the workflow stream, completion latency, and durable
step observations. Provider token usage and
estimated provider cost, tool outcomes and retries, cancellation timing,
browser-disconnect outcomes, lease/process-loss observations, and stale-run
detection feed GET /api/v1/conversations/run/metrics. The report keeps raw
sample counts and denominators and never recommends a runner; product decisions
must be made from the resulting evidence.
Auth
- Browser auth uses the signed httpOnly
overlay_sessioncookie. - Native/mobile/desktop auth uses supported bearer access tokens against the same
/api/v1/*backend. - Internal server-to-server calls use short-lived service auth from
src/server/auth/service-auth.ts. - Shared route authentication flows through the API boundary helpers in
src/server/auth/app-api-auth.ts.
/api/v1/* route should reject requests that lack a valid browser session, mobile bearer token, or service auth token.
App Bootstrap
GET /api/v1/bootstrap is the canonical signed-in startup call. It returns user info, entitlements, UI settings, model catalogs, feature flags, navigation destinations, and defaults.
New clients should start from this route and reuse existing API contracts before adding client-specific endpoints.
Billing
Stripe is the hosted billing provider for subscriptions, top-ups, auto top-up preferences, customer portal sessions, and webhook events. Private deployments can disable billing with runtime config. Convex is the durable entitlement/usage source of truth after webhook processing. Billing ownership is represented separately from user identity by an additiveBillingAccount contract. An account has exactly one scope owner: a user for personal, or a workspace for workspace. markup_25_v1 explicitly preserves the current 25% markup policy. New personal accounts start with an empty budgeted balance so account creation never grants spend implicitly. The compatibility migration creates one personal account per user, attaches legacy billing and usage rows, and dual-writes user and account identifiers plus canonical subscription/balance shadows. User-keyed records remain the enforcement and rollback source until balance parity and exact Stripe-subscription verification pass; the migration never switches reads automatically.
Migration balance parity sums only active reserved and reconcile_required rows through the compound billing-account/status index. Historical finalized and released reservations are deliberately excluded, so a high-volume account does not hit the active-reservation safety cap merely because it has more than 1,000 completed operations.
BillingPayerResolver separates workspace authorization from payment selection. The workspace-wallet master flag and off -> internal -> selected -> general rollout select which organization workspaces have switched. Personal workspaces and non-selected organizations retain the compatibility path. Once selected, an organization call requires an active workspace account and has no personal fallback. Workspace reservations update the account-keyed balance and an optional member or programmatic fixed-period limit atomically; the original personal reservation method remains unchanged for rollback.
The app bootstrap and subscription read use that same resolver. Active members of an eligible organization therefore receive the workspace account’s plan and budget for model gating, while mutating billing operations remain restricted to owners and admins. Workspace-scoped clients send the active workspace header explicitly; a member’s personal subscription is not treated as the organization’s entitlement or payment fallback.
All enabled hosted-cost boundaries now pass the resource workspace plus a member or programmatic subject into the resolver. This includes shared chat and named agents, Postgres and Convex knowledge embeddings, browser/Daytona, hosted media and transcription, gateway search, and automation/API execution. Scheduled automations use automation:{id} and API keys use api-key:{id}. Vercel Workflow overhead is a separate 20-event estimate charged at the provider event rate plus markup_25_v1. Integrations, functions, and MCP execution are bounded to 12 tool steps per agent turn and 50,000 response characters; file ingestion is bounded to 20 requests per user per hour plus the plan storage cap. src/server/billing/billable-feature-coverage.ts is the launch inventory and its test fails when a category lacks metering or a concrete quota.
Chat reservations price the full 12-step model-call envelope, including the
per-step 8,192 output-token ceiling. A tool loop must not reserve only its first
model call: later completed steps would otherwise exceed the reservation and be
quarantined even when provider usage is valid.
Vercel Sandbox is the default managed sandbox provider. Its reservation is based on worst-case
active vCPU time, provisioned-memory wall time, outbound transfer, and sandbox creation, with an
operator-configured safety buffer and pre-provider per-run cost ceiling. Settlement uses provider
SDK counters after the session stops; incomplete counters fail into reconciliation instead of an
estimated charge. The standalone sandbox route always denies outbound network access because a
user-space polling guard cannot make a byte-exact provider-cost promise. It rejects oversized
artifacts before provider transfer, and its reservation includes the maximum bounded artifact
transfer. Networked agent sandboxes use separate metered flows. The default iad1 region and its
configured unit rates must move together. Blank pricing or buffer variables fall back to
conservative defaults rather than silently becoming zero.
Workspace Stripe ownership is account-keyed, never admin-keyed. Owners and admins may initialize the account and open checkout, top-up, verification, and portal flows, but Stripe metadata and durable subscription state bind to billingAccountId plus workspaceId; userId is retained only as the initiating actor. Both the Next/Postgres webhook and hosted Convex webhook preserve the personal metadata path, deduplicate event IDs, and reject stale subscription state with providerEventCreatedAt.
Usage reservations have an evidence-gated recovery path. Work that expires before a provider starts is released automatically. Work that may have reached a provider is quarantined as reconcile_required; it must be finalized or released with a durable provider or internal-operation evidence reference. The five-minute Convex cron and Postgres worker expose queue depth and oldest age but never guess an ambiguous charge.
Finalized usage preserves retail cost and actual provider cost independently. UsageRepository.getBillingAccountOperationalReport is implemented by both Convex and PostgreSQL and reports retail credits, actual cost, realized margin, cost coverage, reconcile queue depth, and the count older than the 15-minute response SLA. The workspace billing UI exposes this only to owners and admins.
Important code:
src/shared/billing/billing-pricing.tssrc/shared/billing/billing-account.tssrc/shared/billing/billing-account-migration.tssrc/server/billing/billing-runtime.tssrc/server/billing/stripe-billing.tssrc/server/billing/BillingCustomerService.tssrc/server/billing/BillingCheckoutService.tssrc/server/billing/BillingPayerResolver.tssrc/server/billing/billable-feature-coverage.tssrc/server/billing/automation-workflow-billing.tssrc/server/billing/WorkspaceBillingService.tssrc/server/billing/providers/stripe-billing-provider.tsconvex/billing/subscriptions.tsconvex/billing/accounts.tsconvex/billing/accountMigration.tsconvex/billing/accountSubscriptions.tsconvex/billing/spendLimits.tsconvex/platform/usage.tsconvex/billing/stripe.tsconvex/billing/stripeSync.tssrc/shared/billing/usage-reconciliation.tssrc/server/usage/UsageRepository.tsscripts/qa/usage-reconciliation-audit.tsscripts/db/personal-billing-account-backfill.tsscripts/qa/stripe-personal-account-verification.ts
Storage
Uploaded files and generated assets are stored through server-mediated flows. Cloudflare R2 remains the managed object store, but it is not used for DNS, web hosting, request proxying, or chat streaming. R2 and S3-compatible object access must remain private and owner-scoped; users should only receive short-lived, validated access.Chat Streaming
Interactive chat streams directly from the Vercel-hosted/api/v1/conversations/act route to the browser. Persistent conversations tee a server-side reader so generation, usage accounting, and message persistence continue after a browser disconnect. Reconnects recover from the authoritative persisted conversation state; there is no external stream relay or stream-auth endpoint.
Observability and Metrics
The measurement layer uses PostHog as the unified metrics sink, emittingoverlay.metrics.* events from both server and client. All emission is
fire-and-forget — a metrics failure must never break a customer-facing
request or crash the UI.
Server-side metrics
src/server/observability/metrics.ts— typed emission contract for BFF requests, Postgres queries, Convex functions, model token breakdowns, AgentRun lifecycle, workflow events, uploads, and business rollups.src/server/observability/business-rollup.ts— periodic aggregation of cost-per-active-user-hour, cost-per-chat-turn, and cost-per-automation.src/app/api/v1/_utils/bff.ts—handleBffRouteemits aoverlay.metrics.bff_requestevent on every exit path (early returns + final return) with route, method, status, duration, auth type, workspace, payload size, and Retry-After.src/server/database/postgres/client.ts— pool wraps each client’squerymethod to emitoverlay.metrics.postgres_querywith duration and rows returned.src/server/conversations/ActContextService.ts—emitTokenBreakdownestimates per-source token counts (history, memory, skills, tools, attachments, system) and emitsoverlay.metrics.model_tokens.
Client-side metrics
src/shared/observability/client-metrics.ts— isomorphic event bus usingwindow.dispatchEvent(CustomEvent). No PostHog import; theObservabilityClientcomponent listens and forwards to PostHog.src/shared/observability/duplicate-tracker.ts— windowed duplicate request detection (1s, 5s, 15s windows) emittingoverlay.metrics.duplicate_request.src/components/providers/ObservabilityClient.tsx— forwards client metrics events to PostHog viaaddMetricsListener.src/shared/chat/chat-list-cache.ts— emits cache hit/stale/miss events.src/features/chat/components/chat/chatTransport.ts— emits chat-open latency, transcript bytes, and message count.src/features/chat/components/chat/useAgentRunLifecycle.ts— emits AgentRun recovery metrics on terminal transitions.src/contexts/AuthContext.tsx— emits session refresh metrics with trigger source (interval, focus, visibility, manual).
Convex function metrics
convex/lib/metrics.ts—withMetricswrapper for mutation handlers, records function name, duration, and error status to thefunctionMetricstable.convex/platform/metrics.ts— cleanup cron (7-day TTL) and aggregation query for export to PostHog.convex/schema.ts—functionMetricstable with indexes by function name and timestamp.convex/platform/rateLimits.ts— instrumented withwithMetricsas the highest-frequency Convex mutation.
Privacy
Metrics events contain only: route names, opaque IDs, sizes, counts, durations, auth type, workspace ID, and categorized metadata. No conversation content, message bodies, authorization tokens, private workspace JSON, or raw user data is sent to analytics.Convex
Development commonly uses a separate dev deployment from production. Follow Worktree Staging QA and Convex Workflow for the deployment lane that matches the current revision. Deploy Convex only from the matching release worktree:convex:push:all, and do not pass .env.local to production Convex deploy commands; that can point deploys at the dev slug.