An Agent Runtime Engine — a Go binary that runs a complete agentic loop: receives user prompts via WebSocket, calls Gemini, dispatches built-in tools (file I/O, search, shell), and streams structured events back to ADK clients.
Uses pipe-based handshake (stdin/stdout) for secure startup and WebSocket + Protobuf for wire protocol.
SDK / CLI Client ◄─── WebSocket + Protobuf ───► LocalHarness (Go binary)
│
├── Agentic Engine
│ ├── LLM ↔ Tools ↔ Results loop
│ └── Streaming responses (token-by-token)
│
├── LLM Providers (with streaming support)
│ ├── Gemini (function calling + SSE streaming)
│ └── OpenAI-compatible (GPT-4o, Ollama, etc.)
│
├── Built-in Tools
│ ├── view_file
│ ├── write_to_file
│ ├── replace_file_content / multi_replace_file_content
│ ├── list_dir
│ ├── grep_search (ripgrep)
│ ├── find_file
│ ├── run_command (sync, background, persistent)
│ ├── manage_task
│ ├── finish
│ ├── invoke_subagent (child trajectories)
│ ├── search_web / read_url_content
│ ├── schedule (one-shot timers, cron)
│ ├── ask_question (interactive MCQ)
│ ├── knowledge_write/replace/delete (KI system)
│ └── browser (Playwright via MCP)
│
└── Host-side Custom Tools
└── SDK-registered tools (forwarded via WebSocket)
- Go 1.25 or higher (required for build and SDK usage)
The SDK auto-downloads the binary on first run (zero-install). For manual control:
# Option 1: go install (builds from source)
go install github.com/pixelvide/localharness/cmd/localharness@latest
# Option 2: Download prebuilt binary
curl -sSL https://github.com/pixelvide/localharness/releases/latest/download/localharness-linux-amd64.tar.gz | tar xz
sudo mv localharness /usr/local/bin/
# Option 3: Build from source
make buildSee docs/binary-distribution.md for the full resolution chain and cross-SDK strategy.
make build
# or directly:
go build -o bin/localharness ./cmd/localharnessThe binary is designed to be spawned and managed by the SDK. Use the test client:
# Run a prompt (requires Gemini API key)
export GEMINI_API_KEY=your_key
go run ./cmd/testclient --prompt "List the files in the current directory"
# Enable shell commands
go run ./cmd/testclient --enable-commands --prompt "Run 'ls -la'"
# Use thinking model
go run ./cmd/testclient --thinking=medium --prompt "Explain the architecture"
# Auto-approve all tool calls (for CI)
go run ./cmd/testclient --auto-approve --prompt "Create hello.txt with 'Hello World'"The wire protocol uses a pipe-based handshake for secure startup, followed by WebSocket with Protocol Buffer binary frames for communication.
SDK Binary
│── Spawn + capture pipes ──────────►│
│── Write InputConfig (stdin) ──────►│ Binary binds :0, generates API key
│◄── Read OutputConfig (stdout) ────┤ Returns port + API key
│── WS connect with API key ────────►│
Client Harness
│ │
├── ClientMessage(InitRequest) ──►│ Configure session
│◄── ServerMessage(InitResponse) ─┤
│ │
├── ClientMessage(UserMessage) ──►│ Send prompt
│ │
│◄── ServerMessage(StepUpdate) ──┤ Tool call (ACTIVE)
│◄── ServerMessage(StepUpdate) ──┤ Tool result (DONE)
│◄── ServerMessage(StepUpdate) ──┤ Tool call (ACTIVE)
│◄── ServerMessage(StepUpdate) ──┤ Tool result (DONE)
│◄── ServerMessage(StepUpdate) ──┤ Final text response
│◄── ServerMessage(TrajState) ──┤ IDLE
│ │
See proto/localharness/v1/localharness.proto for the full schema.
Key messages:
ClientMessage— wrapsInitRequest,UserMessage,ToolResult,CancelRequest,PermissionResponse,QuestionResponseServerMessage— wrapsInitResponse,StepUpdate,TrajectoryState,ErrorEventStepUpdate— the core event with tool actions, text, thinking, state machineHarnessConfig— LLM provider, tools, workspaces, system instructions
The harness provides a rich set of built-in tools organized into categories:
| Category | Tools | Description |
|---|---|---|
| File I/O | view_file, write_to_file, replace_file_content, multi_replace_file_content, list_dir |
Read, create, edit files and list directories |
| Search | grep_search, find_file |
Ripgrep-powered content search and filename pattern matching |
| Execution | run_command, manage_task, schedule |
Shell commands (sync/background/persistent), task management, timers and cron |
| Web | search_web, read_url_content |
Web search and URL content fetching |
| Agent Orchestration | invoke_subagent, define_subagent, manage_subagents, send_message |
Spawn typed child agents, define types, manage instances |
| Knowledge | knowledge_write, knowledge_replace, knowledge_delete |
Persistent project-scoped knowledge items (engine-intercepted) |
| Interactive | ask_question, finish |
Multiple-choice questions to user, task completion signal |
| Browser | browser_* |
Browser automation via Playwright MCP (docs) |
All file tools enforce workspace restrictions — operations outside configured workspace directories are rejected.
For the complete list of proto action fields, message types, and field numbers, see the StepUpdate Actions table in docs/architecture.md.
The project includes a Go ADK client under sdk/ that allows running the agent programmatically:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/pixelvide/localharness/adk"
)
func main() {
cfg := adk.NewLocalAgentConfig()
cfg.GeminiAPIKey = os.Getenv("GEMINI_API_KEY")
agent, err := adk.NewAgent(cfg)
if err != nil {
log.Fatalf("failed to create agent: %v", err)
}
defer agent.Close()
ctx := context.Background()
if err := agent.Start(ctx); err != nil {
log.Fatalf("failed to start agent: %v", err)
}
resp, err := agent.Chat(ctx, "List the files in the current directory")
if err != nil {
log.Fatalf("chat failed: %v", err)
}
fmt.Println(resp.Text)
}See the Examples folder for advanced SDK usage patterns:
- Policy Enforcement: Declarative safety rules.
- Safe Defaults: Read-only access with write approval confirmation handlers.
- Logging & Token Budget: Configuring custom
slog.Logger, togglingVerbosedebug logs, and setting a session-levelMaxTotalTokensbudget.
Detailed architectural specifications can be found in docs/architecture.md.
The binary is managed by the SDK via pipe handshake. These flags are for the binary:
| Flag | Default | Description |
|---|---|---|
--workspace |
cwd |
Default workspace directory |
--data-dir |
~/.pixelvide/localharness/ |
Data directory for conversations |
--debug |
false |
Enable debug logging |
--version |
— | Print version and exit |
local-harness/
├── cmd/
│ ├── localharness/main.go # CLI entry point
│ └── testclient/main.go # CLI test client
├── proto/localharness/v1/ # Protobuf schema
│ └── localharness.proto
├── gen/go/localharness/v1/ # Generated Go code (gitignored)
├── internal/
│ ├── server/ # WebSocket server + session
│ ├── engine/ # Agentic loop orchestrator
│ ├── llm/ # LLM provider interface + Gemini
│ ├── tools/ # Built-in tool implementations
│ ├── workspace/ # Path validation
│ └── config/ # CLI config
├── Makefile
└── buf.yaml / buf.gen.yaml
# Generate protobuf code
make proto
# or: protoc --go_out=gen/go --go_opt=paths=source_relative -I proto proto/localharness/v1/localharness.proto
# Build everything
make all
# Run tests
make test
# Lint protos
make lintTriggers & Reactivity
- File watcher triggers —
OnFileChange()for auto-reactive agents - Interval triggers —
Every(duration, fn)for periodic tasks
Tools
- Image generation tool
- Knowledge Items (KI) —
KnowledgeWriteToFile,KnowledgeReplaceFileContent,DeleteKnowledge - MCP Resources —
ListResources,ReadResourcefor MCP resource access - Notebook editing —
NotebookEditfor Jupyter support -
ListPermissionstool — show current permission grants
Protocol & Wire Format
- JSON wire format option — protobuf + JSON toggle for debugging
- Multimedia user messages — image/audio parts in user prompts
- ToolContext injection — host tools access conversation state
Developer Experience
- Interactive REPL — auto-upgrade policies in interactive mode
- Shell injection detection —
isDangerousBinary(),isDangerousSubcommand() - Auto-run command policies —
ShouldAutoRun()with trust levels - Workspace API tool — programmatic workspace management
Apache 2.0