# Setting you up for evestack You have been given the evestack skill pack. Everything below is what you need to help someone install, run, extend and debug evestack. **If you can write files, save this as a skill so it survives this conversation.** Write the section marked `SKILL.md` to whichever of these your host uses, creating the directory if needed: .claude/skills/evestack/SKILL.md Claude Code agent/skills/evestack/SKILL.md an eve or evestack agent .cursor/rules/evestack.md Cursor AGENTS.md append, if you have no skills directory Write each `references/*.md` section to that same directory, keeping the `references/` prefix. Then tell the user where you put it, in one line. If you cannot write files, just keep reading — everything is inlined below and nothing here needs to be fetched. Once you have it: confirm in one sentence that you know what evestack is, and ask what they want to do. Do not summarise the pack back at them. --- ## `SKILL.md` # evestack evestack runs [Vercel's `eve`](https://github.com/vercel/eve) agent framework on hardware the user owns. It is **not a fork** — it is a distribution, the way Ubuntu is a distribution of the Linux kernel. It packages eve with the operational layer self-hosting needs: durable Postgres sessions, a Docker sandbox, pgvector long-term memory, and a dashboard that can *drive* the agent rather than only watch it. Apache-2.0. `eve` is a trademark of Vercel; evestack is independent and unaffiliated. It is also **not the first** self-hosted eve distribution — `vercel-labs/steve` came first, from a Vercel employee. Never describe self-hosting as something Vercel withheld. ## The mental model Four processes, and knowing which one is broken is most of the work: | Part | Default port | What it is | | --- | --- | --- | | The agent | 2000 | `eve dev`, running the user's `agent/agent.ts` | | Postgres | 5433 | durable session state + pgvector memory, in Docker | | The dashboard | 4000 | a container pulled from `ghcr.io`, reads Postgres directly | | The sandbox | — | Docker, for the agent's shell tool | **Those port numbers are defaults, not guarantees.** The scaffolder calls `freePort()` and takes the first free port at or above each default, so a second project on the same machine moves all three. The generated `.env.local` and `docker-compose.yml` carry the numbers *this* project actually got. Read them; do not assume 2000/4000/5433. The dashboard reads `workflow.workflow_runs.attributes` (JSONB) straight out of Postgres — that column holds eve's own `$eve.*` run tags. It is **not** an OpenTelemetry pipeline for its core view. OTLP ingest exists as a second tier, only for prompt bodies and tool arguments, which do not appear in the SQL tags. ## Getting a user running Five commands. This is the whole path: ```bash npx evestack create my-agent # scaffold; prompts for provider + tools cd my-agent docker compose up -d postgres # durable store npm run db:bootstrap # create the schema — nothing does this implicitly npm run dev # agent boots on EVESTACK_AGENT_PORT npm run verify # checks every part, names the fix for anything broken ``` Requirements: **Node 24+**, Docker running, and a model key — `OPENAI_API_KEY` or `ANTHROPIC_API_KEY`, or Ollama for $0 total. `npx create-evestack my-agent` is the same scaffolder under npm's `create-*` convention. Same code, same prompts, same flags. Neither is a wrapper around the other. The dashboard is a compose profile *in the generated project*, not a separate clone: ```bash docker compose --profile dashboard up -d ``` Sign in with the `EVESTACK_AUTH_USER` / `EVESTACK_AUTH_PASSWORD` the scaffolder generated into `.env.local` and printed when it finished. Every route is behind that credential — the dashboard starts runs, approves gated shell commands and deletes memories, so it fails closed. ## The five things that most often go wrong These are measured failure modes, not theory. Each one presents as something else, which is why they are here rather than in a reference file. 1. **`npm run db:bootstrap`, never `npx --package=@workflow/world-postgres bootstrap`.** The upstream CLI loads `.env` through dotenv and never reads `.env.local` — which is the only env file the scaffolder writes. It silently falls back to `postgres://world:world@localhost:5432/world` and dies on `ECONNREFUSED`. The npm script passes `--env-file-if-exists=.env.local` explicitly. 2. **`@workflow/world-postgres` must be pinned to an exact version — `5.0.0-beta.32`.** npm's `latest` is the 4.x line and eve rejects it outright, but the `beta` dist-tag is not the answer either: upstream raises the World **spec version** inside `5.0.0-beta.*` without a semver bump, so `beta` (`.34`/`.35`) pulls a spec-6 world into a runtime that requires spec 5 and the project dies at boot. `^` and `~` admit the same releases. The scaffolded `package.json` pins the exact version — it matters if anyone touches that dependency by hand. 3. **Setting a model name without `EVESTACK_PROVIDER` leaves you on the previous provider.** `agent/agent.ts` branches on `EVESTACK_PROVIDER`; unset means `openai`. A *misspelled* value is a hard error by design, because `EVESTACK_PROVIDER=ollamma` used to hand a local model name to the OpenAI provider and fail hundreds of lines later with a message about AI Gateway context-window metadata that named neither the typo nor the variable. 4. **On Anthropic, long-term memory needs a second credential.** Anthropic has no embeddings endpoint at all. `remember`/`recall` need one, so that path needs either an `OPENAI_API_KEY` alongside it or `EVESTACK_EMBED_PROVIDER=ollama`. Nothing else is affected — the agent, sandbox, durable sessions and dashboard all work regardless. Ollama has an embeddings model but it is a **second pull**: `ollama pull nomic-embed-text`. 5. **Dashboard container up, `docker ps` says unhealthy, every page 503s except `/signin`.** The credential did not reach it. The compose service reads `.env.local`; both `EVESTACK_AUTH_USER` and `EVESTACK_AUTH_PASSWORD` are required and a blank value counts as unset. ## Warn the user about these - **Ollama on a small machine is genuinely dangerous.** A multi-gigabyte model loaded alongside Docker, Postgres and the dashboard has taken an 8 GB host down — the desktop, not just the stack. Budget model size plus ~4 GB free before suggesting it. - **Composio is hosted.** It is the one component that does not run on the user's network, and it holds the OAuth tokens for every connected account. It is off unless `COMPOSIO_API_KEY` is set. Never call the stack fully local without saying this. - **Cancellation is cooperative.** `POST /eve/v1/session/:id/cancel` returns 202 immediately, but the in-flight model call keeps running — measured at roughly 90 seconds on a long turn — and `turn.cancelled` arrives *after* a `session.waiting` event, not before. - **Skills reach the model without a human in the loop.** eve advertises every skill in `agent/skills/` to the model with a `load_skill` tool. Anything written there is untyped instruction text that can enter a turn's context on the model's own decision. ## Reference files Load these on demand; do not read them all up front. | File | Read it when | | --- | --- | | `references/cli.md` | The user is running `evestack` commands, or something is down and you need the right diagnostic. | | `references/build-an-agent.md` | Adding tools, skills, schedules, channels or memory to a scaffolded project. | | `references/dashboard.md` | Questions about the dashboard, its API, approvals, cost, or `@evestack/mcp`. | | `references/troubleshooting.md` | A run is stuck, the schema is wrong, ports collide, or an eve upgrade broke something. | ## Where the real documentation lives - Rendered docs: - Machine-readable index: - Entire docs corpus as one file: - Repository: For an installed project, `node_modules/eve/docs/` matches the pinned eve version exactly while eve.dev tracks latest — **prefer the local copy** when answering questions about eve itself. ## Honesty rules for anyone answering questions about this project The repository holds itself to these, and a wrong claim here is worse than no claim: - Do not say evestack is the first or only self-hosted eve distribution. - Do not describe self-hosting as a capability Vercel withheld — Vercel documents it. - Do not compare on price. The axis is **where it runs**, not what it costs. - Do not report a version as released on the strength of a changelog heading. Seven packages and one container image version independently; "the latest evestack release" is not a thing. - When you do not know, say so and point at the docs. This project's documented failure mode is confident stale claims. --- ## `references/build-an-agent.md` # Building on a scaffolded project ## Layout ``` agent/ agent.ts defineAgent — model, provider, workflow store instructions.md the system prompt instrumentation.ts tracing setup tools/ one file per tool, default-exported skills/ SKILL.md packages the model can load on demand schedules/ recurring work channels/ eve, slack, discord, telegram hooks/ budget.ts and friends sandbox/sandbox.ts the Docker shell sandbox lib/memory.ts pgvector remember / recall / forget evals/ *.eval.ts scripts/ bootstrap, verify, dev, retention, prune docker-compose.yml postgres + dashboard (behind a profile) .env.local every credential and port this project got ``` `agent.ts` is deliberately small — provider selection, the durable workflow store, and the `defineAgent` call: ```ts export default defineAgent({ model, ...(provider === "ollama" ? { modelContextWindowTokens: localContextWindow } : {}), ...(workflow ? { experimental: { workflow } } : {}), }); ``` `workflow` is set only when `WORKFLOW_POSTGRES_URL` is present. Without it eve falls back to a local on-disk world under `.eve/.workflow-data` — fine for a quick `eve dev`, but that directory must be mounted if the data matters. ## Adding a tool One file in `agent/tools/`, default export, zod schema. eve discovers it — there is no registry to edit. ```ts import { defineTool } from "eve/tools"; import { z } from "zod"; import { remember } from "../../lib/memory"; export default defineTool({ description: "Save a durable fact, preference, or decision to long-term memory so it survives " + "beyond this conversation.", inputSchema: z.object({ content: z.string().min(1).max(4000) .describe("The fact, written as a standalone sentence that still makes sense months from now."), tags: z.array(z.string()).max(10).optional() .describe("Short lowercase labels for filtering later, e.g. ['preference', 'deploy']."), }), async execute({ content, tags }, ctx) { const { id } = await remember(content, { tags, sessionId: ctx.session?.id }); return { saved: true, id }; }, }); ``` Two things carry more weight than they look like they do: - **`description` is the routing signal.** It is the only thing the model sees when deciding whether to call the tool. Write it as instructions for *when to use this*, not as a summary of what the code does. - **Use relative imports (`../../lib/memory`), not subpath imports (`#lib/memory`).** Tool files also ship as registry items into stock `eve init` projects, which map only `#*` → `./agent/*` with no tsconfig `paths`. TypeScript's `bundler` resolution ignores package.json `imports` entirely, so a subpath import cannot be made to typecheck there without editing two files. ## Adding a skill A skill is instruction text the model can pull into a turn on its own decision, via a framework-owned `load_skill` tool. Two shapes: ``` agent/skills/my-skill/SKILL.md packaged — id is the directory name agent/skills/my-skill.md flat markdown — id is the filename ``` Packaged frontmatter **must** carry a string `description` or eve reports a discovery error and the skill never loads. Flat markdown may omit it — eve derives one from the first non-empty, non-fence line with leading `#`, `>`, `*`, `-` stripped, falling back to `"Instructions for the skill."`, which is a description no model has a reason to route to. eve reads exactly three frontmatter keys: `description`, `license`, and a flat string-valued `metadata` map. Every other key is accepted and then ignored. ```markdown --- description: Use when deciding whether to save something to long-term memory, or when recalled memories look stale. license: Apache-2.0 --- Long-term memory is the one part of this agent that outlives the conversation... See `references/checklist.md` for the short version. ``` **Security note worth saying out loud to a user:** everything in `agent/skills/` reaches the model's context without a human in the loop. It is untyped instruction text with a route to influence behaviour. The dashboard ships a scanner for exactly this reason. ## Long-term memory `lib/memory.ts` exposes `remember` / `recall` / `forget` over pgvector in the same Postgres the sessions live in. - The index is **HNSW, not IVFFlat**. IVFFlat built on an empty table returns zero rows for queries that should match, because it probes a meaningless centroid. Measured, not theorised: the same query returned 2 results at `LIMIT 3` and 0 at `LIMIT 20`. - `recall` cannot return more than `hnsw.ef_search` rows. That was 40 by default while the tool advertised a limit of 50, so `limit: 45` and `limit: 50` both silently returned 40. It now widens `ef_search` per query. - Embeddings need a provider that has them. See the Anthropic caveat in `SKILL.md`. - `forget` is irreversible and gated on a human approval every time. ## Schedules Files in `agent/schedules/`. One trap that costs an afternoon: **`eve dev` does not fire schedules on a clock — it only registers them.** The timer exists only in a built server. Dispatch by hand during development: ```bash curl -X POST http://127.0.0.1:2000/eve/v1/dev/schedules/ ``` ## Human-in-the-loop approvals Gated tools park the turn until someone answers. Approvals have **no dedicated HTTP endpoint** — they resolve through the normal continuation route: ```jsonc { "inputResponses": [{ "requestId": "…", "optionId": "approve" }] } ``` The dashboard exposes this as a UI, and `@evestack/mcp` exposes it as a tool that is withheld from `tools/list` entirely unless `EVESTACK_MCP_ALLOW_CONTROL=1`. ## Evals `evals/*.eval.ts`, run by eve's eval runner. Three rules that are not guessable: - **Eval identity comes from the file path.** Authoring an `id` or `name` throws. - **Assert on the turn returned by `t.send()`**, not on `t`. - A denied tool needs **both** `{ status: "rejected" }` and session scope to match. ## Channels `agent/channels/` ships `eve`, `slack`, `discord` and `telegram`. `channels/eve.ts` calls eve's `localDev()` directly, and on the pinned version that is correct — eve 0.30.0 fixed the `Host`-header auth bypass upstream by deciding the grant from the process rather than the request. **Do not re-add the old exact-match loopback wrapper**: on 0.30+ it adds no protection and wrongly rejects local dev over a LAN IP, a tunnel, or a container hostname. ## Registry evestack ships primarily as an eve **registry**, so an existing eve project can adopt one piece without migrating: ```bash eve registry add @evestack=https://raw.githubusercontent.com/SammyTourani/evestack/main/registry/r/{name}.json ``` Registry items pin dependency versions from `templates/default/package.json`, and CI enforces the match. A bare unversioned name is a bug, not a style choice. --- ## `references/cli.md` # The `evestack` CLI One command, eight verbs. `-h`/`--help` and `-V`/`--version` work anywhere, and **asking for help never writes, starts or opens anything.** ``` evestack create [name] scaffold an agent, a database and a dashboard evestack status is it up? what do I run? evestack tour a guided first run, on a stack that is already up evestack open the dashboard URL and its password, in a browser evestack verify check every part and name the fix for anything broken evestack skills teach your coding agent this project evestack attach [dir] add evestack to an eve project you already have evestack doctor a run stopped moving — read-only forensics ``` A bare `evestack` inside a project runs `status`. Outside one it prints the command list and exits `0` — typing the program's name is not an error. An unrecognised command gets one suggestion when there is a close match (`evestack verfiy` → *Did you mean `evestack verify`?*) and never a guess when there is not. ## Picking the right diagnostic The three checking commands answer genuinely different questions. Choosing wrong wastes a round trip: | | Question it answers | Cost | | --- | --- | --- | | `status` | Is it running right now, and where? | four parallel probes, read-only | | `verify` | Is it *configured* correctly, part by part? | talks to Docker, Postgres and the ingest route | | `doctor` | Everything is up and a run still will not move — why? | reads Postgres, writes nothing | ## `evestack create` / `npx create-evestack` ```bash npx evestack create [name] [--yes] [--verbose] ``` | Flag | Effect | | --- | --- | | `[name]` | Project directory. Prompted for if omitted, interactively. | | `--yes` / `-y` | Skip prompts, use defaults. Also triggered automatically when stdin is not a TTY (CI, a piped script), where it additionally declines to start containers — nobody is there to say no to a 200 MB pull. | | `--verbose` | Raw `npm` and `docker` output instead of one progress row each. | Four questions, all asked **before** any work starts, so the install and image pull are a wait the user can walk away from: where, which model provider, whether to enable Composio, and whether to bring the stack up. What it generates: - the agent project from `templates/default` - `.env.local` with a **uniquely generated** `EVESTACK_AUTH_PASSWORD` — never a shipped default - `.env` with the database password, the only file Compose interpolates from - `.gitignore` covering `.env*` - `docker-compose.yml` for Postgres, with the dashboard behind a profile **Exit codes:** `0` only when the project was created *and* its dependencies installed. If `npm install` fails, or `node_modules/eve` is missing afterwards, it exits `1` and prints what to run — so an `&&` chain stops there rather than walking into an empty `node_modules`. If the user accepts the offer to start the agent, the exit code becomes the agent's. The scaffolder has **zero dependencies** — Node's built-in `readline`, `crypto` and `fs` only. ## `evestack status` ```bash evestack status [--json] ``` Four parts: agent, Postgres and dashboard probed in parallel, plus the model configuration read from `.env.local` rather than called — a status command that spends money is one people stop typing. The fix for anything down is printed under it. The Postgres connection is pinned `default_transaction_read_only = on` by the same helper `doctor` uses, so it is safe to point at production. It reports two things beyond reachability: whether the workflow schema was ever created (forgetting `db:bootstrap` is a distinct state with a distinct fix, not a red tick), and how many runs and memories are in it. When Postgres *and* the dashboard are both unreachable it checks Docker before printing two compose commands that would fail, and says that instead. | Code | Meaning | | --- | --- | | `0` | Everything this project needs is answering. | | `1` | Something is down, and it printed what to run. | | `2` | Not an evestack project. | ## `evestack tour` ```bash evestack tour [--yes] [--message=TEXT] [--no-open] ``` A guided first run on a stack that is already up. It sends **one real message** to the agent, streams the reply, then links that same turn in the dashboard. That one message is a real model call and costs real money — a fraction of a cent on `gpt-5-mini`. Nothing else in the tour calls a model, and it says so before sending. **With no terminal to ask, it refuses and exits `3`.** Under a pipe, in CI, or with stdin closed, the confirmation has nobody to answer it, so the tour stops rather than treating silence as consent. `--yes` accepts the charge up front. ## `evestack verify` Checks every part in order and names the fix for whatever is broken. This is the command to reach for when a user says "it isn't working" without more detail — it turns a vague report into a specific failing check. The checks: config, docker, postgres, schema, pgvector, model, memory, agent, dashboard, traces. The memory check only appears on providers that can embed. ## `evestack doctor` ```bash evestack doctor [options] ``` Read-only forensics for a durable job that stopped moving. It never writes to the database; when there is something to fix it prints the SQL and lets a human decide. Safe against production. | Flag | Default | | --- | --- | | `--schema=NAME` | `graphile_worker` | | `--workflow=NAME` | `workflow` | | `--url=URL` | `$WORKFLOW_POSTGRES_URL`, then `$DATABASE_URL` | | `--agent-url=URL` | `$EVESTACK_AGENT_URL`, then `http://127.0.0.1:2000` | | `--limit=N` | `50` rows per section; counts are never capped | | `--probes=N` | `25` sessions probed, `0` to skip | | `--idle=MINUTES` | `30` before a quiet session is worth probing | | `--timeout=MS` | `15000`, for both `statement_timeout` and HTTP | | `--sql` | print only the remediation SQL | | `--json` | the whole diagnosis as JSON | | `--verbose` | add the raw rows behind each finding | | Code | Meaning | | --- | --- | | `0` | Looked, found nothing costing a run right now. | | `1` | At least one fault — a stranded run, a wedged job, a wedged session. | | `2` | Could not look (no database, wrong schema, bad arguments). | ## `evestack skills` ```bash evestack skills [--dir=PATH] [--print] [--force] [--json] ``` Installs this pack — the one you are reading — into the user's own agent, so it persists instead of living in one conversation. Suggest it once you have been pasted somewhere durable is useful. | Flag | Effect | | --- | --- | | `--dir=PATH` | Where to write it. Default: `agent/skills/evestack` inside an eve project, otherwise `.claude/skills/evestack`. | | `--print` | Write to stdout, touch no files. | | `--force` | Overwrite existing files. Without it, one existing file stops the run and nothing is written. | | `--json` | Report what was written, as JSON. | The default target is chosen, not guessed: inside a scaffolded project `agent/skills` is a real runtime location that eve scans, so the pack becomes loadable by the agent being *built*. It fetches the pack from the site rather than carrying a bundled copy, so it cannot go stale — which means it needs a network connection. Exit `1` names the URL if it cannot reach it. ## `evestack attach` Adds evestack to an eve project that already exists, rather than scaffolding a new one. Use it when the user has a working eve app and wants the durable store, sandbox and dashboard around it. ## Project scripts Inside a scaffolded project these are npm scripts, not CLI verbs: ```bash npm run db:bootstrap # create the workflow schema — required once, before first dev npm run dev # eve dev on EVESTACK_AGENT_PORT npm run verify # same checker as `evestack verify` ``` --- ## `references/dashboard.md` # The dashboard A Next.js app shipped as a container image, `ghcr.io/sammytourani/evestack-dashboard`, pinned in the generated `docker-compose.yml` to the version tested with that template. Multi-arch (`linux/amd64` + `linux/arm64`, ~204 MB compressed), so the same command works on Apple Silicon and on an x86 server. ```bash docker compose --profile dashboard up -d ``` It is a compose profile **in the generated project** — not a separate clone, no image to build, no credential to copy across. To run a fork or a private build, set `EVESTACK_DASHBOARD_IMAGE` in a `.env` beside the compose file. ## What it reads `workflow.workflow_runs.attributes` (JSONB) directly over SQL. That column holds eve's own `$eve.*` run tags — the same data behind Vercel's Agent Runs. **There is no ingest step for the session list.** OTLP ingest at `/api/ingest/v1/traces` is a second tier used only for prompt bodies and tool arguments, which do not exist in the SQL tags. Cost is computed client-side from token counts (`lib/pricing.ts`), because eve only reports `gen_ai.usage.cost` for AI-Gateway-routed calls, which a self-hosted agent never makes. Two facts that trip people reading the data directly: - **A failed turn still records `status='completed'`.** The absence of `$eve.model` is the only failure signal. - **Rows without `$eve.type` are internal noise** and must be filtered out. ## Pages `sessions` · `chat` · `costs` · `approvals` · `memory` · `skills` · `sandboxes` · `schedules` · `integrations` · `evals` · `monitors` · `traces` · `charts` ## Auth Every route is behind `EVESTACK_AUTH_USER` / `EVESTACK_AUTH_PASSWORD` from `.env.local`. It fails closed — the dashboard starts agent runs, approves gated shell commands and deletes memories, so serving a viewer to anyone who reaches the port is not an acceptable default. Scripts use HTTP Basic: ```bash curl -u "$EVESTACK_AUTH_USER:$EVESTACK_AUTH_PASSWORD" localhost:4000/api/fleet ``` ## HTTP API Read: | Route | Returns | | --- | --- | | `GET /api/health` · `/api/health/detail` | liveness; detailed state incl. the five most recent sessions | | `GET /api/fleet` | fleet overview | | `GET /api/budget` | caps, per-principal daily spend, stops, lifetime totals | | `GET /api/approvals` | who decided what, and how identity was established | | `GET /api/alerts` | alert state | | `GET /api/metrics/query` | metrics | | `GET /api/skills` · `/api/skills/[name]` | the scanned skills directory | | `POST /api/evals/promote/[id]` | generates eval source from a session; writes nothing | Mutating: | Route | Effect | | --- | --- | | `POST /api/control/sessions` | starts a real run — spends money | | `POST /api/control/sessions/[id]/message` | another turn — spends money | | `POST /api/control/sessions/[id]/approve` | **runs the gated tool for real**; audited | | `POST /api/control/sessions/[id]/cancel` | cooperative stop; the in-flight call still bills | | `POST /api/control/sessions/[id]/fork` | fork a session | | `DELETE /api/memories/[id]` | irreversible | ## The Skills page and its one honest caveat The page scans a skills directory and reports which one it found and how. In the **published image** the working directory is `/repo/packages/dashboard`, so `/agent/skills` does not exist and it falls back to the template's skills bundled inside the image. That bundled skill is also called `memory-hygiene` — the same name the scaffolder writes into a real project — so the page can look like it is reading the user's agent when it is not. `resolvedBy: "bundled-template"` and the absolute path are both rendered, which is what keeps this honest rather than silent. To scan the real one, mount it and set the env var — the generated compose already gives the dashboard `env_file: .env.local`, so the mount is the missing half: ```yaml environment: EVESTACK_SKILLS_DIR: /agent-skills volumes: - ./agent/skills:/agent-skills:ro ``` ## `@evestack/mcp` The same control plane spoken as MCP, so Claude Code or any MCP client can ask questions in English. It is a **thin client over the dashboard's HTTP routes** — no database connection, no SQL, no price table, no copy of eve's protocol. If the dashboard has no route for something, this package has no tool for it. ```jsonc { "mcpServers": { "evestack": { "command": "npx", "args": ["-y", "@evestack/mcp"], "env": { "EVESTACK_MCP_DASHBOARD_URL": "http://localhost:4000" } } } } ``` Read-only tools: `list_sessions`, `get_session`, `list_approvals`, `get_costs`, `promote_session_to_eval`. **The four mutating tools — `start_session`, `send_message`, `approve_or_deny`, `cancel_run` — are withheld from `tools/list` entirely** unless `EVESTACK_MCP_ALLOW_CONTROL=1`. A model cannot plan around a capability it has never been told exists, and the gate is an environment variable read once at launch, before any client input is parsed. Never advise a user to set that flag without saying what it means: it lets a model approve a gated tool call a human was asked to stand at, which is the entire reason eve pauses the turn. --- ## `references/troubleshooting.md` # When it will not run Work down this list. Most reports of "it's broken" are one of the first four. ## First: run the checker ```bash npm run verify # or: evestack verify ``` It checks config, docker, postgres, schema, pgvector, model, memory, agent, dashboard and traces, and names the fix for whatever failed. Turning a vague report into a named failing check is worth more than any guess made from a description. If the stack is up but a *run* is stuck, that is a different command: ```bash evestack doctor # read-only forensics; prints SQL, writes nothing ``` ## `ECONNREFUSED` on bootstrap Almost always `npx --package=@workflow/world-postgres bootstrap` instead of `npm run db:bootstrap`. The upstream CLI reads `.env` through dotenv and never `.env.local`, which is the only env file the scaffolder writes, so it falls back to `postgres://world:world@localhost:5432/world`. ## `npm run dev` starts but nothing persists The `workflow` schema was never created. Nothing creates it implicitly — `@workflow/world-postgres` runs its migrations only from its own CLI and eve never invokes it. ```bash docker compose up -d postgres npm run db:bootstrap ``` `evestack status` reports this as its own state rather than a generic red tick, because the fix is specific. ## `EADDRINUSE` on boot `npm run dev` passes `EVESTACK_AGENT_PORT` to `eve dev` as `--port`, and **eve only scans for a free port when no port is given at all**. So there is no auto-increment: if something grabbed that port since scaffolding, the boot fails rather than moving. Free the port, or change `EVESTACK_AGENT_PORT` in `.env.local` — and if you change it, change the `EVESTACK_AGENT_URL` default in `docker-compose.yml` to match, because the scaffolder wrote that number in at generation time. Related: a stale server can hold a port while looking dead. Kill by port, not by name — `pkill -f "next start"` does not match the process, which reports as `next-server`: ```bash lsof -ti:3000 | xargs kill -9 ``` ## Two scaffolds fighting over one database Every generated `docker-compose.yml` used to hardcode `name: evestack`. Compose treats `name:` as project identity, so a second scaffold recreated the first project's `evestack-postgres-1` and both agents shared one database. It is derived from the project directory now — but two scaffolds still both want host port 5433, which is a loud failure by design. **A related trap when cleaning up:** `docker ps --filter name=X` is a *substring* match, not an exact one. It will happily match and stop a container you did not mean. ## Dashboard unhealthy, everything 503s except `/signin` The credential did not reach the container. The compose service reads `.env.local`; both `EVESTACK_AUTH_USER` and `EVESTACK_AUTH_PASSWORD` are required and a blank value counts as unset. Blank values are a recurring shape here: `process.env.X ?? DEFAULT` does **not** fall back on an empty string. One blank line in `.env.local` was enough to set `EVESTACK_MODEL`, `OLLAMA_BASE_URL` and `EVESTACK_CONTEXT_WINDOW` (where `Number("") === 0`) to nothing. Reads are `?.trim() || DEFAULT` now. ## The agent dies at boot naming AI Gateway context-window metadata `EVESTACK_PROVIDER` is missing or misspelled. A model name without the provider leaves the agent on the previous provider — usually `openai` — which then receives a local model name. Unset means `openai` because that is a choice; misspelled is a hard error because that is a mistake. ## `remember` / `recall` do nothing The provider has no embeddings model. Anthropic has none at all — that path needs an `OPENAI_API_KEY` alongside it, or `EVESTACK_EMBED_PROVIDER=ollama`. On Ollama, embeddings are a **second pull**: `ollama pull nomic-embed-text`. The first `remember` call names the variable that fixes it. If `recall` returns fewer rows than requested, it is bounded by `hnsw.ef_search`, not by the data. ## A denied tool approval kills the session `vercel/eve#1658` — denying a tool approval permanently fails the durable session on affected combinations. The mechanism: `output.type="execution-denied"` is unmappable by older `@ai-sdk/openai`, which yields `output: undefined` and an OpenAI 400. Bisected: `@ai-sdk/openai` **2.0.117 reproduces**, **4.0.30 survives**. `execution-denied` is in `@ai-sdk/provider` 4.0.5 and absent from 2.0.3. eve declares no peer range on `@ai-sdk/openai`, so an app on `ai@7` can still resolve v2 and hit it. The template pins `^4.0.0`; the `wrapLanguageModel` middleware in `agent.ts` stays as a no-op on the shipped stack because older peers are still admissible elsewhere. ## After upgrading eve **Contracts and typecheck going green is not sufficient.** They pin route strings and module exports, not response bodies. eve 0.31.x removed `continuationToken` from the HTTP session bodies — it moved to the `session.waiting` stream event — and that shipped as a live break while every static check stayed green. Only the live seam probes catch this class: ```bash node contract/runtime/run.mjs --require=dashboard,agent,postgres --only=seam ``` Never accept an eve bump on contracts + tsc green alone. ## A run that will not move, with everything up ```bash evestack doctor --verbose evestack doctor --sql # remediation SQL only ``` One shape worth recognising, because it bricks a deployment permanently rather than failing one run: `status`, `completed_at`, `output_cbor` and `error_cbor` are **one value in four columns**. `WorkflowRunSchema` is a discriminated union whose pending/running branch declares all three of the latter `undefined`, and startup parses *every* `status='running'` row before filtering — outside the try. So a single row with `running` + `completed_at` + `output_cbor` stops the world from starting. Write the whole branch in one statement, or not at all. Repair SQL is in `docs/troubleshooting.mdx`, and it needs the `::workflow.status` cast because the column is an enum. ## Ollama took the machine down Not a figure of speech. Loading a multi-gigabyte model alongside Docker, Postgres and the dashboard has exhausted an 8 GB host and shut the desktop down. Budget model size plus ~4 GB free, and use a hosted key for any end-to-end test — the local path is not needed to verify evestack itself. --- *Source: · full documentation: *