Everyone can call an agent framework. Almost nobody can rebuild what's under it — and then something breaks: a tool call loops forever, the token bill explodes, and the framework's abstraction is suddenly the thing standing between you and the fix.
Here's the uncomfortable secret: a coding agent — the thing under Claude Code, Cursor, and every "AI engineer" product — is one loop. Ask the model what to do, run the tools it asks for, feed the results back, repeat until it says done. This post builds that loop in plain Node, zero dependencies, and shows the two traps almost every first implementation falls into.
The loop
Any model — real or mocked — is just a function of { messages, tools } that returns either { toolCalls: [...] } or { done: true, text }. Everything plugs into this:
export async function runAgent({ task, model, tools, maxSteps = 10, log = () => {} }) {






