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. Any ACP agent that implements this contract can run locally through the machine catalog in plan, research, code, review, resolve conflicts, or a future workflow mode — mode eligibility is opt-out (unsupportedModes in the manifest), never an allowlist your runtime has to earn.
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": {
"attemptNumber": {
"type": "number"
},
"taskMode": {
"type": "string",
"enum": [
"plan",
"code",
"review",
"research",
"resolve conflicts"
]
},
"taskModeGuidance": {
"type": "string"
},
"stageContract": {
"type": "object",
"properties": {
"currentStage": {
"type": "string"
},
"objective": {
"type": "string"
},
"inputsReviewSubject": {
"type": "string"
},
"expectationBoundary": {
"type": "string"
},
"outcomeDiscipline": {
"type": "string"
}
},
"required": [
"currentStage",
"objective",
"inputsReviewSubject",
"expectationBoundary",
"outcomeDiscipline"
],
"additionalProperties": false
},
"expectedInputsGuidance": {
"type": "string"
},
"workflowBranch": {
"type": "string"
},
"nextStep": {
"type": "object",
"properties": {
"terminal": {
"type": "boolean"
},
"nextStepName": {
"type": "string"
},
"nextStepType": {
"type": "string",
"enum": [
"agent",
"human_gate",
"unknown"
]
},
"guidance": {
"type": "string"
}
},
"required": [
"terminal",
"guidance"
],
"additionalProperties": false
},
"verdictRoutesGuidance": {
"type": "string"
},
"finalReviewPassGuidance": {
"type": "string"
},
"completedGateDecisions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"stepName": {
"type": "string"
},
"decision": {
"type": "string",
"enum": [
"approve",
"reject",
"feedback",
"reassign"
]
},
"actorName": {
"type": "string"
},
"decidedAt": {
"type": "string"
}
},
"required": [
"stepName",
"decision",
"decidedAt"
],
"additionalProperties": false
}
}
},
"required": [
"attemptNumber",
"taskModeGuidance",
"stageContract"
],
"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, never by a human. The host resolves the exact requesting tool from the fulltoolCallpayload and its correlatedtool_call/tool_call_update; a request it can confidently match to the injectedone_platformserver gets a one-time allow automatically, including Vibe'sone_platform_<tool>identifier form, so a default-Ask permission mode does not block your agent from calling the platform tools a step requires. This is cooperative automation, not a sandbox — everything else (your own tools, external MCP servers, or an ambiguous match) falls back to the agent's configured permission policy unchanged, and an unresolvable identity is cancelled rather than approved. 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; routine lifecycle updates are retained for permission correlation but are not persisted as user-facing activity |
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 and research turns must write their declared artifact; review turns must call report_workflow_verdict. Every successful mode should call report_result with a one- or two-sentence, non-technical owner summary. Only execute requires that call; every other current or future mode falls back to the normal successful terminal summary when it is omitted or fails. The injected MCP tools are scoped to the active step.
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
ACP runtimes are eligible for every current and future workflow mode unless their manifest explicitly lists that mode in unsupportedModes. Installation performs a bounded doctor, ACP initialize, and session setup check; optional per-mode conformance remains diagnostic evidence and never unlocks routing.