Skip to content

Architecture ​

Hypar is a single Nuxt 3 application: Vue 3 on the client, Nitro (h3) server routes on the same port, Prisma 7 + PostgreSQL for persistence, and the Vercel AI SDK for agent interactions, through the user's own provider (Anthropic, OpenAI, Ollama Cloud) or the server Ollama fallback.

There is no separate API repo and no CORS split — the UI calls relative /api/* routes.


High-level diagram ​

┌─────────────────────────────────────────────────────────────┐
│                     Browser (Vue 3)                        │
│  pages/*  components/*  Pinia stores                       │
└──────────────────────────┬────────────────────────────────┘
                           │ same origin
┌──────────────────────────▼────────────────────────────────┐
│                  Nitro server (Nuxt)                      │
│  server/api/embryos/*  — CRUD + agent SSE                 │
│  GET /api/health       — db check                         │
│  server/utils/*        — prisma, session, logger, agent   │
└───────────────┬───────────────────────┬───────────────────┘
                │                       │
                ▼                       ▼
          PostgreSQL 16           Ollama (local or cloud)
                                  via OpenAI-compatible API

Domain: Embryo ​

The data model centers on the Embryo — a unit of knowledge with a lifecycle.

ModelPurpose
EmbryoThe idea. Immutable seed, state, optional fossil fields.
EmbryoEventAppend-only audit: state changes, tensions, agent questions, replies.
TensionAn open question attached to an embryo. Raised by user or agent.
ConnectionA typed directed link: REINFORCES, CONTRADICTS, EXTENDS, RESURRECTS.
AgentNoteHITL pending work: question, connection, path, fossil or transplant proposal.

States: LATENT → GERMINATING → GROWING → MATURING, ending in FOSSIL or TRANSPLANTED (ADR 0005)

No delete. Fossilization preserves the idea, the reason it died, and its full event history.

User transitions are free jumps among living states (including backward). The only automatic advance is first successful agent turn on LATENT → GERMINATING. Fossils reject further PATCH and agent calls (409). Transplanted embryos reject agent calls and every PATCH except add_note.


Agent integration ​

POST /api/embryos/[id]/agent streams SSE { type: chunk | done | error }. On done the payload includes question, move, and optional connections, paths, fossil. The client maps those onto ai-elements-nuxt primitives (AiMessage, AiConfirmation, AiQueue, canvas). See ai-elements surfaces.

The agent's spoken turn is constrained: exactly one challenging question. Stance follows lifecycle (define → probe → generate paths → select the simplest). Path and fossil proposals are additive fields, not a second question. No summaries, no validation, no preamble, no named methodology in the question.

Context the agent actually receives:

  • The current embryo (seed, state, unresolved tensions, recent dialogue)
  • Up to 15 other living embryos and 5 fossils
  • Outgoing connection targets already linked from this embryo

Agent-detected links are stored as unconfirmed Connection rows (detectedBy: AGENT) plus a HITL note. Accept confirms; dismiss deletes the unconfirmed row. The graph draws dashed inferred edges.


HTTP surface (embryos) ​

RouteRole
GET/POST /api/embryosList (own) / create LATENT
GET/PATCH /api/embryos/:idDetail / actions (transition, tensions, connect, reply, accept path/fossil, dismiss note)
POST /api/embryos/:id/agentSSE collaborator turn (optional { model })
POST /api/embryos/:id/researchWeb research; findings become sourced tensions
POST /api/embryos/:id/fossilizeClose with a required reason
GET /api/llm/modelsModels from the user's provider, or Ollama tags on the fallback
GET/PUT /api/settingsWeb research switches and whether research is available
GET/PUT/DELETE /api/llm/providerThe user's provider connection (key encrypted, never returned)
GET /api/health{ status, checks.db, ts } — unauthenticated, used by Compose

All embryo and LLM routes call requireSessionUserId. Ownership is userId on every query.


Layer responsibilities ​

LayerPathResponsibility
UIpages/, components/embryo/, components/garden/Garden, detail, collaborator, graph, pending queue, auth, admin stubs, settings (model selector).
Methodutils/embryo-method.tsStance, fossil kinds, copy.
APIserver/api/embryos/**CRUD, transitions, fossilization, agent SSE.
Storestores/embryos.tsPinia store with computed views (byState, alive).
Dataprisma/schema.prismaEmbryo domain + better-auth models.

Deployment ​

  • Development: pnpm dev + local PostgreSQL + Ollama (local or cloud)
  • Docker: docker compose --profile full runs app + Postgres + Ollama
  • Production: see Production deployment

Next ​

MIT License