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
480+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 and 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 it 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 4/4 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 are validated against strict schemas, so malformed input is rejected

Tool Whitelist

The agent can only call 11 predefined tools; hallucinated tool names are rejected

Field Whitelist

Only specific claim fields can be modified, with no arbitrary DB writes

Injection Resistance

Untrusted text (operator goals, public intake) is delimited as data in agent prompts, and 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 so the agent can recover

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:

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