Unified Harness Protocol

Sessions

Unified Harness Protocol, version 2026-09-12

A session is what makes a second task cheaper than the first: the conversation is still there, and so is the working directory. This chapter defines continuing a session, inspecting one, and stopping work in one.

1. Continuing a session

Send the next task with previous_response_id:

{
  "input": "Now add tests for the function you just wrote.",
  "previous_response_id": "resp_a1b2c3"
}

The server MUST:

  • run the new task in the same session, with the same working directory and its files;
  • give the harness the conversational context of the earlier tasks;
  • use the same configured harness;
  • report the same metadata.session_id.

model MAY differ between tasks in a session. A server MUST honour a per-task model change, because switching to a cheaper model for a follow-up is a common and legitimate pattern.

If previous_response_id names an unknown response, the server MUST fail with 404 and code: "response_not_found". If the session it referred to has expired, 404 with code: "session_expired" — a client can retry the first from scratch, and should not retry the second.

Why chain on the response id rather than the session id? Because the response id is what the client already has: it comes back from the task it just ran. Chaining on it also names an exact point in the conversation, which leaves room for a server to branch from an earlier response later without changing the request shape.

2. Listing sessions

Conformance class Extended.

GET /v1/sessions?limit=20&cursor=&harness=chrn_…
{
  "sessions": [
    {
      "id": "hsess7e78…",
      "object": "session",
      "harness_id": "chrn_…",
      "title": "Summarise README.md",
      "status": "completed",
      "created_at": 1786400000,
      "updated_at": 1786400240
    }
  ],
  "next_cursor": null
}

Pagination is cursor-based. A server MUST return next_cursor: null on the last page, and MUST NOT require a client to detect the end by receiving fewer items than it asked for — that heuristic is wrong whenever a page is exactly full.

3. Inspecting a session

GET /v1/sessions/{session_id}
GET /v1/sessions/{session_id}/turns

/turns returns the ordered task history of the session, so a client can rebuild a transcript it did not store. Each turn identifies its response id, so a client can fetch the full response for any of them.

Each item in turns MUST carry at least:

Field Type Meaning
id string The response id of the turn, usable with GET /v1/responses/{id}.
status string The turn's status, in the response-status vocabulary of Tasks §1.

and SHOULD carry, when the server has them: user (string, the user message), assistant (string, the assistant's final text), tools (array, the tool calls of the turn), and files (array, the files the turn produced). A server MAY add fields; a client MUST ignore fields it does not understand. This was previously an object with no stated shape, which meant no conformance check could assert anything past the status code, and every client that rebuilt a transcript did so against one implementation's habits.

4. Cancelling

Two scopes, deliberately distinct:

POST /v1/responses/{response_id}/cancel     # stop this task
POST /v1/sessions/{session_id}/cancel       # stop whatever is running in this session

Semantics:

  • Cancellation is a request, not a guarantee of immediacy. A server MUST stop the work as soon as it can and MUST reach a terminal state.
  • A cancelled task MUST end with status: "cancelled", never failed.
  • Output produced before cancellation MUST be retained.
  • Cancelling an already-terminal task MUST succeed and change nothing. A client retrying a cancel after a dropped connection should not receive an error for having succeeded twice.
  • Cancelling MUST NOT delete the session. The conversation remains continuable.

A server SHOULD respond to cancel within one second even if the harness takes longer to wind down. The client is usually a user interface, and a Stop button that does nothing visible for thirty seconds reads as broken.

5. Session sharing

Conformance class Full. A server MAY let a client publish a read-only view of a session.

POST   /v1/sessions/{session_id}/share      # publish (a body is OPTIONAL; no body means publish)
GET    /v1/sessions/{session_id}/share      # read the share back
DELETE /v1/sessions/{session_id}/share      # revoke

If implemented, the share object returned by POST and GET MUST carry:

Field Type Meaning
id string The share's identity.
url string Where the shared view is served. May be relative, in which case it resolves against the base URL the caller is already using — a server behind a proxy cannot know its public origin, and a base-relative path survives every fronting.

object SHOULD be "session.share". A server MAY carry additional fields (the reference also returns an enabled toggle and accepts {"enabled": bool} bodies); a client MUST ignore fields it does not understand. A second POST MAY return the existing share or mint a new one, but revocation MUST reach every link minted for the session — revoking only the newest tells an operator the session is private while somebody still holds a working link.

The rules, unchanged:

  • the shared view MUST be read-only — it MUST NOT permit continuing, cancelling, or uploading;
  • GET on the published url MUST serve the view to a holder of the link with no other credential — a view only its creator can open has not been published to anyone;
  • after DELETE, and after the session itself is deleted (§6), the published url MUST stop resolving (404 or 410);
  • the share's id MUST NOT function as a credential for the rest of the API;
  • the server MUST NOT expose provider credentials, tokens, or another principal's data through the view.

These were previously behaviors the conformance suite had to discover by probing (the endpoints and the object had no stated shape, so a portable check could only guess at paths and skip when the guesses missed). They are codified from the shapes the reference implementation demonstrates and the R-series measures; the suite now asserts them.

6. Deleting

DELETE /v1/sessions/{session_id}

Deletes the session and its stored history: the transcript, the trace, and the working folder the session's tasks wrote. Afterwards the session MUST NOT count toward any storage or memory allowance the server enforces, because deletion is how a client makes room. A server MUST answer 2xx, and a later GET /v1/sessions/{session_id} MUST return 404.

DELETE /v1/traces/{session_id} is the older path for the same operation. It predates the session vocabulary; a server MAY keep serving it (the reference implementation does, as the same handler), but the path above is the one the protocol names and the one clients SHOULD use.

A server MUST cancel any in-flight task in the session first, and MUST make the session unreadable afterwards. Deletion is the one place where cancel and delete are legitimately coupled, because the alternative is a running task writing into storage that no longer has an owner.

Unified Harness Protocol 2026-09-12 · Apache-2.0 · defined and maintained in the HarnessRouter open-source repository. The standard can be implemented independently of any hosted service.