Files

1 line
20 KiB
JSON

{"repo": "av/holoiconic", "files": {"README.md": {"1f1373a082e980d98531a60f5da1d2fa2381d072": {"t": 1785674372.1802168, "n": 8824, "h": "fe135b77ef4ff100", "c": "# holoiconic\n\nHoloiconic is a self-modifying agentic runtime where everything, including code, data, configuration, conversation history, metrics, and versions, lives as RDF quads in a reactive graph database backed by Turso/libSQL. The kernel is small plumbing. Runtime behavior lives in graph-resident nodes that can inspect, modify, and spawn each other.\n\n## Quick start\n\n```bash\nbun install\nbun start\n```\n\nThis boots the kernel, seeds an empty graph from the template, installs the reactive compiler, starts the supervisor, API server, WebUI, and REPL.\nSee \"Using custom OpenAI-compatible providers\" below for `OPENAI_BASE_URL=...` etc (Groq, Ollama, vLLM...).\n\n## Using custom OpenAI-compatible providers (Groq, Ollama, vLLM, OpenRouter, ...)\n\nTrivial to launch against any OpenAI-compat endpoint via 6 methods.\n\nPrecedence (highest wins): CLI flags > config file > environment variables > defaults (`mock:llm` only when neither key nor `baseUrl`).\n\n| Source | Example use | Scope |\n|--------|-------------|-------|\n| CLI flags | `bun start -- --openai-base-url=...` | per-invocation |\n| `.holoiconic.json` (or `holoiconic.config.json`) | persistent file (cwd or `~/`) | across restarts |\n| env vars | `OPENAI_BASE_URL=... bun start` | shell / `.env` |\n| REPL `.provider` | `.provider set --base ...` | REPL session |\n| WebUI form | inline inputs in chat header | browser tab |\n| per-request | body fields or `x-openai-*` headers | single API call |\n\n**1. Persistent config file** (`.holoiconic.json` or `holoiconic.config.json` in cwd or home dir):\n\nCopy the ready-to-use documented template (contains Groq, Ollama local, OpenRouter examples + comments + security notes + precedence):\n```bash\ncp .holoiconic.json.example .holoiconic.json\n```\nThen edit the copy (valid JSON only; supports the shape below or flat keys). `bun start` loads it automatically from cwd or `~/`.\n\n```json\n{\n \"provider\": {\n \"baseUrl\": \"https://api.groq.com/openai\",\n \"apiKey\": \"gsk_...\",\n \"model\": \"llama-3.1-70b-versatile\"\n }\n}\n```\n(Flat top-level keys like `\"baseUrl\"` are also accepted, as are snake_case variants.) See `.holoiconic.json.example` for 3 full commented examples.\n\nOverride the searched locations (cwd + ~/) with an exact path using env `HOLOICONIC_CONFIG` (or `HOLOICONIC_CONFIG_PATH`) or CLI `--config PATH` (CLI > file > env precedence).\n\n**2. CLI flags** (pass after `--` for `bun start`; direct boot also supports):\n\n```bash\nbun start -- --openai-base-url=https://api.groq.com/openai \\\n --openai-api-key=gsk_... --model=llama-3.1-8b-instant\n# shorts: -b URL -k KEY -m MODEL ; also --provider, --api-port etc.\nbun src/boot.ts --help\n```\n\n**3. Environment variables** (copy `.env.example` to `.env` and edit):\n\n```bash\nOPENAI_BASE_URL=https://api.groq.com/openai \\\nOPENAI_API_KEY=gsk_... \\\nHOLOICONIC_MODEL=llama-3.1-70b-versatile \\\nbun start\n```\nSupported: `OPENAI_BASE_URL` (or `OPENAI_API_BASE`), `OPENAI_API_KEY`, `HOLOICONIC_MODEL` (or `OPENAI_MODEL` / `MODEL`).\n\n**4. REPL** (per-session override, after boot):\n\n```\nholo> .provider set --base https://api.groq.com/openai --key gsk_... --model llama-3.1-70b\nholo> .provider show\nholo> .provider clear\n```\n\n**5. WebUI** (http://localhost:3002): the chat header includes a status badge. Use the compact form right below it (baseUrl, apiKey, model fields + set/clear buttons). Configuration is saved to localStorage and survives reloads; values are forwarded on each send.\n\n**6. Per-request** (overrides everything for one call to the OpenAI-compatible API on :3001):\n\n```bash\ncurl -X POST http://localhost:3001/v1/chat/completions \\\n -H \"Content-Type: application/json\" \\\n -H \"x-openai-base-url: https://api.groq.com/openai\" \\\n -H \"x-openai-api-key: gsk_...\" \\\n -d '{\"model\":\"llama-3.1-70b-versatile\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}'\n```\nBody also accepts `baseUrl`, `apiKey`, `model` (and snake_case variants). `Authorization: Bearer <key>` works for the key (base from header or body).\n\n**Notes**\n\n- When a `baseUrl` is supplied (any method), a dummy key `sk-local` is used automatically if none provided — this works for local servers (Ollama, llama.cpp, vLLM, etc.) that need no auth.\n- Specify the exact model name expected by your target endpoint.\n- `bun src/boot.ts --help` (or via `bun start -- --help`) shows the full flag list.\n- Custom provider errors are actionable: they report the attempted base URL, the original fetch/HTTP error, verification advice, and the configuration source (CLI / config / env / REPL / per-request / WebUI).\n- Cross-references: `.env.example` (detailed vars + snippets), CLAUDE.md (\"Environment variables\" section), `facts list --section providers --tags spec` (and `facts check --tags \"spec\"` for the full provider spec).\n\n## Environment variables\n\n| Variable | Required | Description |\n|---|---|---|\n| `OPENAI_API_KEY` | For OpenAI-compatible LLMs and embeddings | Used by the default `openai` provider path. |\n| `ANTHROPIC_API_KEY` | For Anthropic models | Used when selecting the `anthropic` provider. |\n| `TURSO_URL` | For cloud DB | Turso Cloud URL, for example `libsql://db-org.turso.io`. |\n| `TURSO_AUTH_TOKEN` | For cloud DB | Turso Cloud auth token. |\n\nWithout LLM API keys, `main` spawns the graph-resident `mock:llm` node. It registers a pi-ai faux provider for completions and serves OpenAI-compatible mock embeddings. Without Turso credentials, data is stored locally in `holoiconic.db`. For custom providers see 'Using custom OpenAI-compatible providers' section above (6 methods + table).\n\n## Interfaces\n\nThe runtime exposes three interfaces, all started automatically by the `main` node:\n\n- **REPL**: interactive command line at the `holo>` prompt. Natural language input routes through `agent:loop`; dot-commands provide direct graph manipulation.\n- **API**: OpenAI-compatible API on `localhost:3001`, including `/v1/chat/completions`, streaming SSE support, `/v1/models`, and `/health`.\n- **WebUI**: browser-based chat and graph explorer on `localhost:3002`.\n\n## The ctx primitives\n\nEvery node receives a `ctx` object with these primitives:\n\n| Primitive | Signature | Description |\n|---|---|---|\n| `insert` | `ctx.insert(subject, predicate, object, graph?, embedding?)` | Insert an RDF quad. Idempotent for duplicate `(subject, predicate, object, graph)` values. |\n| `remove` | `ctx.remove(subject, predicate, object?, graph?)` | Remove matching quads. Omitting `object` removes all values for `(subject, predicate, graph)`. |\n| `query` | `ctx.query({ subject?, predicate?, object?, graph? })` | Pattern-match quads. Omitted fields are wildcards. |\n| `call` | `ctx.call(name, args?)` | Execute a graph-resident node by name. |\n| `set` | `ctx.set(subject, predicate, object, graph?, embedding?)` | Atomically replace all values for `(subject, predicate, graph)` with one value. |\n| `on` | `ctx.on(pattern, callback)` | Subscribe to quad changes matching a pattern. Returns an unsubscribe function. |\n\n`ctx.self` returns the name of the currently executing node via AsyncLocalStorage.\n\n## REPL commands\n\n```text\n.query {\"subject\":\"...\",\"predicate\":\"...\"} query quads by pattern\n.insert subject predicate object [--g graph] insert a quad\n.remove subject predicate object [--g graph] remove a quad\n.call name [argsJSON] call a node\n.nodes list Function nodes\n.source <name> view node source\n.edit <name> edit node source inline\n.create <name> create a node interactively\n.spawn <name> spawn a long-lived node\n.sessions list conversation sessions\n.session show current session ID\n.export [path] export graph snapshot JSON\n.import <path> import graph snapshot JSON\n.deps <name> show node dependencies\n.inspect <name> show comprehensive node info\n.versions <name> list saved source versions\n.restore <name> <seq> restore a source version\n.cron <name> <interval_ms> run a node on a timer\n.crons list cron jobs\n.metrics show metrics report\n.eval <code> eval code with ctx in scope\n.help show help\n```\n\nAny input not starting with `.` is sent to `agent:loop`.\n\n## Running tests\n\n```bash\nbun run src/test-boot.ts\n```\n\nThe integration test suite uses a custom harness and boots the full runtime against a local file DB.\n"}}, "AGENTS.md": {"1f1373a082e980d98531a60f5da1d2fa2381d072": {"t": 1785674372.2258658, "n": 10737, "h": "fd711ec62a5e0283", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Commands\n\n```bash\nbun install # install dependencies\nbun start # boot the full runtime (REPL + API:3001 + WebUI:3002)\nbun run src/test-boot.ts # run integration tests\n```\n\nThere is no `bun test` glob match — tests live in a single file (`src/test-boot.ts`) with a custom harness (no test framework). To run a subset, comment out test function calls at the bottom of the file.\n\n## Architecture\n\n```\nboot.ts (kernel, ~30 lines)\n -> createDatabase() + initSchema() [src/db.ts]\n -> createCtx(db) [src/ctx.ts]\n -> seedTemplate(ctx) [src/template.ts]\n -> ctx.call('main')\n -> sys:compiler (replaces ctx.call with cached + reactive version)\n -> spawn sys:supervisor (manages node lifecycle with retry/backoff)\n -> agent:tools (registers tool definitions as Tool-typed quads)\n -> spawn api:server (port 3001)\n -> spawn web:ui (port 3002)\n -> spawn repl (stdin)\n```\n\nThe kernel has zero policy. All behavior is defined in graph-resident nodes.\n\nEverything is an RDF quad: `(subject, predicate, object, graph)` — all strings, stored in a single `quads` table in Turso/libSQL. Code, data, configuration, conversation history, metrics, versions — all quads.\n\n### Key files\n\n| File | Purpose |\n|---|---|\n| `src/boot.ts` | Kernel entry point. Connects DB, creates ctx, seeds if empty, calls main. |\n| `src/ctx.ts` | The 6 primitives: assert, retract, query, call, set, on. Plus ctx.self via AsyncLocalStorage. |\n| `src/db.ts` | Turso/libSQL connection factory + schema init (quads table + optional vector column). |\n| `src/template.ts` | All 29 node source strings in the `nodes` record + `seedTemplate()` function. This is the largest and most-edited file. |\n| `src/test-boot.ts` | Integration tests with custom harness (no framework). |\n| `index.ts` | Re-exports public API. |\n\n### How nodes work\n\nNodes are AsyncFunction bodies stored as strings in the graph as `(name, 'source', code)` quads, with a `(name, 'type', 'Function')` companion quad. At call time, sys:compiler does:\n\n```js\nconst fn = new AsyncFunction('ctx', 'args', sourceString);\nreturn nodeStorage.run(name, () => fn(ctx, args));\n```\n\nNodes receive `ctx` (6 primitives + `ctx.self`) and `args` (optional caller-provided object). Nodes cannot use `import` — they run in AsyncFunction scope with access to Bun globals, `process`, and `console`.\n\n### How ctx.call evolves during boot\n\nThe naive `ctx.call` in `ctx.ts` recompiles on every call. `sys:compiler` replaces it in-place with a cached version that:\n1. Caches compiled functions by name\n2. Watches `{ p: 'source' }` changes to invalidate the cache reactively\n3. Auto-saves old source as a version on retract (via `version:save`)\n4. Records metrics (call count, duration, errors) for every call except metrics nodes\n\nThis is why `ctx` has `[key: \\`_${string}\\`]: any` — nodes like sys:compiler and sys:supervisor store runtime state (caches, controllers, locks) as `ctx._` properties.\n\n### The reactive loop\n\nWhen a node's source quad is retracted+reasserted: sys:compiler invalidates its cache, version:save snapshots the old source, and if the node is spawned, sys:supervisor aborts the old instance and restarts it with the new source. This is how hot-reload works.\n\n## How to add a new node\n\n1. Add the source string to the `nodes` record in `src/template.ts`.\n2. `seedTemplate()` automatically registers it with `(name, 'source', code)` and `(name, 'type', 'Function')` quads.\n3. If the node should be an LLM tool: register it in `agent:tools` with an `input_schema`, and add a dispatch handler in `agent:loop`. Tool names use underscores (e.g., `snapshot_export`), node names use colons (`snapshot:export`) — the generic fallback translates between them.\n4. Add tests in `src/test-boot.ts`.\n5. To auto-start: add `ctx.call('spawn', { node: 'my:node' })` in the `main` node.\n\n## Conventions\n\n- Node names use colon namespacing: `sys:compiler`, `agent:loop`, `version:save`\n- Long-lived nodes receive `args.signal` (AbortController signal) for cooperative shutdown\n- `ctx.set(s, p, o, g?)` is the primitive for single-valued predicates: atomic delete+insert via `db.batch`\n- The `set` node delegates to `ctx.set` — use either depending on whether you're in a node or calling from outside\n- sys:supervisor retries crashed nodes with exponential backoff (max 3, delays 500/1000/2000ms)\n- Metrics auto-recorded by sys:compiler for every ctx.call (except metrics nodes)\n- `ctx.on` subscribers must be unsubscribed to avoid leaks — store the unsub function\n\n## Testing\n\nTests boot the full system against a local file DB — no mocking. Each test function groups related assertions under a section header. Add new tests by writing a `async function testFoo(ctx: Ctx)` and calling it from the bottom of the file.\n\n## Common operations\n\n- **Reset the graph**: delete `holoiconic.db` and restart. Seed recreates all nodes.\n- **Debug a node**: `.source <name>` or `.inspect <name>` in the REPL.\n- **Hot-reload a node**: retract+assert its source quad. sys:compiler and sys:supervisor handle the rest.\n\n## Environment variables\n\nWithout `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`, `main` spawns the `mock:llm` node — a graph-resident mock server that registers a pi-ai faux provider for LLM completions and runs an HTTP server for OpenAI-compatible embeddings. The `llm`, `agent:loop`, and `embed` nodes route through it automatically. Without `TURSO_URL`/`TURSO_AUTH_TOKEN`, data is stored locally in `holoiconic.db`.\n\n**Custom OpenAI-compatible providers are first-class and trivial to launch against** (Groq, Ollama, vLLM, llama.cpp, local servers, Together, Fireworks, any OpenAI-/v1 endpoint). The nodes (main, llm, agent:loop, embed, api:server, repl) all support:\n\n- `OPENAI_BASE_URL` (or alias `OPENAI_API_BASE`) — target URL\n- `OPENAI_API_KEY` — key (dummy `sk-local` auto-used when base present, works for auth-less servers)\n- `HOLOICONIC_MODEL` (aliases: `OPENAI_MODEL`, `MODEL`)\n- Ports: `HOLO_API_PORT` (3001), `HOLO_WEB_PORT` (3002)\n- Also `ANTHROPIC_API_KEY`, `TURSO_URL`/`TURSO_AUTH_TOKEN`\n\n**Persistent config file** (`.holoiconic.json` or `holoiconic.config.json` in cwd or ~/) is a first-class method for persistent custom provider defaults (loaded early in boot, survives restarts, applies to REPL + API + WebUI):\n\n```bash\ncp .holoiconic.json.example .holoiconic.json\n# (or: cp ... ~/holoiconic.config.json)\n# edit the copy (valid JSON; {provider:{baseUrl,apiKey,model}} or flat top-level keys; snake_case ok;\n# Groq/Ollama/OpenRouter examples + precedence + security notes inside the .example)\n# Override exact path: HOLOICONIC_CONFIG=/abs/my.json (or HOLOICONIC_CONFIG_PATH) env or --config CLI e.g. HOLOICONIC_CONFIG=./groq.json bun start\nbun start\n```\n\nPrecedence (highest wins): CLI flags > config file > env vars > defaults. (See `.holoiconic.json.example`, c3t/ioy in facts, and boot.ts:loadConfigProvider.)\n\n**Concrete launch examples for custom provider:**\n\nConfig file (persistent default):\n```bash\n# after cp .holoiconic.json.example + edit as above\nbun start\n```\n\nEnv (simplest for ad-hoc):\n```bash\nOPENAI_BASE_URL=https://api.groq.com/openai OPENAI_API_KEY=gsk_... HOLOICONIC_MODEL=llama-3.1-70b-versatile bun start\n```\n\nCLI flags (highest precedence; `bun start` requires `--` separator to pass args):\n```bash\nbun start -- --openai-base-url=https://api.groq.com/openai --openai-api-key=gsk_... --model=llama-3.1-8b-instant --api-port=3001\nbun src/boot.ts --openai-base-url=URL -b KEY -m MODEL --provider=openai-completions --help\n```\n\nREPL (per-session override after start; stored in `ctx._providerConfig`; affects subsequent calls):\n```text\nholo> .provider set --base https://api.groq.com/openai --key gsk_... --model llama-3.1-70b\nholo> .provider show\nholo> .provider clear\n```\n\nPer-request (dynamic, for any call to the OpenAI-compat API `:3001` or originating from WebUI):\n- Request body fields: `baseUrl` / `base_url` / `baseURL`, `apiKey` / `api_key`, `model`, `provider`\n- Headers: `x-openai-base-url`, `x-openai-api-key` (or `x-api-key`, `openai-base-url`), `Authorization: Bearer <key>`\n- Dummy key handling and forwarding to downstream nodes is automatic.\n\nFull list + precedence (CLI > config file > env > default) + comments in the committed `.env.example` and `.holoiconic.json.example` at repo root. (WebUI inline form also supported, persisted in localStorage.)\n\nUse `--help` (via CLI) to see all boot flags for trivial non-interactive custom launches.\n\nSee also: README.md \"Environment variables\" and \"Using custom OpenAI-compatible providers\" section (6 methods + table), `.facts` (providers section, facts 5l0/clp/l8u/c3t/ioy/bs6 etc), `.holoiconic.json.example`, and REPL `.help`.\n\n<!-- facts:start -->\n## Fact-driven development\n\nThis project uses [facts](https://github.com/av/facts) for specification and documentation. All work flows through the fact sheet — it is the source of truth.\n\n**Every change starts with a fact.** Facts are the spec — they define what \"done\" means. Code that isn't described by a fact is unverifiable and will be treated as incorrect. The skill `facts skills show facts` has the full format spec and command reference.\n\n1. `facts list` — read the current spec to orient. Fact sheets can be large — use filters to focus: `--section \"cli/init\"`, `--tags \"draft\"`, `--file api.facts`, `--manual`. Read only the section relevant to your task, not the entire sheet.\n2. `facts add` — write facts describing what should be true when done. Each fact is a testable claim. You are not ready to write code until this step is complete.\n3. Implement the code to make those facts true\n4. `facts check --tags \"<tag>\"` or `facts get <id>` — verify your changes. Never run bare `facts check` unless asked.\n5. `facts edit <id> --add-tag implemented` — mark verified facts done\n\nStep 4 only works if step 2 happened. If you skipped step 2, go back now — you cannot verify work that has no fact.\n\n**Manual facts (`?` in check output):** these have no command, so you verify them by reading the relevant code. For each `?` fact: read what it claims, check the code, report PASS or FAIL with a one-line reason. Reporting \"N manual\" without verifying each one is not acceptable.\n\n**Lifecycle:** `@draft` → `@spec` → `@implemented`\n\n**Skills** (invoke via `facts skills show <name>`):\n- `facts-refine` — sharpen `@draft` facts into `@spec` with the user\n- `facts-discover` — scan the codebase and sync facts to reality (only when explicitly asked)\n- `facts-implement` — implement `@spec` facts in code, verify, tag `@implemented`\n<!-- facts:end -->\n"}}}}