JournalAI Engineering

Field guide / 6

nano-pi shows the coding-agent loop is the small part

PI from Scratch reduces a working TypeScript coding agent to a readable loop, making it clear which parts are mechanism and which belong to production policy.

Sep 2, 20266By ISH Team
nano-pi shows the coding-agent loop is the small part
Advertisement

nano-pi shows the coding-agent loop is the small part

PI from Scratch reduces a working coding agent to five TypeScript files. Its nano-pi program streams a model response, reads and changes files, runs shell commands, saves a session, and keeps calling tools until the model stops.

The project is a lesson, not a production starter kit. It removes much of pi's engineering detail so readers can follow data through the system. The result makes it unusually easy to see which behavior belongs to the agent loop and which protections must come from the application around it.

Five files expose the mechanism

The PI from Scratch README divides nano-pi into an LLM adapter, agent loop, tool registry, terminal interface, and CLI assembly layer. Its accompanying website reveals the source as the lesson progresses and includes a trace debugger built from pre-generated data. Browsing the website does not send model requests.

The LLM layer converts nano-pi's plain JSON context into OpenAI-compatible chat-completions messages. It parses streamed SSE chunks into four event types: text, tool call, completion, and error. The terminal interface therefore does not need to understand provider responses. The tools do not know that a model exists. Only the CLI connects every component.

The agent loop is a literal while (true). It sends the current context to the model, collects tool calls, appends the assistant message, executes calls serially, records their results, and starts another pass. When no tool call arrives, control returns to the user. At this level, an agent is a conversation that can act, observe the result, and continue.

Several details keep that conversation internally consistent. An AbortSignal travels from the terminal to the network request and shell process. When a turn is interrupted, the code drops unexecuted calls or supplies aborted results, avoiding a later request with unmatched call-result pairs. If a response ends at max_tokens, nano-pi refuses to execute possibly incomplete arguments. It reports the truncation to the model and lets the model retry.

Session state is similarly plain. The CLI appends each new message as JSON to ~/.nanopi/session.jsonl and reloads valid lines when it starts. A partially written line is skipped instead of discarding the entire history. This design is easy to inspect and recover manually, although it does not provide branching, transactional writes, or multi-process coordination. The project's loop tutorial walks through the same data path from user input to model event, tool result, and persisted context.

Four tools provide most of the power

Nano-pi's tool file defines read_file, write_file, edit, and run_bash. Read returns text. Write creates parent directories and overwrites a file. Edit requires the old string to occur exactly once before replacement. Bash runs a command for up to 30 seconds. Outputs longer than 200 lines retain their tail while the complete text is written to a temporary file.

Those operations let a model inspect a project, modify it, and run checks. They also reveal how much the harness shapes behavior. Change the edit contract, command timeout, output policy, or system prompt and the same model will work differently.

We checked commit 599d0ba646c5e737881ca1a576c8009ee065cffd, dated August 18, 2026. npm test passed all 25 tests in the repository, and npm run build completed successfully. The tests cover message conversion, streamed tool calls, loop behavior, the four tools, session persistence, and terminal handling. They validate the teaching implementation. They do not establish a security boundary.

Production policy is outside the lesson

The source notes that tool arguments go directly to execute without validation against the supplied JSON Schema. File tools accept paths supplied by the model. Bash executes a command string in the current process environment. Nano-pi has no built-in path confinement, approval prompt, command allowlist, credential filter, sandbox, or hard maximum number of agent steps. Its comments identify argument validation and pi's stop callback as production concerns omitted from this version.

That is the project's stated scope, but it matters to anyone reusing the code. A model with unrestricted file and shell tools receives the authority of the hosting process. Our least-privilege checklist covers the next layer: constrain the working directory, credentials, network access, and destructive operations before the model can act.

Context management shows the same teaching tradeoff. Nano-pi starts compaction at 50 messages. It asks the model to summarize all but the latest 20, then replaces the older history with that summary. The lesson intentionally uses message count instead of token estimation and omits pi's more precise cut-point handling. A production implementation needs to measure actual context use, preserve decisions, and detect when a summary drops an obligation. Agentiloop's recoverable compaction design offers one defensive approach.

The summarization request is itself a model call, so interruption and failure handling matter. Nano-pi checks for an aborted signal before compaction and keeps the original messages if the summary fails or comes back empty. That fallback is modest but important: losing the summary should not also destroy the history it was meant to replace.

Use it as a lab, not a drop-in worker

The project requires Node.js 22 or newer and an OpenAI-compatible API. Its README gives this local setup:

npm install
export NANOPI_API_KEY=your-api-key
npm run dev

NANOPI_MODEL selects the model, while NANOPI_BASE_URL changes the endpoint. Start in a disposable repository or container with a low-privilege key. Keep real credentials out of the child process and record every tool call and result.

Then add controls in a sequence you can test. Validate tool inputs first. Resolve every path against an explicit workspace root and reject escapes. Put shell execution behind approval or enforceable policy. Add a step budget and a clear stop condition. Preserve a clean checkpoint so edits can be inspected and reversed. An AGENTS.md file can guide model behavior, but it cannot enforce any of these boundaries.

Nano-pi makes one architectural fact concrete: model interaction, tool dispatch, and observation fit in a small, testable loop. Permissions, recovery, and operational control are the product engineering around it.

#nano-pi#coding agents#TypeScript#agent loop#open source
Advertisement

Keep reading

Related stories

Browse the archive