Skip to Content
InternalsOverview

Internals

This will be a guide to how FreeCode is made — each component explained in a beginner-friendly way, complete enough to know how real-world production AI agent harnesses work.

This section is the architecture, not the usage. It follows one request all the way down: a prompt enters the loop, becomes a streamed provider call, comes back as tool calls, gets gated by permissions and hooks, executes, and folds back into the next turn — while the bus narrates the whole thing to your screen.

INTERACTIVE FREECODE SYSTEM BLUEPRINT
>_ FREECODE CLI BACKEND (CORE)JSON-RPC Taskdelegate subtasksresultson startupread / write contextcallresultpromptresponsesession history ⇄ summaryEVENT BUSpub / subYOU / CLIENTSTerminal TUIpi-tuiVSCode ExtReact webviewWebNext.jsDesktopTauri + ViteThin clientsFreeCode AGENTReasoning LoopSUB-AGENTSSub-AgentExplorerSub-AgentTesterParallel executionCONTEXTAGENTS.mdSkillsProject conventionsMemorycross-sessionHooksPreToolUsePERMISSIONallow · ask · denyCOMPACTIONcontext windowsummaryon window overflowTOOLSFile Read/Writeread.ts, write.ts>_Bash Shellbash.tsGrep Searchgrep.tsGlob Matchglob.tsAction handler suiteAI PROVIDERSVercel AI SDKClaude · GPT · Geministreaming API
Click any system component above to highlight it — the pages in this section cover each subsystem in depth.

The thin-client boundary

TUI, VS Code, Web, and the desktop app are pure presentation layers — they render and speak IPC, nothing else. Every frontend talks to apps/core over the same JSON-RPC protocol (session.send, tools.call, …), so a bug fix or a new capability written once in core reaches all four frontends without touching any of them. It also means core has no UI to leak into: it can be driven headlessly (CI, freecode eval, a script) with zero frontend code loaded at all. See IPC protocol for the wire format.

Where each subsystem lives

Everything under this section maps to a directory in apps/core/src/:

SectionDirectory
IPC protocolserver.ts, packages/shared/src/ipc/
Agent loopagent/
Provider layerproviders/
Tool systemtools/
Context enginecontext/
Event busbus/
Lifecycle hookshooks/
Permission enginepermission/
Sub-agent runtimeagent/subagent.ts
Memorymemory/
Knowledge graphmemory/graph/
Sessions, store & rolloutsession/, store/, rollout/
Compactioncompaction/
Effect runtime & DIeffect/
Eval harnesseval/
MCP clientmcp/
Skills systemskills/
CLI entrypoint & subcommandscli/, cli.ts
Slash commandscommands/
Usage accountingusage/
Repo maprepo-map/
Claude Code session importclaude-sessions/
Graph explorer addongraph-explorer/
Shared utilities (no page yet)utils/
Autonomous runs — Phase 0 only, nothing executes yetautonomous/

Each page follows the request through its subsystem with references to the actual source files, not just a description of the concept — the goal is that you can read a page, then go straight to the file it names.

The invariants the codebase holds to

A handful of rules recur across every subsystem in this section, and are worth knowing before diving into any one page:

  • Frontends have zero business logic. No provider calls, no file reading, no tool execution outside apps/core.
  • One egress, one bridge. The event bus has exactly one subscribeAll writer per process — adding a second silently double-prints every event.
  • Fail closed. An unclassified tool is mutating (permissions), an unconfigured eval judge closes the gate rather than passing quietly (eval), a denied tool call still leaves a trace (agent loop).
  • DI over singletons. Services are resolved through Effect layers, not imported as module-level singletons, so tests can swap a whole dependency graph at once.
  • A denial is not silence. A refused tool call is its own event type (function.denied), never absent — see Agent loop §“Denials” for why folding it into normal tool spans breaks several consumers downstream.