Skip to content

tvmaly/nanogo

Repository files navigation

nanogo — Your Child's Personal AI Tutor, at Home, for Free

A free, open-source alternative to AlphaSchool that runs on your own computer and remembers your child across every session.


Who is this for?

Parents who want more for their kids — without a $200/month subscription.

If you've heard of AI tutoring platforms like AlphaSchool and thought "my child could really benefit from that," nanogo is built for you. It gives your child a patient, knowledgeable tutor available any time of day or night — one that never gets tired, never judges, and picks up exactly where it left off last time.

You don't need to be a programmer to use it. If you can open a terminal and paste a command, you're ready.


Why should you care?

Most AI tutoring tools are either:

  • Expensive — subscription fees that add up fast
  • Generic — one-size-fits-all lessons with no memory of your child's progress
  • Cloud-dependent — your child's learning history lives on someone else's server

nanogo is different:

  • Free to run — you only pay for the AI calls you make (a few cents per session using cheap models)
  • Remembers your child — it builds a persistent memory of what your child knows, struggles with, and enjoys
  • Runs on your computer — your family's data stays with you
  • Fully customizable — create lessons, quizzes, and study plans by writing simple text files
  • Voice mode — Phase 19 adds contract-backed speech-to-text and text-to-speech so a child can speak to the tutor and hear responses when a voice provider is configured
  • Extensible operator interfaces — Phase 19.7 adds a shared gateway surface so the same tutor runtime can be driven from a local TUI, an OpenAI-compatible HTTP API, or an OpenClaw-style WebSocket gateway
  • Local interactive help — Phase 19.9 adds deterministic help topics that can be searched from the CLI, TUI, GatewayWS, and the OpenAI-compatible operations endpoint without calling an LLM
  • Hardened extension boundaries — Phase 21.x keeps the kernel small, makes runtime extension registration auditable by capability family, and moves file-backed session persistence into modules/session
  • Optional local browser lessons — The browser-control phase adds config-gated local browser sessions for interactive lessons, with deterministic fake tests and an agent-browser adapter for manual headed smoke runs

For parents, Phase 19 voice support matters because it makes tutoring less like typing into a chatbot and more like working with a patient adult helper. Younger learners can answer out loud before they type confidently, parents can turn listening or speaking on and off during a session, and raw microphone audio is not persisted by default.

Phase 19.7 matters for families and builders who want more than one way to operate the tutor. A local terminal operator can monitor sessions in a TUI, existing OpenAI-compatible clients can call nanogo through /v1/chat/completions, and future apps can subscribe to normalized gateway events without coupling to the core agent loop.

Browser-control support matters when a lesson is more than a chat transcript. Nanogo can open a local lesson page, inspect accessible controls, click or fill answers, capture screenshots as local artifacts, and close the session when a trusted lesson wrapper reports completion. It is off by default so normal tutoring and tests do not require a browser.

The hardened browser path adds an extra safety layer for families using local interactive lessons. Lesson completion now requires a per-session nonce from the trusted wrapper, local file lessons are checked against symlink-safe roots, external scripts in local wrapper HTML are rejected by default, and browser session records avoid sensitive data such as profiles, cookies, storage, auth headers, and screenshots.


What it looks like in practice

Ask it to tutor your 10-year-old in fractions:

nanogo -p "Explain fractions to a 10-year-old. Start with what half a pizza means, then ask me a question."

Come back tomorrow and it already knows where you left off:

nanogo -p "I'm back. What should we work on today?"

It answers: "Welcome back! Last time we covered equivalent fractions and you were doing great. Want to try a short quiz on adding fractions with different denominators?"

Run a structured lesson defined in a file — for example, a daily math warm-up:

nanogo skill run daily-math --grade=4 --student=Emma

Installation

Step 1 — Install Go

nanogo is written in Go. Download and install it from https://go.dev/dl (free). Choose the installer for your operating system (Mac, Windows, or Linux) and follow the on-screen instructions. You only need to do this once.

Verify it worked by opening a terminal and typing:

go version

You should see something like go version go1.22.0.

Step 2 — Download nanogo

git clone https://github.com/tvmaly/nanogo.git
cd nanogo
go build -o nanogo ./cmd/nanogo

This creates a nanogo program in the current folder.

Step 3 — Get an API key

nanogo uses AI models through OpenRouter — a free-to-join service that gives you access to powerful AI at very low cost. Sign up at https://openrouter.ai, then create an API key in your account dashboard.

Set it in your terminal (replace your-key-here with your actual key):

Mac / Linux:

export OPENROUTER_API_KEY=your-key-here

Windows (Command Prompt):

set OPENROUTER_API_KEY=your-key-here

Phase 15 includes an optional realtime voice extension. The first provider target is xAI Grok Voice. You need an xAI account with API access and purchased credits before live voice calls will work.

Mac / Linux:

export XAI_API_KEY=your-xai-key-here
export XAI_REALTIME_MODEL=grok-voice-think-fast-1.0

Windows (Command Prompt):

set XAI_API_KEY=your-xai-key-here
set XAI_REALTIME_MODEL=grok-voice-think-fast-1.0

Deepgram Voice Agent is planned behind the same adapter pattern for later testing:

Mac / Linux:

export DEEPGRAM_API_KEY=your-deepgram-key-here

Windows (Command Prompt):

set DEEPGRAM_API_KEY=your-deepgram-key-here

To avoid setting this every time, add it to your shell profile (.zshrc or .bashrc on Mac/Linux, or System Environment Variables on Windows).

Optional browser lesson setup:

scripts/setup_agent_browser.sh
./nanogo browser doctor --driver agent-browser
./nanogo browser open --driver agent-browser --headed https://example.com
./nanogo browser smoke-duckduckgo --driver agent-browser --headed
./nanogo browser smoke-youtube --driver agent-browser --headed --seconds 75
./nanogo --workspace ./workspace browser smoke-youtube-iframe --driver agent-browser --headed

Browser support is disabled by default. To expose browser tools to agents or gateway operations, add:

{
  "browser": {
    "enabled": true,
    "driver": "agent-browser",
    "allow_file_roots": ["workspace/lessons"],
    "artifact_root": ".nanogo/browser-artifacts",
    "max_sessions": 2
  }
}

Trusted local wrapper lessons under allow_file_roots can report progress, quiz_answer, error, and completion events, but completion closes the session only when the wrapper includes the session nonce returned by browser_session_start. browser_eval remains policy-gated and reports that JavaScript-initiated network requests are not isolated by Nanogo domain policy.

Manual headed live-web smoke commands write redacted JSON artifacts under .nanogo/browser-smokes by default. Each artifact includes the final URL, title, screenshot path, console/network summaries, and cleanup status. The YouTube iframe smoke defaults to the checked-in local fixture at workspace/lessons/yoyo_youtube_iframe/index.html; use --url to point it at another trusted wrapper.

Step 4 — Try it

./nanogo -p "Hello! I am a parent setting this up for my 8-year-old. Can you introduce yourself and ask what subject they want to study today?"

Creating your first lesson

Lessons in nanogo are plain text files called "skills." Create a folder called skills/ and add a file called math-basics.md:

---
name: math-basics
description: Daily math warm-up for kids
args:
  - grade
  - student
---
You are a friendly math tutor. Your student is {{student}}, who is in grade {{grade}}.
Start with a warm greeting, then give them three math problems appropriate for their grade level.
After they answer each one, give encouraging feedback and move to the next.
At the end, summarize how they did and suggest what to practice next.

Run it with:

./nanogo --skills=./skills skill run math-basics --grade=3 --student=Emma

nanogo will ask for any missing information interactively if you forget to supply it.

You can make a skill for any subject: reading comprehension, spelling practice, science questions, history flashcards, foreign language vocabulary — anything a human tutor could teach.


How the memory works

After each session, nanogo automatically:

  1. Saves the conversation to a local history file on your computer
  2. Runs a background pass that reads the history and updates a MEMORY.md file
  3. Loads that memory at the start of every future session

This means the tutor genuinely remembers across days and weeks:

  • Which topics your child has covered
  • Where they struggled and where they excelled
  • Their name, grade level, and learning style
  • What you worked on in the last session

Everything is stored as plain text files in ~/.nanogo/workspace/ on your own machine — you can read and edit them any time.


Agent-readable observability

nanogo can write schema-versioned observation records for agents and research loops as JSONL. Add the jsonl observer to your config:

{
  "obs": [
    {
      "driver": "jsonl",
      "config": {
        "root": ".nanogo/obs",
        "failure_policy": "best_effort",
        "flush_on_error": true,
        "flush_on_run_finish": true
      }
    }
  ]
}

By default this writes one JSON object per line to .nanogo/obs/observations.jsonl. Each record includes schema_version, id, type, time, event lineage in attributes.event_kind when it comes from the runtime event bus, optional artifact references, error details, repair hints, and links. Query contracts are present for future readers; the JSONL store currently returns a typed not-implemented error for queries.

Run the live OpenRouter smoke suite with:

OPENROUTER_API_KEY=sk-or-v1-... make test

Executable lesson artifacts

The meta research loop can create deterministic draft lesson artifacts without calling a model or starting a preview server:

nanogo --workspace ./workspace meta lesson create \
  --kind manim_lesson \
  --prompt "Teach multiplying fractions to a 9-year-old" \
  --runner fake

nanogo --workspace ./workspace meta lesson create \
  --kind browser_game_lesson \
  --prompt "Teach states of matter with a drag-and-drop sorting game" \
  --runner fake

These commands write run-scoped candidates under lessons/generated/<lesson-id>/runs/<run-id>/, including lesson.bundle.yaml, source files, logs, validation reports, static preview files, and fake video or browser-game artifacts. Lineage and graph evidence are appended under memory/meta/. Passing smoke gates make a candidate eligible for promotion, but the command does not promote it.

For parents, this means nanogo can produce inspectable draft animations and interactive activities before anything becomes a stable lesson. For developers, the first slice is fake-backed and deterministic; real Manim, Vite, and Playwright execution remains a manual follow-up.


Phase 19 voice support

Phase 19 separates voice into stable contracts and optional providers:

  • core/contracts defines speech-to-text and text-to-speech interfaces, audio formats, transcript events, and TTS stream events.
  • modules/voice owns the reusable voice session controller, final-transcript routing, automatic speaking, voice_say, and runtime STT/TTS toggles.
  • ext/voice/providers/apple contains Apple SpeechAnalyzer and AVSpeech provider entry points, kept outside the kernel.
  • CLI commands include nanogo voice providers, nanogo voice stt, nanogo voice tts, and nanogo voice chat.

Runtime voice state is session-scoped. A future web interface can use the same control path as the CLI to turn listening or speaking on and off without starting a new tutoring session.

Manual Apple smoke targets:

make apple-voice-helpers
make test-19.apple-tts
make test-19.apple-stt

These require macOS voice capabilities and local microphone/speaker permissions. Deterministic Go tests use fakes and do not require real voice providers.

Phase 19.5 replaces the Apple placeholders with optional Swift helper binaries. The Go providers talk to those helpers over JSONL stdio, so ordinary Go builds and tests remain portable and do not require Swift, macOS, a microphone, a speaker, or Apple speech assets.

To build the helpers on a supported Mac:

make apple-voice-helpers

The helper build skips on non-Mac systems. On macOS it requires swiftc and a macOS SDK 26+ because the STT helper uses Apple's SpeechAnalyzer, SpeechTranscriber, and AssetInventory APIs. Helper paths can be overridden with NANOGO_APPLE_AVSPEECH_HELPER and NANOGO_APPLE_SPEECHANALYZER_HELPER.

Live Apple STT/TTS chat:

make test-19.5.apple-voice-chat

Direct CLI form:

./nanogo voice chat --stt apple --tts apple --locale en-US

This path uses local microphone capture, Apple speech-to-text, the configured OpenRouter-backed nanogo agent, Apple text-to-speech, and local speaker playback. Raw microphone and speaker PCM are not persisted by default.

Phase 19.6 routes voice chat through the full nanogo agent loop, so voice turns can use the same configured tools as text turns. ask_user is intentionally hidden in voice chat until nanogo has a voice-native ask/answer flow.

OpenRouter web search is available in voice chat by default as a provider-side server tool. The voice system prompt tells the model to use search only when you explicitly ask it to search, browse, look up information, or answer current/latest factual questions.

Advanced web search settings can be configured:

{
  "voice": {
    "web_search": {
      "engine": "auto",
      "max_results": 8,
      "max_total_results": 20,
      "search_context_size": "medium",
      "allowed_domains": ["arxiv.org", "nature.com"],
      "excluded_domains": ["reddit.com"]
    }
  }
}

Useful live prompts:

What tools do you have available in this voice session?
Use a tool to list the files in this repository root, then summarize what you found.
Search the web for the latest OpenAI model news and summarize the top results.
Explain magnets to a 10-year-old without searching the web.

Workspace contracts and local verification

Phase 17 moved nanogo toward a smaller execution kernel and more behavior in modules and editable workspace assets. The preferred place to change tutor behavior is now plain files before Go code:

workspace/tools/<name>/tool.yaml
workspace/tools/<name>/prompt.md
workspace/tools/<name>/tests.yaml
workspace/skills/*.md
workspace/tutorials/*.md
workspace/policies/*.md

Workspace tool definitions are validated by ext/workspace: missing commands, unknown manifest fields, path traversal, duplicate names, and missing tests fail with field-specific errors. Phase 17.5 adds schema_version metadata to workspace tool manifests so future workspace assets can evolve without ambiguous migrations.

For local verification, run:

make verify-local

That target builds the binary, runs normal and race-detector tests, runs go vet, checks the Phase 17 core boundary, enforces the core/ and modules/ import invariants, checks required fakes, and verifies the core LOC budget.

Current architecture split:

Directory Role Examples
core/ Tiny execution kernel and stable contracts used directly by the agent loop. agent, contracts, event, harness, llm, session, tools
modules/ Stable first-party runtime subsystems: shared contracts, registries, and default product behavior. modules/obs, modules/transport, modules/memory, modules/tools/builtin
ext/ Optional concrete adapters, providers, domains, and experiments that plug into core or module contracts. ext/obs/slog, ext/transport/rest, ext/scheduler/cron, ext/adaptive, ext/voice
workspace/ Editable data-first behavior and user/product assets. workspace/tools/..., workspace/skills/*.md, workspace/policies/*.md

A useful rule of thumb: modules/ is the first-party subsystem layer, while ext/ is the plug-in and adaptation layer. For example, modules/obs defines the shared observability API and fan-out behavior, while ext/obs/slog, ext/obs/file, and ext/obs/cost are concrete observers. Similarly, modules/transport defines the transport contract and registry, while ext/transport/cli, ext/transport/rest, and ext/transport/webui are concrete transports.

Not every modules/ package is interfaces-only. modules/memory, modules/skills, and modules/tools/builtin contain default first-party behavior. They stay outside core/ because they are product/runtime behavior rather than kernel primitives.

Phase 21.x hardens the extension boundary without changing user-facing tutor behavior:

  • core/session now contains only stable session contracts; the JSONL and metadata-file store lives in modules/session and preserves existing <id>.jsonl and <id>.meta.json compatibility.
  • cmd/nanogo/extensions.go groups package-init registrations by LLM providers, transports, schedulers, harness sensors, and adaptive domains/tools, with command-package tests that fail when expected runtime registrations disappear.
  • modules/gateway registers operations through feature-family functions for status, chat/sessions/events, skills/tools, costs/models, voice/realtime, and help.
  • Architecture guards now have explicit regression coverage for core -> modules, core -> ext, and modules -> ext import violations.

Phase 17.5 closes runtime-wiring gaps that matter for Phase 18 AgentFlow tutor flows:

  • spawn is now wired through configured subagent runners in prompt, REPL, skill, heartbeat, and transport-driven turns, so future flow executors can delegate without bypassing nanogo sessions, tools, events, or concurrency limits.
  • Configured transports use a shared modules/transport.App adapter over the existing agent loop, which reduces one-off CLI/REST/WebUI composition paths before adding flow run.
  • Voice sessions persist normalized events by default, while raw provider event logs require explicit opt-in, reducing privacy risk for child transcripts and provider payloads.
  • Adaptive archive records, voice event records, raw voice logs, and workspace tool manifests now include schema-version metadata to make evidence archives safer to inspect and migrate.

Phase 18 v3 interface readiness adds a narrow core/contracts package for cross-cutting capability boundaries without moving product behavior into the kernel:

  • core/contracts defines small agent, subagent, tool runtime, pattern runtime, handoff, trace, and approval interfaces plus request/result shapes for Phase 18 v3 orchestration.
  • core/tools.NewContractRuntime adapts existing core/tools.Source values into the new tool catalog/invocation contracts while preserving the existing tool API.
  • core/agent.SubagentRunner now also satisfies the neutral subagent spawner contract, and builtin spawn keeps its existing compatibility behavior.
  • Voice provider adapters, including xAI realtime voice, remain in ext/voice; future voice orchestration can depend on contracts.PatternRunner or contracts.HandoffTarget at the bridge boundary.

Phase 18 v3 adds a concrete contract-backed pattern runtime outside core:

  • ext/agentpatterns.Runtime implements contracts.PatternRuntime and keeps router, supervisor-worker, sequential, parallel, loop, review, handoff, and human-review behavior out of core/contracts.
  • Pattern execution uses injected contracts.ToolRuntime, contracts.SubagentSpawner, contracts.TraceSink, and contracts.ApprovalGate; tests use deterministic fakes by default.
  • Skills can opt into patterns with frontmatter such as mode: agentpatterns, pattern: sequential, and pattern_manifest: fraction_lesson without importing the concrete extension package.
  • Setting "agent_patterns": {"enabled": true} in config adds pattern_run, pattern_status, pattern_resume, and pattern_list to the tool source.
  • Pattern traces and checkpoints are extension-local JSONL surfaces; trace redaction is enabled in the concrete JSONL sink.

Features — what this means for your family

Feature What it means for you
Persistent memory across sessions The tutor remembers your child's progress every time — no need to re-explain the basics each session
Custom lesson files Write a simple text file to define any lesson, quiz, or study plan — no coding required
Interactive Q&A If the tutor needs more information (grade, topic, name), it asks your child directly
Your data stays home All history and memory files live on your own computer — nothing is sent to a third-party server beyond the AI call itself
Any subject Math, reading, science, history, coding, languages — if a human tutor could teach it, nanogo can too
Always available 3am panic before a test? nanogo is there, patient as ever
Very low cost A typical 20-minute tutoring session costs less than one cent using the default model
Multiple tutor personalities Define a strict grammar checker, an encouraging math coach, and a Socratic science guide — each as a separate skill file
Open source and free forever No subscription, no lock-in. Audit the code, modify it, share it with other parents

How nanogo evolves to fit your child

Phases 12–14 introduce a self-improving education loop that no static tutoring app can match. Here is what that means in plain language:

For parents

Turn a rough idea into a ready-to-use lesson. Write a few sentences about what you want your child to learn — "I want Emma to understand fractions using cooking recipes" — and the system builds a complete, leveled lesson bundle: introduction, worked examples, practice questions, a rubric, and a parent summary. You review and approve before anything reaches your child.

Lessons improve over time based on your child's actual results. Every session generates a signal — did your child master the concept, get frustrated, breeze through, or stall on a specific step? The system archives those outcomes and, over multiple sessions, shifts toward lesson variants and teaching approaches that work best for that specific child. You can see exactly which variants are running and why.

No two children get the same tutor. A child who learns visually by analogy gets different explanations than one who prefers step-by-step worked examples. The system tracks what works per child and applies it automatically.

You stay in control. Every generated lesson requires parent approval before it is used. You can inspect, edit, or reject any variant. The archive is plain text files on your machine — readable and auditable at any time.

For teachers and homeschool educators

Rapidly prototype differentiated instruction. Write a single rough prompt per topic and let the factory generate multiple pathways: a visual/analogical path, a step-by-step procedural path, a challenge path for advanced students, and a remediation path for students who struggle. Each pathway is a plain markdown file you can edit directly.

Data-driven lesson refinement without a data science background. Mastery scores, retention signals, and engagement data accumulate automatically. The system surfaces which pathways produce the best outcomes per student profile so you can spend your time teaching, not analyzing spreadsheets.

Reusable skill libraries. Every lesson produced by the factory becomes a reusable skill file. Build a library of proven, child-tested lessons over a semester and share them with other families or colleagues — they are plain text and require no special tools to read or run.


Cost estimate

nanogo uses anthropic/claude-haiku-4-5 by default via OpenRouter:

Session Approximate cost
10-minute tutoring session ~$0.005 (half a cent)
30-minute deep dive ~$0.015
A full week of daily sessions ~$0.10

For comparison: AlphaSchool costs roughly $2500-$5000/month. nanogo's AI costs for equivalent usage run under $2/month.


Questions and feedback

Open an issue at https://github.com/tvmaly/nanogo/issues. Parent feedback directly shapes the roadmap.


Implementation Status

This table shows each build phase, what AI tutor capability it unlocks, and whether it is complete.

Phase Description AI Tutor Capability Status
1 Event bus + LLM interface + Router + OpenAI ext + CLI transport Basic single-question tutoring — child asks, tutor answers ✅ Complete
2 Tool interface + 5 builtins + agent loop + session + subagent concurrency Tutor can read/write files, run code, and delegate to specialist sub-tutors (math agent, grammar agent) ✅ Complete
3 Skills frontmatter + dispatcher + ask_user integration Named lesson plans ("do my math homework", "quiz me on fractions") — tutor asks for missing details interactively ✅ Complete
4 Memory (consolidator + dream + curator) Tutor remembers your child across sessions — past mistakes, strengths, learning style, goals ✅ Complete
5 REST + REPL transports Multi-interface access — tutor available via browser/API (REST) and interactive terminal (REPL) simultaneously ✅ Complete
6 Harness interfaces + sensors + binding-signal support Tutor self-corrects when it makes a mistake — test failures inject feedback that forces revision ✅ Complete
7 Scheduler + heartbeats (4 action kinds) + CLI management Scheduled tutoring — daily vocabulary quiz at 8am, weekly progress review on Fridays ✅ Complete
Post-phase-7 integration fixes: CLI transport init() registration, router factory in ext/llm/router/, signal injection wired, SubagentRunner isolated sessions, session-backed ask_user, tools allowlist, config loading from ~/.nanogo/config.json All runtime-wiring gaps from REVIEW.md closed; Phase 8 prerequisites met ✅ Complete
8 Obs interfaces + slog + file + cost adapter Full observability and per-session cost tracking — know exactly what you spent and on what ✅ Complete
9 Evolve extension (full, test-gated) Self-improving tutor — agent proposes improvements to its own lesson files, tests them, deploys on green ✅ Complete
10 Telegram + cron + otel + progressive tools + MCP + mutants + classifier-router Full ecosystem — tutor on Telegram, mutation-tested lesson scripts, multi-model routing by difficulty ✅ Complete
10.5 Contract-driven progressive tool design Nanogo can expose small, safe tool surfaces by default and author new tools from clear operation contracts, manifests, compact outputs, and tests ✅ Complete
11 Web tutor UI extension: student lessons + parent admin + reporting Family-friendly browser experience — student lessons, parent dashboards, lesson editing, and homeschool reporting ✅ Complete
12 Adaptive experiment engine: artifacts, outcomes, archive, islands, scoring System learns which lesson variants actually work — tracks mastery gain, engagement, and retention per child ✅ Complete
13 Adaptive lesson factory: rough parent markdown → polished child-specific lesson bundles Parents write a rough idea; the system generates complete, leveled lessons tailored to their child's style ✅ Complete
14 Adaptive tutor runtime: live policy selection, mastery scoring, remediation, evolve loop Tutor adapts its teaching style in real time — hints, pacing, difficulty, and encouragement evolve per child ✅ Complete
15 Realtime voice extension: xAI Grok Voice backend sessions, voice smoke CLI, optional local audio evaluation Talk to the tutor through a backend voice session now, with browser microphone/speaker handling still cleanly separable for later phases ✅ Complete
15.5 Live hands-free voice loop: MacBook microphone capture, xAI realtime streaming, speaker playback Talk to the tutor hands-free from the command line with raw audio kept private unless debug files are requested ✅ Complete
17.5 Phase 18 readiness hardening: spawn runtime wiring, configured transport app adapter, schema-versioned evidence, module import guard, raw voice log opt-in Complex tutor flows can safely delegate, run through configured transports, and keep auditable evidence without exposing raw provider data by default ✅ Complete
18 v3 interface prerequisite Narrow core/contracts package plus tool and subagent adapters AgentFlow-style runtimes can depend on stable kernel contracts for tools, subagents, handoff, traces, and approvals without importing concrete product extensions ✅ Complete
18 v3 pattern runtime Contract-backed ext/agentpatterns runtime, pattern tools, skill opt-in, voice bridge handoff, trace/checkpoint stores Tutor workflows can route through single, router, supervisor-worker, sequential, parallel, loop, review, handoff, and human-review patterns without leaking extension code into core or modules ✅ Complete
19.7 Extensible interface surface: shared modules/gateway, Bubble Tea TUI, OpenAI-compatible HTTP API, and OpenClaw-style Gateway WebSocket Operators and apps can drive the same tutor sessions, skills, tools, costs, and events through stable adapter contracts without adding interface logic to core/ ✅ Complete
19.8 Rich Bubble Tea TUI with slash commands, session-local model switching, model catalog cache, cost lookup, STT/TTS toggles, and xAI realtime controls The terminal operator can chat, inspect sessions, run skills, monitor costs/events, switch OpenRouter models per session, and control voice state without widening core/ ✅ Complete
21.x Extension boundary hardening: module-owned session persistence, grouped runtime registrations, gateway operation families, and stronger import-guard evidence New providers, transports, schedulers, tool sources, adaptive domains, and gateway features can grow without widening core/ or hiding command composition changes ✅ Complete

Quick Start Guide

Use this section if you want the shortest path from clone to a working local build and the current acceptance test suite.

1. Install prerequisites

You need:

  • Go 1.22 or newer
  • Git
  • An OpenRouter API key for live LLM tests or manual runs

Verify the basics:

go version
git --version

2. Clone the repository

git clone https://github.com/tvmaly/nanogo.git
cd nanogo

3. Build the binary

go build -o nanogo ./cmd/nanogo

4. Set your API key

Mac / Linux:

export OPENROUTER_API_KEY=your-key-here

Windows Command Prompt:

set OPENROUTER_API_KEY=your-key-here

5. Run a first prompt

./nanogo -p "Reply with exactly: OK"

If your setup is working, the response should begin with OK.

6. Run the automated acceptance gates

go test -race ./...
scripts/check_imports.sh
scripts/loc_budget.sh
scripts/check_fakes.sh

If you also want the coverage gate used from Phase 4 onward:

go test -coverprofile=cover.out ./core/agent/... ./modules/memory/... ./modules/tools/builtin/...
go tool cover -func=cover.out | tail -1

Tool Contracts And Progressive Disclosure

Phase 10.5 adds a contract-first tool layer for extension authors and future self-evolution work. New tools can be written as operations with one clear contract: name, description, input schema, output schema, bounded output rules, safety metadata, data-access mode, examples, and tests. Nanogo adapts that contract into core/tools.Tool values, generated help, manifests, and optional future CLI/MCP surfaces without duplicating invocation logic.

Progressive disclosure keeps the default tool surface small. A manifest can make only tool_list, tool_help, tool_reveal, and a few safe tools visible at first, while hidden tools remain real callable tools once revealed for the current session. The agent loop refreshes tool schemas after reveal, so a tool can be revealed and used in the same turn.

Configured tool sources use the tools.sources config block. With no config, nanogo keeps the existing builtin-only behavior.

{
  "tools": {
    "sources": [
      {"driver": "builtin"},
      {
        "driver": "progressive",
        "config": {
          "manifest": "./tools/progressive.json",
          "sources": [{"driver": "adaptive", "config": {"root": "~/.nanogo/workspace"}}]
        }
      }
    ]
  }
}

Running Manual Tests

The manual tests exercise the full stack end-to-end against a real LLM via OpenRouter. They cover one test per completed phase in the order phases were delivered.

With make (recommended)

# Build the binary and run all manual tests in phase order:
make test

# Or run a single test by phase:
make test-1.9    # TEST-1.9  — real LLM round trip
make test-2.12   # TEST-2.12 — agent creates and reads a file
make test-4.9    # TEST-4.9  — memory persists across two sessions
make test-8.5    # TEST-8.5  — event kinds visible in log
make test-8.10   # TEST-8.10 — cost tracker records real turns
make test-9.8    # TEST-9.8  — evolve building blocks (sandbox, path guard, learnings)
make test-9.9    # TEST-9.9  — self-edit attack rejected by path guard
make test-12.20  # TEST-12.20 — adaptive experiment demo and inspect report
make test-13.24  # TEST-13.24 — lesson factory compile/review/approve/assign

make test will fail immediately if OPENROUTER_API_KEY is not set.

Without make

  1. Build the binary:

    go build -o /tmp/nanogo ./cmd/nanogo
  2. Set your API key:

    export OPENROUTER_API_KEY=sk-or-v1-...
  3. Write a config file (/tmp/nanogo-test-config.json):

    {
      "llm": {
        "driver": "openai",
        "config": {
          "base_url": "https://openrouter.ai/api/v1",
          "api_key_env": "OPENROUTER_API_KEY",
          "model": "anthropic/claude-haiku-4-5"
        }
      },
      "transports": [{"driver": "cli"}]
    }
  4. Run each test in order:

    TEST-1.9 — Real LLM round trip

    /tmp/nanogo --config /tmp/nanogo-test-config.json \
      --workspace /tmp/nanogo-workspace \
      --skills testdata/skills \
      -p "Reply with exactly: OK"
    # Pass: output contains OK

    TEST-2.12 — Agent performs file edit

    /tmp/nanogo --config /tmp/nanogo-test-config.json \
      --workspace /tmp/nanogo-workspace \
      --skills testdata/skills \
      -p "Create a file /tmp/nanogo-demo.txt containing exactly the word 'hello', then read it back and tell me its contents."
    cat /tmp/nanogo-demo.txt
    # Pass: file exists and contains "hello"

    TEST-4.9 — Memory persists across sessions

    /tmp/nanogo --config /tmp/nanogo-test-config.json \
      --workspace /tmp/nanogo-workspace \
      --skills testdata/skills \
      -p "Remember that my favorite programming language is Go."
    
    /tmp/nanogo --config /tmp/nanogo-test-config.json \
      --workspace /tmp/nanogo-workspace \
      --skills testdata/skills \
      -p "What is my favorite programming language?"
    # Pass: second response mentions Go

    TEST-8.5 — All event kinds visible

    /tmp/nanogo --config /tmp/nanogo-test-config.json \
      --workspace /tmp/nanogo-workspace \
      --skills testdata/skills \
      -p "Create a file /tmp/nanogo-event-test.txt with content 'y'"
    jq -r '.kind' /tmp/nanogo-workspace/log.jsonl | sort -u
    # Pass: includes turn.started, turn.token, tool.started, tool.result, turn.completed

    TEST-8.10 — Cost tracker records real turns

    /tmp/nanogo --config /tmp/nanogo-test-config.json \
      --workspace /tmp/nanogo-workspace \
      --skills testdata/skills \
      -p "Reply with OK"
    /tmp/nanogo --config /tmp/nanogo-test-config.json \
      --workspace /tmp/nanogo-workspace \
      cost
    # Pass: cost.jsonl exists or cost summary prints

    TEST-9.8 — Evolve building blocks

    go test -v -run "TestSandbox|TestPathGuard|TestLearnings|TestSynthesis" ./ext/evolve/...
    # Pass: all four tests green

    TEST-9.9 — Self-edit attack rejected

    go test -v -run "TestPathGuard|TestPathGuardLearningsEntry" ./ext/evolve/...
    # Pass: IsBlocked returns true for core/ and ext/evolve/ paths; rejection logged

    TEST-12.20 — Adaptive experiment smoke

    /tmp/nanogo --workspace /tmp/nanogo-workspace adaptive demo --child cross --subject science --topic magnets
    /tmp/nanogo --workspace /tmp/nanogo-workspace adaptive inspect --child cross --subject science --topic magnets
    # Pass: demo selects a winner, writes child patterns, and inspect prints top artifacts

    TEST-13.24 — Lesson factory workflow

    mkdir -p /tmp/nanogo-workspace/inbox/lessons
    cp ext/adaptive/domains/lessonfactory/testdata/magnets.md /tmp/nanogo-workspace/inbox/lessons/magnets.md
    /tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory compile --source /tmp/nanogo-workspace/inbox/lessons/magnets.md
    /tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory review --lesson latest
    /tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory approve --lesson latest
    /tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory assign --lesson latest --child cross
    # Pass: bundle, review, parent guide, child pathways, and assignment queue are written

Adaptive Experiment Engine

Phase 12 adds an extension-only adaptive experiment engine under ext/adaptive/. It stores artifacts and outcomes under memory/adaptive/, scores outcomes across mastery, retention, transfer, engagement, quality, parent rating, frustration, time, and cost, and writes parent-readable reports.

Manual smoke:

./nanogo --workspace /tmp/nanogo-workspace adaptive demo --child cross --subject science --topic magnets
./nanogo --workspace /tmp/nanogo-workspace adaptive inspect --child cross --subject science --topic magnets

The demo creates fake adaptive artifacts, records outcomes, selects a winner, writes memory/adaptive/child_patterns/<child-id>.md, and creates an inspect report under memory/adaptive/reports/experiments/.

Adaptive Lesson Factory

Phase 13 adds ext/adaptive/domains/lessonfactory/, an extension-only adaptive domain that compiles rough parent-authored markdown into deterministic lesson bundles under lessons/generated/<lesson-id>/.

Generated bundles include lesson.yaml, parent_guide.md, child_summary.md, per-child default/hands-on/remediation pathways, age/depth levels, activities, quick checks, rubrics, transfer questions, retention review, sources.md, and review.md. Assignment is blocked until parent approval is recorded.

CLI workflow:

/tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory compile --source /tmp/nanogo-workspace/inbox/lessons/magnets.md
/tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory review --lesson latest
/tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory approve --lesson latest
/tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory assign --lesson latest --child cross

The domain registers adaptive artifact kinds for lesson bundles, pathways, rubrics, and templates. It also exposes a lessonfactory tool source with parse, compile, review, package, assign, parent-review, child-outcome, and template-mutation operations.

Adaptive Tutor Runtime

Phase 14 adds ext/adaptive/domains/tutorruntime/, an extension-only adaptive domain for live tutoring sessions. It selects tutor policies from parent pins and archive outcomes, records turn evidence, grades answers deterministically, updates mastery, schedules retention reviews, recommends remediation, tracks misconceptions through profile approval, mutates policy versions, and writes parent-readable session summaries.

Initial tutor policies live under ext/adaptive/domains/tutorruntime/policies/: socratic-guide, worked-example-first, hands-on-remediation, visual-analogy, story-explanation, retrieval-practice, challenge-mode, and gentle-coach.

Runtime files live under:

memory/adaptive/tutorruntime/sessions.jsonl
memory/adaptive/tutorruntime/turns.jsonl
memory/adaptive/tutorruntime/pending_reviews.jsonl
memory/adaptive/tutorruntime/strategy_switches.jsonl
memory/adaptive/tutorruntime/policy_activations.jsonl
memory/adaptive/artifacts/tutor_policies/
memory/adaptive/reports/tutorruntime/

Manual smoke:

make test-14.27

The tutorruntime tool source exposes policy selection, turn recording, answer grading, mastery update, misconception detection, remediation recommendation, review scheduling, and session summary tools.

Phase 19.7 and 19.8 Interface Surface

Phase 19.7 adds modules/gateway as the shared interface-facing service. The gateway owns operator operations for chat, sessions, skills, tools, costs, and normalized events while keeping core/ limited to stable agent, session, event, tool, and LLM contracts.

Phase 19.8 expands the local Bubble Tea TUI into the main terminal operator console. It adds slash commands for current-session costs, session-local OpenRouter model switching, STT/TTS toggles, and xAI realtime speech controls.

Phase 19.9 adds a contract-backed local help system. Help content lives in docs/help, domain behavior lives in modules/help, filesystem loading lives in ext/help/files, and transports access help through modules/gateway.

Build and run the local operator console:

make build
OPENROUTER_API_KEY=sk-or-v1-... \
/tmp/nanogo --config /tmp/nanogo-test-config.json \
  --workspace /tmp/nanogo-workspace \
  --skills testdata/skills \
  tui

Useful TUI keys:

Key Action
Tab / Shift+Tab Move between Chat, Sessions, Skills, Tools, Costs, and Events panes
Enter Send chat input, select the highlighted session, or run the highlighted skill
Up / Down Scroll Chat history, or move selection inside panes that support selection
PageUp / PageDown Scroll Chat history by a page
Home / End Jump to the oldest or newest visible Chat history
n Create a session in the Sessions pane
d Delete the highlighted non-current session in the Sessions pane
r Refresh gateway data
Esc / Ctrl+C Quit

Supported TUI slash commands from the chat input:

Command Behavior
/cost Show the current session's cost summary
/model current Show the current session model
/model list List available OpenRouter models, cached for 24 hours
/model use <model-id> Switch only the active TUI session to the selected model
/model flush Clear the model catalog cache so the next list refetches
/stt on / /stt off Enable or disable speech-to-text state for the active TUI voice session
/tts on / /tts off Enable or disable text-to-speech state for the active TUI voice session
/xai on Start the xAI realtime speech control path when XAI_API_KEY is configured
/xai status Show xAI provider, model, session, and connection status
/xai off Stop the xAI realtime speech session and release resources
/help Open contextual help suggestions without sending a chat message
/help <query> Search local help topics
/help topic <topic-id> Open one structured help topic
/help validate Validate the configured help pack

Phase 19.9 Help System

Use the built-in local help pack from the CLI:

/tmp/nanogo help
/tmp/nanogo help search gateway
/tmp/nanogo help tools.contracts
/tmp/nanogo help validate

The same help service is available through gateway operations:

{"method":"help.search","params":{"query":"voice privacy","limit":5}}
{"method":"help.topic","params":{"id":"voice.privacy"}}
{"method":"help.suggest","params":{"interface":"tui.chat","limit":5}}
{"method":"help.validate"}

OpenAI-compatible clients should use POST /nanogo/v1/operations for help operations. Phase 19.9 intentionally does not add a dedicated help GET route and does not inject help as an implicit model tool into /v1/chat/completions.

Help topics are deterministic local content. Normal help lookup does not call an LLM, invoke tools, use embeddings, read vector stores, or write session history. The checked-in pack can be validated with:

go test ./modules/help/... ./ext/help/files/... ./modules/gateway ./ext/transport/tui ./ext/transport/gatewayws ./ext/transport/openaiapi ./cmd/nanogo
make test-19.9

Non-interactive TUI smoke against OpenRouter:

OPENROUTER_API_KEY=sk-or-v1-... make test-19.8

OpenAI-compatible HTTP API:

export NANOGO_GATEWAY_TOKEN=local-secret
/tmp/nanogo --config /tmp/nanogo-test-config.json \
  --workspace /tmp/nanogo-workspace \
  --skills testdata/skills \
  openaiapi --addr :8081

It exposes GET /healthz, GET /v1/models, POST /v1/chat/completions, and nanogo control endpoints under /nanogo/v1/.

Gateway WebSocket:

export NANOGO_GATEWAY_TOKEN=local-secret
/tmp/nanogo --config /tmp/nanogo-test-config.json \
  --workspace /tmp/nanogo-workspace \
  --skills testdata/skills \
  gatewayws --addr :8082 --path /gateway

The WebSocket adapter uses OpenClaw-style JSON text envelopes with a first connect request, v1 protocol negotiation, bearer auth, req/res frames, and normalized event frames. Client-supplied OpenAI tools are rejected by the HTTP compatibility layer; nanogo runtime tools remain server-side policy.

Realtime Voice Extension

Phase 15 adds ext/voice/, an extension-only backend voice session layer that can later be consumed by the web UI. The internal contract is modeled around OpenAI Realtime-style events so provider adapters can be swapped without changing the session manager.

The first concrete provider target is xAI Grok Voice using:

wss://api.x.ai/v1/realtime?model=grok-voice-think-fast-1.0

Configuration uses XAI_API_KEY and XAI_REALTIME_MODEL. The xAI account must have voice/API credits enabled; buy credits from xAI before running the smoke or live voice commands. Deepgram Voice Agent is named as a planned second adapter using DEEPGRAM_API_KEY, with automated Deepgram tests deferred until requested.

The backend API supports voice session start, audio append/commit/clear, text-only turns, response creation, event streaming, transcript/raw event persistence under memory/voice/, and clean session close. The provider WebSocket layer uses github.com/coder/websocket.

Build and run a text-only xAI smoke test:

go build -o /tmp/nanogo ./cmd/nanogo
export XAI_API_KEY=your-xai-key-here
export XAI_REALTIME_MODEL=grok-voice-think-fast-1.0
/tmp/nanogo --workspace /tmp/nanogo-workspace voice smoke \
  --provider xai \
  --child cross \
  --text "Say hello in one short sentence."

Run the Makefile xAI smoke tests:

make test-15.17   # text-only realtime voice
make test-15.18   # raw PCM file input/output

Evaluate local microphone/speaker plumbing with github.com/gen2brain/malgo:

go build -tags malgo -o /tmp/nanogo ./cmd/nanogo
/tmp/nanogo --workspace /tmp/nanogo-workspace voice smoke \
  --provider xai \
  --child cross \
  --mic \
  --speaker

make test-15.19

malgo is optional and isolated behind the malgo build tag because it uses cgo/miniaudio and may require local audio permissions. The backend voice session API does not depend on malgo, so browser-based audio can replace local device handling in a later phase.

Phase 15.5 — live hands-free voice

Phase 15.5 adds a live local mic/speaker loop for command-line use:

go build -tags malgo -o /tmp/nanogo ./cmd/nanogo
export XAI_API_KEY=your-xai-key-here
export XAI_REALTIME_MODEL=grok-voice-think-fast-1.0
/tmp/nanogo --workspace /tmp/nanogo-workspace voice live \
  --provider xai \
  --child cross

You can also use the Makefile target:

make voice-live

The live loop streams MacBook microphone audio to xAI, relies on xAI server_vad to detect spoken turns, and plays response audio through local speakers. macOS may prompt for microphone permission. Headphones are recommended to reduce speaker-to-microphone feedback.

By default, live sessions persist normalized events and transcripts under memory/voice/, but not raw microphone or speaker PCM. Raw PCM is saved only when explicitly requested:

/tmp/nanogo --workspace /tmp/nanogo-workspace voice live \
  --provider xai \
  --child cross \
  --save-capture-pcm /tmp/nanogo-workspace/voice/capture.pcm \
  --save-playback-pcm /tmp/nanogo-workspace/voice/playback.pcm
make voice-live-debug

Browser Micro-Lessons With Physical Skill Capture

Phase 22 v2 adds the contracts needed for short browser micro-lessons where a child can practice a physical skill, such as a beginner yo-yo throw, and advance from deterministic evidence. For physical_skill objectives, a visual physical-performance pass can advance the lesson when that is the only configured evidence. Mixed, deep, and transfer objectives still require their configured reasoning, reflection, transfer, or parent-confirmation evidence.

Parent-facing benefits:

  • Rough topics like "beginner yo-yo tricks" can produce curated source notes and a chain of small browser micro-lessons.
  • Physical activities include child-facing safety setup before capture starts.
  • Parent guide output discloses video segment provenance and flags model-inferred segments for verification.
  • Captured clips stay local by default; persisted records use metadata, hashes, tombstones, and privacy-safe references.

Technical usage:

go build -o /tmp/nanogo ./cmd/nanogo
/tmp/nanogo --workspace /tmp/nanogo-workspace lessonfactory research \
  --driver fake \
  --topic "beginner yo-yo tricks" \
  --child-age 7 \
  --skill-type physical

Manual Phase 22 smoke targets:

make test-22.1   # lesson_research sources.md smoke; requires OPENROUTER_API_KEY for openrouter driver
make test-22.3   # headed player smoke when agent-browser is installed
VISION_MODEL=openrouter/model-id make test-22.5

test-22.5 requires both OPENROUTER_API_KEY and VISION_MODEL. The deterministic local gate remains:

make verify-local

About

Go based framework for building AI tutoring systems

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors