diff --git a/README.md b/README.md new file mode 100644 index 0000000..a139c31 --- /dev/null +++ b/README.md @@ -0,0 +1,186 @@ +# Coding Mentor Agent + +[![Node.js](https://img.shields.io/badge/Node.js-24.x-3c873a?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178c6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org) +[![React](https://img.shields.io/badge/React-19-149eca?style=flat-square&logo=react&logoColor=white)](https://react.dev) +[![Docker sandbox](https://img.shields.io/badge/Docker-Python_sandbox-2496ed?style=flat-square&logo=docker&logoColor=white)](https://www.docker.com) + +A local-first AI coding mentor for a Chinese-language Python course. The app combines a React learning interface, a TypeScript HTTP/SSE backend, a Practical Python knowledge base, adaptive diagnostics, progress tracking, and a Docker-backed Python sandbox. + +[Features](#features) • [Getting Started](#getting-started) • [Configuration](#configuration) • [Architecture](#architecture) • [Development](#development) • [Troubleshooting](#troubleshooting) + +## Features + +- **Tutor chat for Python learners**: explains concepts, reviews code, and guides debugging without bypassing the learning process. +- **Course-grounded knowledge base**: ships with a curated Practical Python catalog containing 9 units, 34 concepts, 25 exercises, mistake tags, and prerequisite relations. +- **Adaptive diagnostic flow**: places learners by concept readiness before unlocking structured practice. +- **Structured practice and project workflows**: selects exercises, creates practice contracts, reviews submissions, and records learning evidence. +- **Isolated Python execution**: runs student code and public tests in a Docker container with no network, dropped capabilities, memory limits, PID limits, and output truncation. +- **Local learning state**: stores sessions, mastery, evidence, practice reviews, and audit summaries in a local SQLite database under `.app` by default. +- **Tool policy and auditing**: gates model-visible tools by intent, caller, capability, and risk level, with redaction for secrets and local paths. + +> [!IMPORTANT] +> Model-backed tutoring requires AI provider settings. Without `AI_PROVIDER` and `AI_API_KEY`, model-dependent chat turns return `MODEL_UNAVAILABLE`, although local catalog and diagnostic data can still initialize. + +## Getting Started + +### Prerequisites + +- Node.js 24.x recommended. The Docker Compose setup uses `node:24.14.0-bookworm-slim`. +- npm. +- Docker Desktop or Docker Engine for code execution and grading. +- Python 3 if you plan to run the Python-based student-loop test harness. +- An AI provider API key for tutor responses. + +### Run locally + +```bash +npm install +cp .env.example .env +docker build -t coding-mentor-python-runner:0.1.0 -f sandbox-runner.Dockerfile . +npm start +``` + +Open [http://127.0.0.1:3000](http://127.0.0.1:3000). + +On Windows PowerShell, use this instead of `cp`: + +```powershell +Copy-Item .env.example .env +``` + +Then edit `.env` with your AI settings: + +```ini +AI_PROVIDER=openai +AI_API=openai-responses +AI_BASE_URL=https://api.openai.com/v1 +AI_MODEL=gpt-5.5 +AI_API_KEY=your-api-key +``` + +### Run with Docker Compose + +```bash +docker compose up --build +``` + +The Compose setup starts the app at [http://127.0.0.1:3000](http://127.0.0.1:3000), starts a sandbox service on the internal Compose network, and mounts the Docker socket so the sandbox service can launch isolated runner containers. + +## Configuration + +The app loads `.env` and `.env.local` from the repository root. Useful settings: + +| Variable | Default | Purpose | +| --- | --- | --- | +| `PORT` | `3000` | Local HTTP server port. | +| `APP_DATA_DIR` | `.app` | SQLite database, Pi session files, backups, and test artifacts. | +| `COURSE_KB_ROOT` | `kb/python-course-kb-practical-python/wiki` | Course catalog and source content root. | +| `COURSE_KB_VERSION` | `kb-local` | Version label included in tutor prompts and catalog identity. | +| `ENABLED_BATCH` | `full` | Tool allowlist: `batch-a`, `batch-b`, `batch-c`, or `full`. | +| `SANDBOX_IMAGE` | `coding-mentor-python-runner:0.1.0` | Docker image used for Python execution. | +| `SANDBOX_SERVICE_URL` | empty | Optional HTTP sandbox service URL. Empty uses local Docker directly. | +| `SANDBOX_TIMEOUT_MS` | `3000` | Max runtime for simple Python execution. | +| `SANDBOX_PYTEST_TIMEOUT_MS` | `8000` | Max runtime for pytest grading. | +| `SANDBOX_MEMORY_MB` | `128` | Container memory limit. | +| `AI_PROVIDER` | empty | Provider name used by `@earendil-works/pi-ai`. | +| `AI_API` | empty | Set to `openai-responses` for the built-in Responses-compatible adapter. | +| `AI_BASE_URL` | `https://api.openai.com/v1` | HTTPS model API base URL. | +| `AI_MODEL` | `gpt-5.5` | Model id passed to the provider adapter. | +| `AI_API_KEY` | empty | API key for tutor responses. | + +## Architecture + +```mermaid +flowchart LR + UI["React + CodeMirror UI"] --> API["TypeScript HTTP/SSE server"] + API --> DB["Local SQLite progress DB"] + API --> KB["Course knowledge base"] + API --> Tutor["Pi AI tutor session"] + API --> Gate["Tool policy gate"] + Gate --> Sandbox["Docker Python sandbox"] + Gate --> Tools["KB, progress, exercise, project tools"] +``` + +Key paths: + +| Path | Purpose | +| --- | --- | +| `src/frontend` | React app, CodeMirror editor, SSE state handling, safe Markdown rendering. | +| `src/server` | HTTP API, sessions, diagnostics, progress policy, recommendations, project flow, local data management. | +| `src/agent` | Tutor prompt construction and Pi AI / Pi coding-agent integration. | +| `src/tools` | Course tools, schemas, envelopes, tool registry, and capability policy. | +| `src/sandbox` | Docker runner and optional HTTP sandbox service. | +| `src/db` | SQLite schema, migrations, bootstrap, and validators. | +| `src/security` | Redaction, path validation, ids, and in-memory rate limits. | +| `kb/python-course-kb-practical-python` | Bundled Practical Python source material, summaries, concepts, exercises, and catalog manifest. | +| `tests` | Vitest unit/integration coverage plus Playwright and Python student-loop harnesses. | + +## API Surface + +The backend serves the React app and exposes local JSON/SSE endpoints: + +- `POST /api/sessions` creates or resumes a local tutor session. +- `GET /api/sessions/:id/events` streams assistant messages and tool events over SSE. +- `GET /api/sessions/:id/snapshot` returns the current conversation, practice, progress, and tutor state. +- `POST /api/sessions/:id/messages` sends a learner message and optional code. +- `GET /api/diagnostics/next` and `POST /api/diagnostics/:id/answers` drive the adaptive diagnostic flow. +- `GET /api/progress/me` returns local mastery and evidence summaries. +- `POST /api/code/run` executes Python through the gated sandbox. +- `GET /api/exercises/next` and `POST /api/exercises/:id/submissions` handle structured practice. +- `GET /api/data/export`, `POST /api/data/delete`, and `POST /api/data/backups` manage local learning data. + +## Development + +Common scripts: + +| Command | Description | +| --- | --- | +| `npm start` | Build the Vite client and run the local server on `127.0.0.1`. | +| `npm run dev` | Same server entry point as `npm start`. | +| `npm run dev:frontend` | Start Vite for frontend-only iteration. | +| `npm run build` | Type-check and build the client into `dist/client`. | +| `npm run lint` | Run TypeScript type-checking with `tsc --noEmit`. | +| `npm test` | Run Vitest tests under `tests/**/*.test.ts`. | +| `npm run start:sandbox` | Start the optional HTTP sandbox service. | +| `npm run test:student-loop:realistic` | Run the realistic Python student-loop discovery harness. | +| `npm run test:student-loop:release` | Run the release-profile student-loop harness. | +| `npm run test:student-loop:security` | Run the security-profile student-loop harness. | + +For UI-oriented checks, Playwright is configured to build the app and preview it at `http://127.0.0.1:4173`. + +## Local Data + +By default, all learner state lives under `.app`: + +- `progress.db` stores profile, sessions, mastery, exercises, diagnostics, practice reviews, projects, audit logs, and security events. +- `pi-sessions` stores Pi agent session files. +- `student-loop` stores e2e harness artifacts. + +Use `GET /api/data/export` for a redacted JSON export, `POST /api/data/backups` for an encrypted database backup, and `POST /api/data/delete` with `{"confirm":"DELETE_LOCAL_LEARNING_DATA"}` to clear local learner state. + +## Troubleshooting + +**`MODEL_UNAVAILABLE`** + +Set `AI_PROVIDER`, `AI_MODEL`, and `AI_API_KEY` in `.env`. If using the built-in OpenAI-compatible Responses adapter, also set `AI_API=openai-responses`. + +**Docker sandbox errors** + +Make sure Docker is running and the runner image exists: + +```bash +docker build -t coding-mentor-python-runner:0.1.0 -f sandbox-runner.Dockerfile . +``` + +**Port already in use** + +Change `PORT` in `.env`, then restart the server. + +**Practice is locked** + +Complete the initial diagnostic first. The backend intentionally blocks structured practice until it has enough placement evidence. + +**Reset local progress** + +Delete `.app` while the server is stopped, or use the data deletion endpoint with the explicit confirmation token shown above.