# Agent: Session settings **Page:** api/agent/sessions/settings [Download Raw Markdown](./api/agent/sessions/settings.md) --- # Agent: Session settings Most endpoints on this page are `PATCH` calls that change one property of an existing agent session and take effect from the next turn. Each call is safe to issue while a turn is running; the gateway forwards the change to the live session and, when a confirm or question gate is parked, defers the change to apply between turns (a 200 response with `deferred: true` reports this). Two settings break the pattern. The model switch applies inline and synchronously, so a busy session returns `409` instead of a deferred success. The after-compaction message is a `PUT` and is the other exception: the agent re-adds the text as a user message right after the summary of every compaction, before the next model request, rather than waiting for the next turn. ## Agent and model ### `PATCH /api/v1/agent/sessions/{id}/agent` Live chat-agent switch, echoed as a session event. Safe to call while a turn is running; deferred between turns if a gate is parked. #### Parameters | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `id` | path | string | Yes | Path identifier. | | `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `todos.create` also accepts a body cwd). | | `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves against. | | `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. | | `X-Hoody-Realm` | header | string | No | Per-request realm selector: `global` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | | `realm` | query | string | No | Per-request realm selector, the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `global` or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | #### Request body | Field | Type | Required | Description | |-------|------|----------|-------------| | `agent` | string | No | Chat-agent name to switch to. | ```bash curl -X PATCH "https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com/api/v1/agent/sessions/{id}/agent" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"agent":"build"}' ``` ```ts import { HoodyClient } from 'hoody-sdk'; const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com', token: process.env.HOODY_TOKEN }); await client.agent.sessions.setAgent('{id}', { agent: 'build' }); ``` ```json { "status": "ok" } ``` #### Responses Applied. ```json { "status": "ok" } ``` When a gate is parked, the response also carries `deferred: true` and a `note` explaining why the command has not yet been applied. | Field | Type | Description | |-------|------|-------------| | `status` | string | `ok` on success. | | `deferred` | boolean | Present and `true` when a gate is parked: the command is applied only between turns. | | `note` | string | Present alongside `deferred`: why the command has not been applied yet. | ```json { "code": "bad_request", "message": "invalid request" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. | | `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only or global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. | | `invalid_realm` | Invalid realm selector | The realm selector is malformed (not ""/"global"/a 24-hex id). | Pass a valid realm selector. | ```json { "code": "forbidden", "message": "request must arrive through the Hoody proxy" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `forbidden` | Forbidden (Source IP Guard) | The request did not come through the program's URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with `hoody agent …`. | ```json { "code": "not_found", "message": "resource not found" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. | ```json { "code": "payload_too_large", "message": "request body exceeds the configured size limit" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it, so a well-formed-but-large body is a size violation, not a JSON syntax error. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. | ```json { "code": "rate_limited", "message": "request rate limit exceeded" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. | ```json { "code": "internal_error", "message": "internal server error" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. | ```json { "code": "service_unavailable", "message": "service unavailable" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor `Retry-After` and retry. | ### `PATCH /api/v1/agent/sessions/{id}/model` Synchronous inline model switch: applies the model now and returns exactly what happened (`persisted: false` means the live switch stands but reverts next session). Safe to call while a turn is running, but returns `409` instead of deferring if the session is busy or external-agent owned. A successful switch persists into the chat agent's frontmatter, which is a global repin for future sessions of that agent, not a session-scoped choice. Connected peers converge via the emitted `init_state` event. #### Parameters | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `id` | path | string | Yes | Path identifier. | | `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `todos.create` also accepts a body cwd). | | `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves against. | | `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. | | `X-Hoody-Realm` | header | string | No | Per-request realm selector: `global` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | | `realm` | query | string | No | Per-request realm selector, the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `global` or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | #### Request body | Field | Type | Required | Description | |-------|------|----------|-------------| | `model` | string | Yes | Model spec to switch to (provider-prefixed, e.g. `anthropic/claude-opus-4-8`, or `fusion/`). A blank value is rejected and never treated as a silent no-op. | ```bash curl -X PATCH "https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com/api/v1/agent/sessions/{id}/model" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"anthropic/claude-opus-4-8"}' ``` ```ts import { HoodyClient } from 'hoody-sdk'; const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com', token: process.env.HOODY_TOKEN }); await client.agent.sessions.setModel('{id}', { model: 'anthropic/claude-opus-4-8' }); ``` ```json { "status": "ok", "model": "anthropic/claude-opus-4-8", "persisted": true } ``` #### Responses Applied. ```json { "status": "ok", "model": "anthropic/claude-opus-4-8", "persisted": true } ``` | Field | Type | Description | |-------|------|-------------| | `status` | string | `ok` on a successful switch. | | `model` | string | The model spec now active on the session. | | `persisted` | boolean | Whether the choice was written to the chat agent's frontmatter. `false` means the live switch stands but reverts next session. | ```json { "code": "bad_request", "message": "invalid request" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `bad_request` | Bad request | The request was malformed or carried invalid parameters (including a blank `model`). | Correct the request body or query parameters. | | `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only or global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. | | `invalid_realm` | Invalid realm selector | The realm selector is malformed (not ""/"global"/a 24-hex id). | Pass a valid realm selector. | ```json { "code": "forbidden", "message": "request must arrive through the Hoody proxy" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `forbidden` | Forbidden (Source IP Guard) | The request did not come through the program's URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with `hoody agent …`. | Session unknown, closed (including the close-vs-switch race), cross-realm, or foreign-owner, all collapsed into one answer so nothing leaks. ```json { "code": "not_found", "message": "session not live" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `not_found` | Session not found | Unknown, closed (including the close-vs-switch race), cross-realm, or foreign-owner session, all collapsed into one code and one message so nothing leaks. | List sessions and retry against a live one. | Session busy or delegated. ```json { "code": "turn_in_flight", "message": "a turn is running (or parked on a gate) — retry when the session is idle" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `gate_parked` | Gate parked | A confirm or question gate is parked; the switch cannot apply until it resolves. | Answer or cancel the gate under `details.pending_gate`, then retry. | | `turn_in_flight` | Turn in flight | A turn is running (or parked) on the session's single-writer slot. | Retry when the session is idle. | | `delegated_session` | Delegated session | The external agent owns its model; switching is unavailable. | Use the external agent's own configuration. | ```json { "code": "payload_too_large", "message": "request body exceeds the configured size limit" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it, so a well-formed-but-large body is a size violation, not a JSON syntax error. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. | Model cannot be constructed. ```json { "code": "model_unavailable", "message": "cannot switch to model \"x/y\": unknown provider" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `model_unavailable` | Model unavailable | Unknown model, missing credential, or invalid fusion composite. | Check the spec and provider credentials. | ```json { "code": "rate_limited", "message": "request rate limit exceeded" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. | ```json { "code": "internal_error", "message": "internal server error" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. | ```json { "code": "service_unavailable", "message": "service unavailable" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor `Retry-After` and retry. | Session still initializing when the request ended. ```json { "code": "timeout", "message": "session still initializing" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `timeout` | Initialization wait cut short | The switch waited on session readiness and the request context ended first. | Retry once the session finishes initializing. | ## Reasoning and response ### `PATCH /api/v1/agent/sessions/{id}/effort` Live reasoning-effort change. Accepts `low`, `medium`, `high`, `xhigh`, `max`, or an empty string for the model default. Safe to call while a turn is running; deferred between turns if a gate is parked. #### Parameters | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `id` | path | string | Yes | Path identifier. | | `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `todos.create` also accepts a body cwd). | | `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves against. | | `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. | | `X-Hoody-Realm` | header | string | No | Per-request realm selector: `global` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | | `realm` | query | string | No | Per-request realm selector, the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `global` or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | #### Request body | Field | Type | Required | Description | |-------|------|----------|-------------| | `effort` | string | No | `low`, `medium`, `high`, `xhigh`, `max`, or `""` for the model default. | ```bash curl -X PATCH "https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com/api/v1/agent/sessions/{id}/effort" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"effort":"high"}' ``` ```ts import { HoodyClient } from 'hoody-sdk'; const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com', token: process.env.HOODY_TOKEN }); await client.agent.sessions.setEffort('{id}', { effort: 'high' }); ``` ```json { "status": "ok" } ``` #### Responses Applied. ```json { "status": "ok" } ``` When a gate is parked, the response also carries `deferred: true` and a `note` explaining why the command has not yet been applied. | Field | Type | Description | |-------|------|-------------| | `status` | string | `ok` on success. | | `deferred` | boolean | Present and `true` when a gate is parked: the command is applied only between turns. | | `note` | string | Present alongside `deferred`: why the command has not been applied yet. | ```json { "code": "bad_request", "message": "invalid request" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. | | `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only or global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. | | `invalid_realm` | Invalid realm selector | The realm selector is malformed (not ""/"global"/a 24-hex id). | Pass a valid realm selector. | ```json { "code": "forbidden", "message": "request must arrive through the Hoody proxy" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `forbidden` | Forbidden (Source IP Guard) | The request did not come through the program's URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with `hoody agent …`. | ```json { "code": "not_found", "message": "resource not found" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. | ```json { "code": "payload_too_large", "message": "request body exceeds the configured size limit" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it, so a well-formed-but-large body is a size violation, not a JSON syntax error. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. | ```json { "code": "rate_limited", "message": "request rate limit exceeded" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. | ```json { "code": "internal_error", "message": "internal server error" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. | ```json { "code": "service_unavailable", "message": "service unavailable" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor `Retry-After` and retry. | ### `PATCH /api/v1/agent/sessions/{id}/verbosity` Live verbosity change. Accepts `normal`, `concise`, `terse`, or `minimal`. Safe to call while a turn is running; deferred between turns if a gate is parked. #### Parameters | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `id` | path | string | Yes | Path identifier. | | `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `todos.create` also accepts a body cwd). | | `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves against. | | `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. | | `X-Hoody-Realm` | header | string | No | Per-request realm selector: `global` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | | `realm` | query | string | No | Per-request realm selector, the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `global` or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | #### Request body | Field | Type | Required | Description | |-------|------|----------|-------------| | `level` | string | No | `normal`, `concise`, `terse`, or `minimal`. | ```bash curl -X PATCH "https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com/api/v1/agent/sessions/{id}/verbosity" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"level":"concise"}' ``` ```ts import { HoodyClient } from 'hoody-sdk'; const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com', token: process.env.HOODY_TOKEN }); await client.agent.sessions.setVerbosity('{id}', { level: 'concise' }); ``` ```json { "status": "ok" } ``` #### Responses Applied. ```json { "status": "ok" } ``` When a gate is parked, the response also carries `deferred: true` and a `note` explaining why the command has not yet been applied. | Field | Type | Description | |-------|------|-------------| | `status` | string | `ok` on success. | | `deferred` | boolean | Present and `true` when a gate is parked: the command is applied only between turns. | | `note` | string | Present alongside `deferred`: why the command has not been applied yet. | ```json { "code": "bad_request", "message": "invalid request" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. | | `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only or global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. | | `invalid_realm` | Invalid realm selector | The realm selector is malformed (not ""/"global"/a 24-hex id). | Pass a valid realm selector. | ```json { "code": "forbidden", "message": "request must arrive through the Hoody proxy" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `forbidden` | Forbidden (Source IP Guard) | The request did not come through the program's URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with `hoody agent …`. | ```json { "code": "not_found", "message": "resource not found" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. | ```json { "code": "payload_too_large", "message": "request body exceeds the configured size limit" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it, so a well-formed-but-large body is a size violation, not a JSON syntax error. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. | ```json { "code": "rate_limited", "message": "request rate limit exceeded" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. | ```json { "code": "internal_error", "message": "internal server error" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. | ```json { "code": "service_unavailable", "message": "service unavailable" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor `Retry-After` and retry. | ## Auto-reply loop ### `PATCH /api/v1/agent/sessions/{id}/auto-reply` Arm or disarm the self-driving auto-reply loop, including the round budget, replier model, and write opt-in. Safe to call while a turn is running; deferred between turns if a gate is parked. #### Parameters | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `id` | path | string | Yes | Path identifier. | | `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `todos.create` also accepts a body cwd). | | `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves against. | | `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. | | `X-Hoody-Realm` | header | string | No | Per-request realm selector: `global` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | | `realm` | query | string | No | Per-request realm selector, the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `global` or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | #### Request body | Field | Type | Required | Description | |-------|------|----------|-------------| | `armed` | boolean | No | `true` to arm the auto-reply loop, `false` to disarm. | | `rounds` | integer | No | Number of auto-reply rounds budgeted. | | `model` | string | No | Replier model override. | | `allow_writes` | boolean | No | Opt in to write-class actions during auto-reply. | ```bash curl -X PATCH "https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com/api/v1/agent/sessions/{id}/auto-reply" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"armed":true,"rounds":5,"model":"anthropic/claude-haiku-4-5","allow_writes":false}' ``` ```ts import { HoodyClient } from 'hoody-sdk'; const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com', token: process.env.HOODY_TOKEN }); await client.agent.sessions.setAutoReply('{id}', { armed: true, rounds: 5, model: 'anthropic/claude-haiku-4-5', allow_writes: false, }); ``` ```json { "status": "ok" } ``` #### Responses Applied. ```json { "status": "ok" } ``` When a gate is parked, the response also carries `deferred: true` and a `note` explaining why the command has not yet been applied. | Field | Type | Description | |-------|------|-------------| | `status` | string | `ok` on success. | | `deferred` | boolean | Present and `true` when a gate is parked: the command is applied only between turns. | | `note` | string | Present alongside `deferred`: why the command has not been applied yet. | ```json { "code": "bad_request", "message": "invalid request" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. | | `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only or global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. | | `invalid_realm` | Invalid realm selector | The realm selector is malformed (not ""/"global"/a 24-hex id). | Pass a valid realm selector. | ```json { "code": "forbidden", "message": "request must arrive through the Hoody proxy" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `forbidden` | Forbidden (Source IP Guard) | The request did not come through the program's URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with `hoody agent …`. | ```json { "code": "not_found", "message": "resource not found" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. | ```json { "code": "payload_too_large", "message": "request body exceeds the configured size limit" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it, so a well-formed-but-large body is a size violation, not a JSON syntax error. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. | ```json { "code": "rate_limited", "message": "request rate limit exceeded" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. | ```json { "code": "internal_error", "message": "internal server error" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. | ```json { "code": "service_unavailable", "message": "service unavailable" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor `Retry-After` and retry. | ### `PATCH /api/v1/agent/sessions/{id}/auto-reply/writes` Flip the write-class opt-in on an already-armed auto-reply loop without re-arming (no budget reset). Safe to call while a turn is running; deferred between turns if a gate is parked. #### Parameters | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `id` | path | string | Yes | Path identifier. | | `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `todos.create` also accepts a body cwd). | | `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves against. | | `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. | | `X-Hoody-Realm` | header | string | No | Per-request realm selector: `global` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | | `realm` | query | string | No | Per-request realm selector, the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `global` or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | #### Request body | Field | Type | Required | Description | |-------|------|----------|-------------| | `allow_writes` | boolean | No | New write-class opt-in state. | ```bash curl -X PATCH "https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com/api/v1/agent/sessions/{id}/auto-reply/writes" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"allow_writes":true}' ``` ```ts import { HoodyClient } from 'hoody-sdk'; const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com', token: process.env.HOODY_TOKEN }); await client.agent.sessions.setAutoReplyWrites('{id}', { allow_writes: true }); ``` ```json { "status": "ok" } ``` #### Responses Applied. ```json { "status": "ok" } ``` When a gate is parked, the response also carries `deferred: true` and a `note` explaining why the command has not yet been applied. | Field | Type | Description | |-------|------|-------------| | `status` | string | `ok` on success. | | `deferred` | boolean | Present and `true` when a gate is parked: the command is applied only between turns. | | `note` | string | Present alongside `deferred`: why the command has not been applied yet. | ```json { "code": "bad_request", "message": "invalid request" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. | | `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only or global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. | | `invalid_realm` | Invalid realm selector | The realm selector is malformed (not ""/"global"/a 24-hex id). | Pass a valid realm selector. | ```json { "code": "forbidden", "message": "request must arrive through the Hoody proxy" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `forbidden` | Forbidden (Source IP Guard) | The request did not come through the program's URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with `hoody agent …`. | ```json { "code": "not_found", "message": "resource not found" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. | ```json { "code": "payload_too_large", "message": "request body exceeds the configured size limit" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it, so a well-formed-but-large body is a size violation, not a JSON syntax error. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. | ```json { "code": "rate_limited", "message": "request rate limit exceeded" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. | ```json { "code": "internal_error", "message": "internal server error" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. | ```json { "code": "service_unavailable", "message": "service unavailable" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor `Retry-After` and retry. | ## Shell environment ### `PATCH /api/v1/agent/sessions/{id}/hoody-env` Live toggle of the session's `HOODY_*` shell-env contract for the bash tool. Safe to call while a turn is running; deferred between turns if a gate is parked. #### Parameters | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `id` | path | string | Yes | Path identifier. | | `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `todos.create` also accepts a body cwd). | | `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves against. | | `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. | | `X-Hoody-Realm` | header | string | No | Per-request realm selector: `global` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | | `realm` | query | string | No | Per-request realm selector, the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `global` or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | #### Request body | Field | Type | Required | Description | |-------|------|----------|-------------| | `enabled` | boolean | No | Whether to inject the `HOODY_*` shell-env contract. | ```bash curl -X PATCH "https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com/api/v1/agent/sessions/{id}/hoody-env" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"enabled":false}' ``` ```ts import { HoodyClient } from 'hoody-sdk'; const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com', token: process.env.HOODY_TOKEN }); await client.agent.sessions.setHoodyEnv('{id}', { enabled: false }); ``` ```json { "status": "ok" } ``` #### Responses Applied. ```json { "status": "ok" } ``` When a gate is parked, the response also carries `deferred: true` and a `note` explaining why the command has not yet been applied. | Field | Type | Description | |-------|------|-------------| | `status` | string | `ok` on success. | | `deferred` | boolean | Present and `true` when a gate is parked: the command is applied only between turns. | | `note` | string | Present alongside `deferred`: why the command has not been applied yet. | ```json { "code": "bad_request", "message": "invalid request" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. | | `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only or global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. | | `invalid_realm` | Invalid realm selector | The realm selector is malformed (not ""/"global"/a 24-hex id). | Pass a valid realm selector. | ```json { "code": "forbidden", "message": "request must arrive through the Hoody proxy" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `forbidden` | Forbidden (Source IP Guard) | The request did not come through the program's URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with `hoody agent …`. | ```json { "code": "not_found", "message": "resource not found" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. | ```json { "code": "payload_too_large", "message": "request body exceeds the configured size limit" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it, so a well-formed-but-large body is a size violation, not a JSON syntax error. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. | ```json { "code": "rate_limited", "message": "request rate limit exceeded" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. | ```json { "code": "internal_error", "message": "internal server error" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. | ```json { "code": "service_unavailable", "message": "service unavailable" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `service_unavailable` | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor `Retry-After` and retry. | ## After-compaction message ### `PUT /api/v1/agent/sessions/{id}/after-compaction` Sets the text the agent re-adds to the conversation as a user message right after the summary of every compaction (automatic, manual, or the recovery pass that follows a context overflow), so it is in place before the next model request. This is the exception to the "from the next turn" rule above: the text is re-added right after the summary, not deferred to the next turn. The text is stored and re-added byte for byte, never in the system prompt; an empty or whitespace-only text removes the message. The cap is 16384 bytes (16 KiB). The message is saved with the session, kept across restarts, and inherited by a fork; `GET /sessions/{id}` and `GET /sessions/{id}/state` show it as `after_compaction`. A Claude Code session answers `409 delegated_session` because Claude Code manages its own context; a closed session answers `404 not_found`. Active-realm-scoped. The text is stored exactly as supplied (byte for byte) and re-added as a user message after every compaction. It is never merged into the system prompt, so the model still sees it as a user turn rather than as a system instruction. #### Parameters | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `id` | path | string | Yes | The session id. | | `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: the `.hoody` project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. `POST /todos`; `todos.create` also accepts a body cwd). | | `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override selecting which on-disk `.hoody` install a stateless read/write resolves against. | | `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. | | `X-Hoody-Realm` | header | string | No | Per-request realm selector: `global` or a 24-hex id (also accepted as `?realm=`). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | | `realm` | query | string | No | Per-request realm selector, the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent): `global` or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. | #### Request body | Field | Type | Required | Description | |-------|------|----------|-------------| | `text` | string | Yes | The message text, at most 16384 bytes in UTF-8. Empty or whitespace-only removes the message. | ```bash curl -X PUT "https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com/api/v1/agent/sessions/{id}/after-compaction" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"text":"Identity: you are reviewing PRs for the payments service. Always run the unit tests before reporting back."}' ``` ```ts import { HoodyClient } from 'hoody-sdk'; const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-agent-1.node-example-1.containers.hoody.com', token: process.env.HOODY_TOKEN }); await client.agent.sessions.setAfterCompaction('{id}', { text: 'Identity: you are reviewing PRs for the payments service. Always run the unit tests before reporting back.', }); ``` ```json { "status": "ok", "bytes": 121 } ``` #### Responses Saved. ```json { "status": "ok", "bytes": 121 } ``` | Field | Type | Description | |-------|------|-------------| | `status` | string | `ok`. | | `bytes` | integer | The size of the saved text in bytes; `0` when the message was removed. | ```json { "code": "bad_request", "message": "invalid request" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `bad_request` | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. | | `realm_scope_unsupported` | Realm scope unsupported | A per-request realm header was supplied to an active-only / global-no-realm RPC, which has no realm dimension to scope. | Omit the realm header on this route, or open a session to scope by realm. | | `invalid_realm` | Invalid realm selector | The realm selector is malformed (not ""/"global"/a 24-hex id). | Pass a valid realm selector. | ```json { "code": "forbidden", "message": "request must arrive through the Hoody proxy" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `forbidden` | Forbidden (Source IP Guard) | The request did not come through the program's URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with `hoody agent …`. | ```json { "code": "not_found", "message": "resource not found" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. | The session is a Claude Code session. ```json { "code": "delegated_session", "message": "an after-compaction message is not available on a delegated session — the external agent owns its context" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `delegated_session` | Delegated session | Claude Code manages its own context, so the agent cannot add a message after its compaction. | Use a session with a Hoody model. | ```json { "code": "payload_too_large", "message": "request body exceeds the configured size limit" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `payload_too_large` | Payload too large | The request body exceeds the configured size cap (`MaxBodyBytes`). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it, so a well-formed-but-large body is a size violation, not a JSON syntax error. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. | ```json { "code": "rate_limited", "message": "request rate limit exceeded" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `rate_limited` | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the `Retry-After` header and retry; reduce the request rate. | ```json { "code": "internal_error", "message": "internal server error" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `internal_error` | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. | | `store_failed` | Agent store failed | One of the agent's own stores could not be read, parsed, locked or written, so the operation did not complete. The message names the store (for example "the agent's settings file is damaged and was left unchanged"). A damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. The agent's log (`GET /logs`) records a "store failure" entry with the store, the failure kind (io, corrupt or lock) and a path-free cause such as "open: permission denied" or "invalid JSON at offset 12"; it does not name the file's path. | ```json { "code": "restriction_unknown", "message": "the login's realm restriction could not be read — retry" } ``` | Error Code | Title | Description | Resolution | |------------|-------|-------------|------------| | `restriction_unknown` | Login restriction unknown | The agent cannot read yet which realms its login may serve (`GET /hoody/auth/status` reports restriction "unknown"), so it refuses realm-scoped requests until it can. Nothing was read or changed. | Retry after the restriction is readable. |