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
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_runsplus a flat, orderedrun_stepslist 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
runningrows asinterrupted(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
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.