# Agent: Workspace Sessions

**Page:** api/agent/workspace-sessions

[Download Raw Markdown](./api/agent/workspace-sessions.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



# Agent: Workspace Sessions

These workspace-scoped session endpoints operate on a specific session within a workspace, providing session inspection, autonomous loop control, background job management, and CLI agent run capabilities. All endpoints require a `workspaceID` and `sessionID` in the path. Streaming endpoints return `text/event-stream`; the rest return JSON.

## Session Inspection

Inspect the runtime state of a session: effective permission rulesets, individual tool-call results, and live message updates.

### `GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/permissions`

Return the static merged ruleset that `PermissionNext.ask()` passes to `evaluate()` at tool-invocation time. Tool dispatch merges `agent.permission` with `session.permission` — not raw `config.permission` — because each agent definition pre-bakes defaults, YOLO overrides, agent-specific rules, and user `cfg.permission` into its own ruleset. The response includes the resolved `agent` name, the agent's pre-baked `agentRuleset`, the session-scoped `sessionRuleset`, and the `effective` ruleset (the exact array passed to `PermissionNext.evaluate()` with last-match-wins semantics).

This endpoint reflects the persisted ruleset only. Transient `"always"` approvals from `PermissionNext.reply` that have not yet been written to `session.permission` are NOT included. Pass `?agent=` to inspect the ruleset under a non-default agent.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier (project ID or workspace entry ID; 24-char lowercase hex) |
| `sessionID` | path | string | Yes | Session identifier |
| `agent` | query | string | No | Agent name to evaluate the ruleset under (defaults to the configured default agent or `build`) |



```bash
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/permissions?agent=build" \
  -H "Authorization: Bearer <token>"
```


```typescript
const result = await client.agent.workspaceSession.sessionsGetPermissions({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  agent: "build",
});
```



#### Response



```json
{
  "agent": "build",
  "agentRuleset": [
    { "permission": "bash", "pattern": "*", "action": "allow" }
  ],
  "sessionRuleset": [
    { "permission": "bash", "pattern": "rm -rf *", "action": "deny" }
  ],
  "effective": [
    { "permission": "bash", "pattern": "rm -rf *", "action": "deny" },
    { "permission": "bash", "pattern": "*", "action": "allow" }
  ]
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Session not found"
  }
}
```



### `GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/messages/{messageID}/tools/{callID}`

Return the `ToolPart` on a message that matches `callID` — without forcing the SDK consumer to fetch and scan the full message. Returns 404 if the message does not belong to this session, the message is missing, or the message has no `ToolPart` with that `callID`.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier (project ID or workspace entry ID; 24-char lowercase hex) |
| `sessionID` | path | string | Yes | Session identifier |
| `messageID` | path | string | Yes | Message identifier |
| `callID` | path | string | Yes | Tool-call ID |



```bash
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/messages/msg_01HXYZABCDEFGHJKMNPQRSTVW/tools/call_01HXYZABCDEFGHJKMNPQRSTVW" \
  -H "Authorization: Bearer <token>"
```


```typescript
const part = await client.agent.workspaceSession.sessionsGetToolCall({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  messageID: "msg_01HXYZABCDEFGHJKMNPQRSTVW",
  callID: "call_01HXYZABCDEFGHJKMNPQRSTVW",
});
```



#### Response



```json
{
  "id": "prt_01HXYZABCDEFGHJKMNPQRSTVW",
  "sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  "messageID": "msg_01HXYZABCDEFGHJKMNPQRSTVW",
  "type": "tool",
  "callID": "call_01HXYZABCDEFGHJKMNPQRSTVW",
  "tool": "bash",
  "state": {
    "status": "completed",
    "input": { "command": "ls -la" },
    "output": "total 12\ndrwxr-xr-x 3 user user 4096 ...\n",
    "title": "List directory",
    "metadata": {},
    "time": {
      "start": 1700000000000,
      "end": 1700000000100
    }
  },
  "metadata": {}
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Tool call not found for this message"
  }
}
```



### `GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/messages/{messageID}/stream`

Server-Sent Events stream of `MessageV2` updates scoped to a single `(sessionID, messageID)`. The server emits a `snapshot` event first with the current message and parts, then `part.updated`, `part.removed`, `message.updated`, and `message.removed` events as they happen on the bus. Heartbeats arrive every 30 seconds. The stream closes automatically once the assistant message becomes terminal (`message.time.completed` is set), sending a `done` event with the final message info just before close. Returns 404 if the message is missing in this session.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier (project ID or workspace entry ID; 24-char lowercase hex) |
| `sessionID` | path | string | Yes | Session identifier |
| `messageID` | path | string | Yes | Message identifier |



```bash
curl -N -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/messages/msg_01HXYZABCDEFGHJKMNPQRSTVW/stream" \
  -H "Authorization: Bearer <token>" \
  -H "Accept: text/event-stream"
```


```typescript
const stream = await client.agent.workspaceSession.sessionsStreamMessage({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  messageID: "msg_01HXYZABCDEFGHJKMNPQRSTVW",
});

for await (const event of stream) {
  console.log(event.type, event);
}
```



#### Response



```http
HTTP/1.1 200 OK
Content-Type: text/event-stream

event: snapshot
data: {"type":"snapshot","message":{"id":"msg_01HXYZ...","role":"assistant","time":{"created":1700000000000}},"parts":[]}

event: part.updated
data: {"type":"part.updated","part":{"id":"prt_01HX...","type":"text","text":"Let me think..."}}

event: done
data: {"type":"done","message":{"id":"msg_01HXYZ...","time":{"completed":1700000005000}}}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Message not found in this session"
  }
}
```



## Autonomous Loop

Drive the session's prompt loop as a repeating directive. While a loop is active, the frontend disables the chat input.

### `GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop`

Return the active loop directive for the session, or `null` if no loop is active.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier (project ID or workspace entry ID; 24-char lowercase hex) |
| `sessionID` | path | string | Yes | Session identifier |



```bash
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/loop" \
  -H "Authorization: Bearer <token>"
```


```typescript
const loop = await client.agent.workspaceSession.sessionsLoopPeek({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
});
```



#### Response



```json
{
  "prompt": "Run the test suite and fix any failures.",
  "iters": 5,
  "iter": 1,
  "started_at": 1700000000000,
  "launch_id": "launch_01HXYZABCDEFGHJKMNPQRSTVW",
  "agent": "build",
  "model": {
    "providerID": "anthropic",
    "modelID": "claude-3-5-sonnet-20241022"
  },
  "system": "You are a careful engineer."
}
```

When no loop is active, the response is the JSON literal `null`.


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Session not found"
  }
}
```



### `POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop`

Install a loop directive on the session. The session re-enters its prompt loop for `iters` iterations, synthesizing the same `prompt` as the user turn each iteration. Stop with `DELETE /loop`. Returns 409 if a loop is already active or the session is busy.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier (project ID or workspace entry ID; 24-char lowercase hex) |
| `sessionID` | path | string | Yes | Session identifier |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `prompt` | string | Yes | Loop prompt (1–8000 chars) |
| `iters` | integer | Yes | Total iterations (1–100) |



```bash
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/loop" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Run the test suite and fix any failures.",
    "iters": 5
  }'
```


```typescript
const loop = await client.agent.workspaceSession.sessionsLoopInstall({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  data: {
    prompt: "Run the test suite and fix any failures.",
    iters: 5,
  },
});
```



#### Response



```json
{
  "prompt": "Run the test suite and fix any failures.",
  "iters": 5,
  "iter": 0,
  "started_at": 1700000000000,
  "launch_id": "launch_01HXYZABCDEFGHJKMNPQRSTVW",
  "agent": "build",
  "model": {
    "providerID": "anthropic",
    "modelID": "claude-3-5-sonnet-20241022"
  },
  "system": "You are a careful engineer."
}
```


```json
{
  "data": {},
  "errors": [
    { "field": "iters", "message": "Must be between 1 and 100" }
  ],
  "success": false
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Session not found"
  }
}
```


```json
{
  "error": "A loop is already active on this session",
  "code": "loop_already_active"
}
```



### `DELETE /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/loop`

Clear the loop directive AND cancel the running prompt. Idempotent — safe to call when no loop is active.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier (project ID or workspace entry ID; 24-char lowercase hex) |
| `sessionID` | path | string | Yes | Session identifier |



```bash
curl -X DELETE "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/loop" \
  -H "Authorization: Bearer <token>"
```


```typescript
const cleared = await client.agent.workspaceSession.sessionsLoopClear({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
});
```



#### Response



```json
true
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Session not found"
  }
}
```



## Background Jobs

Manage background jobs created on a session — RSI, self-tuning, `cli_agent`, `bash`, `webfetch`. Jobs run with a `wakePolicy` (when the result is delivered back into the conversation) and a `durability` (how long they are kept).

### `GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs`

Return background jobs created on this session. Filter by status with `?status=active|completed|all`. Paginate with `?limit&cursor` — when `limit` is set and more results exist after the page, the response includes `nextCursor` (use it as `cursor` on the next call). Jobs are sorted ascending by creation time, so `nextCursor` resumes after the last seen job ID.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `status` | query | string | No | Status filter. Allowed values: `active`, `completed`, `all`. Default: `"all"`. |
| `limit` | query | integer | No | Max jobs to return per page. Defaults to no cap (returns the full filtered set). |
| `cursor` | query | string | No | Pagination cursor. Pass the previous response's `nextCursor` to fetch the next page. |
| `workspaceID` | path | string | Yes | Workspace identifier |
| `sessionID` | path | string | Yes | Session identifier |



```bash
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs?status=all&limit=20" \
  -H "Authorization: Bearer <token>"
```


```typescript
const page = await client.agent.workspaceSession.sessionsJobsList({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  status: "all",
  limit: 20,
});
```



#### Response



```json
{
  "jobs": [
    {
      "id": "job_01HXYZABCDEFGHJKMNPQRSTVW",
      "sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
      "messageID": "msg_01HXYZABCDEFGHJKMNPQRSTVW",
      "callID": "call_01HXYZABCDEFGHJKMNPQRSTVW",
      "tool": "bash",
      "status": "completed",
      "input": { "command": "npm test" },
      "progress": {
        "message": "Running tests...",
        "percent": 75
      },
      "output": "All tests passed.",
      "fullOutput": "All tests passed.\n",
      "attachments": [
        { "mime": "text/plain", "url": "https://files.hoody.com/..." }
      ],
      "metadata": {},
      "time": {
        "created": 1700000000000,
        "started": 1700000000050,
        "completed": 1700000005000
      },
      "wakePolicy": "auto",
      "durability": "session",
      "groupID": "grp_01HXYZABCDEFGHJKMNPQRSTVW",
      "timeout": 60000,
      "retries": { "max": 2, "count": 0 },
      "completionProcessed": true,
      "parentJobId": null,
      "childJobIds": [],
      "totalChildren": 0,
      "finishedChildJobIds": []
    }
  ],
  "nextCursor": "job_01HXYZABCDEFGHJKMNPQRSTVW"
}
```



### `GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}`

Fetch a single job's full info: status, progress, output, error, timing. Use this to poll a job started by RSI, self-tuning, or `cli_agent` if the SSE stream is unavailable.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier |
| `sessionID` | path | string | Yes | Session identifier |
| `jobId` | path | string | Yes | Job identifier |



```bash
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/job_01HXYZABCDEFGHJKMNPQRSTVW" \
  -H "Authorization: Bearer <token>"
```


```typescript
const job = await client.agent.workspaceSession.sessionsJobsGet({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  jobId: "job_01HXYZABCDEFGHJKMNPQRSTVW",
});
```



#### Response



```json
{
  "id": "job_01HXYZABCDEFGHJKMNPQRSTVW",
  "sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  "messageID": "msg_01HXYZABCDEFGHJKMNPQRSTVW",
  "callID": "call_01HXYZABCDEFGHJKMNPQRSTVW",
  "tool": "bash",
  "status": "running",
  "input": { "command": "npm test" },
  "progress": {
    "message": "Running test suite...",
    "percent": 42
  },
  "output": "Test 1 passed\nTest 2 passed\n",
  "fullOutput": "Test 1 passed\nTest 2 passed\nTest 3 running...\n",
  "metadata": {},
  "time": {
    "created": 1700000000000,
    "started": 1700000000050
  },
  "wakePolicy": "auto",
  "durability": "session",
  "timeout": 60000,
  "retries": { "max": 2, "count": 0 },
  "completionProcessed": false
}
```



### `GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/output`

Return the complete output buffer of a background job. `Job.Info.output` may be truncated for index payloads; this endpoint returns the full uncapped output.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier |
| `sessionID` | path | string | Yes | Session identifier |
| `jobId` | path | string | Yes | Job identifier |



```bash
curl -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/job_01HXYZABCDEFGHJKMNPQRSTVW/output" \
  -H "Authorization: Bearer <token>"
```


```typescript
const result = await client.agent.workspaceSession.sessionsJobsGetOutput({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  jobId: "job_01HXYZABCDEFGHJKMNPQRSTVW",
});
```



#### Response



```json
{
  "output": "Test 1 passed\nTest 2 passed\nTest 3 passed\nTest 4 passed\nAll tests passed.\n"
}
```



### `POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/cancel`

Request cancellation of a running background job. Already-terminal jobs are returned with their existing status; running jobs are signalled to stop.

#### Parameters



| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `workspaceID` | path | string | Yes | workspaceID path parameter |
| `sessionID` | path | string | Yes | sessionID path parameter |
| `jobId` | path | string | Yes | jobId path parameter |




```bash
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/job_01HXYZABCDEFGHJKMNPQRSTVW/cancel" \
  -H "Authorization: Bearer <token>"
```


```typescript
const result = await client.agent.workspaceSession.sessionsJobsCancel({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  jobId: "job_01HXYZABCDEFGHJKMNPQRSTVW",
});
```



#### Response



```json
{
  "job": {
    "id": "job_01HXYZABCDEFGHJKMNPQRSTVW",
    "sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
    "messageID": "msg_01HXYZABCDEFGHJKMNPQRSTVW",
    "callID": "call_01HXYZABCDEFGHJKMNPQRSTVW",
    "tool": "bash",
    "status": "cancelled",
    "input": { "command": "npm test" },
    "time": {
      "created": 1700000000000,
      "started": 1700000000050,
      "completed": 1700000005000
    },
    "wakePolicy": "auto",
    "durability": "session",
    "timeout": 60000,
    "retries": { "max": 2, "count": 0 },
    "completionProcessed": false
  },
  "message": "Job cancelled"
}
```



### `POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/cancel`

Bulk-cancel a set of jobs in one call. Each ID is processed independently — unknown IDs and any other per-job errors are reported in `failed[]` rather than aborting the batch. Already-terminal jobs are reported in `failed[]` with reason `already_terminal`. Returns 200 even when every job fails — inspect `cancelled` and `failed` to interpret the outcome.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier |
| `sessionID` | path | string | Yes | Session identifier |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `jobIds` | array of string | Yes | Job IDs to cancel. Minimum 1, maximum 1000 per call. Unknown IDs are reported in `failed`, not 404. |



```bash
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/cancel" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "jobIds": [
      "job_01HXYZABCDEFGHJKMNPQRSTVW",
      "job_02HXYZABCDEFGHJKMNPQRSTVW",
      "job_03HXYZABCDEFGHJKMNPQRSTVW"
    ]
  }'
```


```typescript
const result = await client.agent.workspaceSession.sessionsJobsCancelBulk({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  data: {
    jobIds: [
      "job_01HXYZABCDEFGHJKMNPQRSTVW",
      "job_02HXYZABCDEFGHJKMNPQRSTVW",
      "job_03HXYZABCDEFGHJKMNPQRSTVW",
    ],
  },
});
```



#### Response



```json
{
  "cancelled": 2,
  "failed": [
    {
      "jobId": "job_03HXYZABCDEFGHJKMNPQRSTVW",
      "reason": "already_terminal"
    }
  ]
}
```



### `POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/{jobId}/retry`

Re-run a job that ended in a non-success terminal state. Only `failed`, `expired`, and `cancelled` jobs are eligible; `completed` jobs return 409. Child jobs (those with `parentJobId`) cannot be retried in isolation — retry the top-level parent instead. The retry creates a NEW job with a new ID, copying the original `tool`, `input`, `messageID`, `callID`, `wakePolicy`, `durability`, and `timeout`. The original job record is left intact for audit.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier |
| `sessionID` | path | string | Yes | Session identifier |
| `jobId` | path | string | Yes | Job identifier |



```bash
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/job_01HXYZABCDEFGHJKMNPQRSTVW/retry" \
  -H "Authorization: Bearer <token>"
```


```typescript
const result = await client.agent.workspaceSession.sessionsJobsRetry({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  jobId: "job_01HXYZABCDEFGHJKMNPQRSTVW",
});
```



#### Response



```json
{
  "jobID": "job_02HXYZABCDEFGHJKMNPQRSTVW",
  "sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  "status": "queued",
  "retriedFrom": "job_01HXYZABCDEFGHJKMNPQRSTVW"
}
```


```json
{
  "error": "Job not found",
  "message": "No job with id job_01HXYZABCDEFGHJKMNPQRSTVW in this session"
}
```


```json
{
  "error": "Job is not in a retryable terminal state",
  "message": "Only failed, expired, or cancelled jobs can be retried. Current status: completed"
}
```


```json
{
  "error": "Tool factory no longer registered",
  "message": "Cannot retry: the tool that produced this job is no longer available"
}
```


```json
{
  "error": "Job is a child of another job",
  "message": "Retry the parent job (job_01HXYZABCDEFGHJKMNPQRSTVW) instead of this child"
}
```



### `POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/jobs/inject`

Mirror the agent prompt-loop's own injection step: pull terminal, unprocessed jobs and attach summary parts to the last user message (or mint one if none). Pass `jobIds: [...]` to inject a specific subset; pass `jobIds: []` to inject nothing explicitly; omit the field to drain all manual-policy unprocessed jobs. Returns 409 if the prompt loop is active.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier |
| `sessionID` | path | string | Yes | Session identifier |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `jobIds` | array of string | No | Optional subset of job IDs to inject. Omit the field to drain all manual-policy unprocessed jobs. Pass `[]` to explicitly inject nothing. Max 1000 IDs per call. |



```bash
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/jobs/inject" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "jobIds": [
      "job_01HXYZABCDEFGHJKMNPQRSTVW",
      "job_02HXYZABCDEFGHJKMNPQRSTVW"
    ]
  }'
```


```typescript
const result = await client.agent.workspaceSession.sessionsJobsInject({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  data: {
    jobIds: [
      "job_01HXYZABCDEFGHJKMNPQRSTVW",
      "job_02HXYZABCDEFGHJKMNPQRSTVW",
    ],
  },
});
```



#### Response



```json
{
  "injected": 2,
  "failed": 0,
  "message": "Injected 2 job result(s) into session context"
}
```


```json
{
  "error": "Session prompt loop is active",
  "code": "loop_already_active"
}
```



## CLI Agent

Spawn and stream an external CLI agent (gemini, codex, claude) as a side job on a session. The session's existing prompt loop is untouched — CLI agent runs are independent and their results can later be injected via `POST /jobs/inject`.

### `POST /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/cli-agent`

Run an external CLI agent against the session's working directory in the background. Returns a queued `jobID`; subscribe to `/cli-agent/runs/{jobID}/stream` for progress and final output.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier |
| `sessionID` | path | string | Yes | Session identifier |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `agent` | string | Yes | Configured CLI agent name (case-insensitive). e.g. `Gemini Flash`, `Codex`. |
| `prompt` | string | Yes | Prompt to send to the CLI agent. Max 100K chars. |
| `model` | string | No | Override the agent's default model. |
| `git` | boolean | No | Request git access (only honoured if agent's `allow_git` is true, or `codex-rw`). |
| `timeout` | integer | No | Override timeout in ms; clamped to the agent's configured ceiling. |



```bash
curl -X POST "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/cli-agent" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "Codex",
    "prompt": "Investigate the failing login test and propose a fix.",
    "model": "gpt-4o",
    "git": false,
    "timeout": 300000
  }'
```


```typescript
const run = await client.agent.workspaceSession.sessionsCliAgentStart({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  data: {
    agent: "Codex",
    prompt: "Investigate the failing login test and propose a fix.",
    model: "gpt-4o",
    git: false,
    timeout: 300000,
  },
});
```



#### Response



```json
{
  "jobID": "job_01HXYZABCDEFGHJKMNPQRSTVW",
  "sessionID": "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  "status": "queued"
}
```



### `GET /api/v1/workspaces/{workspaceID}/sessions/{sessionID}/cli-agent/runs/{jobID}/stream`

Server-Sent Events stream of progress events for a CLI agent job. The server emits a snapshot first (so late subscribers see the current state), then live events; if the job is already terminal, it emits the completion event and closes.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `workspaceID` | path | string | Yes | Workspace identifier |
| `sessionID` | path | string | Yes | Session identifier |
| `jobID` | path | string | Yes | CLI agent job identifier |



```bash
curl -N -X GET "https://api.hoody.com/api/v1/workspaces/5f9b3a2e1c8d4f7b6a5e3d2c/sessions/sess_01HXYZABCDEFGHJKMNPQRSTVW/cli-agent/runs/job_01HXYZABCDEFGHJKMNPQRSTVW/stream" \
  -H "Authorization: Bearer <token>" \
  -H "Accept: text/event-stream"
```


```typescript
const stream = await client.agent.workspaceSession.sessionsCliAgentStream({
  workspaceID: "5f9b3a2e1c8d4f7b6a5e3d2c",
  sessionID: "sess_01HXYZABCDEFGHJKMNPQRSTVW",
  jobID: "job_01HXYZABCDEFGHJKMNPQRSTVW",
});

for await (const event of stream) {
  console.log(event.type, event);
}
```



#### Response



```http
HTTP/1.1 200 OK
Content-Type: text/event-stream

event: snapshot
data: {"type":"snapshot","job":{"id":"job_01H...","status":"running","progress":{"message":"Reading files...","percent":20}}}

event: progress
data: {"type":"progress","message":"Analyzing test output","percent":55}

event: complete
data: {"type":"complete","job":{"id":"job_01H...","status":"completed","output":"Found the issue: ..."}}
```