Skip to documentation

Ports

Agents & the harness

An eli.ai agent is a durable, KB-grounded worker: it reasons over your knowledge base with tools, respects hard budgets and guardrails, pauses for human approval when it needs to, and records every step to Postgres so a run survives a crash or restart. The harness is a thin, hand-owned loop on the Vercel AI SDK — no LangGraph, no Temporal — that runs identically across every provider.

The loop

Durable agent execution loop

One durable step loop; state persists at every step.

Rendering diagram

Downloads

Concepts

  • Loop

Keywords

  • persist queued run + pg-boss job
  • execute under withWorkspace (RLS)
  • human approves via UI / API
  • worker pins agent_version + config snapshot
  • kb_search / kb_graph_query / kb_doc_read
  • SUSPEND → run_approvals (persisted)
  • UI / REST / schedule
  • tool call?
  • step loop (single-step generateText)
  • atomic decision + queued continuation
  • budget / wall-clock / cancel?
  • graceful stop: partial answer
  • deterministic citation guardrail
  • persist run + steps + output + trace
  • requiresApproval?
  • mcp:server:tool
  • yes
  • no
  • exceeded
Source and generation provenance

Status: current

Generated at: 2026-08-12T23:38:05.080Z

Source hash: f29dda8d38da5e2223933d1a5bfecabca5dea1abc03f6a839c10e534d06728ea

Metadata payload hash: 717013047497cdb57a4662e7618180f73b67afbac2d6da409efa719b2f97a8b5

Canonical appearance

src/app/(docs)/docs/concepts/agents/page.tsx:29 route /docs/concepts/agents

All appearances

  • canonicalsrc/app/(docs)/docs/concepts/agents/page.tsx:29 route /docs/concepts/agents

No mirrored appearances.

Generation versions

App: eli-ai 0.1.0

Mermaid: 11.16.0 · Mermaid CLI: 11.16.0

Node: v26.3.1 · Yarn: 4.17.1

Renderer config hash: 68c10966fe84406ee626034d58bfabd555df9f65f691204b7c46db24038da101

Renderer theme hash: c80287a78d80ad63d27bd5ca348b2ef9a7e2f44da289e436be6484ea28a1b033

Adapter versions: diagramGenerator=2, drawioFlowchart=1, drawioGantt=1, drawioSequence=1, drawioState=1

Full sidecar JSON: durable-agent-execution-loop-f29dda8d.json

Each iteration is a single-step generateText call: the model sees the conversation and the tool schemas but the SDK never auto-executes. The harness owns the messages array and runs tools itself, because durable suspend/resume requires resuming from Postgres alone after hours or a restart — something an in-memory SDK loop cannot provide. Every provider call happens outside any database transaction.

Why in-process, RLS-scoped tools

The determining constraint is that the agent's core tools are KB tools kb_search, kb_graph_query, kb_entity_lookup, kb_doc_read, kb_list_docs — that must execute in our process, inside withWorkspace(), under Postgres row-level security. The registry injects workspaceId and kbScope into every tool call; the model never supplies scope. That is the entire tenant-isolation guarantee — prompt injection cannot cross workspaces by construction. See Tenancy & security.

One portable runtime

AgentConfigSchema currently accepts only runtimeTier: "portable". The same worker-owned loop, state model, tool registry, budget checks, and HITL protocol run across every provider:

  • Portable state. Memory, context compaction, run cursors, approvals, host-side MCP, traces, and costs live in the platform rather than a provider runtime.
  • Provider-aware calls. Recognized Anthropic models can receive supported prompt-caching and adaptive-thinking options. These are call-level options, not a second agent executor.
  • Design intent is not a selectable capability. Server-side provider memory/context editing and managed sandbox runtimes remain historical roadmap decisions until a config, executor, residency policy, lifecycle, and tests ship together.

Budgets & guardrails

Every agent version pins budgets: maxSteps, maxTokens, maxCostUsd, and maxWallClockMs. Budgets are enforced as graceful stop-conditions: when one is hit, the run delivers the partial answer with status budget_exceeded instead of aborting and discarding the work already paid for. Every harness-initiated call — schema retries, citation retries, judge calls — draws down the same budget, so the ceiling is real. Env-level hard ceilings (MAX_AGENT_STEPS, MAX_AGENT_COST_USD) clamp anything a stored config can express.

Guardrails run as middleware: deterministic pre-checks (input validation, injection heuristics that flag rather than quarantine), and post-checks for schema validity, citation validity (the deterministic citation guardrail), and groundedness. A citation requirement can block after bounded retries; the groundedness judge only flags. See Tools, MCP & HITL.

Durability

  • State at every step. agent_runs plus a flat, ordered run_steps list are written as the loop advances — not held only in memory. A crash leaves a resumable record.
  • Boot reaper. On startup the worker marks crash-orphaned running rows as interrupted (plus a lazy sweep on read), so a stuck run is never a silent zombie.
  • Web vs worker split. UI, API, and scheduled starts execute in the worker via pg-boss after workspace monthly-spend and active-run admission. The web process serves database-backed reattachment streams; cancellation uses a DB flag polled between steps. A pre-created queued run is also an initial-start outbox, so boot/minute reconciliation repairs a lost broker handoff using the pinned agent version.
  • HITL survives restarts. A run suspended for approval persists its full resume cursor; the human can take hours and the process can restart. The decision and queued continuation are one durable transition, and the worker reconciles an interrupted enqueue.

The run trace

agent_runs, run_steps, and guardrail_verdictsare first-party, queryable tables — the "show me exactly why it answered that" artifact. Each step records its kind (model_call, tool_call, mcp_call, guardrail, approval, retry), tokens, cost, and latency. Runs pin the immutable agent_version and a content-addressed config snapshot, so an eval can compare two configs like-for-like.

An eval run is just a run

An eval execution is an ordinary agent run with trigger='eval'. The feedback loop is not a separate system — it is the same trace/judge/golden machinery pointed at agents.

Same harness, two front doors

The in-app UI and the authenticated /api/v1 surface drive the exact same harness. Build and configure agents in Creating agents, watch them in Running & tracing, and drive them programmatically via the Agents & runs API.