Two packages. One working agent.

Install the core runtime and one provider adapter. Everything else on this page is a package you add when the product needs it.

$pnpm add @anvia/core @anvia/openai
agent.ts
1import { Agent } from '@anvia/core'2import { OpenAIClient } from '@anvia/openai'34const client = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })5const model = client.completionModel({6  modelId: 'gpt-5.6-sol',7  api: 'responses',8})910const agent = new Agent({11  id: 'support',12  model,13  instructions: 'Answer support questions clearly.',14  maxTurns: 4,15})1617const response = await agent.generate({18  prompt: 'Draft a reply.',19})

Change the model. Keep the agent.

Every provider adapter returns the same model contract, so tools, memory, streaming, and results stay as they are. Point @anvia/openai at any OpenAI-compatible endpoint with baseUrl.

Models you already use

  • OpenAI
  • Azure
  • Anthropic
  • Gemini
  • Mistral
  • Grok
agent.ts
-import { OpenAIClient } from '@anvia/openai'+import { AnthropicClient } from '@anvia/anthropic'-const client = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! })-const model = client.completionModel({ modelId: 'gpt-5.6-sol', api: 'responses' })+const client = new AnthropicClient({ apiKey: process.env.ANTHROPIC_API_KEY! })+const model = client.completionModel({ modelId: 'claude-opus-5' })const agent = new Agent({ id: 'support', model, tools, memory })

Memory lives in your database.

Sessions, embeddings, and graphs persist in stores you already run. Let Anvia provision the tables, or validate against the migrations you own.

memory.ts
1const database = new PostgresMemoryClient({ connectionString: process.env.DATABASE_URL! })2const store = database.memoryStore()3await store.ensure()45const agent = new Agent({6  id: 'support',7  model,8  memory: { store, savePolicy: 'turn' },9})1011await agent.generate({12  prompt: 'Remember that my order number is 1234.',13  session: { sessionId: 'support-123', userId: 'user-456' },14})

Agents act. Your code decides.

Tools validate their input before they run. A protected action can suspend the run, hand an approval to your application, and resume in a linked phase, even from another process. generate() and stream() always end in a state you can handle.

refund-tool.ts
1const issueRefund = createTool({2  name: 'issue_refund',3  inputSchema: refundInput,4  outputSchema: refundResult,5  requiresApproval: ({ amount }) =>6    amount > 1007      ? { reason: 'High-value refund.' }8      : false,9  async execute(input) {10    await auth.require(actor, input)11    return billing.refund(input)12  },13})
Anvia runtimeYour application
01

Generate

One typed input starts a bounded model-and-tool loop.

02

Tool requested

Arguments are validated before the protected action can run.

03

Suspended

An interaction and JSON-safe continuation return to your application.

04

Resume

Your server claims the response and starts a linked phase.

05

Completed

The approved tool runs and one typed result closes the lifecycle.

  • Policy at the boundary

    Validate input, require approval, and authorize the side effect in your code.

  • One result contract

    Generate and stream finish as completed, blocked, or suspended.

  • Linked across processes

    Every resumed phase keeps its relationship to the original run.

Work that survives a restart.

@anvia/durable journals submissions, model responses, tool results, approvals, and progress events. When a worker restarts, the runtime finds unfinished runs and recovers them from committed checkpoints.

worker.ts
1const runtime = await DurableRuntime.open({2  store: new SqliteDurableStore('./support-runs.sqlite'),3  agents: [{ agent, version: '1', stream: true }],4})56await runtime.resume() // recover unfinished work78const run = await runtime.submit({9  agentId: agent.id,10  sessionId: 'customer-42',11  requestId: 'support-request-123',12  prompt: 'Explain how to resolve a duplicate payment.',13})1415for await (const event of run.stream()) render(event)
  • Checkpoints, not replays

    Completed model responses and tool results are reused when unfinished work recovers.

  • Submitted once

    Submissions are deduplicated by session and request ID, so a retry never starts the work twice.

  • Approvals that wait

    Pending approvals and questions are still there after a restart.

  • Streams that reconnect

    Clients pick up from the last event cursor they applied.

Interrupted tools default to manual reconciliation. Mark read-only tools safe, and idempotent ones keyed by operation ID.

See every step of every run.

Each generation, tool call, and subagent is a span. Inspect them in Studio while you build, then send the same traces to Lens, OpenTelemetry, or Langfuse in production.

Refund order A-100Success

Your infrastructure stays yours.

Anvia coordinates the bounded model-and-tool loop. Your application keeps authority over users, credentials, data, permissions, persistence, and deployment.

Your application owns

  • Users, identity, and permissions
  • Credentials and vendor clients
  • Data, memory, and retention
  • Approval and authorization policy
  • Where and how it is deployed

Anvia handles

  • The bounded model-and-tool loop
  • Typed tool input and output
  • Completed, blocked, and suspended results
  • Streaming, events, and traces
  • Recovery from committed checkpoints

Start from a real system boundary.

Three complete patterns for products that need grounded answers, connected data, or visible browser work.

03

Visible browser automation

Run Chromium in Docker, restrict navigation, expose semantic tools, and coordinate human takeover.

  1. sandbox
  2. Chromium
  3. semantic tools
  4. human
@anvia/core@anvia/browser@anvia/sandbox

Anvia v1 is stable.

Agents, providers, memory, streaming, React, Studio, observability, and evaluation now share runtime contracts. Packages release independently, so check dependency compatibility when upgrading.

$pnpm add @anvia/core @anvia/openaiBuild your first agent
Anvia