A complete LLM agent in ~30 lines of TypeScript. An LLM in a loop, with one tool. That's it. That's an agent.
Inspired by Thomas Ptacek's You Should Write An Agent on the Fly.io blog — go read it, it's great.
Open main.ts and hit Run. The agent will fetch the Hacker News front page and tell you the top story:
tool: fetch_url({"url":"https://news.ycombinator.com/"})
>>> The top story on Hacker News right now is titled "..."
You didn't write a loop to fetch URLs. You gave the model permission to fetch, and it figured out the rest.
Rendering mermaid diagram...
The whole thing:
- Load prior
messagesfrom SQLite (or start fresh on first run). - Append a new user message and call the LLM with the messages and a list of tools.
- If the response has
tool_calls, run them, save results to SQLite, loop. - Otherwise, print the answer and exit.
The LLM is stateless. The conversation is an illusion we cast on ourselves by replaying the whole transcript every turn. SQLite makes the illusion persist across runs.
Messages are scoped to conversations — isolated threads stored in SQLite.
createConversation(title?)— starts a new conversation, returns its id.getLatestConversation()— returns the most recently updated conversation id (ornull).listConversations()— lists all conversations, most recent first.loadMessages(conversationId)— reads a conversation's message history.saveMessages(conversationId, msgs)— appends messages to a conversation.clearMessages(conversationId)— deletes a conversation and its messages.
Toggle START_NEW in main.ts to start a fresh conversation or resume the latest one.
- Add a tool. Append another entry to the
toolsarray and a branch in the loop. Tryread_sqlite,send_email,roll_dice, whatever. - Make it interactive. Wrap the loop in a
for awaitoverDeno.stdinlines (or turn it into an HTTP val with a chat UI). - Reset memory. Call
clearMessages()frommemory.tsto start a fresh conversation. - Sub-agents. Spawn another
messagesarray with different tools. Have them talk. - Swap the model. Change
gpt-4o-minito anything elsestd/openaisupports.
Your wackiest idea will probably (1) work and (2) take 30 minutes to code. — tqbf
All credit for the framing and most of the prose-worth ideas goes to Thomas Ptacek (@tqbf) and the Fly.io blog: https://fly.io/blog/everyone-write-an-agent/. This val is just a Val Town / TypeScript port of the spirit of that post.