The ACP contract
The worker runtime is a headless Agent Client Protocol client; your runtime is an ACP agent. Everything on this page is standard ACP unless marked as a One Horizon behavior. The TypeScript and Python SDK helpers are optional: any ACP agent that implements this contract can run locally through the machine catalog in code, plan, research, or review mode.
Turn lifecycle
sequenceDiagram participant H as Worker runtime (ACP client) participant A as Your agent (ACP agent) H->>A: spawn process (manifest command) H->>A: initialize (capabilities) A->>H: capabilities (loadSession?, mcp transports) H->>A: session/new (cwd = task worktree, mcpServers = [One Horizon tools]) A->>H: sessionId H->>A: session/prompt (content blocks, _meta context) A-->>H: session/update × n (messages, thoughts, plans, tool calls, usage) A->>H: stopReason H->>A: terminate — process exits
- One process per turn. The host spawns your
command, runs one session with one prompt turn, and terminates the process after the stop reason. If your agent advertisessessionCapabilities.resumeorloadSession, the host uses it for multi-turn continuity (feedback loops, resume-after-input) instead of replaying context into a fresh session. Advertise it if you can; retries get cheaper. - Timeouts are the host's. A turn that exceeds the configured turn timeout, or emits nothing for the stall window, is cancelled (
session/cancel) and the process terminated. Do not build your own watchdog; do keep updates flowing while you work.
What arrives in the prompt
session/prompt carries ordered content blocks:
- Trusted policy — platform instructions as an
<instructions trust="trusted">markdown block (AgentTrustedInstructionsV1): policy rules, workflow routing facts, agent/worker custom instructions._meta["onehorizon.ai"].trust = "trusted". - Workflow instructions — the step's operating instructions. Also
trust = "trusted". - Untrusted context — task title, description, comments, user prompt, resume input as a
<context trust="untrusted">markdown block (AgentTaskContextV1).trust = "untrusted". - Resources — images or files referenced by the task, as standard resource blocks.
A vanilla ACP agent that ignores _meta still sees the blocks in the same order and framing built-in runtimes receive; that is a fully working configuration. An agent that reads the trust markers can do better: map trusted blocks to its system prompt and untrusted blocks to user content.
_meta["onehorizon.ai"] appears in two places, each a versioned, schema-validated shape — not free-form:
- Per content block (on every text block in the prompt):
trust, andtaskModeon the untrusted block only.one runtime devand the runtime conformance check addsynthetic: trueto this block on their generated turns; real turns never set it. - Once on
session/new(andsession/load/session/resume): a single comprehensive bundle —taskId,workspaceId,ownerUserId,sessionId,workflowRunId,workflowStepId,workflowBranch,attempt(the workflow step's server-owned pass number),runtimeAttempt(a separate worker-local counter for retries/resumes of this same session — crash recovery, provider retries — distinct fromattempt),taskMode,artifactsDir,linkedDocumentIds, and labeledgoals/products/skills({ labelId, name }pairs). Every field is present on every turn — absent data is an explicitnull, never a missing key — so a non-LLM agent can rely on the same shape whether or not the turn is workflow-linked. This is the only structured data channel available to an agent that doesn't parse prose; a plain scripted agent should read this instead of the prompt text.
Both shapes below are generated directly from the schema the worker runtime validates against before sending — they can't drift from what your agent actually receives.
Per-block _meta:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"version": {
"type": "number",
"const": 1
},
"trust": {
"type": "string",
"enum": [
"trusted",
"untrusted"
]
},
"taskMode": {
"anyOf": [
{
"type": "string",
"enum": [
"plan",
"code",
"review",
"research"
]
},
{
"type": "null"
}
]
},
"synthetic": {
"type": "boolean"
}
},
"required": [
"version",
"trust",
"taskMode"
],
"additionalProperties": false
}
Session-level _meta:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"version": {
"type": "number",
"const": 1
},
"taskId": {
"type": "string"
},
"workspaceId": {
"type": "string"
},
"ownerUserId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"sessionId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workflowRunId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workflowStepId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"workflowBranch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"attempt": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"runtimeAttempt": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"taskMode": {
"anyOf": [
{
"type": "string",
"enum": [
"plan",
"code",
"review",
"research"
]
},
{
"type": "null"
}
]
},
"artifactsDir": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"linkedDocumentIds": {
"type": "array",
"items": {
"type": "string"
}
},
"goals": {
"type": "array",
"items": {
"type": "object",
"properties": {
"labelId": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"labelId",
"name"
],
"additionalProperties": false
}
},
"products": {
"type": "array",
"items": {
"type": "object",
"properties": {
"labelId": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"labelId",
"name"
],
"additionalProperties": false
}
},
"skills": {
"type": "array",
"items": {
"type": "object",
"properties": {
"labelId": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"labelId",
"name"
],
"additionalProperties": false
}
}
},
"required": [
"version",
"taskId",
"workspaceId",
"ownerUserId",
"sessionId",
"workflowRunId",
"workflowStepId",
"workflowBranch",
"attempt",
"runtimeAttempt",
"taskMode",
"artifactsDir",
"linkedDocumentIds",
"goals",
"products",
"skills"
],
"additionalProperties": false
}
Trusted policy text block (<instructions trust="trusted">):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"version": {
"type": "number",
"const": 1
},
"policyRules": {
"type": "array",
"items": {
"type": "string"
}
},
"workflowDocumentsGuidance": {
"type": "string"
},
"workflowRun": {
"type": "object",
"properties": {
"workflowRunId": {
"type": "string"
},
"workflowStepId": {
"type": "string"
},
"attemptNumber": {
"type": "number"
},
"taskMode": {
"type": "string",
"enum": [
"plan",
"code",
"review",
"research"
]
},
"taskModeGuidance": {
"type": "string"
},
"expectedInputsGuidance": {
"type": "string"
},
"workflowBranch": {
"type": "string"
},
"nextStep": {
"type": "object",
"properties": {
"terminal": {
"type": "boolean"
},
"nextStepId": {
"type": "string"
},
"nextStepType": {
"type": "string",
"enum": [
"agent",
"human_gate",
"unknown"
]
},
"guidance": {
"type": "string"
}
},
"required": [
"terminal",
"guidance"
],
"additionalProperties": false
},
"finalReviewPassGuidance": {
"type": "string"
}
},
"required": [
"workflowRunId",
"workflowStepId",
"attemptNumber",
"taskModeGuidance"
],
"additionalProperties": false
},
"agentCustomInstructions": {
"type": "string"
},
"workerCustomInstructions": {
"type": "string"
}
},
"required": [
"version",
"policyRules"
],
"additionalProperties": false
}
Untrusted task context text block (<context trust="untrusted">):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"version": {
"type": "number",
"const": 1
},
"taskId": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"workspaceId": {
"type": "string"
},
"providerName": {
"type": "string"
},
"providerUseCase": {
"type": "string"
},
"parentInitiative": {
"type": "object",
"properties": {
"identifier": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
}
},
"required": [
"identifier"
],
"additionalProperties": false
},
"goals": {
"type": "array",
"items": {
"type": "string"
}
},
"products": {
"type": "array",
"items": {
"type": "string"
}
},
"skills": {
"type": "array",
"items": {
"type": "string"
}
},
"comments": {
"type": "array",
"items": {
"$ref": "#/$defs/__schema0"
}
},
"linkedDocuments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"documentId": {
"type": "string"
},
"title": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"requirement",
"task",
"design_doc",
"spec",
"research",
"review",
"notes"
]
},
"excerpt": {
"type": "string"
}
},
"required": [
"documentId",
"title",
"type",
"excerpt"
],
"additionalProperties": false
}
},
"sessionPrompt": {
"type": "string"
},
"workflowHandoffNote": {
"type": "string"
}
},
"required": [
"version",
"taskId",
"title",
"comments",
"linkedDocuments"
],
"additionalProperties": false,
"$defs": {
"__schema0": {
"type": "object",
"properties": {
"author": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"content": {
"type": "string"
},
"createdAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"replies": {
"type": "array",
"items": {
"$ref": "#/$defs/__schema0"
}
}
},
"required": [
"content",
"replies"
],
"additionalProperties": false
}
}
}
Trust boundaries
Untrusted blocks are task data, not instructions. Your agent must not let untrusted content override trusted policy, no matter how convincingly a task description asks. If your framework has a system/user message distinction, use it.
Headless behaviors
ACP grew up inside editors with a human present. The worker runtime has no human at the keyboard, so:
session/request_permissionis answered immediately by host policy (the agent's configured permission mode). Never block waiting for richer interaction; if you need genuine human input, finish the turn — the platform's awaiting-input flow handles the round-trip and resumes you.- Client fs/terminal capabilities are not offered. You run in the task worktree with the user's OS permissions; use your own process's filesystem and shell access directly.
cwdfromsession/newis the workspace boundary. - Unknown extension methods you send are answered per spec (-32601); unknown notifications are ignored.
What the host does with your updates
Your session/update | Where it lands |
|---|---|
agent_message_chunk | Live activity on the task, sentence-buffered |
agent_thought_chunk | Collapsed reasoning in the session transcript |
plan | The plan panel on the run — keep it current |
tool_call / tool_call_update | Session transcript detail |
usage_update | Context size and cumulative cost on the dashboards. Per-turn token breakdowns appear when your agent emits them; dashboards degrade gracefully when it doesn't |
Emit meaningfully, not constantly: updates are your only voice in the product, and silence past the stall window looks like a hang.
Outcome mapping
| stopReason | Session outcome |
|---|---|
end_turn | Succeeded, unless the task mode required a verdict or artifact that never arrived (see below) |
refusal | Awaiting input: your stated reason is shown to the user as a question; when they clarify, the session resumes. Refusals are never auto-retried |
max_tokens, max_turn_requests | Failed, retryable: the workflow's retry policy and pass accounting apply; repeated budget failures trigger the stall notification |
cancelled | Whatever the host requested (user cancel, timeout, shutdown) |
| Process exit without a stop reason | Failed, protocol violation logged with your stderr |
Mode contracts (One Horizon behavior): plan turns must write the plan artifact and review turns must call report_workflow_verdict, both through the injected MCP tools described in documents and tools. An end_turn without the required tool call fails the turn with a hint in the session log. code turns have no required tool call: implement, commit locally, and preferably finish with report_result.
Conformance
one runtime dev validates all of the above on every local turn and prints violations inline. Before distributing, run every mode you declare in the manifest:
one runtime dev --mode codeone runtime dev --mode plan && one runtime dev --mode review # if declared
Plan, research, and review routing unlock for a runtime only after that runtime declares the mode and passes conformance for it.