yo is a small TypeScript coding-agent harness for learning how model/tool loops work. It lets a model inspect one approved workspace and, for one narrow case, propose an exact patch that the trusted terminal must approve before writing. It returns an evidence-backed answer without giving the model direct filesystem, process, credential, or network access.
The project takes architectural inspiration from pi, especially its separation between the agent loop, model transport, tool execution, and interactive presentation. yo intentionally keeps a smaller boundary: one Codex transport, one bounded single-agent loop, three read-only tools, and one approval-gated exact-patch proposal with local validation and permission checks.
Milestone 1 established the original bounded read-only harness. Its one-shot
yo ask command has since been retired in favor of the single agent workflow:
yo. The chat supports an ephemeral multi-turn conversation through
ChatGPT Plus OAuth with live model/tool status and safe final-answer delivery.
Milestone 3 is complete: the model may propose an exact replacement patch for
one existing workspace file, but the harness displays the complete diff and
writes only after explicit terminal approval.
See the current project state →
Milestone 4 leaves 10.1–10.2 provide session-local run records, a live event
feed, and result cards with errors and patch-approval state. Leaf 10.3 adds
between-turn inspection through /runs and /run N; this increment and Milestone 4 closure are verified. The later
sequence is cancellation, explicit rerun, then Milestone 5 test/build validation.
General shell execution, persistence, MCP, and subagents are not implemented.
Read the implemented observation specification →
- Node.js
22.18.0or newer - npm
- A ChatGPT Plus account for real Codex requests
npm ci
npm run buildNo OPENAI_API_KEY is used. Authentication is completed through the OpenAI website:
node dist/cli.js login
node dist/cli.js auth statusThe resulting OAuth credential is trusted CLI state stored at ~/.yo/auth.json. It is not exposed to the model or available through a tool.
Start an in-memory interactive chat in the current directory:
node dist/cli.jsOverride the workspace or default model when needed:
node dist/cli.js --cwd /path/to/workspace --model <name>Use the exact /exit command or EOF to leave chat. The transcript is discarded
when the process exits.
Between tasks, use /runs to list current-session runs and /run 1 (or another
positive run number) to inspect a settled run. Inspection prints its ordered
events, retained answer, and result, then returns to yo>. The retained answer
is a sanitized preview limited to 16,000 characters; truncation or a missing
answer is explicitly labelled. The original live answer is still delivered in full.
/runs and /run are reserved local command tokens, including malformed
arguments. Run numbers use positive decimal digits without leading zeros.
Surrounding whitespace is accepted for these commands. Other text, including
unrelated slash commands, is submitted unchanged as a task. Inspection creates
no model request or run, changes no conversation context, and grants no patch
consent. At the approval prompt, command-like input is a denial under the
existing approval vocabulary. Inspection also works in plain non-TTY output.
History is local to this chat process; no paging, persistence, cancellation,
or rerun is provided.
When the model proposes a patch, yo displays the full diff and asks Apply this patch? [y/N]. Only y or yes approves; any other input, EOF, missing
interactive input, or non-TTY execution denies it without changing the
workspace. A proposal can target only one existing regular text file and is
revalidated immediately before the trusted atomic replacement.
Remove the stored credential:
node dist/cli.js logoutThe model-visible registry contains exactly:
list_filessearch_coderead_filepropose_patch
The harness treats every model-proposed tool name and argument object as untrusted. Application code performs closed lookup, strict schema validation, workspace permission checks, bounded execution, and structured result creation. Every requested tool call receives exactly one result, including denial, invalid arguments, timeout, or execution failure.
propose_patch is untrusted proposal data, not a write primitive. The model
cannot directly invoke writes, shell commands, processes, arbitrary network
requests, credential access, or environment-variable reads. Trusted network
access is limited to ChatGPT OAuth and the Codex model transport.
| Path | Role |
|---|---|
src/runtime/ |
Provider-neutral contracts, bounded agent loop, permissions, read tools, approval-gated patches, and workspace enforcement |
src/provider/ |
ChatGPT Codex Responses transport and provider-format conversion |
src/auth/ |
OAuth login, credential contracts, refresh, and secure file storage |
src/cli-app.ts |
Injectable CLI parsing, command composition, output, and evidence reporting |
src/cli.ts |
Thin Node process entrypoint |
AGENTS.md— collaboration rules and the required workflow for changing the repository.PRD.md— stable product boundaries and links to current OpenSpec requirements.IMPLEMENTATION_PLAN.md— current milestone state, permanent constraints, and links to detailed plans.openspec/changes/— proposed changes, design decisions, task lists, requirement deltas, and archived completed changes.examples/— repeatable faux demonstration and captured terminal output.openspec/specs/— current implemented requirements for harness, chat, OAuth/transport, patches, and observation.CONTRIBUTING.md— pinned OpenSpec setup, Codex skills, source ownership, and bounded change workflow.
These files have separate roles. Requirements and milestone state should not be copied into this README beyond a short orientation because duplicated details become stale.
npm run spec:check
npm test
npm run build
npm run format:check
git diff --checkTests use faux transports, injected HTTP, temporary credential stores, and fixture workspaces so normal verification does not require real credentials, network requests, or paid model calls.
Run node examples/run-observation-demo.ts for a repeatable demonstration
of empty history, retained results, a transport failure, controlled model delay,
explicit patch approval, and continued chat. It uses a faux transport and a
temporary file; no OAuth or network is needed. See the
captured transcript. The demo makes
progress lines durable for readability; physical TTY behavior remains unverified.