Architecture

A map of the code for people who want to change it. For what Circle does, read How Circle works first.

Circle is a TypeScript program for Node.js 24, under src/, compiled with strict checks. The agent loop is its own: there is no LangChain, LangGraph or deepagents. Model access goes through the openai and @anthropic-ai/sdk packages, MCP through @modelcontextprotocol/sdk, and sessions are stored with node:sqlite.

Entry points

Module What it does
cli.ts Reads the command line (folder, @files, messages, options) and picks the full-screen interface, line mode, print mode, JSON events or RPC; also circle update.
run_options.ts The options of one run that are not settings: tool limits, system prompt text, --models and the like.
headless.ts Print mode, JSON events and line mode, and what they write to standard error.
line_setup.ts Setup and the trust question asked line by line, for runs without the full-screen interface.
rpc.ts, rpc_codec.ts RPC mode over standard input and output, and the messages and jobs as it sends them.
exit_guard.ts Stops background jobs when the terminal closes or Circle is terminated.

The agent

Module What it does
runtime.ts Puts a session together: the model, tools, MCP servers, extensions, policy, system prompt and session store. Switches, forks and imports sessions, and reloads integrations.
session_manager.ts, session_lock.ts Keep independent runtimes per conversation, coordinate deletion and shutdown, and hold heartbeat leases against concurrent writers in other Circle processes. UI selection does not own execution.
harness.ts The loop: one model request, then the tool calls it asked for, until the model stops. Steering messages are read before the next request; follow-ups start a new turn when one ends. task calls next to each other run together, other calls one after another.
system_prompt.ts Builds the system prompt from prompts/, the instruction files, skills, SYSTEM.md and APPEND_SYSTEM.md.
memory_sources.ts Finds the instruction files (AGENTS.md, CLAUDE.md and the like) for a folder.
skills.ts Finds skills in the built-in, user, project and .agents/skills folders and loads their text. The built-in ones are in builtin_skills/ beside the module itself, the lowest-priority source.
commands.ts Custom commands: finds them and expands their templates.
mentions.ts @file in a message (bare, quoted or with escaped spaces): attaches the file, completes paths for the input box, and reads a paste of dropped image files.
events.ts The event bus. A subscriber that throws cannot stop the agent.
types.ts Messages, tool calls, usage and the tool contract.
testing.ts ScriptedModel, the model the tests drive.

Models

Module What it does
model.ts Talks to OpenAI-compatible and Anthropic-compatible endpoints and turns text, thinking, tool calls and usage into one form.
tool_history.ts Keeps complete tool-result batches before internal mode transitions in outbound requests. Original checkpoints stay unchanged; missing or duplicate results and ordinary user input are not hidden or fabricated.
model_guard.ts Retries by kind of failure, drops parameters an endpoint rejects, and catches stalled streams, repetition and a missing finish.
text_repetition.ts Notices output that repeats itself, for model_guard.ts.
tool_call_compat.ts Repairs tool names and arguments when the repair is unambiguous, and validates arguments before anything runs.
model_media.ts, media.ts Images and PDFs: reading them, checking them and putting them into a request in each protocol’s form.
image_fit.ts Scaling a large PNG down (pngjs, area averaging) before it is sent.
screenshot.ts The screenshot tool’s capture: screencapture and a JXA window list on macOS, PowerShell on Windows, the first screenshot program that works on Linux.
probe.ts Asks an endpoint for its models and works out its protocol and base URL, for setup and /models.
model_catalog.ts Context windows, prices and whether a model reads images, from models.dev: the snapshot in data/models_dev.json.gz, refreshed into the data folder once a day.
model_profiles.ts Thinking depths and other capabilities per model, from data/provider_profiles.json.gz.
model_scope.ts Which models ctrl+p goes through.
pricing.ts The cost of each call and the totals the footer shows.
compaction.ts Shared operation snapshots, progress and persistent outcomes.
request_budget.ts Model input/output reservations, complete provider-frame estimates and shared recovery allowances.
compaction_engine.ts Content-only serialization, bounded parallel parts, complete coverage, cached nodes and atomic activation.
compaction_store.ts Independent SQLite operation, attempt and summary-node records; branch-aware export and restore.
context_middleware.ts What the model sees of a long session: large results kept in files, older messages summarized, older images left out, and the originals read back when needed.
middleware/ Reminders added to a request: running and polled jobs (job_notice.ts), repeated tool calls (loop_guard.ts), the plan (plan_tail.ts), and pruning of old tool results (tool_result_prune.ts).

Anthropic responses carry an opaque thinking_scope derived from the API URL, key and requested model. When that identity changes, model.ts leaves historical thinking and redacted_thinking blocks out of the outbound request, keeping text, tool calls and results. Original checkpoint messages are unchanged. For older or imported messages without a scope, an explicit HTTP 400 error rejecting a thinking signature triggers one retry without historical thinking. The model instance remembers fingerprints of the rejected signed payloads, so reused message ids do not suppress different valid thinking. A restart can need that recovery again for unscoped history. Other errors are not handled as signature errors, and signature errors do not disable thinking.

Tools and safety

Module What it does
tools.ts The built-in tools: files, execute, task, write_todos, jobs, plan mode, question, the web, skills and compaction. grep, apply_patch and lsp have modules of their own.
grep_tool.ts The grep tool: literal search, its output modes and limits.
apply_patch.ts The apply_patch tool.
file_mutations.ts The file backend the tools may use: a file’s text and its revision, a batch of changes checked before any write, and a restore of all of it or none.
file_mutations_fs.ts That backend on the host filesystem, which the terminal, line, print and RPC modes use; its read says what it cannot bring back byte for byte.
file_rewind.ts The turn journal of the text tools: what a turn changed, the preview that reads every file again, and the checked restore. Its receipts live in circle.sqlite, so /rewind still knows a turn after a restart.
file_read.ts Reads a window of lines without loading a whole file.
lsp_tool.ts The lsp tool: language servers over standard input and output.
websearch.ts webfetch, DuckDuckGo fallback requests and search-result HTML parsing.
websearch_service.ts Runtime-owned native search capability cache, deadlines, normalized results and DuckDuckGo fallback.
native_websearch.ts Provider search protocols, structured execution evidence, citations and auxiliary model usage.
questions.ts The question tool and its answers.
plan_file.ts Plan mode’s plan: where each conversation’s plan file is kept, and the choices plan_exit offers and how their answers read.
secret_prompt.ts Secret requests: the files a task and Circle exchange, and writing the value into its target file.
editor_bridge.ts The localhost MCP server an editor plugin talks to: what is open, what is selected, diff requests, and the write that follows an accept. Loopback only, token only, workspace only.
memory_write.ts The dated lines Circle keeps for itself in MEMORY.md: write one, list them, take one back, under the length and read-size caps the prompt works with.
approval_rules.ts The structured rules: {tool, pattern, effect} in your approvals.json and the project’s .circle/approvals.json, the layer that decided, and what a project may and may not grant.
approvals.ts Sorts commands and tool calls, keeps the session’s rules and decides what needs your answer.
subagent_files.ts The agent folders: <name>.md per subagent (keys name, description, tools, model, body the prompt), read from ~/.circle/agents, ~/.agents/agents, ~/.claude/agents and the project’s own, with what could not be used reported for /agents.
hooks.ts Declarative hooks: hooks.json for you, a trusted project (re-approved when the file changes) and an optional skill, the shell protocol, and decisions that override the approval mode.
sandbox.ts Resolves paths (including /conversation_history/, /large_tool_results/, /background_jobs/ and /session_plans/, which live in the data folder), refuses credential files, runs commands with secret variables stripped from their environment, and stops their process groups. It is a policy boundary, not an operating-system sandbox.
redact.ts Hides passwords and tokens in command and job output.
secrets.ts Variables and saved secrets written %NAME%: secrets.json, putting values into a call’s arguments after its approval, and turning a secret’s value back into %NAME% in what the model reads.
browser/ The conversation browser. tools.ts has the browser_* tools, each action’s site for its approval and the handshake with the person. page.ts is the script that reads a page and moves the pointer. protocol.ts, format.ts, hosts.ts, certificates.ts and files.ts are what both browsers share. chrome.ts and cdp.ts drive Chrome, Edge or Chromium for the terminal; the desktop’s browser is apps/desktop/src/browser.ts.
terminal/ The device terminals: SSH and Telnet consoles the model drives and the person can take over. tools.ts has the terminal_* tools and the bridge that carries them to the desktop’s main process; sessions.ts owns each conversation’s consoles (holder, serial queue, wait, the @xterm/headless screen). transport.ts is the connector seam, with ssh.ts and telnet.ts behind it: an unknown host key pauses the SSH handshake on its open wire and sessions.ts keeps that paused login for the person’s answer, so a “connect once” resumes the same handshake rather than paying for a second one (a wire that died or a wait that ran out falls back to logging in from scratch); protocol.ts fixes the wire shape, profiles.ts reads the person’s terminal-sessions.json and hostkeys.ts the trusted host keys. tui_screen.ts draws a device screen into the TUI’s rows (colours only from ink/theme.ts); keys.ts turns an attached keystroke into device bytes. The desktop hosts the same TerminalSessions in apps/desktop/src/terminal.ts.
user_shell.ts Your own !command and !!command.

Sessions and data

Module What it does
checkpoint_store.ts circle.sqlite: sessions, their messages as a tree of checkpoints, branches, labels, context versions and one turn’s file changes for /rewind. Stored messages are never rewritten.
session_graph.ts Checks that a session’s messages and branches fit together.
session_export.ts /export and --export: Markdown, HTML and JSONL, and reading a JSONL export back.
settings.ts settings.json and credentials.json: reading, project overrides in a trusted folder, and saving one change at a time.
paths.ts The data folder and the paths inside it.
keybindings.ts keybindings.json: the actions and their default keys.
git_info.ts The folder’s git branch.

Background work and integrations

Module What it does
jobs.ts Background jobs: commands and subagents, their output files, their notices to the model, and stopping them.
watch.ts api.Watch, which an extension tool returns to wait for something slow.
schedule.ts Scheduled tasks: the store under schedules/, the clocks (every, cron, once), the run records and the per-schedule run lock.
scheduler.ts Firing them: one headless circle -p in a child process per run, the tick loop, the daemon file.
schedule_cli.ts The circle schedule command line.
mcp_loader.ts Connects MCP servers and offers their tools under the same policy as built-in ones.
extensions.ts Loads extensions and what they register: tools, commands, middleware, renderers and event handlers.
net.ts The proxy and certificate settings for every request Circle makes itself.

The terminal interface

src/ink/ draws: it reads keys and terminal reports, builds the palette, and turns state into rows. src/tui/ decides what is on screen and what a key does. The rules for both are in The TUI contract.

Module What it does
ink/parse_keypress.ts Turns terminal input into keys, pastes, mouse events and colour reports.
ink/theme.ts The palette, built from the terminal’s colours: text, tints, lamps and the rainbow.
ink/theme_watch.ts Asks the terminal for its colours and follows a switch between dark and light.
ink/screen.ts Writes only the rows that changed.
ink/string_width.ts Display width, wrapping and cutting of text with colour codes.
ink/selection.ts What a mouse selection covers on the painted screen.
ink/clipboard.ts Copying: OSC 52, the system’s clipboard tool and tmux’s buffer. Pasting: a picture or copied image files from the system clipboard (ctrl+v).
ink/components/dialog_card.ts The card in the input box’s frame: title with its lamp, body, options, input row.
ink/components/approval_card.ts, question_card.ts, secret_card.ts The approval, question and secret cards and their keys.
ink/components/loop_frame.ts The input box’s frame: rainbow, busy label and mode word.
ink/components/picker.ts, popup.ts The lists above the input box, and the completion list’s rows.
ink/components/plan_panel.ts The plan box.
ink/components/markdown_renderer.ts Markdown in answers, thinking and records.
ink/components/welcome.ts The welcome block and its logo.
tui/session_app.ts The full-screen session: input dispatch, cards, lists, the terminal’s modes, and the commands that need its own state (/models, /login, /reload, /editor).
tui/render.ts Lays out a frame: conversation, plan box, waiting messages, input box, footer and strip.
tui/composer.ts The input box: editing, pastes, word keys, growth and the completion list.
tui/input_history.ts The prompt history and ctrl+r.
tui/slash_commands.ts The built-in commands, parsing, close matches, /help and /hotkeys text.
tui/slash_handlers.ts What each built-in command does.
tui/interaction_queue.ts One card at a time, the turn’s own before a background agent’s, none while you type.
tui/approval_preview.ts What an approval card shows: the command, the file and its diff.
tui/tool_rows.ts, tui/agent_rows.ts, tui/display_lexicon.ts Rows for tool calls, results, thinking and subagents, and the short names and phrases they use.
tui/status_rows.ts The header, the busy label, the footer, the line under each turn, the compaction row and the window title.
tui/strip_rows.ts The strip of subagents, jobs and live device terminals under the footer, and the band each of their pages wears.
tui/turn_status.ts Per-turn time, tokens and thinking time, from the event bus.
tui/welcome_state.ts, tui/folder_inventory.ts What the welcome shows: the folder’s own instruction files, skills, commands, extensions and settings, and its recent sessions.
tui/subagents.ts Selecting a subagent in the strip and paging through records.
tui/conversation_tree.ts The session as a tree, for /tree and /fork.
tui/undo_history.ts What /undo and /redo put back on screen.
tui/transcript_find.ts ctrl+f.
tui/mouse_selection.ts Dragging, clicking and copying with the mouse.
tui/external_editor.ts Which editor /editor runs.
tui/exit_lines.ts What the terminal shows after Circle leaves: stopped jobs and the command that reopens the session.

Install and update

Module What it does
install_layout.ts Release archives and the install folder: versions/<version>/, current.ref, installation.json, checksums and pruning.
install_manager.ts Installs a release folder; run by install.sh and install.ps1.
install_processes.ts The processes running a program of an installation, which circle uninstall (uninstall.ts) looks for before it removes anything. Held Windows programs are matched by native process name and canonical path; an unidentified busy executable stops the removal.
update.ts The daily update reminder and circle update.
version.ts The version and version comparison.

install.sh, install.ps1 and these modules share one layout; tests/install-layout.test.ts, tests/install-manager.test.ts and npm run release:smoke pin it. See Releasing.

Other folders

  • src/prompts/: the system prompts per model family, the tool descriptions, the subagent and summary prompts, and the /init template.
  • src/data/: the models.dev snapshot and the provider profiles, compressed.
  • src/builtin_skills/: the skills that ship with Circle, one folder each (create-pr, pr-review). scripts/copy-assets.ts copies them to dist/builtin_skills, and the desktop build (apps/desktop/scripts/runtime-assets.mjs) puts them beside its bundled runtime, inside app.asar once packaged. A release without them fails its check (install_layout.ts).
  • scripts/: building and smoke-testing release packages, the release script, copying assets into dist/, and update-models-dev.ts (refreshes src/data/models_dev.json.gz; run it before a release).
  • tests/: one *.test.ts file per area, run with Node’s test runner through tsx. They drive ScriptedModel or a local HTTP gateway and need no network.

This document is one of Circle’s design specifications, licensed under CC BY 4.0: quote or build on it with attribution to Circle and qingshanfeihu and a link to the original. See the design specifications index.