A small Python learning project for building an agent harness from first principles.
- Calls a Groq-hosted model through Groq's OpenAI-compatible API using the OpenAI Python SDK.
- Uses GPT-OSS 120B as the primary agent model and Qwen 3 32B as a bounded JSON fallback.
- Supports plain chat mode with message history.
- Supports agent mode with JSON actions and tool calls.
- Validates model-produced agent actions with Pydantic schemas before execution.
- Includes a minimal coding-agent tool registry with command execution, patch editing, project inspection, user questions, and todo tracking.
python3 -m pip install -r requirements.txt
cp .env.example .envThen add your Groq API key to .env.
GROQ_MODEL selects the primary model. GROQ_FALLBACK_MODEL selects the fallback used when Groq rejects the primary model's generated tool/JSON output or when the bounded GPT action-repair attempt still fails.
Command execution uses the restricted container backend by default. Install and start Docker, then build the local sandbox image:
docker build -f Dockerfile.sandbox -t agent-harness-sandbox:latest .The harness fails closed when Docker or the image is unavailable. The deprecated macOS backend remains available only as an explicit development fallback:
AGENT_HARNESS_SANDBOX_BACKEND=macos python3 main.pyAgent mode opens the full-screen supervised TUI:
python3 main.pyThe conversation uses the full terminal width, with a soft-wrapped > editor
anchored at the bottom. It grows up to five lines while typing. Submitted
prompts use a lighter neutral surface with continuation lines aligned after the
marker, while
each tool action renders as a compact nested trace showing the tool, target,
result, and detailed output only when it matters. Todo updates are repeated as
checklist snapshots so completed work remains visible as the plan advances.
Any run that uses project tools requires that checklist before other tools can
run, without trying to infer intent from wording, grammar, or typos. It also
requires one final checklist refresh after the last tool before showing the
answer. Repeated identical inspections are skipped while the project is
unchanged, and similar filenames require confirmation before mutation.
Transient activity such as thinking, tool execution, pausing, and rate-limit
waiting appears inline above the input instead of in the header. Terminals
smaller than 60×20 show a resize message.
Enter: send the current instruction or approval responseEsc: request a pause at the next safe agent boundaryCtrl+E: return focus to the input linePageUp/PageDown: navigate the transcriptCtrl+Nor/new: start a new session and clear retained context
Use the legacy scrolling interface when needed:
python3 main.py --classicChat mode:
python3 main.py --mode chatList the files in this project.
Read main.py and summarize what it does.
Search the project for run_agent.
Run git status.
Permissions are evaluated per invocation:
- Known read-only command shapes run automatically.
- Unknown, executable, destructive, write, and complex shell actions ask.
- Catastrophic commands and sensitive-file access are always denied.
- Approval offers once, deny, or a narrowly defined current-run scope when a tested matcher can generate one safely.
- External reads use current-run directory grants; sensitive paths remain denied inside approved directories.
- Commands run as a non-root user in a read-only container with no network, no Linux capabilities, process/CPU/memory limits, bounded output, and a writable project mount.
- Sensitive project files are replaced by unreadable bind-mount masks; host secrets and home directories are not mounted or passed through the environment.
docs/PROJECT_STEPS.md: running build-history ledger for major harness milestones.docs/AGENT_HARNESS_GUIDE.html: current architecture guide.docs/CURRENT_IMPLEMENTATION_GUIDE.md: detailed walkthrough of the current code organization and runtime flow.docs/ENGINEERING_NOTES.md: deep explanations of validation, repair, and other engineering concepts.docs/ONE_DAY_FINISH_PLAN.md: frozen one-day plan for completing the learning project.
Run a tool directly without asking the model to choose it:
python3 main.py --tool list_files --tool-input .
python3 main.py --tool search_files --tool-input run_agent
python3 main.py --tool read_file --tool-input main.py
python3 main.py --tool run_command --tool-input "git status --short"