Agent: Tasks
Section titled “Agent: Tasks”Inspect a session’s background subagent task snapshot, read a single task’s transcript, and cancel one or all background tasks on a live session. The catalog and transcripts survive session dormancy and daemon restarts because terminal projections are persisted per session. All routes are active-realm-scoped: a per-request realm header is rejected.
List and read
Section titled “List and read”GET /api/v1/agent/sessions/{id}/tasks
Section titled “GET /api/v1/agent/sessions/{id}/tasks”Returns the session’s background-subagent task catalog INLINE. No attach, no live session, no stream to observe. The catalog is the union of the live registry and the persisted per-session task store, keyed by task id with the live entry winning: the live registry evicts completed tasks at spawn time, so a finished task can leave memory while its transcript is still durable, and a live-only list would hide it. session_live reports whether a live session backed this read. A session that is not visible to this caller is a plain 404 with the standard error envelope. Use the X-Hoody-Config-Dir header to select the record store.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Session identifier. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope: the .hoody project layer, record cwd, or tool/workflow cwd. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk .hoody install a stateless read or 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 | The in:query alias of the X-Hoody-Realm header; read only when the header is absent. |
Response
Section titled “Response”{ "items": [ { "id": "task_01HK8ZQ7Y3N4D5E6F7G8H9J0PQR", "name": "research-assistant", "status": "running", "kind": "subagent", "prompt": "Search the web for recent Hoody SDK release notes and summarize the breaking changes.", "started_at": "2026-01-15T10:30:00Z", "updated_at": "2026-01-15T10:31:45Z", "tool_calls": 4, "elapsed_ms": 105000, "output_preview": "The latest Hoody SDK release introduces...", "container": "container-abc", "cwd": "/workspace/research" }, { "id": "task_01HK8ZQ7Y3N4D5E6F7G8H9J0PQS", "name": "code-reviewer", "status": "done", "kind": "subagent", "prompt": "Review the diff in pull request 42 for security issues.", "started_at": "2026-01-15T10:00:00Z", "updated_at": "2026-01-15T10:05:30Z", "tool_calls": 12, "elapsed_ms": 330000, "output_preview": "No critical issues found." } ], "meta": { "total": 2 }, "session_live": true}| Field | Type | Required | Description |
|---|---|---|---|
items | array | No | One row per background task, oldest-first by start time. Each row carries {id, name, status, kind?, prompt, started_at, updated_at?, tool_calls?, elapsed_ms?, output_preview?, container?, cwd?}. status is the status the task store holds for the row (for example running, done, failed or cancelled); it is not checked against a fixed set. |
meta | object | No | {total} — the number of rows returned. The catalog is unpaginated. |
session_live | boolean | No | Whether a live session backed this read. False for a dormant or closed session served entirely from the persisted task store. |
{ "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. |
{ "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 …. |
{ "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. |
{ "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. |
{ "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. |
| 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. |
Code samples
Section titled “Code samples”curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/tasks" \ -H "Authorization: Bearer <HOODY_TOKEN>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const catalog = await client.agent.tasks.list('session-id');GET /api/v1/agent/sessions/{id}/tasks/{tid}/transcript
Section titled “GET /api/v1/agent/sessions/{id}/tasks/{tid}/transcript”Returns a background subagent task’s transcript INLINE: entries, the task’s summary row (the same fields a client.agent.tasks.list() item carries), and the cursor state. No attach or live session is required: a task that reached a terminal state is persisted per session, so its transcript is readable for a dormant session and after a daemon restart. A task that was still running when the daemon died is not recoverable (persistence happens at terminal status) and reads 404.
source is live when served from the live registry and store when served from the persisted task store. complete reports whether this response reflects a terminal projection durably committed to that store; it is false for a task that has just finished but whose write has not landed, and false for a placeholder written by session teardown whose real terminal projection may still arrive.
The after_seq cursor is an int64 upsert-poll cursor, exclusive, except that a still-open entry is re-sent even when its seq is at or below the cursor (viewers upsert by seq). Omitting after_seq returns the whole transcript from the beginning, which is distinct from after_seq=0 (that value is exclusive and skips a closed seq-0 entry).
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Session identifier. |
tid | path | string | Yes | Task identifier. |
after_seq | query | integer | No | Exclusive int64 upsert-poll cursor: entries with seq strictly greater than it, plus any still-open entry regardless of its seq. Omit for the whole transcript (distinct from 0, which skips a closed seq-0 entry). Negative or non-integer values yield 400. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope: the .hoody project layer, record cwd, or tool/workflow cwd. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk .hoody install a stateless read or 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 | The in:query alias of the X-Hoody-Realm header; read only when the header is absent. |
Response
Section titled “Response”{ "session_id": "sess_01HK8ZQ7Y3N4D5E6F7G8H9J0PQR", "task_id": "task_01HK8ZQ7Y3N4D5E6F7G8H9J0PQX", "task": { "id": "task_01HK8ZQ7Y3N4D5E6F7G8H9J0PQX", "name": "research-assistant", "status": "done", "kind": "subagent", "prompt": "Search the web for recent Hoody SDK release notes and summarize the breaking changes.", "started_at": "2026-01-15T10:30:00Z", "updated_at": "2026-01-15T10:35:30Z", "tool_calls": 8, "elapsed_ms": 330000, "output_preview": "The latest Hoody SDK release introduces...", "container": "container-abc", "cwd": "/workspace/research" }, "entries": [ { "seq": 0, "kind": "text", "text": "Looking up recent Hoody SDK release notes." }, { "seq": 1, "kind": "tool_call", "tool": "web_search" }, { "seq": 2, "kind": "tool_result", "tool": "web_search" }, { "seq": 3, "kind": "thinking", "text": "Synthesizing results." }, { "seq": 4, "kind": "text", "text": "Summary: ..." } ], "trimmed": false, "after_seq": -1, "complete": true, "source": "store", "session_live": false}| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | No | The session the task belongs to. |
task_id | string | No | The task id, as supplied in the path. |
task | object | No | The task’s summary row, in the same shape as a client.agent.tasks.list() item. |
entries | array | No | Transcript entries {seq, kind, text?, tool?, is_error?, open?}. kind is one of text, thinking, tool_call, tool_result, usage, note. A seq of -1 is the presentation-only ...earlier output trimmed... marker, present only on a from-the-beginning read. |
trimmed | boolean | No | Whether entries were dropped (by the live ring before the snapshot, or by the store’s per-task cap). |
after_seq | integer | No | The applied cursor, echoed. -1 means the request omitted it (from the beginning). |
complete | boolean | No | Whether this response reflects a terminal projection durably committed to the task store. False for a task that just finished but whose write has not landed, and for a teardown-written placeholder whose real terminal result may still arrive. |
source | string | No | live when served from the live task registry, store when served from the persisted task store. |
session_live | boolean | No | Whether a live session backed this read. |
{ "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. |
{ "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 …. |
{ "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. |
{ "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. |
{ "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. |
| 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. |
Code samples
Section titled “Code samples”curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/tasks/{tid}/transcript" \ -H "Authorization: Bearer <HOODY_TOKEN>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const transcript = await client.agent.tasks.getTranscript('session-id', 'task-id', { after_seq: 4 });Cancel
Section titled “Cancel”POST /api/v1/agent/sessions/{id}/tasks/{tid}/cancel
Section titled “POST /api/v1/agent/sessions/{id}/tasks/{tid}/cancel”Cancels one background subagent task on a live session. Handled at the server layer so it works mid-turn. The 200 response is a gateway acknowledgement that the cancel was forwarded, not a daemon reply, and not a report that the task reached a cancelled state. Re-read the task to see the outcome.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Session identifier. |
tid | path | string | Yes | Task identifier. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope: the .hoody project layer, record cwd, or tool/workflow cwd. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk .hoody install a stateless read or 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 | The in:query alias of the X-Hoody-Realm header; read only when the header is absent. |
Response
Section titled “Response”{ "status": "ok", "note": "cancel requested for task task_01HK8ZQ7Y3N4D5E6F7G8H9J0PQX"}| Field | Type | Required | Description |
|---|---|---|---|
status | string | No | ok on success. |
note | string | No | What was requested. |
{ "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. |
{ "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 …. |
{ "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. |
{ "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. 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. |
{ "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. |
{ "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. |
{ "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. |
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. |
Code samples
Section titled “Code samples”curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/tasks/{tid}/cancel" \ -H "Authorization: Bearer <HOODY_TOKEN>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.tasks.cancel('session-id', 'task-id');POST /api/v1/agent/sessions/{id}/tasks/cancel
Section titled “POST /api/v1/agent/sessions/{id}/tasks/cancel”Cancels all background subagent tasks on a live session. The 200 response is a gateway acknowledgement that the cancel-all was forwarded; it carries no count and no list of affected tasks. Re-read the task list to see the outcome.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Session identifier. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope: the .hoody project layer, record cwd, or tool/workflow cwd. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk .hoody install a stateless read or 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 | The in:query alias of the X-Hoody-Realm header; read only when the header is absent. |
Response
Section titled “Response”{ "status": "ok", "note": "cancel-all requested for session sess_01HK8ZQ7Y3N4D5E6F7G8H9J0PQR"}| Field | Type | Required | Description |
|---|---|---|---|
status | string | No | ok on success. |
note | string | No | What was requested. |
{ "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. |
{ "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 …. |
{ "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. |
{ "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. 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. |
{ "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. |
{ "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. |
{ "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. |
Code samples
Section titled “Code samples”curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/{id}/tasks/cancel" \ -H "Authorization: Bearer <HOODY_TOKEN>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.sessions.cancelTasks('session-id');