An advanced, autonomous, Claude-Code-style AI agent execution loop built completely from scratch in TypeScript and running under Bun.
It natively orchestrates tool-calling, multi-layered context memory, tree-structured session persistence, and permission gating without using external agent frameworks (like LangChain or LangGraph).
π¬ Watch Coding Harness in Action:
Coding-Harness.4.mp4
Coding-Harness.4.mp4
- πΉ Demo
- β¨ Features
- ποΈ High-Level Architecture
- π§ Context Management Engine
- πΎ Session Management & Tree Persistence
- π οΈ Tool Ecosystem
- π€ Multi-Provider LLM Support
- π Execution Modes
- π¦ Permission Gate & Safety Engine
- β‘ Quick Start
- π Repository Structure
- π» Dynamic Interactive CLI REPL: Live streaming of assistant responses, formatted
π Thinkingblocks, tool call parameters, execution results, and runtime token dashboard. - βοΈ Headless Mode: Non-interactive automation entry point that accepts
--taskand--cwdand outputs structured JSON results for script/CI integration. - π§ Advanced Context Engineering: Automatic file read staleness invalidation (tombstoning), tool-call microcompaction, LLM summarization compaction, and
cache_controlbreakpoint injection. - π³ Tree-Structured Session Storage: Append-only JSONL event-log format supporting linear history, parent-pointer branching, leaf rewinding, and session resume capabilities.
- π Dual Tool Execution Modes: Switch dynamically between Parallel execution (running independent read/write calls concurrently via
Promise.all) and Sequential execution. - π Range-Targeted Editing with Drift Recovery: Line-targeted find-and-replace (
startLine/endLine) with sliding-window offset recovery (Β±10 lines tolerance) and mismatch diagnostics. - π Fast Search Capabilities: Ripgrep-backed (
rg) recursive text pattern matching with Bun-glob fallback and wildcard glob scanning. - π€ Provider Agnostic: Seamlessly switch between local Ollama models (
qwen3:14b,qwen3:8b,llama3.1:8b) and cloud Gemini models (gemini-1.5-flash,gemini-1.5-pro,gemini-3.1-flash-lite). - π‘οΈ Permission Gate & Policy Engine: Safety barrier enforcing user confirmation before running mutating actions (writes, edits, shell commands) with policy-based auto-approval.
- π°οΈ Read-Only Sub-Agent Dispatch: Isolated sub-agent worker context for performing background research without mutating workspace files.
The framework is decoupled into modular layers, separating execution control, context lifecycle, persistence, tool execution, and model connectivity:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLI Layer β
β Interactive REPL (repl.ts) β Headless CLI (headless.ts) β
βββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β
ββββββββββΌβββββββββ
β Agent Core β
β (src/agent.ts) β
ββββββββββ¬βββββββββ
β
ββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββ
β β β
ββββββββΌββββββββββ ββββββββββΌβββββββββ βββββββββββΌβββββββββ
β ContextManager β β SessionStore β β PermissionGate β
β - History β β - JSONL Tree β β - Read/Mutate β
β - Invalidation β β - Branching β β Classification β
β - Compaction β β - Path Resolve β β - Policy Engine β
ββββββββ¬ββββββββββ βββββββββββββββββββ ββββββββββββββββββββ
β
β βββββββββββββββββββ
ββββββββββββββββββΊβ ToolRegistry βββββββββββ
β β - 10 Built-ins β β
β ββββββββββ¬βββββββββ β
β β β
β ββββββββββΌβββββββββ βββββββββ΄βββββββββ
β β Sub-Agent Engineβ β Tool Execution β
β β (Read-only) β β Sequential/ β
β βββββββββββββββββββ β Parallel β
β ββββββββββββββββββ
ββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ChatModelClient Interface β
β OllamaClient (Local) β GeminiClient (API) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The ContextManager (src/context/contextManager.ts) handles memory representation, context limits, and message cleanup across turns.
When a tool modifies a file (write_file or edit_file), any earlier read_file output for that same file path residing in message history becomes outdated and potentially misleading to the model.
ContextManager.invalidateStaleReads(mutatedPath)scans history for pastread_filetool results matching the mutated file.- It overwrites the old content with a tombstone message:
[File content of <path> has been modified by a subsequent edit/write tool call. This read result is now stale and has been invalidated to save context space.] - Benefits: Prevents context window bloat, eliminates stale code references, and reduces token cost.
Continuous passive optimization (microcompact() in src/context/compaction.ts) runs before every turn. It scans history and deduplicates redundant tool operations (such as repeated identical file reads or superseded checks), replacing duplicated payload blocks with compact references.
When total session tokens exceed the configured threshold (default: 8,000 tokens):
compactIfNeeded()invokessummarizeHistory().- The LLM summarizes the oldest 50% of the message transcript into a structured system summary block.
- The original old turns are removed from the active context window, and the summary is inserted as a system block.
- Active tool call/result pairing structures are preserved to maintain model validation constraints.
ContextManager.getPayload() automatically attaches cache_control: { type: "ephemeral" } metadata to:
- The base System Prompt (containing runtime environmental rules and tool specifications).
- The
AGENT.mdProject Memory block.
This enables models supporting prompt caching (e.g. Anthropic / Gemini) to skip redundant prompt processing overhead across chat turns.
At startup, Agent checks for an AGENT.md file in the workspace root. If present, its contents are injected into system context as persistent project memory (coding conventions, architecture guidelines, forbidden files).
Session persistence (src/session/sessionStore.ts) uses an append-only JSONL format to guarantee crash-resilient storage and support non-linear conversation branching.
Sessions are saved in ~/.harness/agent/session/--<encoded-cwd>--/<sessionId>.jsonl.
Each line in the file represents a single SessionEntry with strict metadata:
export interface BaseEntry {
id: string; // Unique entry UUID/timestamp identifier
parentId: string | null; // Pointer to preceding entry ID
timestamp: string; // ISO timestamp
}SessionMessageEntry: User, assistant, or tool interaction messages.ThinkingLevelChangeEntry: Model thinking parameter changes.ModelChangeEntry: Model or provider switches.CompactionEntry: System summary generated during context compaction.BranchSummaryEntry: Context state preserved when creating alternative branches.CustomEntry/CustomMessageEntry: Metadata extensions and hook payload data.LabelEntry/SessionInfoEntry: User annotations and session summaries.
Because every entry explicitly references a parentId, history forms a Directed Acyclic Graph (DAG) / Tree:
ββββ Entry 3 (Branch A) βββ Entry 4A
Entry 1 ββ Entry 2
ββββ Entry 3 (Branch B) βββ Entry 4B <-- leafId
branch(branchFromId): Rewinds the session's activeleafIdback to an earlier entry. Subsequent entries are appended as children ofbranchFromId, creating a side branch without altering original history.branchWithSummary(branchFromId, summary): Creates a branch and records aBranchSummaryEntrycapturing context from the abandoned branch.resetLeaf(): Rewinds the leaf pointer to root, allowing full conversation restarts.
When preparing messages to send to the LLM:
buildSessionPath(entries, leafId)starts atleafIdand walks backwards viaparentIdpointers to the root. It reverses the list to reconstruct the active linear path.buildContextEntries(path)inspects the resolved path for anyCompactionEntry. If found, it drops raw messages prior tofirstKeptEntryIdand prefixes context with the compaction summary.sessionEntryToContextMessages()converts the entries into standard modelMessageobjects.
To prevent creating empty session files when users open and immediately close the CLI, Session uses deferred flushing:
- New sessions accumulate entries in memory (
flushed = false). - Disk creation and bulk writing (
flush()) occur only when the first assistant message is generated. - After flushing, subsequent entries are appended synchronously (
fs.appendFileSync).
Session files include a version header and automatically upgrade legacy formats upon loading:
- v1 β v2: Upgrades flat message arrays to tree nodes (
id,parentId) and converts index pointers to entry IDs. - v2 β v3: Normalizes legacy roles (
hookMessageβcustom).
SessionStore.getLatestSessionId()readslatest_id.txtto identify the most recent session for the current workspace.- The REPL CLI automatically detects existing sessions and prompts the user to resume or start fresh upon launch.
- Dynamic
/modelschanges,/modetoggling, andclearcommands immediately sync session state.
The harness provides 10 core tools in src/tools/:
| Tool Name | Type | Description |
|---|---|---|
read_file |
Read-only | Reads text files with line numbering, offset paging, and line range slicing. |
write_file |
Mutating | Creates new files or overwrites existing files completely. |
edit_file |
Mutating | Performs range-targeted find-and-replace using startLine/endLine, with sliding-window line drift recovery (Β±10 lines tolerance) and exact line mismatch diagnostics. |
run_command |
Mutating | Executes shell commands on the host machine. Gated by permission checks and safety policies. |
check_syntax |
Read-only | Validates JavaScript/TypeScript files using Bun's internal bundler compiler to report syntax errors prior to execution. |
glob |
Read-only | Performs fast wildcard pattern file and directory scanning across the workspace. |
grep |
Read-only | Executes workspace text searches using system ripgrep (rg) with a native Bun glob fallback. |
todo_read |
Read-only | Reads the persistent checklist file (.todo.md). |
todo_write |
Mutating | Updates and manages the project task checklist (.todo.md). |
dispatch_subagent |
Read-only | Spawns an isolated, read-only sub-agent to perform deep research tasks without file mutation access. |
The harness abstracts LLM integrations behind a unified ChatModelClient interface (src/providers/types.ts):
export interface ChatModelClient {
chatStream(
messages: Message[],
tools: ToolDefinition[],
onChunk: (chunk: { content: string; thinking: string; toolCalls: ToolCall[] }) => void
): Promise<Message>;
}- Ollama (
src/providers/ollama.ts):- Native streaming, thinking tag extraction (
<think>...</think>), raw tool payload parsing, and token usage reporting. - Built-in support for
qwen3:14b,qwen3:8b, andllama3.1:8b.
- Native streaming, thinking tag extraction (
- Gemini (
src/providers/gemini.ts):- Google Generative AI REST API streaming integration.
- Handles thought signatures (
thought_signature), structured tool declarations, and usage metadata. - Built-in support for
gemini-1.5-flash,gemini-1.5-pro, andgemini-3.1-flash-lite.
Model switching can be done interactively during REPL sessions using the /models command.
Launch using bun start or bun run src/cli/repl.ts:
- Features colored streaming responses and thinking visualization.
- Displays live tool invocation summaries and execution results.
- Includes interactive command shortcuts:
/models: Open interactive model selector menu./mode: Toggle betweenparallelandsequentialtool execution.clear: Reset history and delete current session.exit/quit: Terminate the REPL.
- Displays token usage metrics and context window percentage after every turn.
Launch using bun run src/cli/headless.ts:
bun run src/cli/headless.ts --task "Fix bug in search parser" --cwd "/path/to/repo" --max-iterations 30- Redirects logs to
stderrand prints clean JSON results tostdout:
{
"status": "success",
"output": "Task completed successfully...",
"filesChanged": ["src/parser.ts"]
}The Agent loop supports two tool execution modes:
- Parallel Mode (Default): When the model outputs multiple tool calls in a single turn, permissions are checked sequentially, and all approved tool executions run concurrently via
Promise.all. - Sequential Mode: Executes tool calls one by one in serial order.
Pressing Ctrl+C (SIGINT) while the agent is running tool cycles triggers mid-run steering:
- The agent loop pauses after the current tool execution completes.
- Prompts the user for a steering instruction:
steer instruction (or press Enter to resume)>. - Injects
[User Steering Instruction]: <input>into conversation history without destroying session context. - Pressing
Ctrl+Ca second time forces an immediate program exit.
PermissionGate (src/permissions/permissionGate.ts) acts as a security barrier between the agent and host machine operations:
- Read-Only Operations: (
read_file,grep,glob,todo_read,check_syntax,dispatch_subagent) execute automatically. - Mutating Operations: (
write_file,edit_file,run_command,todo_write) require user confirmation in interactive mode. - Policy Engine Auto-Approval: Evaluates safe command patterns (e.g.
git status,ls,npm test) against policy rules to bypass prompts for non-destructive operations. - Dangerous Command Blocking: Rejects destructive system commands (e.g.
rm -rf /) automatically. - Auto-Confirm Option:
autoConfirm: true(used in headless mode) automatically approves non-blocked mutating actions.
- Install Bun (v1.0+):
powershell -c "irm bun.sh/install.ps1 | iex" - Install and launch Ollama (optional if using Gemini API key):
ollama pull qwen3:14b ollama serve
- (Optional) Configure Gemini API key in
src/.envor project root.env:GEMINI_API_KEY=your_gemini_api_key_here
git clone https://github.com/raghuttama-dev/Coding-harness.git
cd Coding-harness
bun installbun startbun run src/cli/headless.ts --task "Refactor search utility to use async/await" --cwd "."Execute the Vitest-compatible Bun test suite covering tools, context compaction, staleness tracking, session storage, and execution modes:
bun testCoding-harness/
βββ src/
β βββ agent.ts # Core execution loop, steering, and turn orchestrator
β βββ client.ts # Provider export bridge
β βββ cli/
β β βββ repl.ts # Interactive terminal REPL interface
β β βββ headless.ts # Non-interactive JSON automation CLI entry point
β βββ context/
β β βββ contextManager.ts # History state, staleness tombstoning, token tracking
β β βββ compaction.ts # Microcompaction & LLM summarization compaction
β βββ permissions/
β β βββ permissionGate.ts # Read/mutate safety gate and command policy engine
β βββ providers/
β β βββ types.ts # ChatModelClient, Message, and ToolCall interface types
β β βββ ollama.ts # Ollama API client implementation
β β βββ gemini.ts # Gemini REST API client implementation
β βββ session/
β β βββ sessionStore.ts # Append-only JSONL tree persistence, branching, and migrations
β βββ tools/
β β βββ index.ts # Unified ToolRegistry definition
β β βββ read.ts # Range-sliced file reader with line numbers
β β βββ write.ts # File creator and overwriter
β β βββ edit.ts # Targeted find-replace editor with sliding drift recovery
β β βββ bash.ts # Command execution tool
β β βββ checkSyntax.ts # JS/TS syntax validator (Bun build compiler)
β β βββ glob.ts # Wildcard pattern file scanner
β β βββ grep.ts # Ripgrep-backed workspace search tool
β β βββ todo.ts # Task list checklist management (.todo.md)
β β βββ subagent.ts # Read-only background sub-agent dispatcher
β β βββ activeClient.ts # Active LLM client reference container
β β βββ types.ts # Tool interface contracts
β βββ tests/
β βββ context.test.ts # Staleness and compaction test suite
β βββ executionMode.test.ts # Parallel vs sequential mode test suite
β βββ gemini.test.ts # Gemini provider test suite
β βββ search.test.ts # Glob and Grep test suite
β βββ tools.test.ts # File edit, read, syntax, and todo tool test suite
β βββ v4.test.ts # Session tree, diff, policy engine, and subagent test suite
βββ AGENT.md # Workspace project memory rules file
βββ agent-harness-architecture.md # Architecture specification document
βββ pi-agent-session-storage.md # Session storage specification document
βββ package.json # Dependencies and run scripts
βββ tsconfig.json # TypeScript compiler configuration
MIT License. Built for autonomous AI agent research and development.