
Agentic Control is a small Go control layer for products that need to run
local agent CLIs without hard-coding each runtime into the product itself.
It gives upstream services two surfaces:
agent_control for app-owned sessions across Codex, Gemini, Claude,
OpenCode, and pi.
agent_harness for passive hook, plugin, and extension telemetry from
sessions launched outside your app.
It contains Court as an internal subsystem for multi-agent workflow
orchestration.
For new integrations, start with agent_control. It discovers local installs,
reports auth state, exposes available models where the runtime has a stable
inventory surface, starts and resumes sessions, sends input, interrupts work,
answers runtime requests, and streams normalised events.
The passive harness remains available for diagnostics and unmanaged sessions,
while agent_control is the primary integration surface.

What This Repo Provides
Agentic Control keeps runtime-specific behaviour behind a small contract that
host applications can import or run over a Unix socket.
It is the native home for workflow orchestration primitives, with Court as the
advanced review-oriented subsystem on top.
| Need |
Use |
Primary files |
| Discover installed runtimes, auth, capabilities, and models |
agent_control describe or embedded.ControlPlane.Describe() |
cmd/agent-control/main.go, pkg/controlplane/embedded/embedded.go |
| Start, resume, send to, interrupt, respond to, stop, and list sessions |
agent_control serve or embedded.ControlPlane |
internal/controlplane/, pkg/controlplane/types.go |
| Track downstream session history and token economics |
control-plane session ledger |
internal/controlplane/sessionledger.go, docs/session-ledger.md |
| Own backend/provider/model definitions and validation |
shared runtime target helpers |
pkg/controlplane/runtime_targets.go, docs/model-registry.md |
| Route model-backed text generation work in upstream services |
pkg/controlplane.TextGenerationRouter |
pkg/controlplane/textgen.go |
| Run Court workflow orchestration in process |
internal/court |
internal/court/, docs/court-subsystem.md |
| Capture unmanaged runtime telemetry |
agent_harness install, agent_harness listen, and runtime bundles |
cmd/agent-harness/main.go, internal/harness/harness.go, internal/harness/install.go, internal/harness/run.go |
| Consume stable JSON contracts |
Go contract types |
pkg/contract/controlplane.go, pkg/contract/harness.go |
The runtime adapters are deliberately boring:
- Codex uses
codex app-server for controlled sessions and native hooks for
passive telemetry.
- Gemini uses
gemini --acp for controlled sessions and native hooks for
passive telemetry.
- Claude uses the official Agent SDK through a local bridge for controlled
sessions and native hooks for passive telemetry.
- OpenCode uses
opencode serve for controlled sessions and native plugins for
passive telemetry.
- pi uses
pi --mode rpc for controlled sessions and native extensions for
passive telemetry.
Read these next:
- Control-plane guide for
system.describe, session
RPC, local probes, auth state, and model inventory.
- Session ledger for downstream session tracking and
token economics.
- Model registry for unified backend/provider/model
sourcing, validation, and operator surfaces.
- Smoke testing for repeatable live runtime/orchestration
verification through the CLI.
- Orchestration surface for the first native
non-Court workflow entry point.
- Court subsystem for the in-process workflow engine
layering inside Agentic Control.
- Integration guide for embedding Agentic Control into a
host service.
- Event contract for the passive harness event shape.
- Runtime guides for Codex, Gemini,
Claude, OpenCode, and pi.

Quick Start
Build the two binaries:
mise trust
mise install
mise run build
Start the control-plane:
agent_control serve --socket-path /tmp/agentic-control.sock
agent_control wait-ready --socket-path /tmp/agentic-control.sock
In another terminal, ask it what the local machine can run:
agent_control describe --socket-path /tmp/agentic-control.sock
That call is the first useful integration point. It returns:
- the control-plane schema and wire protocol versions
- supported RPC methods
- one descriptor per runtime
- local binary install status
- runtime version and resolved binary path
- auth state where a runtime exposes a usable status command
- model inventory and model option metadata where the runtime exposes it
For typed model selection and orchestration, the most useful next commands are:
agent_control models --socket-path /tmp/agentic-control.sock --runtime opencode --provider google
agent_control smoke --socket-path /tmp/agentic-control.sock
agent_control court run --socket-path /tmp/agentic-control.sock --task "review this repo" --provider opencode --model-selection google/gemini-3-flash-preview --workspace . --watch
The same control-plane can be imported directly from Go:
package main
import (
"fmt"
"github.com/benjaminwestern/agentic-control/pkg/controlplane/embedded"
)
func main() {
cp := embedded.New()
describe := cp.Describe()
for _, runtime := range describe.Runtimes {
status := "unknown"
if runtime.Probe != nil {
status = runtime.Probe.Status
}
fmt.Printf("%s: %s\n", runtime.Runtime, status)
}
}
The build bootstraps the Claude SDK bridge dependency the first time
agent_control is compiled. That bridge is only for Claude's official Agent
SDK approval and input callback path; there is no extra bridge for pi model
inventory.

Runtime Matrix
The release was checked on April 24, 2026 against these CLI/package
versions:
| Runtime |
Validated version |
Controlled session transport |
Passive telemetry |
Model inventory |
| Codex |
codex-cli 0.124.0 |
codex app-server |
native hooks |
built-in catalogue; app-server model/list pending |
| Gemini |
0.39.0 via package version check; local smoke 0.38.2 |
gemini --acp |
native hooks |
built-in catalogue |
| Claude |
2.1.104 (Claude Code) local CLI; Agent SDK 0.2.119 package |
Claude Agent SDK bridge |
native hooks |
built-in catalogue |
| OpenCode |
1.14.22 via package version check; local CLI 1.14.20 |
opencode serve |
native plugin |
dynamic /provider inventory when the server exposes it |
| pi |
0.70.0 via package version check; local CLI 0.68.0 |
pi --mode rpc |
native extension |
dynamic RPC get_available_models pending |
Install references:
pi documents a machine-readable RPC model inventory path through
get_available_models. Agentic Control reports pi install and version
state with an empty built-in model list until that RPC probe is implemented
and smoke-tested against the 0.70.0 package.

Control-Plane Contract
system.describe is the bootstrap call every upstream service should make
before rendering a backend picker or starting work.
Minimal request:
{"id":"describe-1","method":"system.describe"}
Response shape:
{
"schema_version": "agentic-control.control-plane.v1",
"wire_protocol_version": "agentic-control.rpc.v1",
"methods": [
"system.ping",
"system.describe",
"events.subscribe",
"events.unsubscribe",
"session.start",
"session.resume",
"session.send",
"session.interrupt",
"session.respond",
"session.stop",
"session.list"
],
"runtimes": [
{
"schema_version": "agentic-control.control-plane.v1",
"runtime": "codex",
"ownership": "controlled",
"transport": "app_server",
"capabilities": {
"start_session": true,
"resume_session": true,
"send_input": true,
"interrupt": true,
"respond": true,
"stop_session": true,
"list_sessions": true,
"stream_events": true,
"approval_requests": true,
"user_input_requests": true,
"immediate_provider_session": true,
"resume_by_provider_id": true,
"adopt_external_sessions": false
},
"probe": {
"installed": true,
"status": "ready",
"version": "codex-cli 0.124.0",
"binary_path": "/opt/homebrew/bin/codex",
"auth": {
"status": "authenticated",
"method": "login status"
},
"model_source": "built_in",
"models": [
{
"id": "gpt-5.4",
"label": "GPT-5.4",
"provider": "codex",
"default": true,
"capabilities": {
"reasoning_effort_levels": [
{"value": "xhigh", "label": "Extra High"},
{"value": "high", "label": "High", "is_default": true},
{"value": "medium", "label": "Medium"},
{"value": "low", "label": "Low"}
],
"supports_fast_mode": true
}
}
],
"message": "Runtime binary found",
"probed_at_ms": 1775200000000
}
}
]
}
Use the probe for UX, not as the source of provider capability truth:
capabilities says what the Agentic Control provider implementation can do.
probe.installed, probe.status, probe.version, and
probe.binary_path describe the current machine.
probe.auth describes whether the local runtime appears authenticated.
probe.models and probe.model_source describe the models available for
picker UI and upstream routing.
Session and event types live in pkg/contract/controlplane.go.
Provider request types live in pkg/controlplane/types.go.

Unified Text Router
Upstream services often need model-backed text generation without caring which
runtime should handle the request. The public router in
pkg/controlplane/textgen.go centralises that
selection logic.
Supported generation surfaces:
- commit messages
- pull-request titles and bodies
- branch names
- thread titles
The router resolves providers in this order:
- explicit provider
- provider inferred from the model ID
- fallback providers
- router default provider
Built-in inference rules:
| Model shape |
Provider |
claude-* |
Claude |
gemini-* or auto-gemini-* |
Gemini |
gpt-*, o1*, o3*, or o4* |
Codex |
provider-scoped IDs like anthropic/claude-sonnet-4-6 |
OpenCode |
Example:
router := controlplane.NewTextGenerationRouter("codex", map[string]controlplane.TextGenerationProvider{
"codex": codexProvider,
"claude": claudeProvider,
"gemini": geminiProvider,
"opencode": openCodeProvider,
})
result, err := router.GenerateCommitMessageForSelection(ctx, controlplane.CommitMessageInput{
ModelSelection: controlplane.TextGenerationModelSelection{
Model: "claude-sonnet-4-6",
Options: controlplane.ModelOptions{
ReasoningEffort: "high",
},
Fallbacks: []string{"codex"},
},
Diff: diff,
})
That keeps product code focused on its own workflow model while preserving the
selected runtime, model, reasoning effort, thinking level, and thinking budget.

Architecture

The controlled-session path is:
- The host app imports
pkg/controlplane/embedded or starts
agent_control serve.
- The app calls
system.describe and renders only the runtimes that are
installed and usable.
- The app starts or resumes a runtime session.
- Agentic Control normalises runtime events, session state, approvals, and
user-input requests.
- The app subscribes to events and responds through one control-plane API.
The passive telemetry path is:
- The host installs a runtime bundle with
agent_harness install.
- The runtime invokes
agent_harness through its native hook, plugin, or
extension surface.
- The helper translates native payloads into the harness event contract.
- The host consumes events from stdout or a local Unix socket listener.
Generated diagrams come from assets/architecture.d2.

Passive Harness
Use agent_harness when your app does not own the runtime process or when you
need to inspect native runtime events during integration work.
Start a listener:
.artifacts/bin/agent_harness listen --socket-path /tmp/agent-harness.sock
Install repo-local bundles for hook-based runtimes:
.artifacts/bin/agent_harness install --runtime codex --scope repo --socket-env AGENT_HARNESS_SOCKET
.artifacts/bin/agent_harness install --runtime gemini --scope repo --socket-env AGENT_HARNESS_SOCKET
.artifacts/bin/agent_harness install --runtime claude --scope repo --socket-env AGENT_HARNESS_SOCKET
.artifacts/bin/agent_harness install --runtime pi --scope repo --socket-env AGENT_HARNESS_SOCKET
Install OpenCode globally when you want its normal plugin auto-load path:
.artifacts/bin/agent_harness install --runtime opencode --scope global --socket-env AGENT_HARNESS_SOCKET
Remove only Agentic Control-managed config later:
.artifacts/bin/agent_harness uninstall --runtime codex --scope repo
.artifacts/bin/agent_harness uninstall --runtime gemini --scope repo
.artifacts/bin/agent_harness uninstall --runtime claude --scope repo
.artifacts/bin/agent_harness uninstall --runtime opencode --scope global
.artifacts/bin/agent_harness uninstall --runtime pi --scope repo
Fixture replay and live smoke tasks:
mise run diag:fixtures:all
mise run diag:codex:smoke
mise run diag:gemini:bash
mise run diag:claude:approval
mise run diag:opencode:approval
mise run diag:pi:smoke
The normalised passive event contract is documented in
docs/contract.md.

Maintenance
The local automation entrypoints live in mise.toml, and hook
checks live in hk.pkl.
Useful tasks:
| Command |
Purpose |
mise run build |
Build agent_harness and agent_control. |
mise run control:serve |
Start the control-plane on /tmp/agentic-control.sock unless SOCKET_PATH is set. |
mise run validate-docs |
Validate README links and required generated assets. |
mise run generate-assets |
Regenerate README SVG assets and the architecture diagram. |
mise run hk:check |
Run the configured local checks. |
Documentation assets are generated by scripts/generate_assets.py and checked
by scripts/validate_readme.py. The generated files are:
Regenerate and validate after README structure changes:
mise run generate-assets
mise run validate-docs

Repository Layout
| Path |
Purpose |
| cmd/ |
Binary entrypoints for agent_control and agent_harness. |
| internal/controlplane/ |
Session registry, event bus, RPC server, probes, model catalogue, and provider implementations. |
| internal/harness/ |
Passive hook, plugin, and extension translation plus bundle install and live-run diagnostics. |
| pkg/contract/ |
Public JSON contract types for controlled sessions and passive harness events. |
| pkg/controlplane/ |
Public Go request types, session helpers, metadata helpers, and text-generation router. |
| runtimes/ |
Runtime-specific fixtures, prompts, plugin source, extension notes, and passive harness README files. |
| docs/ |
Durable integration, contract, control-plane, and runtime documentation. |
| scripts/ |
README asset generation and README validation. |
| assets/ |
Generated README images and architecture source. |
| go.mod |
Go module definition. |
For a host integration, wire system.describe into your backend picker first,
then decide whether you need controlled sessions, passive telemetry, or both.