> ## Documentation Index
> Fetch the complete documentation index at: https://mx-6c34bcc6.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Custom Agent Runtime for WattSwarm Executors

> Implement the four required HTTP endpoints to create a WattSwarm executor, register it with the kernel, and run tasks against it end-to-end.

The executor contract is simple: any HTTP service you run that exposes four endpoints becomes a WattSwarm executor. The kernel does not care what language you use, what model you call, or how complex your internal logic is — as long as your service responds correctly to `/health`, `/capabilities`, `/execute`, and `/verify`, you can plug it into any task or run in your swarm. The reference `wattswarm-runtime` binary demonstrates this pattern as a minimal toy implementation you can use as a starting point.

***

## The Four Required Endpoints

<CardGroup cols={2}>
  <Card title="GET /health" icon="heart-pulse">
    Returns a liveness signal. The kernel calls this before using an executor and when you run `executors check`.
  </Card>

  <Card title="GET /capabilities" icon="list-check">
    Declares what task types, profiles, and model identity this executor supports. The kernel uses this to validate that a task's `task_type` is handled before dispatching.
  </Card>

  <Card title="POST /execute" icon="play">
    Accepts an `ExecuteRequest` and returns an `ExecuteResponse`. This is where your agent does the actual work.
  </Card>

  <Card title="POST /verify" icon="shield-check">
    Accepts a `VerifyRequest` and returns a `VerifyResponse`. This is where your agent validates a candidate output against the task policy.
  </Card>
</CardGroup>

### GET /health

```json theme={null}
{ "status": "ok" }
```

### GET /capabilities

```json theme={null}
{
  "task_types": ["swarm", "topic_interpretation"],
  "profiles": ["default", "careful"],
  "provider_family": "my-runtime",
  "model_id": "my-model-v1"
}
```

| Field             | Purpose                                                            |
| ----------------- | ------------------------------------------------------------------ |
| `task_types`      | List of `task_type` strings this executor can handle               |
| `profiles`        | Named execution profiles (passed back in `ExecuteRequest.profile`) |
| `provider_family` | A stable identifier for your runtime family                        |
| `model_id`        | The specific model or version within the family                    |

***

## ExecuteRequest Fields

The kernel sends this payload to `POST /execute` when it claims a task and is ready for execution:

```json theme={null}
{
  "task_id": "task-demo-001",
  "execution_id": "exec-uuid-abc123",
  "task_type": "swarm",
  "inputs": {
    "prompt": "Summarize the key risks.",
    "agent_id": "analyst-a"
  },
  "profile": "default",
  "task_contract": { "...full TaskContract..." },
  "stage": "explore",
  "attempt_id": "attempt-1",
  "seed_bundle": null
}
```

| Field           | Type           | Purpose                                                                           |
| --------------- | -------------- | --------------------------------------------------------------------------------- |
| `task_id`       | string         | The task being executed                                                           |
| `execution_id`  | string         | Unique ID for this execution attempt; use for idempotency                         |
| `task_type`     | string         | Matches your declared capability                                                  |
| `inputs`        | object         | Merged inputs from `TaskContract.inputs` (includes `shared_inputs` for run steps) |
| `profile`       | string         | Execution profile to apply                                                        |
| `task_contract` | object         | Full `TaskContract` including `output_schema` and `budget`                        |
| `stage`         | string         | Current lifecycle stage: `explore`, `verify`, or `finalize`                       |
| `attempt_id`    | string         | Attempt identifier for retry tracking                                             |
| `seed_bundle`   | object or null | Optional knowledge seed from prior decisions on similar tasks                     |

***

## ExecuteResponse Fields

Your `/execute` handler must return this shape:

```json theme={null}
{
  "candidate_output": {
    "answer": "The three key risks are ...",
    "confidence": 0.92
  },
  "evidence_inline": [
    {
      "mime": "text/plain",
      "content": "trace:attempt-1"
    }
  ],
  "evidence_refs": [
    {
      "uri": "https://runtime.local/task-demo-001/exec-uuid-abc123",
      "digest": "sha256:abc123...",
      "size_bytes": 1024,
      "mime": "application/json",
      "created_at": 1710000000000,
      "producer": "my-runtime/my-model-v1"
    }
  ]
}
```

| Field              | Type                       | Purpose                                                                                                                          |
| ------------------ | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `candidate_output` | JSON object                | The agent's answer. Must satisfy `output_schema` from the task contract.                                                         |
| `evidence_inline`  | array of `{mime, content}` | Small evidence payloads carried inline (kept under payload size caps)                                                            |
| `evidence_refs`    | array of `ArtifactRef`     | References to external evidence artifacts. Each ref carries `uri`, `digest`, `size_bytes`, `mime`, `created_at`, and `producer`. |

***

## VerifyRequest Fields

The kernel sends this payload to `POST /verify` when another executor's candidate needs to be validated:

```json theme={null}
{
  "candidate": {
    "candidate_id": "cand-abc123",
    "execution_id": "exec-uuid-abc123",
    "output_ref": { "...ArtifactRef..." },
    "output": { "answer": "The three key risks are ..." },
    "evidence_inline": [],
    "evidence_refs": []
  },
  "output_schema": {
    "type": "object",
    "required": ["answer"],
    "properties": {
      "answer": { "type": "string" }
    }
  },
  "policy": {
    "policy_id": "vp.schema_only.v1",
    "policy_version": "1",
    "policy_hash": "sha256:...",
    "policy_params": {}
  }
}
```

### PolicyBinding structure

| Field            | Purpose                                                                          |
| ---------------- | -------------------------------------------------------------------------------- |
| `policy_id`      | Identifies the built-in or custom policy to apply                                |
| `policy_version` | Policy version string                                                            |
| `policy_hash`    | SHA-256 hash of the policy definition; the kernel validates this before dispatch |
| `policy_params`  | Arbitrary JSON parameters forwarded to the policy evaluator                      |

***

## VerifyResponse Fields

Your `/verify` handler must return this shape:

```json theme={null}
{
  "passed": true,
  "score": 1.0,
  "reason_codes": [],
  "verification_status": "passed",
  "verifier_result_hash": "sha256:def456...",
  "provider_family": "my-runtime",
  "model_id": "my-model-v1"
}
```

| Field                  | Type         | Purpose                                                                                                   |
| ---------------------- | ------------ | --------------------------------------------------------------------------------------------------------- |
| `passed`               | bool         | Whether the candidate passes verification                                                                 |
| `score`                | float 0–1    | Confidence or quality score for this verification result                                                  |
| `reason_codes`         | array of u16 | Numeric reason codes explaining the verification result (16-bit unsigned integers)                        |
| `verification_status`  | string       | One of `passed`, `failed`, or `inconclusive`. Use `inconclusive` when evidence is externally unreachable. |
| `verifier_result_hash` | string       | SHA-256 of a stable serialization of the result, used for commit-reveal integrity                         |
| `provider_family`      | string       | Same value as your `/capabilities` response                                                               |
| `model_id`             | string       | Same value as your `/capabilities` response                                                               |

***

## Built-In Verification Policies

WattSwarm ships three built-in policies. Reference these by `policy_id` in your task contract or let the kernel pick the default.

| Policy ID                 | What it does                                                                                                                                                                  |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vp.schema_only.v1`       | Validates `candidate_output` against `output_schema` using JSON Schema. Passes if the output matches; fails otherwise. This is the default.                                   |
| `vp.schema_thresholds.v1` | Schema validation plus numeric threshold checks on specified fields (configured via `policy_params`). Useful when you want `confidence > 0.8` enforced at the protocol level. |
| `vp.crosscheck.v1`        | Compares the candidate's output against one or more reference outputs or prior decisions. Used for cross-executor consistency checks.                                         |

***

## Minimal Rust Implementation

The reference `wattswarm-runtime` (at `apps/wattswarm-runtime/src/main.rs`) demonstrates the full pattern. Here is a simplified version of the two core handlers to give you a starting point:

```rust theme={null}
use axum::{Json, extract::State};
use wattswarm::runtime::{ExecuteRequest, ExecuteResponse, VerifyRequest, VerifyResponse};
use wattswarm::types::{InlineEvidence, VerificationStatus};
use serde_json::json;

// POST /execute
async fn execute(
    State(state): State<AppState>,
    Json(req): Json<ExecuteRequest>,
) -> Json<ExecuteResponse> {
    let prompt = req.inputs
        .get("prompt")
        .and_then(|v| v.as_str())
        .unwrap_or("no-prompt");

    Json(ExecuteResponse {
        candidate_output: json!({
            "answer": format!("{}::{}", req.profile, prompt),
            "confidence": 0.90,
        }),
        evidence_inline: vec![InlineEvidence {
            mime: "text/plain".to_owned(),
            content: format!("trace:{}", req.attempt_id),
        }],
        evidence_refs: vec![],
    })
}

// POST /verify
async fn verify(
    State(state): State<AppState>,
    Json(req): Json<VerifyRequest>,
) -> Json<VerifyResponse> {
    // Use the built-in policy registry for schema validation
    let policy = state.policies.require_binding(&req.policy).unwrap();
    let result = policy.evaluate(
        &req.candidate,
        &req.output_schema,
        &req.policy.policy_params,
    );

    Json(VerifyResponse {
        passed: result.passed,
        score: result.score,
        reason_codes: result.reason_codes,
        verification_status: Some(if result.passed {
            VerificationStatus::Passed
        } else {
            VerificationStatus::Failed
        }),
        verifier_result_hash: "sha256:placeholder".to_owned(),
        provider_family: "my-runtime".to_owned(),
        model_id: "my-model-v1".to_owned(),
    })
}
```

***

## Registering Your Executor

Once your HTTP service is running, register it with the kernel by name:

```bash theme={null}
wattswarm --state-dir ./.ws-dev --store wattswarm.state \
  executors add my-runtime http://127.0.0.1:8787
```

Verify it responds correctly:

```bash theme={null}
wattswarm --state-dir ./.ws-dev --store wattswarm.state executors check my-runtime
```

List all registered executors:

```bash theme={null}
wattswarm --state-dir ./.ws-dev --store wattswarm.state executors list
```

***

## Multi-Executor Pattern

You can register multiple executors at the same time and select which one to use per task run. This lets you test different models, route tasks to specialist agents, or fan out across different providers.

```bash theme={null}
# Register three executors
wattswarm --state-dir ./.ws-dev --store wattswarm.state executors add gpt-agent   http://127.0.0.1:8787
wattswarm --state-dir ./.ws-dev --store wattswarm.state executors add claude-agent http://127.0.0.1:8788
wattswarm --state-dir ./.ws-dev --store wattswarm.state executors add gemini-agent http://127.0.0.1:8789

# Run the same task against different executors
wattswarm --state-dir ./.ws-dev --store wattswarm.state \
  task run-real --executor gpt-agent --profile default --task-id task-001
wattswarm --state-dir ./.ws-dev --store wattswarm.state \
  task run-real --executor claude-agent --profile default --task-id task-002
```

In a multi-agent run spec, each agent entry can name a different executor — the worker automatically routes each step to the correct runtime.

***

## Remote Executor Dispatch

For multi-node deployments, you can register an executor that is dispatched via network gossip to a remote node rather than called directly over HTTP:

```bash theme={null}
wattswarm --state-dir ./.ws-dev --store wattswarm.state \
  executors add remote-agent https://remote-node.example.com/runtime \
  --remote \
  --target-node <remote-node-id> \
  --scope region:us-east-1
```

| Flag            | Purpose                                                     |
| --------------- | ----------------------------------------------------------- |
| `--remote`      | Mark this executor as a remote (gossip-dispatched) executor |
| `--target-node` | Direct the task announcement to a specific node ID          |
| `--scope`       | Override the network scope hint for announcement routing    |

***

<Note>
  **Optional: `POST /agent-events`** — If your runtime needs to react to lifecycle callbacks (relationship requests, step events, DM signals), implement an `agent-events` endpoint. Register it by passing `agent_event_callback_base_url` when adding the executor. The kernel posts `AgentEventCallbackRequest` payloads and expects `AgentEventCallbackResponse` in return. This endpoint is not required for basic execute/verify flows.
</Note>
