Architecture

How Tempo is wired.

AI doesn't assist the workflow — it drives it. Below is how the system is designed.

58/58AI evals passing
8eval fixtures
280+unit + route tests
11validated tools
4agentic surfaces
System stack

Next.js 16

App Router, API Routes, Middleware Auth

Auth Layer

NextAuth JWT, per-route validation, role hierarchy

AI Engine

Claude with tool_use — chat, single-shot, and multi-step runs

Tool Execution

11 tools: search, update, remind, note, query, policy lookup, email, documents

Supabase

Postgres, RLS, Storage, real-time

Agentic loop

The AI agent uses tool_use to reason and act in a loop. Each iteration: the AI decides what to do, the system validates and executes the tool, then returns the result for the next reasoning step.

User Message
AI Reasoning
Tool Validation
Execute & Log
Return Result
Final Response

while (iterations < MAX_TOOL_CALLS) {
  response = ai.messages.create(tools, messages)
  if (no tool_use) return response.text
  for (tool in response.tool_use) {
    validate(tool.name) // reject hallucinated tools
    result = executeTool(tool) // try/catch with error recovery
    messages.push(tool_result)
  }
}

Agentic surfaces

The chat loop above is one entry point. The same tool surface also drives goal-directed runs, saved procedures, and reach into external systems — with a human in the loop on the decisions that matter.

Multi-step runs

lib/agent-runner.ts

Give the agent a goal on a claim. It plans 3–8 steps, executes each through the same validated tool surface, and writes a live trace you can watch. High-stakes steps (settlement, denial) pause for human sign-off instead of auto-applying.

Operating procedures

app/aops

Saved, named procedures — a plain-English goal plus the conditions for when it applies. Run on demand, or fire automatically on matching new claims. Draft / Live / Paused, so a manager rolls one out without surprising the existing book.

External systems

app/portal/carrier-x

The agent reaches beyond its own DB: lookup_policy pulls coverage from a carrier portal; send_email_and_wait emails a claimant and blocks on their reply. Both shaped like real integrations, so swapping in a live adapter is a one-file change.

Email intake

lib/email-intake.ts

The claims@ inbox, triaged by the agent: each inbound email is classified and extracted by the model, then deterministic rules route it — create the claim, attach to the referenced claim, or park it as an exception with a stated reason. Low confidence never acts alone.

Forward-deployed: an agent operating a legacy carrier portal

The same external-systems idea, taken to the real case: an agent that reads and drives a legacy portal in a browser, no API, with a 3/3 eval scored against the system of record.

Safety layers

JWT Auth

Protected API routes validate the NextAuth token before processing; public demo routes have explicit gates, limits, and budgets

Zod Validation

Request bodies validated against strict schemas — rejects malformed input

Tool Whitelist

Agent can only call 11 predefined tools — hallucinated tool names are rejected

Field Whitelist

Only specific claim fields can be modified — no arbitrary DB writes

Injection Resistance

Untrusted text (operator goals, public intake) is delimited as data in agent prompts — instructions inside it are not followed

Spend Cap

Every Claude call is metered to a usage table; a global daily budget returns HTTP 429 before the bill runs away

Human Sign-off

High-stakes agent steps (settlements, denials) pause the run for review instead of auto-applying

Error Recovery

Tool failures return structured errors to the AI — agent recovers gracefully

Audit Trail

Every agent action logged with timestamp, tool name, and outcome

Eval Gate

CI runs the eval suite on every PR that touches an AI path — regressions block the merge

Production roadmap

What I'd add before deploying to enterprise customers:

Token streaming (SSE) in the claims chat — the morning brief already streams
Exponential backoff with jitter on Claude API failures
Per-tenant cost budgets (the spend cap is global today)
Durable rate limiting via Upstash (per-instance in-memory today)
Multi-tenant data isolation via org_id scoping + per-tenant RLS
Prompt versioning and A/B testing framework
OpenTelemetry tracing for end-to-end observability beyond Sentry, Langfuse, and Vercel Analytics
SSO (SAML / OIDC) against the carrier identity provider
Tech stack
Next.js 16React 19TypeScriptSupabaseClaude (Anthropic)Tailwind CSS v4shadcn/uiZodNextAuthVercel