Architecture
A host and a harness compile against one contract package. The host decides who a tool call runs as; the harness decides which call to make.
A host and a harness compile against one contract package. The host decides who a tool call runs as; the harness decides which call to make.
host ──── agentcontract.Harness ──── bluecollar
│ │
│ owns: tools, identity, task store, │ owns: the turn loop, skills,
│ routing, approvals, isolation │ completion judgment
│ │
└──────── executes every tool call ──────┘The port
type Harness interface {
RunTurn(context.Context, AgentTurnRequest) (AgentTurnResult, error)
}The port used to be nine methods. Routing, addressing, follow-up classification and one-shot replies moved off it once it was clear they are host policy: a host that answers its own messenger decides what an inbound message means before anything runs a turn. bluecollar still ships those pieces in intake, and a host is free to use them or bring its own. A harness that implements only RunTurn is complete.
Who owns what
| layer | owns |
|---|---|
| host | connectors and messengers, tool execution and its isolation boundary, the task store, approvals, the agent's identity, the workspace layout, company context |
agentcontract, toolcontract, model, taskstate | the vocabulary both sides speak: requests and results, tool descriptors and results, model ports, task runs and ledger events |
loop | the turn: action schema, plan, tool exposure, completion gate and judge, recovery, budgets, context building and compaction |
intake | what a message means: route, addressing, follow-up, level, likely tools |
A harness that executes its own tools defeats the host's isolation boundary and is not a valid implementation of this contract. With no identity supplied the agent calls itself "the assistant" and knows nothing about where it runs.
blueclaw is one host. It projects each requester to a POSIX user and runs every tool call as that user, so the permission boundary is the operating system's. cmd/bluecollar and cmd/bluecollar-acp are two more, small enough to read in one sitting.
Packages
| path | holds |
|---|---|
agentcontract/ | the harness port, turn requests and results, task runs, statuses and event names |
toolcontract/ | tool descriptors, tool sets, results, the kernel tool names |
model/ | the language model and decision model ports; openaicompatible, decisions and tape implement them |
loop/ | the agent loop, AgentKernel and AgentTurnRunner |
intake/ | the turn router and the decision planner |
taskstate/ | the in-memory services over task runs, steps, events and artifacts |
turnstream/ | a view of a turn's ledger events as they are appended |
trace/ | one run's ledger rendered as a single JSON or Markdown file |
bench/ | run metrics and a runner that measures any Harness |
cmd/bluecollar/ | the command-line runner |
cmd/bluecollar-acp/ | the loop as an Agent Client Protocol agent, in its own module |
ACP
cmd/bluecollar-acp runs the loop as an Agent Client Protocol agent. It owns no tools: the tool catalog arrives on the MCP servers the host names when it opens a session, and a tool's blueclaw/sideEffectClass and blueclaw/approvalScope metadata become its descriptor. Ledger events go out on session/update, tool calls as the standard variants and every event's name and body in _meta. A host that kept those records hands them back in the prompt's _meta, and the turn resumes on the work they describe. A steer injected mid-turn reaches only an in-process host, because the protocol has no message for it during a turn.
Quickstart
The module needs Go 1.26 and depends on github.com/google/jsonschema-go and github.com/ergochat/readline. The ACP agent in cmd/bluecollar-acp is a second…
Concepts
These pages follow one request through the loop. The names match the code, so each page can be read beside the package it describes.