Principles
tinyloop is designed to keep the agent understandable.
The point is not to look like a complete coding assistant. The point is to make the agent loop, tool boundary, event stream, and UI reducer legible enough that a learner can change one part without losing the plot.
Principles in code
The rules are visible in the files.
Instead of abstract values, each principle maps to a place a learner can open and inspect.
- 01Readable before powerful
Agent.runOneUserTurnis a direct model/tool loop with a small max-turn guard.packages/agent/src/agent.ts - 02Events are the public surface
AgentEventPayloadnames turn, message, tool, progress, completion, and failure moments.packages/agent/src/event.ts - 03Drivers keep the UI honest
SessionDriverexposes onlysendUserMessageandevents.packages/tui/src/session/session-driver.ts - 04State beats side effects
reduceSessionEventis where streamed progress becomes transcript state.packages/tui/src/state/reduce-session-event.ts
Readable before powerful.
tinyloop chooses a small model/tool loop over a feature-rich orchestration layer. That makes the cost visible: learners can see where conversation state is carried with previous_response_id, where tool calls are detected, and where the loop stops.
Contracts before convenience.
A tool is not a vague capability. It has a strict JSON schema, a handler, a string output for the model, and optional details for the UI. This keeps the model boundary explicit.
Events before components.
The UI does not ask tools what happened. It consumes UiSessionEvent values. This is whycreateDemoSessionDriver can teach the same interface without an API key.
Honesty before polish.
tinyloop names what it lacks: approvals, cancellation, persistence, and sandboxing. Those missing layers are not swept under the rug; they become the next lessons.
A principle in code
The session contract is intentionally tiny.
SessionDriver is the best example of tinyloop's design taste. It gives the UI exactly two moves: send user text and listen for events.
export type SessionDriver = {
sendUserMessage(text: string): Promise<void>
events(): AsyncIterable<UiSessionEvent>
}Learning exercises
Change one thing, then trace the consequences.
A good tinyloop exercise should make you predict the event flow before writing code. Then you can verify the prediction in the reducer or the transcript.
Add a tool under packages/agent/src/tools. Then ask: what schema does the model see, and what should the tool output say back to the model?
Add an event to AgentEventPayload. Then ask: does the TUI need a normalized event name or a new reducer branch?
Change ToolCard rendering. Then ask: did you improve the transcript without making the UI call agent internals?
North star
The right abstraction is the one a learner can inspect.
tinyloop should grow by making the loop clearer: better auth, better installation, stronger tool boundaries, more explicit events. If a feature hides the model/tool/session/UI relationship, it works against the project.